1. 程式人生 > >linux下 gpio ma63xx_wdt.c驅動

linux下 gpio ma63xx_wdt.c驅動

  專案中需要實現watchdog功能,研究了下系統中的watchdog。硬體watchdog使用晶片max6371,看晶片資料,結合硬體設計,發現需要控制的訊號有兩個,是系統的GPIO,GPIO1是使能watchdog的訊號,連線到max6371的set2引腳 ,GPIO2是喂狗的訊號,連線到max6371的wdi引腳。如下圖:

     系統使用linux核心版本4.9.75,核心配置CONFIG_MAX63XX_WATCHDOG=y 後,系統下未生成/dev/watchdog。 走讀程式碼發下max63xx_wdt.c原始碼和硬體設計不符,不支援GPIO,原始碼註釋中也有說明:

 * This driver assumes the watchdog pins are memory mapped (as it is
 * the case for the Arcom Zeus). Should it be connected over GPIOs or
 * another interface, some abstraction will have to be introduced.

網上找了一些資料,有支援GPIO的補丁,參考補丁連結:https://lore.kernel.org/patchwork/patch/571616/,支援了GPIO的配置後,發現還是未生成/dev/watchdog 裝置檔案。經過除錯發現,沒有進入probe函式,所以沒有註冊。走讀程式碼發現,驅動是以platform driver形式註冊了驅動,但是沒有註冊platform device,因此不能進入probe。加上platform device add之後,可以生成/dev/watchdog了。

補丁:

diff -ruNa a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c
--- a/drivers/watchdog/max63xx_wdt.c    2018-11-20 18:17:19.969287012 +0800
+++ b/drivers/watchdog/max63xx_wdt.c    2018-11-20 17:43:32.605287012 +0800
@@ -20,6 +20,7 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/watchdog.h>
+#include <linux/gpio.h>
 #include <linux/bitops.h>
 #include <linux/platform_device.h>
 #include <linux/spinlock.h>
@@ -28,6 +29,7 @@
 
 #define DEFAULT_HEARTBEAT 60
 #define MAX_HEARTBEAT     60
+#define MAX63XX_DISABLED        3 
 
 static unsigned int heartbeat = DEFAULT_HEARTBEAT;
 static bool nowayout  = WATCHDOG_NOWAYOUT;
@@ -43,10 +45,25 @@
 
 static int nodelay;
 
+ /* struct max63xx_platform_data - MAX63xx connectivity info
+ * @wdi:    Watchdog Input GPIO number.
+ * @set0:    Watchdog SET0 GPIO number.
+ * @set1:    Watchdog SET1 GPIO number.
+ * @set2:    Watchdog SET2 GPIO number.
+ */
+struct max63xx_platform_data {
+    unsigned int wdi;
+    unsigned int set0, set1, set2;
+};
+
+static struct max63xx_platform_data st_max63xx_gpio_data = {382, 0, 0, 387};
+
 struct max63xx_wdt {
     struct watchdog_device wdd;
+    struct platform_device *pdev;
     const struct max63xx_timeout *timeout;
-
+    /* platform settings e.g. GPIO */
+    struct max63xx_platform_data *pdata;
     /* memory mapping */
     void __iomem *base;
     spinlock_t lock;
@@ -56,6 +73,9 @@
     void (*set)(struct max63xx_wdt *wdt, u8 set);
 };
 
+
+static struct max63xx_wdt *max63xx_wdt_data = NULL; 
+
 /*
  * The timeout values used are actually the absolute minimum the chip
  * offers. Typical values on my board are slightly over twice as long
@@ -116,129 +136,121 @@
     return NULL;
 }
 
-static int max63xx_wdt_ping(struct watchdog_device *wdd)
-{
-    struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
-
-    wdt->ping(wdt);
-    return 0;
-}
-
-static int max63xx_wdt_start(struct watchdog_device *wdd)
-{
-    struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
-
-    wdt->set(wdt, wdt->timeout->wdset);
-
-    /* check for a edge triggered startup */
-    if (wdt->timeout->tdelay == 0)
-        wdt->ping(wdt);
-    return 0;
-}
-
-static int max63xx_wdt_stop(struct watchdog_device *wdd)
-{
-    struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
-
-    wdt->set(wdt, MAX6369_WDSET_DISABLED);
-    return 0;
-}
-
-static const struct watchdog_ops max63xx_wdt_ops = {
-    .owner = THIS_MODULE,
-    .start = max63xx_wdt_start,
-    .stop = max63xx_wdt_stop,
-    .ping = max63xx_wdt_ping,
-};
 
 static const struct watchdog_info max63xx_wdt_info = {
     .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
     .identity = "max63xx Watchdog",
 };
 
