diff options
-rw-r--r-- | drivers/watchdog/orion_wdt.c | 114 |
1 files changed, 69 insertions, 45 deletions
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index 65aa65560730..9d3a5b97845b 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c | |||
@@ -44,55 +44,66 @@ | |||
44 | 44 | ||
45 | static bool nowayout = WATCHDOG_NOWAYOUT; | 45 | static bool nowayout = WATCHDOG_NOWAYOUT; |
46 | static int heartbeat = -1; /* module parameter (seconds) */ | 46 | static int heartbeat = -1; /* module parameter (seconds) */ |
47 | static unsigned int wdt_max_duration; /* (seconds) */ | 47 | |
48 | static struct clk *clk; | 48 | struct orion_watchdog { |
49 | static unsigned int wdt_tclk; | 49 | struct watchdog_device wdt; |
50 | static void __iomem *wdt_reg; | 50 | void __iomem *reg; |
51 | static void __iomem *wdt_rstout; | 51 | void __iomem *rstout; |
52 | unsigned long clk_rate; | ||
53 | struct clk *clk; | ||
54 | }; | ||
52 | 55 | ||
53 | static int orion_wdt_ping(struct watchdog_device *wdt_dev) | 56 | static int orion_wdt_ping(struct watchdog_device *wdt_dev) |
54 | { | 57 | { |
58 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); | ||
55 | /* Reload watchdog duration */ | 59 | /* Reload watchdog duration */ |
56 | writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL); | 60 | writel(dev->clk_rate * wdt_dev->timeout, dev->reg + WDT_VAL); |
57 | return 0; | 61 | return 0; |
58 | } | 62 | } |
59 | 63 | ||
60 | static int orion_wdt_start(struct watchdog_device *wdt_dev) | 64 | static int orion_wdt_start(struct watchdog_device *wdt_dev) |
61 | { | 65 | { |
66 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); | ||
67 | |||
62 | /* Set watchdog duration */ | 68 | /* Set watchdog duration */ |
63 | writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL); | 69 | writel(dev->clk_rate * wdt_dev->timeout, dev->reg + WDT_VAL); |
64 | 70 | ||
65 | /* Enable watchdog timer */ | 71 | /* Enable watchdog timer */ |
66 | atomic_io_modify(wdt_reg + TIMER_CTRL, WDT_EN, WDT_EN); | 72 | atomic_io_modify(dev->reg + TIMER_CTRL, WDT_EN, WDT_EN); |
67 | 73 | ||
68 | /* Enable reset on watchdog */ | 74 | /* Enable reset on watchdog */ |
69 | atomic_io_modify(wdt_rstout, WDT_RESET_OUT_EN, WDT_RESET_OUT_EN); | 75 | atomic_io_modify(dev->rstout, WDT_RESET_OUT_EN, WDT_RESET_OUT_EN); |
76 | |||
70 | return 0; | 77 | return 0; |
71 | } | 78 | } |
72 | 79 | ||
73 | static int orion_wdt_stop(struct watchdog_device *wdt_dev) | 80 | static int orion_wdt_stop(struct watchdog_device *wdt_dev) |
74 | { | 81 | { |
82 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); | ||
83 | |||
75 | /* Disable reset on watchdog */ | 84 | /* Disable reset on watchdog */ |
76 | atomic_io_modify(wdt_rstout, WDT_RESET_OUT_EN, 0); | 85 | atomic_io_modify(dev->rstout, WDT_RESET_OUT_EN, 0); |
77 | 86 | ||
78 | /* Disable watchdog timer */ | 87 | /* Disable watchdog timer */ |
79 | atomic_io_modify(wdt_reg + TIMER_CTRL, WDT_EN, 0); | 88 | atomic_io_modify(dev->reg + TIMER_CTRL, WDT_EN, 0); |
89 | |||
80 | return 0; | 90 | return 0; |
81 | } | 91 | } |
82 | 92 | ||
83 | static int orion_wdt_enabled(void) | 93 | static int orion_wdt_enabled(struct orion_watchdog *dev) |
84 | { | 94 | { |
85 | bool enabled, running; | 95 | bool enabled, running; |
86 | 96 | ||
87 | enabled = readl(wdt_rstout) & WDT_RESET_OUT_EN; | 97 | enabled = readl(dev->rstout) & WDT_RESET_OUT_EN; |
88 | running = readl(wdt_reg + TIMER_CTRL) & WDT_EN; | 98 | running = readl(dev->reg + TIMER_CTRL) & WDT_EN; |
89 | 99 | ||
90 | return enabled && running; | 100 | return enabled && running; |
91 | } | 101 | } |
92 | 102 | ||
93 | static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev) | 103 | static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev) |
94 | { | 104 | { |
95 | return readl(wdt_reg + WDT_VAL) / wdt_tclk; | 105 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); |
106 | return readl(dev->reg + WDT_VAL) / dev->clk_rate; | ||
96 | } | 107 | } |
97 | 108 | ||
98 | static int orion_wdt_set_timeout(struct watchdog_device *wdt_dev, | 109 | static int orion_wdt_set_timeout(struct watchdog_device *wdt_dev, |
@@ -116,12 +127,6 @@ static const struct watchdog_ops orion_wdt_ops = { | |||
116 | .get_timeleft = orion_wdt_get_timeleft, | 127 | .get_timeleft = orion_wdt_get_timeleft, |
117 | }; | 128 | }; |
118 | 129 | ||
119 | static struct watchdog_device orion_wdt = { | ||
120 | .info = &orion_wdt_info, | ||
121 | .ops = &orion_wdt_ops, | ||
122 | .min_timeout = 1, | ||
123 | }; | ||
124 | |||
125 | static irqreturn_t orion_wdt_irq(int irq, void *devid) | 130 | static irqreturn_t orion_wdt_irq(int irq, void *devid) |
126 | { | 131 | { |
127 | panic("Watchdog Timeout"); | 132 | panic("Watchdog Timeout"); |
@@ -157,18 +162,29 @@ static void __iomem *orion_wdt_ioremap_rstout(struct platform_device *pdev, | |||
157 | 162 | ||
158 | static int orion_wdt_probe(struct platform_device *pdev) | 163 | static int orion_wdt_probe(struct platform_device *pdev) |
159 | { | 164 | { |
165 | struct orion_watchdog *dev; | ||
166 | unsigned int wdt_max_duration; /* (seconds) */ | ||
160 | struct resource *res; | 167 | struct resource *res; |
161 | int ret, irq; | 168 | int ret, irq; |
162 | 169 | ||
163 | clk = devm_clk_get(&pdev->dev, NULL); | 170 | dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog), |
164 | if (IS_ERR(clk)) { | 171 | GFP_KERNEL); |
172 | if (!dev) | ||
173 | return -ENOMEM; | ||
174 | |||
175 | dev->wdt.info = &orion_wdt_info; | ||
176 | dev->wdt.ops = &orion_wdt_ops; | ||
177 | dev->wdt.min_timeout = 1; | ||
178 | |||
179 | dev->clk = devm_clk_get(&pdev->dev, NULL); | ||
180 | if (IS_ERR(dev->clk)) { | ||
165 | dev_err(&pdev->dev, "Orion Watchdog missing clock\n"); | 181 | dev_err(&pdev->dev, "Orion Watchdog missing clock\n"); |
166 | return PTR_ERR(clk); | 182 | return PTR_ERR(dev->clk); |
167 | } | 183 | } |
168 | ret = clk_prepare_enable(clk); | 184 | ret = clk_prepare_enable(dev->clk); |
169 | if (ret) | 185 | if (ret) |
170 | return ret; | 186 | return ret; |
171 | wdt_tclk = clk_get_rate(clk); | 187 | dev->clk_rate = clk_get_rate(dev->clk); |
172 | 188 | ||
173 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 189 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
174 | if (!res) { | 190 | if (!res) { |
@@ -176,24 +192,28 @@ static int orion_wdt_probe(struct platform_device *pdev) | |||
176 | goto disable_clk; | 192 | goto disable_clk; |
177 | } | 193 | } |
178 | 194 | ||
179 | wdt_reg = devm_ioremap(&pdev->dev, res->start, resource_size(res)); | 195 | dev->reg = devm_ioremap(&pdev->dev, res->start, |
180 | if (!wdt_reg) { | 196 | resource_size(res)); |
197 | if (!dev->reg) { | ||
181 | ret = -ENOMEM; | 198 | ret = -ENOMEM; |
182 | goto disable_clk; | 199 | goto disable_clk; |
183 | } | 200 | } |
184 | 201 | ||
185 | wdt_rstout = orion_wdt_ioremap_rstout(pdev, res->start & | 202 | dev->rstout = orion_wdt_ioremap_rstout(pdev, res->start & |
186 | INTERNAL_REGS_MASK); | 203 | INTERNAL_REGS_MASK); |
187 | if (!wdt_rstout) { | 204 | if (!dev->rstout) { |
188 | ret = -ENODEV; | 205 | ret = -ENODEV; |
189 | goto disable_clk; | 206 | goto disable_clk; |
190 | } | 207 | } |
191 | 208 | ||
192 | wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk; | 209 | wdt_max_duration = WDT_MAX_CYCLE_COUNT / dev->clk_rate; |
210 | |||
211 | dev->wdt.timeout = wdt_max_duration; | ||
212 | dev->wdt.max_timeout = wdt_max_duration; | ||
213 | watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev); | ||
193 | 214 | ||
194 | orion_wdt.timeout = wdt_max_duration; | 215 | platform_set_drvdata(pdev, &dev->wdt); |
195 | orion_wdt.max_timeout = wdt_max_duration; | 216 | watchdog_set_drvdata(&dev->wdt, dev); |
196 | watchdog_init_timeout(&orion_wdt, heartbeat, &pdev->dev); | ||
197 | 217 | ||
198 | /* | 218 | /* |
199 | * Let's make sure the watchdog is fully stopped, unless it's | 219 | * Let's make sure the watchdog is fully stopped, unless it's |
@@ -201,8 +221,8 @@ static int orion_wdt_probe(struct platform_device *pdev) | |||
201 | * removed and re-insterted, or if the bootloader explicitly | 221 | * removed and re-insterted, or if the bootloader explicitly |
202 | * set a running watchdog before booting the kernel. | 222 | * set a running watchdog before booting the kernel. |
203 | */ | 223 | */ |
204 | if (!orion_wdt_enabled()) | 224 | if (!orion_wdt_enabled(dev)) |
205 | orion_wdt_stop(&orion_wdt); | 225 | orion_wdt_stop(&dev->wdt); |
206 | 226 | ||
207 | /* Request the IRQ only after the watchdog is disabled */ | 227 | /* Request the IRQ only after the watchdog is disabled */ |
208 | irq = platform_get_irq(pdev, 0); | 228 | irq = platform_get_irq(pdev, 0); |
@@ -212,37 +232,41 @@ static int orion_wdt_probe(struct platform_device *pdev) | |||
212 | * watchdog, so let's make it optional. | 232 | * watchdog, so let's make it optional. |
213 | */ | 233 | */ |
214 | ret = devm_request_irq(&pdev->dev, irq, orion_wdt_irq, 0, | 234 | ret = devm_request_irq(&pdev->dev, irq, orion_wdt_irq, 0, |
215 | pdev->name, &orion_wdt); | 235 | pdev->name, dev); |
216 | if (ret < 0) { | 236 | if (ret < 0) { |
217 | dev_err(&pdev->dev, "failed to request IRQ\n"); | 237 | dev_err(&pdev->dev, "failed to request IRQ\n"); |
218 | goto disable_clk; | 238 | goto disable_clk; |
219 | } | 239 | } |
220 | } | 240 | } |
221 | 241 | ||
222 | watchdog_set_nowayout(&orion_wdt, nowayout); | 242 | watchdog_set_nowayout(&dev->wdt, nowayout); |
223 | ret = watchdog_register_device(&orion_wdt); | 243 | ret = watchdog_register_device(&dev->wdt); |
224 | if (ret) | 244 | if (ret) |
225 | goto disable_clk; | 245 | goto disable_clk; |
226 | 246 | ||
227 | pr_info("Initial timeout %d sec%s\n", | 247 | pr_info("Initial timeout %d sec%s\n", |
228 | orion_wdt.timeout, nowayout ? ", nowayout" : ""); | 248 | dev->wdt.timeout, nowayout ? ", nowayout" : ""); |
229 | return 0; | 249 | return 0; |
230 | 250 | ||
231 | disable_clk: | 251 | disable_clk: |
232 | clk_disable_unprepare(clk); | 252 | clk_disable_unprepare(dev->clk); |
233 | return ret; | 253 | return ret; |
234 | } | 254 | } |
235 | 255 | ||
236 | static int orion_wdt_remove(struct platform_device *pdev) | 256 | static int orion_wdt_remove(struct platform_device *pdev) |
237 | { | 257 | { |
238 | watchdog_unregister_device(&orion_wdt); | 258 | struct watchdog_device *wdt_dev = platform_get_drvdata(pdev); |
239 | clk_disable_unprepare(clk); | 259 | struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); |
260 | |||
261 | watchdog_unregister_device(wdt_dev); | ||
262 | clk_disable_unprepare(dev->clk); | ||
240 | return 0; | 263 | return 0; |
241 | } | 264 | } |
242 | 265 | ||
243 | static void orion_wdt_shutdown(struct platform_device *pdev) | 266 | static void orion_wdt_shutdown(struct platform_device *pdev) |
244 | { | 267 | { |
245 | orion_wdt_stop(&orion_wdt); | 268 | struct watchdog_device *wdt_dev = platform_get_drvdata(pdev); |
269 | orion_wdt_stop(wdt_dev); | ||
246 | } | 270 | } |
247 | 271 | ||
248 | static const struct of_device_id orion_wdt_of_match_table[] = { | 272 | static const struct of_device_id orion_wdt_of_match_table[] = { |