in s3c2410_wdt.c [660:819]
static int s3c2410wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct s3c2410_wdt *wdt;
unsigned int wtcon;
int wdt_irq;
int ret;
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
wdt->dev = dev;
spin_lock_init(&wdt->lock);
wdt->wdt_device = s3c2410_wdd;
wdt->drv_data = s3c2410_get_wdt_drv_data(pdev);
if (!wdt->drv_data)
return -EINVAL;
if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
"samsung,syscon-phandle");
if (IS_ERR(wdt->pmureg)) {
dev_err(dev, "syscon regmap lookup failed.\n");
return PTR_ERR(wdt->pmureg);
}
}
wdt_irq = platform_get_irq(pdev, 0);
if (wdt_irq < 0)
return wdt_irq;
/* get the memory region for the watchdog timer */
wdt->reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(wdt->reg_base))
return PTR_ERR(wdt->reg_base);
wdt->bus_clk = devm_clk_get(dev, "watchdog");
if (IS_ERR(wdt->bus_clk)) {
dev_err(dev, "failed to find bus clock\n");
return PTR_ERR(wdt->bus_clk);
}
ret = clk_prepare_enable(wdt->bus_clk);
if (ret < 0) {
dev_err(dev, "failed to enable bus clock\n");
return ret;
}
/*
* "watchdog_src" clock is optional; if it's not present -- just skip it
* and use "watchdog" clock as both bus and source clock.
*/
wdt->src_clk = devm_clk_get_optional(dev, "watchdog_src");
if (IS_ERR(wdt->src_clk)) {
dev_err_probe(dev, PTR_ERR(wdt->src_clk),
"failed to get source clock\n");
ret = PTR_ERR(wdt->src_clk);
goto err_bus_clk;
}
ret = clk_prepare_enable(wdt->src_clk);
if (ret) {
dev_err(dev, "failed to enable source clock\n");
goto err_bus_clk;
}
wdt->wdt_device.min_timeout = 1;
wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt);
ret = s3c2410wdt_cpufreq_register(wdt);
if (ret < 0) {
dev_err(dev, "failed to register cpufreq\n");
goto err_src_clk;
}
watchdog_set_drvdata(&wdt->wdt_device, wdt);
/* see if we can actually set the requested timer margin, and if
* not, try the default value */
watchdog_init_timeout(&wdt->wdt_device, tmr_margin, dev);
ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
wdt->wdt_device.timeout);
if (ret) {
ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
S3C2410_WATCHDOG_DEFAULT_TIME);
if (ret == 0) {
dev_warn(dev, "tmr_margin value out of range, default %d used\n",
S3C2410_WATCHDOG_DEFAULT_TIME);
} else {
dev_err(dev, "failed to use default timeout\n");
goto err_cpufreq;
}
}
ret = devm_request_irq(dev, wdt_irq, s3c2410wdt_irq, 0,
pdev->name, pdev);
if (ret != 0) {
dev_err(dev, "failed to install irq (%d)\n", ret);
goto err_cpufreq;
}
watchdog_set_nowayout(&wdt->wdt_device, nowayout);
watchdog_set_restart_priority(&wdt->wdt_device, 128);
wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
wdt->wdt_device.parent = dev;
/*
* If "tmr_atboot" param is non-zero, start the watchdog right now. Also
* set WDOG_HW_RUNNING bit, so that watchdog core can kick the watchdog.
*
* If we're not enabling the watchdog, then ensure it is disabled if it
* has been left running from the bootloader or other source.
*/
if (tmr_atboot) {
dev_info(dev, "starting watchdog timer\n");
s3c2410wdt_start(&wdt->wdt_device);
set_bit(WDOG_HW_RUNNING, &wdt->wdt_device.status);
} else {
s3c2410wdt_stop(&wdt->wdt_device);
}
ret = watchdog_register_device(&wdt->wdt_device);
if (ret)
goto err_cpufreq;
ret = s3c2410wdt_enable(wdt, true);
if (ret < 0)
goto err_unregister;
platform_set_drvdata(pdev, wdt);
/* print out a statement of readiness */
wtcon = readl(wdt->reg_base + S3C2410_WTCON);
dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
(wtcon & S3C2410_WTCON_ENABLE) ? "" : "in",
(wtcon & S3C2410_WTCON_RSTEN) ? "en" : "dis",
(wtcon & S3C2410_WTCON_INTEN) ? "en" : "dis");
return 0;
err_unregister:
watchdog_unregister_device(&wdt->wdt_device);
err_cpufreq:
s3c2410wdt_cpufreq_deregister(wdt);
err_src_clk:
clk_disable_unprepare(wdt->src_clk);
err_bus_clk:
clk_disable_unprepare(wdt->bus_clk);
return ret;
}