summaryrefslogtreecommitdiff
path: root/drivers/watchdog/s3c2410_wdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog/s3c2410_wdt.c')
-rw-r--r--drivers/watchdog/s3c2410_wdt.c126
1 files changed, 110 insertions, 16 deletions
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 686cf544d0ae..b774477190b6 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -24,16 +24,18 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
+#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/delay.h>
-#include <linux/soc/samsung/exynos-pmu.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 BIT(0)
#define S3C2410_WTCON_INTEN BIT(2)
@@ -63,11 +65,17 @@
#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
@@ -76,6 +84,10 @@
#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
*
@@ -113,6 +125,10 @@
* %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 BIT(0)
#define QUIRK_HAS_PMU_MASK_RESET BIT(1)
@@ -120,6 +136,7 @@
#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 \
@@ -188,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 = {
@@ -253,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,
@@ -262,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 = {
@@ -274,7 +319,8 @@ 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 = {
@@ -303,6 +349,32 @@ static const struct s3c2410_wdt_variant drv_data_gs101_cl1 = {
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 },
@@ -318,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);
@@ -344,9 +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);
+
+ if (t_max > UINT_MAX)
+ t_max = UINT_MAX;
- return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1)
- / S3C2410_WTCON_MAXDIV);
+ return t_max;
}
static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
@@ -500,7 +581,7 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd,
{
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;
@@ -510,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,
@@ -518,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);
@@ -643,7 +724,9 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt)
/* Choose Exynos850/ExynosAutov9 driver data w.r.t. cluster index */
if (variant == &drv_data_exynos850_cl0 ||
variant == &drv_data_exynosautov9_cl0 ||
- variant == &drv_data_gs101_cl0) {
+ variant == &drv_data_gs101_cl0 ||
+ variant == &drv_data_exynosautov920_cl0 ||
+ variant == &drv_data_exynos990_cl0) {
u32 index;
int err;
@@ -662,6 +745,12 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt)
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:
return dev_err_probe(dev, -EINVAL, "wrong cluster index: %u\n", index);
@@ -699,11 +788,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
return ret;
if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
- wdt->pmureg = exynos_get_pmu_regmap_by_phandle(dev->of_node,
- "samsung,syscon-phandle");
+ wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "samsung,syscon-phandle");
if (IS_ERR(wdt->pmureg))
return dev_err_probe(dev, PTR_ERR(wdt->pmureg),
- "PMU regmap lookup failed.\n");
+ "syscon regmap lookup failed.\n");
}
wdt_irq = platform_get_irq(pdev, 0);
@@ -727,6 +816,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
if (IS_ERR(wdt->src_clk))
return dev_err_probe(dev, PTR_ERR(wdt->src_clk), "failed to get source clock\n");
+ 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);