linux 驱动 demo---笔记

#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 的使用以及驱动一些常见宏的使用----原本不需要这么多的东西。看门狗的功能未实际验证,代码仅编译通过。

点击查看该看门狗 datasheet:

关键字:TPL5010 driver, demo

发布了28 篇原创文章 · 获赞 23 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/q1075355798/article/details/103294357