| /* |
| * Copyright (c) 2013 Qualcomm Atheros, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| //#include <linux/config.h> |
| #include <linux/device.h> |
| #include <linux/platform_device.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/resource.h> |
| |
| #include <linux/console.h> |
| #include <linux/proc_fs.h> |
| #include <asm/serial.h> |
| |
| #include <linux/tty.h> |
| #include <linux/time.h> |
| #include <linux/serial_core.h> |
| #include <linux/serial.h> |
| #include <linux/serial_8250.h> |
| #include <linux/miscdevice.h> |
| #include <linux/ctype.h> |
| |
| #include <asm/mach-atheros/atheros.h> |
| #include <asm/delay.h> |
| |
| #define ATH_DEFAULT_WD_TMO (20ul * USEC_PER_SEC) |
| |
| #define wddbg(junk, ...) |
| |
| extern uint32_t ath_ahb_freq; |
| |
| #define ATH_TEST_TIMER_STR_MAX 10 |
| char wdtbuf[ATH_TEST_TIMER_STR_MAX], timerbuf[ATH_TEST_TIMER_STR_MAX]; |
| |
| char * |
| ath_str_to_u(const char *s, uint32_t *u) |
| { |
| int i; |
| |
| *u = 0; |
| for (i = 0; isdigit(s[i]) && i < ATH_TEST_TIMER_STR_MAX; i ++) { |
| *u = (*u * 10) + (s[i] - '0'); |
| } |
| return s + i; |
| } |
| |
| int ath_wdt_read(char *buf, char **start, off_t offset, int count, int *eof, void *data) |
| { |
| strncpy(buf, wdtbuf, sizeof(wdtbuf)); |
| return strlen(wdtbuf); |
| } |
| |
| int ath_wdt_write(struct file *file, const char *buf, unsigned long count, void *data) |
| { |
| uint32_t tmp; |
| const char *p; |
| |
| strncpy(wdtbuf, buf, sizeof(wdtbuf)); |
| |
| ath_reg_wr(RST_WATCHDOG_TIMER_CONTROL_ADDRESS, |
| RST_WATCHDOG_TIMER_CONTROL_ACTION_SET(ATH_WD_ACT_NONE)); |
| |
| p = ath_str_to_u(buf, &tmp); |
| |
| if (tmp > (~0u / ath_ref_freq)) { |
| printk("Value (%u) greater than max (%u)\n", |
| tmp, (~0u / ath_ref_freq)); |
| return -EINVAL; |
| } |
| |
| ath_reg_wr(RST_WATCHDOG_TIMER_ADDRESS, tmp * ath_ref_freq); |
| |
| switch (*p) { |
| case 'n': tmp = ATH_WD_ACT_NMI; break; |
| case 'i': tmp = ATH_WD_ACT_GP_INTR; break; |
| case 'r': tmp = ATH_WD_ACT_RESET; break; |
| case 'c': tmp = ATH_WD_ACT_NONE; break; |
| default: return -EINVAL; |
| } |
| |
| ath_reg_wr(RST_WATCHDOG_TIMER_CONTROL_ADDRESS, |
| RST_WATCHDOG_TIMER_CONTROL_ACTION_SET(tmp)); |
| |
| return strlen(wdtbuf); |
| } |
| |
| |
| int ath_timer_read(char *buf, char **start, off_t offset, int count, int *eof, void *data) |
| { |
| strncpy(buf, timerbuf, sizeof(timerbuf)); |
| return strlen(timerbuf); |
| } |
| |
| irqreturn_t ath_timer_isr(int cpl, void *dev_id) |
| { |
| |
| ath_reg_rmw_clear(ATH_MISC_INT_STATUS, |
| RST_MISC_INTERRUPT_MASK_TIMER2_MASK_MASK); |
| |
| printk("%s: invoked 0x%x 0x%x\n", __func__, |
| ath_reg_rd(RST_GENERAL_TIMER2_RELOAD_ADDRESS), |
| ath_reg_rd(RST_MISC_INTERRUPT_MASK_ADDRESS)); |
| |
| return IRQ_HANDLED; |
| } |
| |
| int ath_timer_write(struct file *file, const char *buf, unsigned long count, void *data) |
| { |
| static int ret = 1; |
| uint32_t tmp; |
| |
| strncpy(timerbuf, buf, sizeof(timerbuf)); |
| |
| ath_str_to_u(buf, &tmp); |
| if (tmp > (~0u / ath_ref_freq)) { |
| printk("Value (%u) greater than max (%u)\n", |
| tmp, (~0u / ath_ref_freq)); |
| return -EINVAL; |
| } |
| |
| ath_reg_wr(RST_GENERAL_TIMER2_RELOAD_ADDRESS, tmp * ath_ref_freq); |
| |
| if (ret && (ret = request_irq(ATH_MISC_IRQ_TIMER2, |
| ath_timer_isr, |
| IRQF_DISABLED, "TEST: Timer", NULL))) { |
| printk("%s: timer request_irq %d\n", __func__, ret); |
| } |
| |
| ath_reg_rmw_set(RST_MISC_INTERRUPT_MASK_ADDRESS, |
| RST_MISC_INTERRUPT_MASK_TIMER2_MASK_MASK); |
| printk("%s: invoked 0x%x 0x%x\n", __func__, |
| ath_reg_rd(RST_GENERAL_TIMER2_RELOAD_ADDRESS), |
| ath_reg_rd(RST_MISC_INTERRUPT_MASK_ADDRESS)); |
| return strlen(timerbuf); |
| } |
| |
| irqreturn_t ath_wdt_isr(int cpl, void *dev_id) |
| { |
| printk("%s: invoked\n", __func__); |
| |
| return IRQ_HANDLED; |
| } |
| |
| int ath_timer_init(void) |
| { |
| struct proc_dir_entry *proc; |
| int ret; |
| |
| proc = create_proc_entry("wdt", 0, NULL); |
| if (!proc) { |
| printk("create proc entry for wdt failed\n"); |
| return -EINVAL; |
| } |
| |
| proc->read_proc = ath_wdt_read; |
| proc->write_proc = ath_wdt_write; |
| proc->size = sizeof(wdtbuf); |
| printk("------------ /proc/wdt created\n"); |
| |
| if ((ret = request_irq(ATH_MISC_IRQ_WATCHDOG, |
| ath_wdt_isr, |
| 0, "TEST: WDT", NULL))) { |
| printk("%s: wdt request_irq %d\n", __func__, ret); |
| return ret; |
| } |
| |
| proc = create_proc_entry("timer", 0, NULL); |
| if (!proc) { |
| printk("create proc entry for timer failed\n"); |
| return -EINVAL; |
| } |
| |
| proc->read_proc = ath_timer_read; |
| proc->write_proc = ath_timer_write; |
| proc->size = sizeof(timerbuf); |
| printk("------------ /proc/timer created\n"); |
| |
| return 0; |
| } |
| |
| late_initcall(ath_timer_init); |