Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * drivers/char/watchdog/max63xx_wdt.c
0003  *
0004  * Driver for max63{69,70,71,72,73,74} watchdog timers
0005  *
0006  * Copyright (C) 2009 Marc Zyngier <maz@misterjones.org>
0007  *
0008  * This file is licensed under the terms of the GNU General Public
0009  * License version 2. This program is licensed "as is" without any
0010  * warranty of any kind, whether express or implied.
0011  *
0012  * This driver assumes the watchdog pins are memory mapped (as it is
0013  * the case for the Arcom Zeus). Should it be connected over GPIOs or
0014  * another interface, some abstraction will have to be introduced.
0015  */
0016 
0017 #include <linux/err.h>
0018 #include <linux/module.h>
0019 #include <linux/moduleparam.h>
0020 #include <linux/mod_devicetable.h>
0021 #include <linux/types.h>
0022 #include <linux/kernel.h>
0023 #include <linux/watchdog.h>
0024 #include <linux/bitops.h>
0025 #include <linux/platform_device.h>
0026 #include <linux/spinlock.h>
0027 #include <linux/io.h>
0028 #include <linux/slab.h>
0029 #include <linux/property.h>
0030 
0031 #define DEFAULT_HEARTBEAT 60
0032 #define MAX_HEARTBEAT     60
0033 
0034 static unsigned int heartbeat = DEFAULT_HEARTBEAT;
0035 static bool nowayout  = WATCHDOG_NOWAYOUT;
0036 
0037 /*
0038  * Memory mapping: a single byte, 3 first lower bits to select bit 3
0039  * to ping the watchdog.
0040  */
0041 #define MAX6369_WDSET   (7 << 0)
0042 #define MAX6369_WDI (1 << 3)
0043 
0044 #define MAX6369_WDSET_DISABLED  3
0045 
0046 static int nodelay;
0047 
0048 struct max63xx_wdt {
0049     struct watchdog_device wdd;
0050     const struct max63xx_timeout *timeout;
0051 
0052     /* memory mapping */
0053     void __iomem *base;
0054     spinlock_t lock;
0055 
0056     /* WDI and WSET bits write access routines */
0057     void (*ping)(struct max63xx_wdt *wdt);
0058     void (*set)(struct max63xx_wdt *wdt, u8 set);
0059 };
0060 
0061 /*
0062  * The timeout values used are actually the absolute minimum the chip
0063  * offers. Typical values on my board are slightly over twice as long
0064  * (10s setting ends up with a 25s timeout), and can be up to 3 times
0065  * the nominal setting (according to the datasheet). So please take
0066  * these values with a grain of salt. Same goes for the initial delay
0067  * "feature". Only max6373/74 have a few settings without this initial
0068  * delay (selected with the "nodelay" parameter).
0069  *
0070  * I also decided to remove from the tables any timeout smaller than a
0071  * second, as it looked completly overkill...
0072  */
0073 
0074 /* Timeouts in second */
0075 struct max63xx_timeout {
0076     const u8 wdset;
0077     const u8 tdelay;
0078     const u8 twd;
0079 };
0080 
0081 static const struct max63xx_timeout max6369_table[] = {
0082     { 5,  1,  1 },
0083     { 6, 10, 10 },
0084     { 7, 60, 60 },
0085     { },
0086 };
0087 
0088 static const struct max63xx_timeout max6371_table[] = {
0089     { 6, 60,  3 },
0090     { 7, 60, 60 },
0091     { },
0092 };
0093 
0094 static const struct max63xx_timeout max6373_table[] = {
0095     { 2, 60,  1 },
0096     { 5,  0,  1 },
0097     { 1,  3,  3 },
0098     { 7, 60, 10 },
0099     { 6,  0, 10 },
0100     { },
0101 };
0102 
0103 static const struct max63xx_timeout *
0104 max63xx_select_timeout(const struct max63xx_timeout *table, int value)
0105 {
0106     while (table->twd) {
0107         if (value <= table->twd) {
0108             if (nodelay && table->tdelay == 0)
0109                 return table;
0110 
0111             if (!nodelay)
0112                 return table;
0113         }
0114 
0115         table++;
0116     }
0117 
0118     return NULL;
0119 }
0120 
0121 static int max63xx_wdt_ping(struct watchdog_device *wdd)
0122 {
0123     struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
0124 
0125     wdt->ping(wdt);
0126     return 0;
0127 }
0128 
0129 static int max63xx_wdt_start(struct watchdog_device *wdd)
0130 {
0131     struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
0132 
0133     wdt->set(wdt, wdt->timeout->wdset);
0134 
0135     /* check for a edge triggered startup */
0136     if (wdt->timeout->tdelay == 0)
0137         wdt->ping(wdt);
0138     return 0;
0139 }
0140 
0141 static int max63xx_wdt_stop(struct watchdog_device *wdd)
0142 {
0143     struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
0144 
0145     wdt->set(wdt, MAX6369_WDSET_DISABLED);
0146     return 0;
0147 }
0148 
0149 static const struct watchdog_ops max63xx_wdt_ops = {
0150     .owner = THIS_MODULE,
0151     .start = max63xx_wdt_start,
0152     .stop = max63xx_wdt_stop,
0153     .ping = max63xx_wdt_ping,
0154 };
0155 
0156 static const struct watchdog_info max63xx_wdt_info = {
0157     .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
0158     .identity = "max63xx Watchdog",
0159 };
0160 
0161 static void max63xx_mmap_ping(struct max63xx_wdt *wdt)
0162 {
0163     u8 val;
0164 
0165     spin_lock(&wdt->lock);
0166 
0167     val = __raw_readb(wdt->base);
0168 
0169     __raw_writeb(val | MAX6369_WDI, wdt->base);
0170     __raw_writeb(val & ~MAX6369_WDI, wdt->base);
0171 
0172     spin_unlock(&wdt->lock);
0173 }
0174 
0175 static void max63xx_mmap_set(struct max63xx_wdt *wdt, u8 set)
0176 {
0177     u8 val;
0178 
0179     spin_lock(&wdt->lock);
0180 
0181     val = __raw_readb(wdt->base);
0182     val &= ~MAX6369_WDSET;
0183     val |= set & MAX6369_WDSET;
0184     __raw_writeb(val, wdt->base);
0185 
0186     spin_unlock(&wdt->lock);
0187 }
0188 
0189 static int max63xx_mmap_init(struct platform_device *p, struct max63xx_wdt *wdt)
0190 {
0191     wdt->base = devm_platform_ioremap_resource(p, 0);
0192     if (IS_ERR(wdt->base))
0193         return PTR_ERR(wdt->base);
0194 
0195     spin_lock_init(&wdt->lock);
0196 
0197     wdt->ping = max63xx_mmap_ping;
0198     wdt->set = max63xx_mmap_set;
0199     return 0;
0200 }
0201 
0202 static int max63xx_wdt_probe(struct platform_device *pdev)
0203 {
0204     struct device *dev = &pdev->dev;
0205     struct max63xx_wdt *wdt;
0206     const struct max63xx_timeout *table;
0207     int err;
0208 
0209     wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
0210     if (!wdt)
0211         return -ENOMEM;
0212 
0213     /* Attempt to use fwnode first */
0214     table = device_get_match_data(dev);
0215     if (!table)
0216         table = (struct max63xx_timeout *)pdev->id_entry->driver_data;
0217 
0218     if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
0219         heartbeat = DEFAULT_HEARTBEAT;
0220 
0221     wdt->timeout = max63xx_select_timeout(table, heartbeat);
0222     if (!wdt->timeout) {
0223         dev_err(dev, "unable to satisfy %ds heartbeat request\n",
0224             heartbeat);
0225         return -EINVAL;
0226     }
0227 
0228     err = max63xx_mmap_init(pdev, wdt);
0229     if (err)
0230         return err;
0231 
0232     platform_set_drvdata(pdev, &wdt->wdd);
0233     watchdog_set_drvdata(&wdt->wdd, wdt);
0234 
0235     wdt->wdd.parent = dev;
0236     wdt->wdd.timeout = wdt->timeout->twd;
0237     wdt->wdd.info = &max63xx_wdt_info;
0238     wdt->wdd.ops = &max63xx_wdt_ops;
0239 
0240     watchdog_set_nowayout(&wdt->wdd, nowayout);
0241 
0242     err = devm_watchdog_register_device(dev, &wdt->wdd);
0243     if (err)
0244         return err;
0245 
0246     dev_info(dev, "using %ds heartbeat with %ds initial delay\n",
0247          wdt->timeout->twd, wdt->timeout->tdelay);
0248     return 0;
0249 }
0250 
0251 static const struct platform_device_id max63xx_id_table[] = {
0252     { "max6369_wdt", (kernel_ulong_t)max6369_table, },
0253     { "max6370_wdt", (kernel_ulong_t)max6369_table, },
0254     { "max6371_wdt", (kernel_ulong_t)max6371_table, },
0255     { "max6372_wdt", (kernel_ulong_t)max6371_table, },
0256     { "max6373_wdt", (kernel_ulong_t)max6373_table, },
0257     { "max6374_wdt", (kernel_ulong_t)max6373_table, },
0258     { },
0259 };
0260 MODULE_DEVICE_TABLE(platform, max63xx_id_table);
0261 
0262 static const struct of_device_id max63xx_dt_id_table[] = {
0263     { .compatible = "maxim,max6369", .data = max6369_table, },
0264     { .compatible = "maxim,max6370", .data = max6369_table, },
0265     { .compatible = "maxim,max6371", .data = max6371_table, },
0266     { .compatible = "maxim,max6372", .data = max6371_table, },
0267     { .compatible = "maxim,max6373", .data = max6373_table, },
0268     { .compatible = "maxim,max6374", .data = max6373_table, },
0269     { }
0270 };
0271 MODULE_DEVICE_TABLE(of, max63xx_dt_id_table);
0272 
0273 static struct platform_driver max63xx_wdt_driver = {
0274     .probe      = max63xx_wdt_probe,
0275     .id_table   = max63xx_id_table,
0276     .driver     = {
0277         .name   = "max63xx_wdt",
0278         .of_match_table = max63xx_dt_id_table,
0279     },
0280 };
0281 
0282 module_platform_driver(max63xx_wdt_driver);
0283 
0284 MODULE_AUTHOR("Marc Zyngier <maz@misterjones.org>");
0285 MODULE_DESCRIPTION("max63xx Watchdog Driver");
0286 
0287 module_param(heartbeat, int, 0);
0288 MODULE_PARM_DESC(heartbeat,
0289          "Watchdog heartbeat period in seconds from 1 to "
0290          __MODULE_STRING(MAX_HEARTBEAT) ", default "
0291          __MODULE_STRING(DEFAULT_HEARTBEAT));
0292 
0293 module_param(nowayout, bool, 0);
0294 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0295          __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0296 
0297 module_param(nodelay, int, 0);
0298 MODULE_PARM_DESC(nodelay,
0299          "Force selection of a timeout setting without initial delay "
0300          "(max6373/74 only, default=0)");
0301 
0302 MODULE_LICENSE("GPL v2");