| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Imagination Technologies PowerDown Controller Watchdog Timer. |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (c) 2014 Imagination Technologies Ltd. |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 7 | | - * under the terms of the GNU General Public License version 2 as published by |
|---|
| 8 | | - * the Free Software Foundation. |
|---|
| 9 | 6 | * |
|---|
| 10 | 7 | * Based on drivers/watchdog/sunxi_wdt.c Copyright (c) 2013 Carlo Caione |
|---|
| 11 | 8 | * 2012 Henrik Nordstrom |
|---|
| .. | .. |
|---|
| 178 | 175 | .restart = pdc_wdt_restart, |
|---|
| 179 | 176 | }; |
|---|
| 180 | 177 | |
|---|
| 178 | +static void pdc_clk_disable_unprepare(void *data) |
|---|
| 179 | +{ |
|---|
| 180 | + clk_disable_unprepare(data); |
|---|
| 181 | +} |
|---|
| 182 | + |
|---|
| 181 | 183 | static int pdc_wdt_probe(struct platform_device *pdev) |
|---|
| 182 | 184 | { |
|---|
| 185 | + struct device *dev = &pdev->dev; |
|---|
| 183 | 186 | u64 div; |
|---|
| 184 | 187 | int ret, val; |
|---|
| 185 | 188 | unsigned long clk_rate; |
|---|
| 186 | | - struct resource *res; |
|---|
| 187 | 189 | struct pdc_wdt_dev *pdc_wdt; |
|---|
| 188 | 190 | |
|---|
| 189 | | - pdc_wdt = devm_kzalloc(&pdev->dev, sizeof(*pdc_wdt), GFP_KERNEL); |
|---|
| 191 | + pdc_wdt = devm_kzalloc(dev, sizeof(*pdc_wdt), GFP_KERNEL); |
|---|
| 190 | 192 | if (!pdc_wdt) |
|---|
| 191 | 193 | return -ENOMEM; |
|---|
| 192 | 194 | |
|---|
| 193 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 194 | | - pdc_wdt->base = devm_ioremap_resource(&pdev->dev, res); |
|---|
| 195 | + pdc_wdt->base = devm_platform_ioremap_resource(pdev, 0); |
|---|
| 195 | 196 | if (IS_ERR(pdc_wdt->base)) |
|---|
| 196 | 197 | return PTR_ERR(pdc_wdt->base); |
|---|
| 197 | 198 | |
|---|
| 198 | | - pdc_wdt->sys_clk = devm_clk_get(&pdev->dev, "sys"); |
|---|
| 199 | + pdc_wdt->sys_clk = devm_clk_get(dev, "sys"); |
|---|
| 199 | 200 | if (IS_ERR(pdc_wdt->sys_clk)) { |
|---|
| 200 | | - dev_err(&pdev->dev, "failed to get the sys clock\n"); |
|---|
| 201 | + dev_err(dev, "failed to get the sys clock\n"); |
|---|
| 201 | 202 | return PTR_ERR(pdc_wdt->sys_clk); |
|---|
| 202 | 203 | } |
|---|
| 203 | 204 | |
|---|
| 204 | | - pdc_wdt->wdt_clk = devm_clk_get(&pdev->dev, "wdt"); |
|---|
| 205 | + pdc_wdt->wdt_clk = devm_clk_get(dev, "wdt"); |
|---|
| 205 | 206 | if (IS_ERR(pdc_wdt->wdt_clk)) { |
|---|
| 206 | | - dev_err(&pdev->dev, "failed to get the wdt clock\n"); |
|---|
| 207 | + dev_err(dev, "failed to get the wdt clock\n"); |
|---|
| 207 | 208 | return PTR_ERR(pdc_wdt->wdt_clk); |
|---|
| 208 | 209 | } |
|---|
| 209 | 210 | |
|---|
| 210 | 211 | ret = clk_prepare_enable(pdc_wdt->sys_clk); |
|---|
| 211 | 212 | if (ret) { |
|---|
| 212 | | - dev_err(&pdev->dev, "could not prepare or enable sys clock\n"); |
|---|
| 213 | + dev_err(dev, "could not prepare or enable sys clock\n"); |
|---|
| 213 | 214 | return ret; |
|---|
| 214 | 215 | } |
|---|
| 216 | + ret = devm_add_action_or_reset(dev, pdc_clk_disable_unprepare, |
|---|
| 217 | + pdc_wdt->sys_clk); |
|---|
| 218 | + if (ret) |
|---|
| 219 | + return ret; |
|---|
| 215 | 220 | |
|---|
| 216 | 221 | ret = clk_prepare_enable(pdc_wdt->wdt_clk); |
|---|
| 217 | 222 | if (ret) { |
|---|
| 218 | | - dev_err(&pdev->dev, "could not prepare or enable wdt clock\n"); |
|---|
| 219 | | - goto disable_sys_clk; |
|---|
| 223 | + dev_err(dev, "could not prepare or enable wdt clock\n"); |
|---|
| 224 | + return ret; |
|---|
| 220 | 225 | } |
|---|
| 226 | + ret = devm_add_action_or_reset(dev, pdc_clk_disable_unprepare, |
|---|
| 227 | + pdc_wdt->wdt_clk); |
|---|
| 228 | + if (ret) |
|---|
| 229 | + return ret; |
|---|
| 221 | 230 | |
|---|
| 222 | 231 | /* We use the clock rate to calculate the max timeout */ |
|---|
| 223 | 232 | clk_rate = clk_get_rate(pdc_wdt->wdt_clk); |
|---|
| 224 | 233 | if (clk_rate == 0) { |
|---|
| 225 | | - dev_err(&pdev->dev, "failed to get clock rate\n"); |
|---|
| 226 | | - ret = -EINVAL; |
|---|
| 227 | | - goto disable_wdt_clk; |
|---|
| 234 | + dev_err(dev, "failed to get clock rate\n"); |
|---|
| 235 | + return -EINVAL; |
|---|
| 228 | 236 | } |
|---|
| 229 | 237 | |
|---|
| 230 | 238 | if (order_base_2(clk_rate) > PDC_WDT_CONFIG_DELAY_MASK + 1) { |
|---|
| 231 | | - dev_err(&pdev->dev, "invalid clock rate\n"); |
|---|
| 232 | | - ret = -EINVAL; |
|---|
| 233 | | - goto disable_wdt_clk; |
|---|
| 239 | + dev_err(dev, "invalid clock rate\n"); |
|---|
| 240 | + return -EINVAL; |
|---|
| 234 | 241 | } |
|---|
| 235 | 242 | |
|---|
| 236 | 243 | if (order_base_2(clk_rate) == 0) |
|---|
| .. | .. |
|---|
| 245 | 252 | do_div(div, clk_rate); |
|---|
| 246 | 253 | pdc_wdt->wdt_dev.max_timeout = div; |
|---|
| 247 | 254 | pdc_wdt->wdt_dev.timeout = PDC_WDT_DEF_TIMEOUT; |
|---|
| 248 | | - pdc_wdt->wdt_dev.parent = &pdev->dev; |
|---|
| 255 | + pdc_wdt->wdt_dev.parent = dev; |
|---|
| 249 | 256 | watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt); |
|---|
| 250 | 257 | |
|---|
| 251 | | - watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev); |
|---|
| 258 | + watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, dev); |
|---|
| 252 | 259 | |
|---|
| 253 | 260 | pdc_wdt_stop(&pdc_wdt->wdt_dev); |
|---|
| 254 | 261 | |
|---|
| .. | .. |
|---|
| 259 | 266 | case PDC_WDT_TICKLE_STATUS_TICKLE: |
|---|
| 260 | 267 | case PDC_WDT_TICKLE_STATUS_TIMEOUT: |
|---|
| 261 | 268 | pdc_wdt->wdt_dev.bootstatus |= WDIOF_CARDRESET; |
|---|
| 262 | | - dev_info(&pdev->dev, |
|---|
| 263 | | - "watchdog module last reset due to timeout\n"); |
|---|
| 269 | + dev_info(dev, "watchdog module last reset due to timeout\n"); |
|---|
| 264 | 270 | break; |
|---|
| 265 | 271 | case PDC_WDT_TICKLE_STATUS_HRESET: |
|---|
| 266 | | - dev_info(&pdev->dev, |
|---|
| 272 | + dev_info(dev, |
|---|
| 267 | 273 | "watchdog module last reset due to hard reset\n"); |
|---|
| 268 | 274 | break; |
|---|
| 269 | 275 | case PDC_WDT_TICKLE_STATUS_SRESET: |
|---|
| 270 | | - dev_info(&pdev->dev, |
|---|
| 276 | + dev_info(dev, |
|---|
| 271 | 277 | "watchdog module last reset due to soft reset\n"); |
|---|
| 272 | 278 | break; |
|---|
| 273 | 279 | case PDC_WDT_TICKLE_STATUS_USER: |
|---|
| 274 | | - dev_info(&pdev->dev, |
|---|
| 280 | + dev_info(dev, |
|---|
| 275 | 281 | "watchdog module last reset due to user reset\n"); |
|---|
| 276 | 282 | break; |
|---|
| 277 | 283 | default: |
|---|
| 278 | | - dev_info(&pdev->dev, |
|---|
| 279 | | - "contains an illegal status code (%08x)\n", val); |
|---|
| 284 | + dev_info(dev, "contains an illegal status code (%08x)\n", val); |
|---|
| 280 | 285 | break; |
|---|
| 281 | 286 | } |
|---|
| 282 | 287 | |
|---|
| .. | .. |
|---|
| 285 | 290 | |
|---|
| 286 | 291 | platform_set_drvdata(pdev, pdc_wdt); |
|---|
| 287 | 292 | |
|---|
| 288 | | - ret = watchdog_register_device(&pdc_wdt->wdt_dev); |
|---|
| 289 | | - if (ret) |
|---|
| 290 | | - goto disable_wdt_clk; |
|---|
| 291 | | - |
|---|
| 292 | | - return 0; |
|---|
| 293 | | - |
|---|
| 294 | | -disable_wdt_clk: |
|---|
| 295 | | - clk_disable_unprepare(pdc_wdt->wdt_clk); |
|---|
| 296 | | -disable_sys_clk: |
|---|
| 297 | | - clk_disable_unprepare(pdc_wdt->sys_clk); |
|---|
| 298 | | - return ret; |
|---|
| 299 | | -} |
|---|
| 300 | | - |
|---|
| 301 | | -static void pdc_wdt_shutdown(struct platform_device *pdev) |
|---|
| 302 | | -{ |
|---|
| 303 | | - struct pdc_wdt_dev *pdc_wdt = platform_get_drvdata(pdev); |
|---|
| 304 | | - |
|---|
| 305 | | - pdc_wdt_stop(&pdc_wdt->wdt_dev); |
|---|
| 306 | | -} |
|---|
| 307 | | - |
|---|
| 308 | | -static int pdc_wdt_remove(struct platform_device *pdev) |
|---|
| 309 | | -{ |
|---|
| 310 | | - struct pdc_wdt_dev *pdc_wdt = platform_get_drvdata(pdev); |
|---|
| 311 | | - |
|---|
| 312 | | - pdc_wdt_stop(&pdc_wdt->wdt_dev); |
|---|
| 313 | | - watchdog_unregister_device(&pdc_wdt->wdt_dev); |
|---|
| 314 | | - clk_disable_unprepare(pdc_wdt->wdt_clk); |
|---|
| 315 | | - clk_disable_unprepare(pdc_wdt->sys_clk); |
|---|
| 316 | | - |
|---|
| 317 | | - return 0; |
|---|
| 293 | + watchdog_stop_on_reboot(&pdc_wdt->wdt_dev); |
|---|
| 294 | + watchdog_stop_on_unregister(&pdc_wdt->wdt_dev); |
|---|
| 295 | + return devm_watchdog_register_device(dev, &pdc_wdt->wdt_dev); |
|---|
| 318 | 296 | } |
|---|
| 319 | 297 | |
|---|
| 320 | 298 | static const struct of_device_id pdc_wdt_match[] = { |
|---|
| .. | .. |
|---|
| 329 | 307 | .of_match_table = pdc_wdt_match, |
|---|
| 330 | 308 | }, |
|---|
| 331 | 309 | .probe = pdc_wdt_probe, |
|---|
| 332 | | - .remove = pdc_wdt_remove, |
|---|
| 333 | | - .shutdown = pdc_wdt_shutdown, |
|---|
| 334 | 310 | }; |
|---|
| 335 | 311 | module_platform_driver(pdc_wdt_driver); |
|---|
| 336 | 312 | |
|---|