#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/sysctl.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/suspend.h>
#include <linux/io.h>
#include <asm/ioctl.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/ktime.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of_gpio.h>
#include <linux/uaccess.h>
#include <linux/notifier.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/miscdevice.h>
#define FEED_DOG_TIMER
typedef struct {
struct device *dev;
struct mutex wdt_lock;
struct work_struct wdt_wq;
struct timer_list wdt_timer;
int wake_up_gp; //wake up gpio, irq;
int feed_dog_gp; // done gpio for feeding watchdog before the next rising edge of wake up gpio
int irq;
int timer_flag;
}tpl5010_wdt_data;
static ssize_t tpl5010_wdt_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int val;
unsigned int ret;
tpl5010_wdt_data *priv = dev_get_drvdata(dev);
ret = kstrtouint(buf, 16, &val);
if (!priv || ret != 0) {
return ret;
}
switch (val) {
//stop feeding watchdog
case 0:
#ifdef FEED_DOG_TIMER
del_timer(&priv->wdt_timer);
#else
disabled_irq_nosync(priv->irq);
#endif
break;
default:
break;
}
return 0;
}
static DEVICE_ATTR(tp5010_wdt, 0644, NULL, tpl5010_wdt_store);
static void tpl5010_wdt_work(struct work_struct *work)
{
tpl5010_wdt_data *priv =
container_of(work, tpl5010_wdt_data, wdt_wq);
gpio_set_value_cansleep(priv->feed_dog_gp, 1);
msleep(1000); //mdelay(1000);
gpio_set_value_cansleep(priv->feed_dog_gp, 0);
msleep(1000);
gpio_set_value_cansleep(priv->feed_dog_gp, 1);
msleep(1000);
gpio_set_value_cansleep(priv->feed_dog_gp, 0);
msleep(1000);
gpio_set_value_cansleep(priv->feed_dog_gp, 1);
return;
}
#ifdef FEED_DOG_TIMER
static void tpl5010_wdt_timer_func(unsigned long arg)
{
tpl5010_wdt_data *priv = (tpl5010_wdt_data *)arg;
schedule_work(&priv->wdt_wq);
mod_timer(&priv->wdt_timer, jiffies + ((HZ * 500)/10) );
return;
}
#else
static irqreturn_t tpl5010_wdt_irq(int irq, void *dev_id)
{
tpl5010_wdt_data *priv = dev_id;
schedule_work(&priv->wdt_wq);
return IRQ_HANDLED;
}
#endif
static int tpl5010_wdt_probe(struct platform_device *pdev)
{
int ret = 0;
struct device_node *node = NULL;
tpl5010_wdt_data *priv = NULL;;
node = pdev->dev.of_node;
priv = devm_kzalloc(&pdev->dev,sizeof(tpl5010_wdt_data),GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->feed_dog_gp = of_get_named_gpio(node, "feed-dog-gpio", 0);
if (priv->feed_dog_gp < 0) {
dev_err(&pdev->dev, "%s: no feed_dog_gp provided\n", __func__);
return -EINVAL;
} else {
dev_info(&pdev->dev, "%s: feed_dog_gp provided ok\n", __func__);
}
if (gpio_is_valid(priv->feed_dog_gp)) {
ret = devm_gpio_request_one(&pdev->dev, priv->feed_dog_gp,
GPIOF_OUT_INIT_HIGH, "feed_dog_gp");
if (ret){
dev_err(&pdev->dev, "%s: feed_dog_gpio request failed\n", __func__);
return -EINVAL;
}else{
dev_info(&pdev->dev, "%s: feed_dog_gpio request succeed. %d \n", __func__,priv->feed_dog_gp);
}
} else {
dev_err(&pdev->dev,"%s: feed_dog_gp [%d] is invalid!!\n",__func__,priv->feed_dog_gp);
return -EINVAL;
}
priv->wake_up_gp = of_get_named_gpio(node, "wake_up_gp", 0);
if (priv->wake_up_gp < 0) {
dev_err(&pdev->dev, "%s: no wake_up_gp provided\n", __func__);
return -EINVAL;
} else {
dev_info(&pdev->dev, "%s: wake_up_gp provided ok\n", __func__);
}
if (gpio_is_valid(priv->wake_up_gp)) {
ret = devm_gpio_request_one(&pdev->dev, priv->wake_up_gp,
GPIOF_IN, "wake_up_gp");
if (ret){
dev_err(&pdev->dev, "%s: wake_up_gp request failed\n", __func__);
return -EINVAL;
}else{
dev_info(&pdev->dev, "%s: wake_up_gp request succeed. %d \n", __func__,priv->wake_up_gp);
}
} else {
dev_err(&pdev->dev,"%s: wake_up_gp [%d] is invalid!!\n",__func__,priv->wake_up_gp);
return -EINVAL;
}
priv->irq = gpio_to_irq(priv->wake_up_gp);
if (priv->irq < 0) {
return -EINVAL;
}
mutex_init(&priv->wdt_lock);
INIT_WORK(&priv->wdt_wq, tpl5010_wdt_work);
/*
* Two ways to feed wdt:
* @1: by interrupt;
* @2: by timer;
*/
#ifdef FEED_DOG_TIMER
init_timer(&priv->wdt_timer);
priv->wdt_timer.function = tpl5010_wdt_timer_func;
priv->wdt_timer.data = (unsigned long)priv;
priv->wdt_timer.expires = jiffies +(( HZ * 500)/10);
add_timer(&priv->wdt_timer);
#else
ret = devm_request_threaded_irq(&pdev->dev, priv->irq, NULL,
tpl5010_wdt_irq, IRQF_ONESHOT|IRQF_TRIGGER_RISING,
dev_name(&pdev->dev), priv);
#endif
ret = device_create_file(&pdev->dev, &dev_attr_tp5010_wdt);
platform_set_drvdata(pdev, priv);
return ret;
}
static int misc_open(struct inode *inode, struct file *file)
{
printk("misc open\n");
return 0;
}
static long
misc_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret = 0;
switch (cmd) {
case 0:
pr_info("%s: case 0\n",__func__);
break;
case 1:
pr_info("%s: case 1\n",__func__);
break;
default:
pr_info("%s: unknow mode\n",__func__);
ret = -1;
}
return ret;
}
static int tpl5010_wdt_remove(struct platform_device *pdev)
{
tpl5010_wdt_data *priv = platform_get_drvdata(pdev);
if (gpio_is_valid(priv->feed_dog_gp))
devm_gpio_free(&pdev->dev, priv->feed_dog_gp);
return 0;
}
static struct of_device_id tpl5010_wdt_table[] = {
{ .compatible = "ls,tpl5010_wdt" },
{},
};
static struct platform_driver tpl5010_wdt_driver = {
.probe = tpl5010_wdt_probe,
.remove = tpl5010_wdt_remove,
.driver = {
.name = "ls,tpl5010_wdt",
.owner = THIS_MODULE,
.of_match_table = tpl5010_wdt_table,
},
};
static const struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.unlocked_ioctl = misc_unlocked_ioctl,
};
static struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "tpl5010-wdt",
.fops = &misc_fops,
};
static int __init tpl5010_wdt_init(void)
{
int ret = 0;
ret = misc_register(&misc_dev);
if (ret)
{
pr_err("misc_register error\n");
return ret;
}
return platform_driver_register(&tpl5010_wdt_driver);
}
static void __exit tpl5010_wdt_exit(void)
{
platform_driver_unregister(&tpl5010_wdt_driver);
}
module_init(tpl5010_wdt_init);
module_exit(tpl5010_wdt_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxxx");
备注:
原本是有一个外部硬件看门狗 TPL5010 需要控制,于是写了这么一点代码。该狗的控制很简单,就是该狗每隔一定的时间,就会通过 GPIO-A (称之为 wake up gpio)发送探测脉冲到MCU 主控端,MCU 主控端需要在下一个 wake up 波形上边沿来之前,通过GPIO-B(称之为 done gpio)回一个脉冲,也就是喂狗。如果不喂狗,则 TPL5010 会通过和MCU相连的 RST 引脚,复位MCU系统。
这里搞了一个 demo,里面包含了 gpio 的使用、中断的使用、sysfs创建、timer 的使用以及驱动一些常见宏的使用----原本不需要这么多的东西。看门狗的功能未实际验证,代码仅编译通过。
关键字:TPL5010 driver, demo