-static void max63xx_mmap_ping(struct max63xx_wdt *wdt)
-{
-    u8 val;
-
-    spin_lock(&wdt->lock);
-
-    val = __raw_readb(wdt->base);
-
-    __raw_writeb(val | MAX6369_WDI, wdt->base);
-    __raw_writeb(val & ~MAX6369_WDI, wdt->base);
-
-    spin_unlock(&wdt->lock);
-}
-
-static void max63xx_mmap_set(struct max63xx_wdt *wdt, u8 set)
-{
-    u8 val;
-
-    spin_lock(&wdt->lock);
-
-    val = __raw_readb(wdt->base);
-    val &= ~MAX6369_WDSET;
-    val |= set & MAX6369_WDSET;
-    __raw_writeb(val, wdt->base);
-
-    spin_unlock(&wdt->lock);
-}
-
-static int max63xx_mmap_init(struct platform_device *p, struct max63xx_wdt *wdt)
-{
-    struct resource *mem = platform_get_resource(p, IORESOURCE_MEM, 0);
-
-    wdt->base = devm_ioremap_resource(&p->dev, mem);
-    if (IS_ERR(wdt->base))
-        return PTR_ERR(wdt->base);
+static int max63xx_gpio_ping(struct watchdog_device *wdev) 
+{ 
+        max63xx_wdt_data = watchdog_get_drvdata(wdev); 
+        
+        gpio_set_value(max63xx_wdt_data->pdata->wdi, 1); 
+        gpio_set_value(max63xx_wdt_data->pdata->wdi, 0); 

+        return 0; 
+} 

+static inline void max63xx_gpio_set(struct watchdog_device *wdev, u8 set) 
+{ 
+        max63xx_wdt_data = watchdog_get_drvdata(wdev); 

+        gpio_set_value(max63xx_wdt_data->pdata->set2, 1); 
+} 

+static int max63xx_gpio_start(struct watchdog_device *wdev) 
+{ 
+        max63xx_wdt_data = watchdog_get_drvdata(wdev); 
+        
+        gpio_set_value(max63xx_wdt_data->pdata->set2, 1); 
+        
+        /* Check for an edge triggered startup */ 
+        if (max63xx_wdt_data->timeout->tdelay == 0) 
+                max63xx_gpio_ping(wdev); 
+        return 0; 
+} 

+static int max63xx_gpio_stop(struct watchdog_device *wdev) 
+{ 
+        max63xx_wdt_data = watchdog_get_drvdata(wdev); 
+        gpio_set_value(max63xx_wdt_data->pdata->set2, 0); 
+     return 0; 
+} 

+static const struct watchdog_ops max63xx_wdt_ops = {
+    .owner = THIS_MODULE,
+    .start = max63xx_gpio_start,
+    .stop = max63xx_gpio_stop,
+    .ping = max63xx_gpio_ping,
+};    
+ static int max63xx_gpio_init(struct platform_device *p, struct max63xx_wdt *wdt)
+ {
+    int err;
 
-    spin_lock_init(&wdt->lock);
+    pinctrl_free_gpio(wdt->pdata->wdi);
+    err = gpio_request_one( wdt->pdata->wdi, GPIOF_OUT_INIT_LOW, "max63xx_wdt WDI");
+    if (err)
+        return err;
 
-    wdt->ping = max63xx_mmap_ping;
-    wdt->set = max63xx_mmap_set;
+    pinctrl_free_gpio(wdt->pdata->set2);
+    err = gpio_request_one(wdt->pdata->set2, GPIOF_OUT_INIT_LOW, "max63xx_wdt SET2");
+    if (err)
+        return err;
+    
+    wdt->ping = max63xx_gpio_ping;
+    wdt->set = max63xx_gpio_set;
     return 0;
 }
-
 static int max63xx_wdt_probe(struct platform_device *pdev)
 {
-    struct max63xx_wdt *wdt;
-    struct max63xx_timeout *table;
-    int err;
-
-    wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
-    if (!wdt)
-        return -ENOMEM;
-
-    table = (struct max63xx_timeout *)pdev->id_entry->driver_data;
+     struct max63xx_wdt *wdt;
+     struct max63xx_timeout *table;
+     int err;

+     wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+     if (!wdt)
+         return -ENOMEM;

+     table = (struct max63xx_timeout *)pdev->id_entry->driver_data;
+     if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
+         heartbeat = DEFAULT_HEARTBEAT;
+
+     wdt->timeout = max63xx_select_timeout(table, heartbeat);
+     if (!wdt->timeout) {
+         dev_err(&pdev->dev, "unable to satisfy %ds heartbeat request\n",
+             heartbeat);
+         return -EINVAL;
+     }
 
+    wdt->pdata = dev_get_platdata(&pdev->dev);
     if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
         heartbeat = DEFAULT_HEARTBEAT;
-
     wdt->timeout = max63xx_select_timeout(table, heartbeat);
     if (!wdt->timeout) {
         dev_err(&pdev->dev, "unable to satisfy %ds heartbeat request\n",
             heartbeat);
         return -EINVAL;
     }
+    /* GPIO or memory mapped? */
 
-    err = max63xx_mmap_init(pdev, wdt);
+    wdt->pdata = &st_max63xx_gpio_data;
+
+    err =  max63xx_gpio_init(pdev, wdt);
     if (err)
         return err;
-
+    
     platform_set_drvdata(pdev, &wdt->wdd);
     watchdog_set_drvdata(&wdt->wdd, wdt);
-
     wdt->wdd.parent = &pdev->dev;
     wdt->wdd.timeout = wdt->timeout->twd;
     wdt->wdd.info = &max63xx_wdt_info;
     wdt->wdd.ops = &max63xx_wdt_ops;
-
     watchdog_set_nowayout(&wdt->wdd, nowayout);
 
     err = watchdog_register_device(&wdt->wdd);
     if (err)
         return err;
-
     dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay\n",
          wdt->timeout->twd, wdt->timeout->tdelay);
     return 0;
@@ -268,11 +280,52 @@
     .remove        = max63xx_wdt_remove,
     .id_table    = max63xx_id_table,
     .driver        = {
-        .name    = "max63xx_wdt",
+        .name    = "max6371_wdt",
     },
 };
