/* * drivers/char/watchdog/max63xx_wdt.c * * Driver for max63{69,70,71,72,73,74} watchdog timers * * Copyright (C) 2009 Marc Zyngier * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_HEARTBEAT 60 #define MAX_HEARTBEAT 60 static unsigned int heartbeat = DEFAULT_HEARTBEAT; static bool nowayout = WATCHDOG_NOWAYOUT; /* * Memory mapping: a single byte, 3 first lower bits to select bit 3 * to ping the watchdog. */ #define MAX6369_WDSET (7 << 0) #define MAX6369_WDI (1 << 3) #define MAX6369_WDSET_DISABLED 3 static int nodelay; struct max63xx_wdt { struct watchdog_device wdd; const struct max63xx_timeout *timeout; /* memory mapping */ void __iomem *base; spinlock_t lock; /* WDI and WSET bits write access routines */ void (*ping)(struct max63xx_wdt *wdt); void (*set)(struct max63xx_wdt *wdt, u8 set); }; /* * The timeout values used are actually the absolute minimum the chip * offers. Typical values on my board are slightly over twice as long * (10s setting ends up with a 25s timeout), and can be up to 3 times * the nominal setting (according to the datasheet). So please take * these values with a grain of salt. Same goes for the initial delay * "feature". Only max6373/74 have a few settings without this initial * delay (selected with the "nodelay" parameter). * * I also decided to remove from the tables any timeout smaller than a * second, as it looked completly overkill... */ /* Timeouts in second */ struct max63xx_timeout { const u8 wdset; const u8 tdelay; const u8 twd; }; static const struct max63xx_timeout max6369_table[] = { { 5, 1, 1 }, { 6, 10, 10 }, { 7, 60, 60 }, { }, }; static const struct max63xx_timeout max6371_table[] = { { 6, 60, 3 }, { 7, 60, 60 }, { }, }; static const struct max63xx_timeout max6373_table[] = { { 2, 60, 1 }, { 5, 0, 1 }, { 1, 3, 3 }, { 7, 60, 10 }, { 6, 0, 10 }, { }, }; static struct max63xx_timeout * max63xx_select_timeout(struct max63xx_timeout *table, int value) { while (table->twd) { if (value <= table->twd) { if (nodelay && table->tdelay == 0) return table; if (!nodelay) return table; } table++; } 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); spin_lock_init(&wdt->lock); wdt->ping = max63xx_mmap_ping; wdt->set = max63xx_mmap_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; 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; } err = max63xx_mmap_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; } static int max63xx_wdt_remove(struct platform_device *pdev) { struct watchdog_device *wdd = platform_get_drvdata(pdev); watchdog_unregister_device(wdd); return 0; } static const struct platform_device_id max63xx_id_table[] = { { "max6369_wdt", (kernel_ulong_t)max6369_table, }, { "max6370_wdt", (kernel_ulong_t)max6369_table, }, { "max6371_wdt", (kernel_ulong_t)max6371_table, }, { "max6372_wdt", (kernel_ulong_t)max6371_table, }, { "max6373_wdt", (kernel_ulong_t)max6373_table, }, { "max6374_wdt", (kernel_ulong_t)max6373_table, }, { }, }; MODULE_DEVICE_TABLE(platform, max63xx_id_table); static struct platform_driver max63xx_wdt_driver = { .probe = max63xx_wdt_probe, .remove = max63xx_wdt_remove, .id_table = max63xx_id_table, .driver = { .name = "max63xx_wdt", }, }; module_platform_driver(max63xx_wdt_driver); MODULE_AUTHOR("Marc Zyngier "); MODULE_DESCRIPTION("max63xx Watchdog Driver"); module_param(heartbeat, int, 0); MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat period in seconds from 1 to " __MODULE_STRING(MAX_HEARTBEAT) ", default " __MODULE_STRING(DEFAULT_HEARTBEAT)); module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); module_param(nodelay, int, 0); MODULE_PARM_DESC(nodelay, "Force selection of a timeout setting without initial delay " "(max6373/74 only, default=0)"); MODULE_LICENSE("GPL v2"); >plain -rw-r--r--bcm21664.h1984logplain -rw-r--r--bcm281xx.h2456logplain -rw-r--r--bcm2835-aux.h635logplain -rw-r--r--bcm2835.h1962logplain -rw-r--r--berlin2.h1034logplain -rw-r--r--berlin2q.h695logplain -rw-r--r--clps711x-clock.h718logplain -rw-r--r--efm32-cmu.h1112logplain -rw-r--r--exynos-audss-clk.h597logplain -rw-r--r--exynos3250.h9083logplain -rw-r--r--exynos4.h8284logplain -rw-r--r--exynos4415.h9828logplain -rw-r--r--exynos5250.h4616logplain -rw-r--r--exynos5260-clk.h14876logplain -rw-r--r--exynos5410.h1689logplain -rw-r--r--exynos5420.h6857logplain -rw-r--r--exynos5433.h45372logplain -rw-r--r--exynos5440.h1141logplain -rw-r--r--exynos7-clk.h5281logplain -rw-r--r--gxbb-aoclkc.h2866logplain -rw-r--r--gxbb-clkc.h592logplain -rw-r--r--hi3516cv300-clock.h1668logplain -rw-r--r--hi3519-clock.h1328logplain -rw-r--r--hi3620-clock.h4496logplain -rw-r--r--hi6220-clock.h4508logplain -rw-r--r--hip04-clock.h1137logplain -rw-r--r--histb-clock.h2012logplain -rw-r--r--hix5hd2-clock.h2415logplain -rw-r--r--imx1-clock.h1055logplain -rw-r--r--imx21-clock.h2461logplain -rw-r--r--imx27-clock.h3494logplain -rw-r--r--imx5-clock.h7212logplain -rw-r--r--imx6qdl-clock.h9593logplain -rw-r--r--imx6sl-clock.h5849logplain -rw-r--r--imx6sx-clock.h9099logplain -rw-r--r--imx6ul-clock.h8203logplain -rw-r--r--imx7d-clock.h15974logplain -rw-r--r--jz4740-cgu.h1028logplain -rw-r--r--jz4780-cgu.h2470logplain -rw-r--r--lpc18xx-ccu.h2134logplain -rw-r--r--lpc18xx-cgu.h1142logplain -rw-r--r--lpc32xx-clock.h1633logplain -rw-r--r--lsi,axm5516-clks.h974logplain -rw-r--r--marvell,mmp2.h2022logplain -rw-r--r--marvell,pxa168.h1654logplain -rw-r--r--marvell,pxa1928.h1535logplain -rw-r--r--marvell,pxa910.h1598logplain -rw-r--r--maxim,max77620.h632logplain -rw-r--r--maxim,max77686.h648logplain -rw-r--r--maxim,max77802.h630logplain -rw-r--r--meson8b-clkc.h523logplain -rw-r--r--microchip,pic32-clock.h1150logplain -rw-r--r--mpc512x-clock.h2236logplain -rw-r--r--mt2701-clk.h13832logplain -rw-r--r--mt8135-clk.h5641logplain -rw-r--r--mt8173-clk.h9293logplain -rw-r--r--oxsemi,ox810se.h1002logplain -rw-r--r--oxsemi,ox820.h1203logplain -rw-r--r--pistachio-clk.h4863logplain -rw-r--r--pxa-clock.h1715logplain -rw-r--r--qcom,gcc-apq8084.h12872logplain -rw-r--r--qcom,gcc-ipq4019.h5423logplain -rw-r--r--qcom,gcc-ipq806x.h8574logplain -rw-r--r--qcom,gcc-mdm9615.h9497logplain -rw-r--r--qcom,gcc-msm8660.h7932logplain -rw-r--r--qcom,gcc-msm8916.h6190logplain -rw-r--r--qcom,gcc-msm8960.h9342logplain -rw-r--r--qcom,gcc-msm8974.h12340logplain -rw-r--r--qcom,gcc-msm8994.h4858logplain -rw-r--r--qcom,gcc-msm8996.h12575logplain -rw-r--r--qcom,lcc-ipq806x.h899logplain -rw-r--r--qcom,lcc-mdm9615.h1701logplain -rw-r--r--qcom,lcc-msm8960.h1616logplain -rw-r--r--qcom,mmcc-apq8084.h5722logplain -rw-r--r--qcom,mmcc-msm8960.h4109logplain -rw-r--r--qcom,mmcc-msm8974.h5223logplain -rw-r--r--qcom,mmcc-msm8996.h9403logplain -rw-r--r--qcom,rpmcc.h2101logplain -rw-r--r--r7s72100-clock.h1218logplain -rw-r--r--r8a73a4-clock.h1596logplain -rw-r--r--r8a7740-clock.h1992logplain -rw-r--r--r8a7743-cpg-mssr.h1269logplain -rw-r--r--r8a7745-cpg-mssr.h1298logplain -rw-r--r--r8a7778-clock.h1855logplain -rw-r--r--r8a7779-clock.h1647logplain -rw-r--r--r8a7790-clock.h4367logplain -rw-r--r--r8a7791-clock.h4388logplain -rw-r--r--r8a7792-clock.h2562logplain -rw-r--r--r8a7793-clock.h4561logplain -rw-r--r--r8a7794-clock.h3679logplain -rw-r--r--r8a7795-cpg-mssr.h1890logplain -rw-r--r--r8a7796-cpg-mssr.h2066logplain -rw-r--r--renesas-cpg-mssr.h542logplain -rw-r--r--rk1108-cru.h6605logplain -rw-r--r--rk3036-cru.h4584logplain -rw-r--r--rk3066a-cru.h1068logplain -rw-r--r--rk3188-cru-common.h6105logplain -rw-r--r--rk3188-cru.h1435logplain