我们假设有一个芯片,它自己能不间断的产生脉冲信号(隔一段时间产生,时间由硬件决定),它理所当然有自己的信号时序;比如说,它在每次脉冲前面都要有一个别的信号来通知它,
“嘿,老兄,一切正常,请继续保持工作”,
基于这样的约定,有人通知它,它就发脉冲;那万一在约定的时间内没人通知呢,那它就不产生脉冲了,直接“躺尸”,在这个芯片产生“躺尸”想法的时候,他会给外界发送一个信号。很常见的就是高低电平,比如本来高电平,芯片凉了,那就把一个信号线拉低变成低电平。
大家比较喜欢把这种行为,称为“喂狗”,你要是不喂了,狗就死给你看! 那我们怎么利用这种行为呢,不然有毛病啊,设计这种芯片干啥的。
很简单的例子是设备死机了,现代的软件实在是太庞大了,难保就遇到稳定性问题,如果我们能够利用这种支持电平变化的芯片,我们可以把这个芯片的reset脚(触发高低电平变化的信号线),连接到硬重启电路中。三极管、MOS管,这些能够把这种电平变化转换成功能。
整个思路就是这样: 某个芯片一直被喂狗,万一系统死机了,就没人喂狗; 狗凉了,引起电平变化;这个电平变化被硬件识别,触发整个设备重启;然后设备活了,继续坚强的生存下去。。。。。。
然后我就写了这样的代码,一个gpio被配置为中断,另一个gpio是通知它的,每次中断触发,调用工作队列,会给一个脉冲。
vendor/qcom/proprietary/devicetree-4.19/qcom/lagoon-mtp.dtsi
tpl5010 {
compatible = "qcom,tpl5010";
interrupt-parent = <&tlmm>;
interrupts = <8 0x2>;
qcom,gpio-tpl5010-int = <&tlmm 8 0x2002>;
qcom,gpio-tpl5010-done = <&tlmm 7 0x2002>;
};
kernel/msm-4.19/drivers/tpl5010/tpl5010.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int tpl5010_gpio = 0;
//static int tpl5010_enable = 1;
//extern int tpl5010_flag = 0;
struct tpl5010_data {
struct platform_device *pdev;
struct miscdevice miscdev;
int gpio;
int gpio_done;
int tpl5010_det_irq;
struct workqueue_struct *workqueue;
struct delayed_work work;
};
static ssize_t tpl5010_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int val;
val = gpio_get_value(tpl5010_gpio);
return sprintf(buf, "%d\n", val);
}
static ssize_t tpl5010_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
ssize_t ret;
unsigned int state;
pr_err("#tpl5010#,debug1,fun=%s,tpl5010_gpio=%d\n",__func__,tpl5010_gpio);
ret = kstrtouint(buf, 10, &state);
if (state) {
gpio_direction_output(tpl5010_gpio, 1);
printk("#fun=%s#,enter\n",__func__);
}else{
gpio_direction_output(tpl5010_gpio, 0);
printk("#fun=%s#,enter\n",__func__);
}
return size;
}
static DEVICE_ATTR(tpl5010_enable, S_IRUGO | S_IWUSR, tpl5010_enable_show, tpl5010_enable_store);
static const struct file_operations tpl5010_fops = {
.owner = THIS_MODULE,
};
static void tpl5010_schedwork(struct work_struct *work)
{
struct tpl5010_data *tpl5010 = container_of(to_delayed_work(work),struct tpl5010_data ,work);
gpio_direction_output(tpl5010->gpio_done, 1);
mdelay(10);
gpio_direction_output(tpl5010->gpio_done, 0);
enable_irq(tpl5010->tpl5010_det_irq);
printk("#fun=%s#,enter\n",__func__);
return;
}
static irqreturn_t gpio_tpl5010_detect_irq(int irq, void *data)
{
struct tpl5010_data *tpl5010 = (struct tpl5010_data *)data;
disable_irq_nosync(tpl5010->tpl5010_det_irq);
schedule_delayed_work(&tpl5010->work,msecs_to_jiffies(20));
printk("#fun=%s#,enter\n",__func__);
return IRQ_HANDLED;
}
static int qpnp_tpl5010_probe(struct platform_device *pdev)
{
int rc = 0;
//struct tpl5010_data *tpl5010 = platform_get_drvdata(pdev);
struct tpl5010_data *tpl5010;
struct device *this_device;
const char *tpl5010_enable_gpio = "qcom,gpio-tpl5010-int";
const char *tpl5010_enable_gpio_done = "qcom,gpio-tpl5010-done";
tpl5010 = devm_kzalloc(&pdev->dev, sizeof(*tpl5010), GFP_KERNEL);
if(!tpl5010) {
dev_err(&tpl5010->pdev->dev,"fail to alloc tpl5010 mem\n");
return -ENOMEM;
}
tpl5010->pdev = pdev;
tpl5010->miscdev.minor = MISC_DYNAMIC_MINOR;
tpl5010->miscdev.name = "ir_tpl5010";
tpl5010->miscdev.fops = &tpl5010_fops;
//creat work queue and gpio wake
tpl5010->workqueue = create_singlethread_workqueue("tpl5010_wq");
INIT_DELAYED_WORK(&tpl5010->work, tpl5010_schedwork);
if (!tpl5010->workqueue) {
dev_err(&pdev->dev,
"%s: Create WorkQueue Fail...\n", __func__);
return -ENOMEM;
}
tpl5010->gpio= of_get_named_gpio(pdev->dev.of_node, tpl5010_enable_gpio, 0);
if (tpl5010->gpio < 0) {
dev_err(&pdev->dev, "Failed to get gpio: %d\n", tpl5010->gpio);
rc = tpl5010->gpio;
goto out_destory_workqueue;
}
rc = gpio_request(tpl5010->gpio, "msm-tpl5010-enable-gpio");
if (rc < 0) {
dev_err(&pdev->dev, "Failed to request gpio: %d\n", rc);
goto out_destory_workqueue;
}
rc = gpio_direction_input(tpl5010->gpio);
pr_err("#tpl5010#,debug,fun=%s,tpl5010->gpio=%d\n",__func__,tpl5010->gpio);
if (rc < 0) {
dev_err(&pdev->dev,
"%s: Set GPIO %d as Input Fail (%d)\n", __func__,
tpl5010->gpio, rc);
goto out_free_irq;
}
tpl5010->tpl5010_det_irq = gpio_to_irq(tpl5010->gpio);
if (tpl5010->tpl5010_det_irq < 0) {
dev_err(&pdev->dev, "get tpl5010_det_irq failed\n");
rc = tpl5010->tpl5010_det_irq;
goto out_free_irq;
}
rc = request_irq(tpl5010->tpl5010_det_irq, gpio_tpl5010_detect_irq ,IRQF_TRIGGER_HIGH,
"tpl5010-irq", tpl5010);
if (rc < 0) {
dev_err(&pdev->dev, "request for tpl5010_det_irq failed: %d\n",rc);
goto out_free_irq;
}
//#define IRQF_TRIGGER_RISING 0x00000001/*指定中断触发类型:上升沿有效*/
//#define IRQF_TRIGGER_FALLING 0x00000002/*中断触发类型:下降沿有效*/
//#define IRQF_TRIGGER_HIGH 0x00000004/*指定中断触发类型:高电平有效*/
//#define IRQF_TRIGGER_LOW 0x00000008/*指定中断触发类型:低电平有效*/
//gpio done
tpl5010->gpio_done= of_get_named_gpio(pdev->dev.of_node, tpl5010_enable_gpio_done, 0);
if (tpl5010->gpio_done < 0) {
dev_err(&pdev->dev, "Failed to get gpio: %d\n", tpl5010->gpio_done);
rc = tpl5010->gpio_done;
goto out_free_irq;
}
rc = gpio_request(tpl5010->gpio_done, "msm-tpl5010-enable-gpio-done");
if (rc < 0) {
dev_err(&pdev->dev,
"%s: Set GPIO %d as Output Fail (%d)\n", __func__,
tpl5010->gpio_done, rc);
goto out_free_irq;
}
pr_err("#tpl5010#,debug,fun=%s,tpl5010->gpio_done=%d\n",__func__,tpl5010->gpio_done);
gpio_direction_output(tpl5010->gpio_done, 1);
mdelay(10);
gpio_direction_output(tpl5010->gpio_done, 0);
rc = device_create_file(&pdev->dev, &dev_attr_tpl5010_enable);
if (rc < 0) {
dev_err(&pdev->dev, "failed to create WLED enable sysfs node rc:%d\n",
rc);
}
this_device = tpl5010->miscdev.this_device;
enable_irq_wake(tpl5010->tpl5010_det_irq);
pr_err("#tpl5010#,fun=%s, probe success\n",__func__);
return rc;
out_free_irq:
if (gpio_is_valid(tpl5010->gpio))
gpio_free(tpl5010->gpio);
out_destory_workqueue:
destroy_workqueue(tpl5010 ->workqueue);
return rc;
}
static int qpnp_tpl5010_remove(struct platform_device *pdev)
{
struct tpl5010_data *tpl5010 = platform_get_drvdata(pdev);
destroy_workqueue(tpl5010 ->workqueue);
return 0;
}
const struct of_device_id qpnp_tpl5010_match_table[] = {
{ .compatible = "qcom,tpl5010",},
{ },
};
static struct platform_driver tpl5010_driver = {
.driver = {
.name = "qcom,tpl5010",
.of_match_table = qpnp_tpl5010_match_table,
},
.probe = qpnp_tpl5010_probe,
.remove = qpnp_tpl5010_remove,
};
static int tpl5010_init(void)
{
return platform_driver_register(&tpl5010_driver);
}
static void tpl5010_exit(void)
{
platform_driver_unregister(&tpl5010_driver);
return;
}
module_init(tpl5010_init);
module_exit(tpl5010_exit);
MODULE_LICENSE("GPL");
这个代码其实很简单,就一个gpio配置中断,然后中断触发会调用配置队列, 工作队列里面enable中断之前,会让done信号给个脉冲,这样写的原因是根据芯片的时序图来的。 还有一个关键点是probe函数里面,先给了一个脉冲,这个也是芯片手册中的资料,其他没啥特别的。
用以下命令手动触发设备死机之后,蓝线(reset)会拉低,这个变化被硬件电路识别到之后,会触发设备重启。
echo c > /proc/sysrq-trigger