| /* |
| * Copyright (C) 2013-2014 Dropcam |
| * All rights reserved. |
| * |
| * 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/module.h> |
| #include <linux/moduleparam.h> |
| #include <linux/types.h> |
| #include <linux/timer.h> |
| #include <linux/miscdevice.h> |
| #include <linux/watchdog.h> |
| #include <linux/fs.h> |
| #include <linux/init.h> |
| #include <linux/platform_device.h> |
| #include <linux/interrupt.h> |
| #include <linux/clk.h> |
| #include <linux/slab.h> |
| #include <linux/workqueue.h> |
| |
| #include <asm/uaccess.h> |
| #include <asm/io.h> |
| |
| #include <mach/init-dropcam.h> |
| |
| #define DC_WDT_ENABLE_GPIO 12 |
| #define DC_WDT_RESET_GPIO 54 |
| |
| #define DC_WDT_HALF_CYCLE_TIME ((HZ * 550)/1000) |
| |
| static struct workqueue_struct *tps3813k33_wdt_kicker; |
| |
| struct dropcam_tps3813k33_info { |
| int last_toggle_value; |
| struct timer_list * ptimer; |
| struct work_struct tps3813k33_work; |
| }; |
| |
| static struct timer_list dropcam_tps3813k33_timer; |
| |
| static void dropcam_tps3813k33_toggle_worker(struct work_struct *work) { |
| struct dropcam_tps3813k33_info *wdt_info = container_of(work, struct dropcam_tps3813k33_info, tps3813k33_work); |
| |
| wdt_info->last_toggle_value = !wdt_info->last_toggle_value; |
| |
| ambarella_gpio_set(DC_WDT_RESET_GPIO, wdt_info->last_toggle_value); |
| |
| ambarella_gpio_set(DC_WDT_ENABLE_GPIO, 1); |
| |
| mod_timer(wdt_info->ptimer, jiffies + DC_WDT_HALF_CYCLE_TIME); |
| } |
| |
| static void dropcam_tps3813k33_toggle_timer_handler(unsigned long data) |
| { |
| struct dropcam_tps3813k33_info *wdt_info = |
| (struct dropcam_tps3813k33_info *)data; |
| |
| queue_work(tps3813k33_wdt_kicker, &wdt_info->tps3813k33_work); |
| } |
| |
| static const struct file_operations dropcam_tps3813k33_fops = { |
| .owner = THIS_MODULE, |
| }; |
| |
| static int __devinit dropcam_tps3813k33_probe(struct platform_device *pdev) |
| { |
| int errorCode = 0; |
| struct dropcam_tps3813k33_info *pinfo; |
| |
| if (!((AMBARELLA_BOARD_TYPE(system_rev) == AMBARELLA_BOARD_TYPE_VENDOR) && |
| ((AMBARELLA_BOARD_REV(system_rev) == DROPCAM_BOARD_REV_CROWN_ROYAL) || |
| (AMBARELLA_BOARD_REV(system_rev) == DROPCAM_BOARD_REV_QUARTZ)))) { |
| printk(KERN_WARNING "dropcam_tps3813k33_probe: Wrong system type, not loading\n"); |
| errorCode = -ENXIO; |
| goto dropcam_tps3813k33_no_dev; |
| } |
| |
| pinfo = kzalloc(sizeof(struct dropcam_tps3813k33_info), GFP_KERNEL); |
| if (pinfo == NULL) { |
| printk(KERN_ERR "dropcam_tps3813k33_probe: out of memory\n"); |
| errorCode = -ENOMEM; |
| goto dropcam_tps3813k33_no_pinfo; |
| } |
| |
| pinfo->last_toggle_value = 0; |
| |
| pinfo->ptimer = &dropcam_tps3813k33_timer; |
| init_timer(pinfo->ptimer); |
| |
| pinfo->ptimer->function = dropcam_tps3813k33_toggle_timer_handler; |
| pinfo->ptimer->data = (unsigned long)pinfo; |
| |
| pinfo->ptimer->expires = jiffies + DC_WDT_HALF_CYCLE_TIME; |
| |
| tps3813k33_wdt_kicker = create_workqueue("tps3813k33_wdt_kicker"); |
| INIT_WORK(&pinfo->tps3813k33_work, dropcam_tps3813k33_toggle_worker); |
| |
| printk(KERN_INFO "Enabling TPS3813 HW WDT\n"); |
| ambarella_gpio_config(DC_WDT_ENABLE_GPIO, GPIO_FUNC_SW_OUTPUT); |
| |
| ambarella_gpio_config(DC_WDT_RESET_GPIO, GPIO_FUNC_SW_OUTPUT); |
| ambarella_gpio_set(DC_WDT_RESET_GPIO, pinfo->last_toggle_value); |
| |
| add_timer(pinfo->ptimer); |
| |
| goto dropcam_tps3813k33_ok; |
| |
| dropcam_tps3813k33_no_pinfo: |
| dropcam_tps3813k33_no_dev: |
| dropcam_tps3813k33_ok: |
| return errorCode; |
| } |
| |
| |
| static int __devexit dropcam_tps3813k33_remove(struct platform_device *pdev) |
| { |
| struct dropcam_tps3813k33_info *pinfo; |
| int errorCode = 0; |
| |
| pinfo = platform_get_drvdata(pdev); |
| |
| printk(KERN_INFO "Disabling TPS3813 HW WDT\n"); |
| |
| ambarella_gpio_set(DC_WDT_ENABLE_GPIO, 0); |
| |
| del_timer(&dropcam_tps3813k33_timer); |
| |
| if (pinfo) |
| { |
| kfree(pinfo); |
| } |
| |
| return errorCode; |
| } |
| |
| static int __devexit dropcam_tps3813k33_shutdown(struct platform_device *pdev) |
| { |
| struct dropcam_tps3813k33_info *pinfo; |
| |
| pinfo = platform_get_drvdata(pdev); |
| |
| printk(KERN_INFO "Kicking TPS3813 HW WDT before reboot\n"); |
| |
| ambarella_gpio_set(DC_WDT_RESET_GPIO, 1); |
| ambarella_gpio_set(DC_WDT_RESET_GPIO, 0); |
| |
| del_timer(&dropcam_tps3813k33_timer); |
| |
| if (pinfo) |
| { |
| kfree(pinfo); |
| } |
| |
| return 0; |
| } |
| |
| |
| static int dropcam_tps3813k33_release(struct platform_device *pdev) |
| { |
| return 0; |
| } |
| |
| static struct platform_driver dropcam_tps3813k33_driver = { |
| .probe = dropcam_tps3813k33_probe, |
| .remove = __devexit_p(dropcam_tps3813k33_remove), |
| .shutdown = __devexit_p(dropcam_tps3813k33_shutdown), |
| .driver = { |
| .owner = THIS_MODULE, |
| .name = "dropcam-tps3813k33", |
| }, |
| }; |
| |
| static struct platform_device dropcam_tps3813k33_device = { |
| .name = "dropcam-tps3813k33", |
| .id = 0, |
| .dev = { |
| .release = dropcam_tps3813k33_release, |
| }, |
| }; |
| |
| static int __init dropcam_tps3813k33_init(void) |
| { |
| platform_device_register(&dropcam_tps3813k33_device); |
| return platform_driver_register(&dropcam_tps3813k33_driver); |
| } |
| |
| static void __exit dropcam_tps3813k33_exit(void) |
| { |
| platform_device_unregister(&dropcam_tps3813k33_device); |
| platform_driver_unregister(&dropcam_tps3813k33_driver); |
| } |
| |
| core_initcall(dropcam_tps3813k33_init); |
| module_exit(dropcam_tps3813k33_exit); |
| |
| MODULE_DESCRIPTION("Dropcam TPS3813K33 Watch Dog Timer"); |
| MODULE_AUTHOR("Dan Girellini, <dan@dropcam.com>"); |
| MODULE_LICENSE("GPL"); |