+static int __init max63xx_wdt_init_module(void)
+{
+    int err;
+    
+    pr_info("Intel max63xx WatchDog Timer Driver \n" );
+
+    err = platform_driver_register(&max63xx_wdt_driver);
+    pr_info("Intel max63xx WatchDog Timer Driver err %d\n", err);
+    if (err)
+        return err;
 
-module_platform_driver(max63xx_wdt_driver);
+    max63xx_wdt_data = kzalloc(sizeof(struct max63xx_wdt), GFP_KERNEL);
+    if (!max63xx_wdt_data) {
+        err = -ENOMEM;
+        platform_driver_unregister(&max63xx_wdt_driver);
+        return err;
+    }
+
+    
+    max63xx_wdt_data->pdev = platform_device_register_simple("max6371_wdt", -1, NULL, 0);
+    if (IS_ERR(max63xx_wdt_data->pdev)) {
+        err = PTR_ERR(max63xx_wdt_data->pdev);
+        platform_driver_unregister(&max63xx_wdt_driver);
+        kfree(max63xx_wdt_data);
+        return err;
+    }
+
+    return 0;
+}
+
+static void __exit max63xx_wdt_cleanup_module(void)
+{
+    platform_device_unregister(max63xx_wdt_data->pdev);
+    platform_driver_unregister(&max63xx_wdt_driver);
+    pr_info("Watchdog Module Unloaded\n");
+
+    
+}
+
+module_init(max63xx_wdt_init_module);
+module_exit(max63xx_wdt_cleanup_module);
+
+//module_platform_driver(max63xx_wdt_driver);
 
 MODULE_AUTHOR("Marc Zyngier <

[email protected]>");
 MODULE_DESCRIPTION("max63xx Watchdog Driver");
@@ -293,3 +346,4 @@
          "(max6373/74 only, default=0)");
 
 MODULE_LICENSE("GPL v2");
+
 

     看了一些linux核心的watchdog的驅動都是max63xx_wdt.c驅動型別,註冊了platform的driver,但並沒有註冊裝置驅動,是如何進入probe函式呢?這點值得研究?