diff options
Diffstat (limited to 'drivers/watchdog/s3c2410_wdt.c')
| -rw-r--r-- | drivers/watchdog/s3c2410_wdt.c | 421 |
1 files changed, 223 insertions, 198 deletions
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index d3fc8ed886ff..b774477190b6 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -9,6 +9,7 @@ * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk> */ +#include <linux/bits.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -23,21 +24,23 @@ #include <linux/slab.h> #include <linux/err.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/mfd/syscon.h> #include <linux/regmap.h> #include <linux/delay.h> +#include <linux/math64.h> #define S3C2410_WTCON 0x00 #define S3C2410_WTDAT 0x04 #define S3C2410_WTCNT 0x08 #define S3C2410_WTCLRINT 0x0c -#define S3C2410_WTCNT_MAXCNT 0xffff +#define S3C2410_WTCNT_MAXCNT_16 0xffff +#define S3C2410_WTCNT_MAXCNT_32 0xffffffff -#define S3C2410_WTCON_RSTEN (1 << 0) -#define S3C2410_WTCON_INTEN (1 << 2) -#define S3C2410_WTCON_ENABLE (1 << 5) +#define S3C2410_WTCON_RSTEN BIT(0) +#define S3C2410_WTCON_INTEN BIT(2) +#define S3C2410_WTCON_ENABLE BIT(5) +#define S3C2410_WTCON_DBGACK_MASK BIT(16) #define S3C2410_WTCON_DIV16 (0 << 3) #define S3C2410_WTCON_DIV32 (1 << 3) @@ -62,11 +65,28 @@ #define EXYNOS850_CLUSTER1_NONCPU_INT_EN 0x1644 #define EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT 0x1520 #define EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN 0x1544 +#define EXYNOSAUTOV920_CLUSTER0_NONCPU_OUT 0x1420 +#define EXYNOSAUTOV920_CLUSTER0_NONCPU_INT_EN 0x1444 +#define EXYNOSAUTOV920_CLUSTER1_NONCPU_OUT 0x1720 +#define EXYNOSAUTOV920_CLUSTER1_NONCPU_INT_EN 0x1744 #define EXYNOS850_CLUSTER0_WDTRESET_BIT 24 #define EXYNOS850_CLUSTER1_WDTRESET_BIT 23 #define EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT 25 #define EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT 24 +#define EXYNOSAUTOV920_CLUSTER0_WDTRESET_BIT 0 +#define EXYNOSAUTOV920_CLUSTER1_WDTRESET_BIT 1 + +#define GS_CLUSTER0_NONCPU_OUT 0x1220 +#define GS_CLUSTER1_NONCPU_OUT 0x1420 +#define GS_CLUSTER0_NONCPU_INT_EN 0x1244 +#define GS_CLUSTER1_NONCPU_INT_EN 0x1444 +#define GS_CLUSTER2_NONCPU_INT_EN 0x1644 +#define GS_RST_STAT_REG_OFFSET 0x3B44 + +#define EXYNOS990_CLUSTER2_NONCPU_OUT 0x1620 +#define EXYNOS990_CLUSTER2_NONCPU_INT_EN 0x1644 +#define EXYNOS990_CLUSTER2_WDTRESET_BIT 23 /** * DOC: Quirk flags for different Samsung watchdog IP-cores @@ -101,12 +121,22 @@ * %QUIRK_HAS_PMU_CNT_EN: PMU block has some register (e.g. CLUSTERx_NONCPU_OUT) * with "watchdog counter enable" bit. That bit should be set to make watchdog * counter running. + * + * %QUIRK_HAS_DBGACK_BIT: WTCON register has DBGACK_MASK bit. Setting the + * DBGACK_MASK bit disables the watchdog outputs when the SoC is in debug mode. + * Debug mode is determined by the DBGACK CPU signal. + * + * %QUIRK_HAS_32BIT_CNT: WTDAT and WTCNT are 32-bit registers. With these + * 32-bit registers, larger values will be set, which means that larger timeouts + * value can be set. */ -#define QUIRK_HAS_WTCLRINT_REG (1 << 0) -#define QUIRK_HAS_PMU_MASK_RESET (1 << 1) -#define QUIRK_HAS_PMU_RST_STAT (1 << 2) -#define QUIRK_HAS_PMU_AUTO_DISABLE (1 << 3) -#define QUIRK_HAS_PMU_CNT_EN (1 << 4) +#define QUIRK_HAS_WTCLRINT_REG BIT(0) +#define QUIRK_HAS_PMU_MASK_RESET BIT(1) +#define QUIRK_HAS_PMU_RST_STAT BIT(2) +#define QUIRK_HAS_PMU_AUTO_DISABLE BIT(3) +#define QUIRK_HAS_PMU_CNT_EN BIT(4) +#define QUIRK_HAS_DBGACK_BIT BIT(5) +#define QUIRK_HAS_32BIT_CNT BIT(6) /* These quirks require that we have a PMU register map */ #define QUIRKS_HAVE_PMUREG \ @@ -175,6 +205,7 @@ struct s3c2410_wdt { struct notifier_block freq_transition; const struct s3c2410_wdt_variant *drv_data; struct regmap *pmureg; + u32 max_cnt; }; static const struct s3c2410_wdt_variant drv_data_s3c2410 = { @@ -240,6 +271,32 @@ static const struct s3c2410_wdt_variant drv_data_exynos850_cl1 = { QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN, }; +static const struct s3c2410_wdt_variant drv_data_exynos990_cl0 = { + .mask_reset_reg = GS_CLUSTER0_NONCPU_INT_EN, + .mask_bit = 2, + .mask_reset_inv = true, + .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, + .rst_stat_bit = EXYNOS850_CLUSTER0_WDTRESET_BIT, + .cnt_en_reg = EXYNOSAUTOV920_CLUSTER0_NONCPU_OUT, + .cnt_en_bit = 7, + .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | + QUIRK_HAS_DBGACK_BIT, +}; + +static const struct s3c2410_wdt_variant drv_data_exynos990_cl2 = { + .mask_reset_reg = EXYNOS990_CLUSTER2_NONCPU_INT_EN, + .mask_bit = 2, + .mask_reset_inv = true, + .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, + .rst_stat_bit = EXYNOS990_CLUSTER2_WDTRESET_BIT, + .cnt_en_reg = EXYNOS990_CLUSTER2_NONCPU_OUT, + .cnt_en_bit = 7, + .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | + QUIRK_HAS_DBGACK_BIT, +}; + static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl0 = { .mask_reset_reg = EXYNOS850_CLUSTER0_NONCPU_INT_EN, .mask_bit = 2, @@ -249,7 +306,8 @@ static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl0 = { .cnt_en_reg = EXYNOS850_CLUSTER0_NONCPU_OUT, .cnt_en_bit = 7, .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | - QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN, + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | + QUIRK_HAS_DBGACK_BIT | QUIRK_HAS_32BIT_CNT, }; static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl1 = { @@ -261,10 +319,65 @@ static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl1 = { .cnt_en_reg = EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT, .cnt_en_bit = 7, .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | - QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN, + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | + QUIRK_HAS_DBGACK_BIT | QUIRK_HAS_32BIT_CNT, +}; + +static const struct s3c2410_wdt_variant drv_data_gs101_cl0 = { + .mask_reset_reg = GS_CLUSTER0_NONCPU_INT_EN, + .mask_bit = 2, + .mask_reset_inv = true, + .rst_stat_reg = GS_RST_STAT_REG_OFFSET, + .rst_stat_bit = 0, + .cnt_en_reg = GS_CLUSTER0_NONCPU_OUT, + .cnt_en_bit = 8, + .quirks = QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_MASK_RESET | + QUIRK_HAS_PMU_CNT_EN | QUIRK_HAS_WTCLRINT_REG | + QUIRK_HAS_DBGACK_BIT, +}; + +static const struct s3c2410_wdt_variant drv_data_gs101_cl1 = { + .mask_reset_reg = GS_CLUSTER1_NONCPU_INT_EN, + .mask_bit = 2, + .mask_reset_inv = true, + .rst_stat_reg = GS_RST_STAT_REG_OFFSET, + .rst_stat_bit = 1, + .cnt_en_reg = GS_CLUSTER1_NONCPU_OUT, + .cnt_en_bit = 7, + .quirks = QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_MASK_RESET | + QUIRK_HAS_PMU_CNT_EN | QUIRK_HAS_WTCLRINT_REG | + QUIRK_HAS_DBGACK_BIT, +}; + +static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl0 = { + .mask_reset_reg = EXYNOSAUTOV920_CLUSTER0_NONCPU_INT_EN, + .mask_bit = 2, + .mask_reset_inv = true, + .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, + .rst_stat_bit = EXYNOSAUTOV920_CLUSTER0_WDTRESET_BIT, + .cnt_en_reg = EXYNOSAUTOV920_CLUSTER0_NONCPU_OUT, + .cnt_en_bit = 8, + .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | + QUIRK_HAS_DBGACK_BIT | QUIRK_HAS_32BIT_CNT, +}; + +static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl1 = { + .mask_reset_reg = EXYNOSAUTOV920_CLUSTER1_NONCPU_INT_EN, + .mask_bit = 2, + .mask_reset_inv = true, + .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, + .rst_stat_bit = EXYNOSAUTOV920_CLUSTER1_WDTRESET_BIT, + .cnt_en_reg = EXYNOSAUTOV920_CLUSTER1_NONCPU_OUT, + .cnt_en_bit = 8, + .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | + QUIRK_HAS_DBGACK_BIT | QUIRK_HAS_32BIT_CNT, }; static const struct of_device_id s3c2410_wdt_match[] = { + { .compatible = "google,gs101-wdt", + .data = &drv_data_gs101_cl0 }, { .compatible = "samsung,s3c2410-wdt", .data = &drv_data_s3c2410 }, { .compatible = "samsung,s3c6410-wdt", @@ -277,8 +390,12 @@ static const struct of_device_id s3c2410_wdt_match[] = { .data = &drv_data_exynos7 }, { .compatible = "samsung,exynos850-wdt", .data = &drv_data_exynos850_cl0 }, + { .compatible = "samsung,exynos990-wdt", + .data = &drv_data_exynos990_cl0 }, { .compatible = "samsung,exynosautov9-wdt", .data = &drv_data_exynosautov9_cl0 }, + { .compatible = "samsung,exynosautov920-wdt", + .data = &drv_data_exynosautov920_cl0 }, {}, }; MODULE_DEVICE_TABLE(of, s3c2410_wdt_match); @@ -303,14 +420,14 @@ static inline unsigned long s3c2410wdt_get_freq(struct s3c2410_wdt *wdt) static inline unsigned int s3c2410wdt_max_timeout(struct s3c2410_wdt *wdt) { const unsigned long freq = s3c2410wdt_get_freq(wdt); + const u64 n_max = (u64)(S3C2410_WTCON_PRESCALE_MAX + 1) * + S3C2410_WTCON_MAXDIV * wdt->max_cnt; + u64 t_max = div64_ul(n_max, freq); - return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1) - / S3C2410_WTCON_MAXDIV); -} + if (t_max > UINT_MAX) + t_max = UINT_MAX; -static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb) -{ - return container_of(nb, struct s3c2410_wdt, freq_transition); + return t_max; } static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt *wdt, bool mask) @@ -381,13 +498,27 @@ static int s3c2410wdt_enable(struct s3c2410_wdt *wdt, bool en) return 0; } +/* Disable watchdog outputs if CPU is in debug mode */ +static void s3c2410wdt_mask_dbgack(struct s3c2410_wdt *wdt) +{ + unsigned long wtcon; + + if (!(wdt->drv_data->quirks & QUIRK_HAS_DBGACK_BIT)) + return; + + wtcon = readl(wdt->reg_base + S3C2410_WTCON); + wtcon |= S3C2410_WTCON_DBGACK_MASK; + writel(wtcon, wdt->reg_base + S3C2410_WTCON); +} + static int s3c2410wdt_keepalive(struct watchdog_device *wdd) { struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned long flags; - spin_lock(&wdt->lock); + spin_lock_irqsave(&wdt->lock, flags); writel(wdt->count, wdt->reg_base + S3C2410_WTCNT); - spin_unlock(&wdt->lock); + spin_unlock_irqrestore(&wdt->lock, flags); return 0; } @@ -404,10 +535,11 @@ static void __s3c2410wdt_stop(struct s3c2410_wdt *wdt) static int s3c2410wdt_stop(struct watchdog_device *wdd) { struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned long flags; - spin_lock(&wdt->lock); + spin_lock_irqsave(&wdt->lock, flags); __s3c2410wdt_stop(wdt); - spin_unlock(&wdt->lock); + spin_unlock_irqrestore(&wdt->lock, flags); return 0; } @@ -416,8 +548,9 @@ static int s3c2410wdt_start(struct watchdog_device *wdd) { unsigned long wtcon; struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned long flags; - spin_lock(&wdt->lock); + spin_lock_irqsave(&wdt->lock, flags); __s3c2410wdt_stop(wdt); @@ -438,22 +571,17 @@ static int s3c2410wdt_start(struct watchdog_device *wdd) writel(wdt->count, wdt->reg_base + S3C2410_WTDAT); writel(wdt->count, wdt->reg_base + S3C2410_WTCNT); writel(wtcon, wdt->reg_base + S3C2410_WTCON); - spin_unlock(&wdt->lock); + spin_unlock_irqrestore(&wdt->lock, flags); return 0; } -static inline int s3c2410wdt_is_running(struct s3c2410_wdt *wdt) -{ - return readl(wdt->reg_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE; -} - static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned int timeout) { struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); unsigned long freq = s3c2410wdt_get_freq(wdt); - unsigned int count; + unsigned long count; unsigned int divisor = 1; unsigned long wtcon; @@ -463,7 +591,7 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, freq = DIV_ROUND_UP(freq, 128); count = timeout * freq; - dev_dbg(wdt->dev, "Heartbeat: count=%d, timeout=%d, freq=%lu\n", + dev_dbg(wdt->dev, "Heartbeat: count=%lu, timeout=%d, freq=%lu\n", count, timeout, freq); /* if the count is bigger than the watchdog register, @@ -471,16 +599,16 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, actually make this value */ - if (count >= 0x10000) { - divisor = DIV_ROUND_UP(count, 0xffff); + if (count > wdt->max_cnt) { + divisor = DIV_ROUND_UP(count, wdt->max_cnt); - if (divisor > 0x100) { + if (divisor > S3C2410_WTCON_PRESCALE_MAX + 1) { dev_err(wdt->dev, "timeout %d too big\n", timeout); return -EINVAL; } } - dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%d (%08x)\n", + dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%lu (%08lx)\n", timeout, divisor, count, DIV_ROUND_UP(count, divisor)); count = DIV_ROUND_UP(count, divisor); @@ -562,73 +690,6 @@ static irqreturn_t s3c2410wdt_irq(int irqno, void *param) return IRQ_HANDLED; } -#ifdef CONFIG_ARM_S3C24XX_CPUFREQ - -static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb, - unsigned long val, void *data) -{ - int ret; - struct s3c2410_wdt *wdt = freq_to_wdt(nb); - - if (!s3c2410wdt_is_running(wdt)) - goto done; - - if (val == CPUFREQ_PRECHANGE) { - /* To ensure that over the change we don't cause the - * watchdog to trigger, we perform an keep-alive if - * the watchdog is running. - */ - - s3c2410wdt_keepalive(&wdt->wdt_device); - } else if (val == CPUFREQ_POSTCHANGE) { - s3c2410wdt_stop(&wdt->wdt_device); - - ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device, - wdt->wdt_device.timeout); - - if (ret >= 0) - s3c2410wdt_start(&wdt->wdt_device); - else - goto err; - } - -done: - return 0; - - err: - dev_err(wdt->dev, "cannot set new value for timeout %d\n", - wdt->wdt_device.timeout); - return ret; -} - -static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt) -{ - wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition; - - return cpufreq_register_notifier(&wdt->freq_transition, - CPUFREQ_TRANSITION_NOTIFIER); -} - -static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt) -{ - wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition; - - cpufreq_unregister_notifier(&wdt->freq_transition, - CPUFREQ_TRANSITION_NOTIFIER); -} - -#else - -static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt) -{ - return 0; -} - -static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt) -{ -} -#endif - static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) { unsigned int rst_stat; @@ -646,8 +707,8 @@ static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) return 0; } -static inline const struct s3c2410_wdt_variant * -s3c2410_get_wdt_drv_data(struct platform_device *pdev) +static inline int +s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt) { const struct s3c2410_wdt_variant *variant; struct device *dev = &pdev->dev; @@ -662,32 +723,48 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev) #ifdef CONFIG_OF /* Choose Exynos850/ExynosAutov9 driver data w.r.t. cluster index */ if (variant == &drv_data_exynos850_cl0 || - variant == &drv_data_exynosautov9_cl0) { + variant == &drv_data_exynosautov9_cl0 || + variant == &drv_data_gs101_cl0 || + variant == &drv_data_exynosautov920_cl0 || + variant == &drv_data_exynos990_cl0) { u32 index; int err; err = of_property_read_u32(dev->of_node, "samsung,cluster-index", &index); - if (err) { - dev_err(dev, "failed to get cluster index\n"); - return NULL; - } + if (err) + return dev_err_probe(dev, -EINVAL, "failed to get cluster index\n"); switch (index) { case 0: - return variant; + break; case 1: - return (variant == &drv_data_exynos850_cl0) ? - &drv_data_exynos850_cl1 : - &drv_data_exynosautov9_cl1; + if (variant == &drv_data_exynos850_cl0) + variant = &drv_data_exynos850_cl1; + else if (variant == &drv_data_exynosautov9_cl0) + variant = &drv_data_exynosautov9_cl1; + else if (variant == &drv_data_gs101_cl0) + variant = &drv_data_gs101_cl1; + else if (variant == &drv_data_exynosautov920_cl0) + variant = &drv_data_exynosautov920_cl1; + break; + case 2: + if (variant == &drv_data_exynos990_cl0) + variant = &drv_data_exynos990_cl2; + break; default: - dev_err(dev, "wrong cluster index: %u\n", index); - return NULL; + return dev_err_probe(dev, -EINVAL, "wrong cluster index: %u\n", index); } } #endif - return variant; + wdt->drv_data = variant; + return 0; +} + +static void s3c2410wdt_wdt_disable_action(void *data) +{ + s3c2410wdt_enable(data, false); } static int s3c2410wdt_probe(struct platform_device *pdev) @@ -706,17 +783,16 @@ static int s3c2410wdt_probe(struct platform_device *pdev) 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; + ret = s3c2410_get_wdt_drv_data(pdev, wdt); + if (ret) + return ret; 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); - } + if (IS_ERR(wdt->pmureg)) + return dev_err_probe(dev, PTR_ERR(wdt->pmureg), + "syscon regmap lookup failed.\n"); } wdt_irq = platform_get_irq(pdev, 0); @@ -728,45 +804,26 @@ static int s3c2410wdt_probe(struct platform_device *pdev) 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; - } + wdt->bus_clk = devm_clk_get_enabled(dev, "watchdog"); + if (IS_ERR(wdt->bus_clk)) + return dev_err_probe(dev, PTR_ERR(wdt->bus_clk), "failed to get bus clock\n"); /* * "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; - } + wdt->src_clk = devm_clk_get_optional_enabled(dev, "watchdog_src"); + if (IS_ERR(wdt->src_clk)) + return dev_err_probe(dev, PTR_ERR(wdt->src_clk), "failed to get source clock\n"); - ret = clk_prepare_enable(wdt->src_clk); - if (ret) { - dev_err(dev, "failed to enable source clock\n"); - goto err_bus_clk; - } + if (wdt->drv_data->quirks & QUIRK_HAS_32BIT_CNT) + wdt->max_cnt = S3C2410_WTCNT_MAXCNT_32; + else + wdt->max_cnt = S3C2410_WTCNT_MAXCNT_16; 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 @@ -778,21 +835,17 @@ static int s3c2410wdt_probe(struct platform_device *pdev) if (ret) { ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device, S3C2410_WATCHDOG_DEFAULT_TIME); - if (ret == 0) { + 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; - } + else + return dev_err_probe(dev, ret, "failed to use default timeout\n"); } 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; - } + if (ret != 0) + return dev_err_probe(dev, ret, "failed to install irq (%d)\n", ret); watchdog_set_nowayout(&wdt->wdt_device, nowayout); watchdog_set_restart_priority(&wdt->wdt_device, 128); @@ -800,6 +853,8 @@ static int s3c2410wdt_probe(struct platform_device *pdev) wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt); wdt->wdt_device.parent = dev; + s3c2410wdt_mask_dbgack(wdt); + /* * 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. @@ -815,13 +870,17 @@ static int s3c2410wdt_probe(struct platform_device *pdev) s3c2410wdt_stop(&wdt->wdt_device); } - ret = watchdog_register_device(&wdt->wdt_device); + ret = devm_watchdog_register_device(dev, &wdt->wdt_device); if (ret) - goto err_cpufreq; + return ret; ret = s3c2410wdt_enable(wdt, true); if (ret < 0) - goto err_unregister; + return ret; + + ret = devm_add_action_or_reset(dev, s3c2410wdt_wdt_disable_action, wdt); + if (ret) + return ret; platform_set_drvdata(pdev, wdt); @@ -835,39 +894,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev) (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; -} - -static int s3c2410wdt_remove(struct platform_device *dev) -{ - int ret; - struct s3c2410_wdt *wdt = platform_get_drvdata(dev); - - ret = s3c2410wdt_enable(wdt, false); - if (ret < 0) - return ret; - - watchdog_unregister_device(&wdt->wdt_device); - - s3c2410wdt_cpufreq_deregister(wdt); - - clk_disable_unprepare(wdt->src_clk); - clk_disable_unprepare(wdt->bus_clk); - - return 0; } static void s3c2410wdt_shutdown(struct platform_device *dev) @@ -922,7 +948,6 @@ static DEFINE_SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops, static struct platform_driver s3c2410wdt_driver = { .probe = s3c2410wdt_probe, - .remove = s3c2410wdt_remove, .shutdown = s3c2410wdt_shutdown, .id_table = s3c2410_wdt_ids, .driver = { |
