aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChen-Yu Tsai <wens@csie.org>2014-09-21 12:05:18 -0400
committerWim Van Sebroeck <wim@iguana.be>2014-10-20 15:06:59 -0400
commitf2147de334703c7c44372f013d7d466d756e6943 (patch)
treee6389b695cb90c96ad48e125d691b3f46a54b9b6
parent334a9d8131254e06685b2af0c0f3cc7b3ec5bd04 (diff)
watchdog: sunxi: support parameterized compatible strings
This patch adds support for hardware parameters tied to compatible strings, so similar hardware can reuse the driver. This will be used to support the newer watchdog found in A31 and later SoCs. Differences in the new hardware include separate interrupt lines for each watchdog, and corresponding interrupt control/status registers. Watchdog control registers were also slightly rearranged. Also replace ioread32()/iowrite32() with readl()/writel() in various places changed. Signed-off-by: Chen-Yu Tsai <wens@csie.org> Signed-off-by: Guenter Roeck <linux@roeck-us.net> Acked-by: Heiko Stuebner <heiko@sntech.de> Signed-off-by: Wim Van Sebroeck <wim@iguana.be> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-rw-r--r--drivers/watchdog/sunxi_wdt.c101
1 files changed, 76 insertions, 25 deletions
diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c
index 480bb557f353..a1f7113fc1d1 100644
--- a/drivers/watchdog/sunxi_wdt.c
+++ b/drivers/watchdog/sunxi_wdt.c
@@ -23,6 +23,7 @@
23#include <linux/moduleparam.h> 23#include <linux/moduleparam.h>
24#include <linux/notifier.h> 24#include <linux/notifier.h>
25#include <linux/of.h> 25#include <linux/of.h>
26#include <linux/of_device.h>
26#include <linux/platform_device.h> 27#include <linux/platform_device.h>
27#include <linux/reboot.h> 28#include <linux/reboot.h>
28#include <linux/types.h> 29#include <linux/types.h>
@@ -30,15 +31,11 @@
30 31
31#define WDT_MAX_TIMEOUT 16 32#define WDT_MAX_TIMEOUT 16
32#define WDT_MIN_TIMEOUT 1 33#define WDT_MIN_TIMEOUT 1
33#define WDT_MODE_TIMEOUT(n) ((n) << 3) 34#define WDT_TIMEOUT_MASK 0x0F
34#define WDT_TIMEOUT_MASK WDT_MODE_TIMEOUT(0x0F)
35 35
36#define WDT_CTRL 0x00
37#define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1)) 36#define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1))
38 37
39#define WDT_MODE 0x04
40#define WDT_MODE_EN (1 << 0) 38#define WDT_MODE_EN (1 << 0)
41#define WDT_MODE_RST_EN (1 << 1)
42 39
43#define DRV_NAME "sunxi-wdt" 40#define DRV_NAME "sunxi-wdt"
44#define DRV_VERSION "1.0" 41#define DRV_VERSION "1.0"
@@ -46,15 +43,29 @@
46static bool nowayout = WATCHDOG_NOWAYOUT; 43static bool nowayout = WATCHDOG_NOWAYOUT;
47static unsigned int timeout = WDT_MAX_TIMEOUT; 44static unsigned int timeout = WDT_MAX_TIMEOUT;
48 45
46/*
47 * This structure stores the register offsets for different variants
48 * of Allwinner's watchdog hardware.
49 */
50struct sunxi_wdt_reg {
51 u8 wdt_ctrl;
52 u8 wdt_cfg;
53 u8 wdt_mode;
54 u8 wdt_timeout_shift;
55 u8 wdt_reset_mask;
56 u8 wdt_reset_val;
57};
58
49struct sunxi_wdt_dev { 59struct sunxi_wdt_dev {
50 struct watchdog_device wdt_dev; 60 struct watchdog_device wdt_dev;
51 void __iomem *wdt_base; 61 void __iomem *wdt_base;
62 const struct sunxi_wdt_reg *wdt_regs;
52 struct notifier_block restart_handler; 63 struct notifier_block restart_handler;
53}; 64};
54 65
55/* 66/*
56 * wdt_timeout_map maps the watchdog timer interval value in seconds to 67 * wdt_timeout_map maps the watchdog timer interval value in seconds to
57 * the value of the register WDT_MODE bit 3:6 68 * the value of the register WDT_MODE at bits .wdt_timeout_shift ~ +3
58 * 69 *
59 * [timeout seconds] = register value 70 * [timeout seconds] = register value
60 * 71 *
@@ -82,19 +93,32 @@ static int sunxi_restart_handle(struct notifier_block *this, unsigned long mode,
82 struct sunxi_wdt_dev, 93 struct sunxi_wdt_dev,
83 restart_handler); 94 restart_handler);
84 void __iomem *wdt_base = sunxi_wdt->wdt_base; 95 void __iomem *wdt_base = sunxi_wdt->wdt_base;
96 const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
97 u32 val;
98
99 /* Set system reset function */
100 val = readl(wdt_base + regs->wdt_cfg);
101 val &= ~(regs->wdt_reset_mask);
102 val |= regs->wdt_reset_val;
103 writel(val, wdt_base + regs->wdt_cfg);
85 104
86 /* Enable timer and set reset bit in the watchdog */ 105 /* Set lowest timeout and enable watchdog */
87 writel(WDT_MODE_EN | WDT_MODE_RST_EN, wdt_base + WDT_MODE); 106 val = readl(wdt_base + regs->wdt_mode);
107 val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift);
108 val |= WDT_MODE_EN;
109 writel(val, wdt_base + regs->wdt_mode);
88 110
89 /* 111 /*
90 * Restart the watchdog. The default (and lowest) interval 112 * Restart the watchdog. The default (and lowest) interval
91 * value for the watchdog is 0.5s. 113 * value for the watchdog is 0.5s.
92 */ 114 */
93 writel(WDT_CTRL_RELOAD, wdt_base + WDT_CTRL); 115 writel(WDT_CTRL_RELOAD, wdt_base + regs->wdt_ctrl);
94 116
95 while (1) { 117 while (1) {
96 mdelay(5); 118 mdelay(5);
97 writel(WDT_MODE_EN | WDT_MODE_RST_EN, wdt_base + WDT_MODE); 119 val = readl(wdt_base + regs->wdt_mode);
120 val |= WDT_MODE_EN;
121 writel(val, wdt_base + regs->wdt_mode);
98 } 122 }
99 return NOTIFY_DONE; 123 return NOTIFY_DONE;
100} 124}
@@ -103,8 +127,9 @@ static int sunxi_wdt_ping(struct watchdog_device *wdt_dev)
103{ 127{
104 struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); 128 struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
105 void __iomem *wdt_base = sunxi_wdt->wdt_base; 129 void __iomem *wdt_base = sunxi_wdt->wdt_base;
130 const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
106 131
107 iowrite32(WDT_CTRL_RELOAD, wdt_base + WDT_CTRL); 132 writel(WDT_CTRL_RELOAD, wdt_base + regs->wdt_ctrl);
108 133
109 return 0; 134 return 0;
110} 135}
@@ -114,6 +139,7 @@ static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev,
114{ 139{
115 struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); 140 struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
116 void __iomem *wdt_base = sunxi_wdt->wdt_base; 141 void __iomem *wdt_base = sunxi_wdt->wdt_base;
142 const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
117 u32 reg; 143 u32 reg;
118 144
119 if (wdt_timeout_map[timeout] == 0) 145 if (wdt_timeout_map[timeout] == 0)
@@ -121,10 +147,10 @@ static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev,
121 147
122 sunxi_wdt->wdt_dev.timeout = timeout; 148 sunxi_wdt->wdt_dev.timeout = timeout;
123 149
124 reg = ioread32(wdt_base + WDT_MODE); 150 reg = readl(wdt_base + regs->wdt_mode);
125 reg &= ~WDT_TIMEOUT_MASK; 151 reg &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift);
126 reg |= WDT_MODE_TIMEOUT(wdt_timeout_map[timeout]); 152 reg |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift;
127 iowrite32(reg, wdt_base + WDT_MODE); 153 writel(reg, wdt_base + regs->wdt_mode);
128 154
129 sunxi_wdt_ping(wdt_dev); 155 sunxi_wdt_ping(wdt_dev);
130 156
@@ -135,8 +161,9 @@ static int sunxi_wdt_stop(struct watchdog_device *wdt_dev)
135{ 161{
136 struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); 162 struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
137 void __iomem *wdt_base = sunxi_wdt->wdt_base; 163 void __iomem *wdt_base = sunxi_wdt->wdt_base;
164 const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
138 165
139 iowrite32(0, wdt_base + WDT_MODE); 166 writel(0, wdt_base + regs->wdt_mode);
140 167
141 return 0; 168 return 0;
142} 169}
@@ -146,6 +173,7 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev)
146 u32 reg; 173 u32 reg;
147 struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); 174 struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
148 void __iomem *wdt_base = sunxi_wdt->wdt_base; 175 void __iomem *wdt_base = sunxi_wdt->wdt_base;
176 const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
149 int ret; 177 int ret;
150 178
151 ret = sunxi_wdt_set_timeout(&sunxi_wdt->wdt_dev, 179 ret = sunxi_wdt_set_timeout(&sunxi_wdt->wdt_dev,
@@ -153,9 +181,16 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev)
153 if (ret < 0) 181 if (ret < 0)
154 return ret; 182 return ret;
155 183
156 reg = ioread32(wdt_base + WDT_MODE); 184 /* Set system reset function */
157 reg |= (WDT_MODE_RST_EN | WDT_MODE_EN); 185 reg = readl(wdt_base + regs->wdt_cfg);
158 iowrite32(reg, wdt_base + WDT_MODE); 186 reg &= ~(regs->wdt_reset_mask);
187 reg |= ~(regs->wdt_reset_val);
188 writel(reg, wdt_base + regs->wdt_cfg);
189
190 /* Enable watchdog */
191 reg = readl(wdt_base + regs->wdt_mode);
192 reg |= WDT_MODE_EN;
193 writel(reg, wdt_base + regs->wdt_mode);
159 194
160 return 0; 195 return 0;
161} 196}
@@ -175,9 +210,25 @@ static const struct watchdog_ops sunxi_wdt_ops = {
175 .set_timeout = sunxi_wdt_set_timeout, 210 .set_timeout = sunxi_wdt_set_timeout,
176}; 211};
177 212
213static const struct sunxi_wdt_reg sun4i_wdt_reg = {
214 .wdt_ctrl = 0x00,
215 .wdt_cfg = 0x04,
216 .wdt_mode = 0x04,
217 .wdt_timeout_shift = 3,
218 .wdt_reset_mask = 0x02,
219 .wdt_reset_val = 0x02,
220};
221
222static const struct of_device_id sunxi_wdt_dt_ids[] = {
223 { .compatible = "allwinner,sun4i-a10-wdt", .data = &sun4i_wdt_reg },
224 { /* sentinel */ }
225};
226MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids);
227
178static int sunxi_wdt_probe(struct platform_device *pdev) 228static int sunxi_wdt_probe(struct platform_device *pdev)
179{ 229{
180 struct sunxi_wdt_dev *sunxi_wdt; 230 struct sunxi_wdt_dev *sunxi_wdt;
231 const struct of_device_id *device;
181 struct resource *res; 232 struct resource *res;
182 int err; 233 int err;
183 234
@@ -187,6 +238,12 @@ static int sunxi_wdt_probe(struct platform_device *pdev)
187 238
188 platform_set_drvdata(pdev, sunxi_wdt); 239 platform_set_drvdata(pdev, sunxi_wdt);
189 240
241 device = of_match_device(sunxi_wdt_dt_ids, &pdev->dev);
242 if (!device)
243 return -ENODEV;
244
245 sunxi_wdt->wdt_regs = device->data;
246
190 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 247 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
191 sunxi_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); 248 sunxi_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res);
192 if (IS_ERR(sunxi_wdt->wdt_base)) 249 if (IS_ERR(sunxi_wdt->wdt_base))
@@ -242,12 +299,6 @@ static void sunxi_wdt_shutdown(struct platform_device *pdev)
242 sunxi_wdt_stop(&sunxi_wdt->wdt_dev); 299 sunxi_wdt_stop(&sunxi_wdt->wdt_dev);
243} 300}
244 301
245static const struct of_device_id sunxi_wdt_dt_ids[] = {
246 { .compatible = "allwinner,sun4i-a10-wdt" },
247 { /* sentinel */ }
248};
249MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids);
250
251static struct platform_driver sunxi_wdt_driver = { 302static struct platform_driver sunxi_wdt_driver = {
252 .probe = sunxi_wdt_probe, 303 .probe = sunxi_wdt_probe,
253 .remove = sunxi_wdt_remove, 304 .remove = sunxi_wdt_remove,