Home | History | Annotate | Download | only in watchdog
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Marvell Armada 37xx SoC Watchdog Driver
      4  *
      5  * Marek Behun <marek.behun (at) nic.cz>
      6  */
      7 
      8 #include <common.h>
      9 #include <dm.h>
     10 #include <wdt.h>
     11 #include <asm/io.h>
     12 #include <asm/arch/cpu.h>
     13 #include <asm/arch/soc.h>
     14 
     15 DECLARE_GLOBAL_DATA_PTR;
     16 
     17 struct a37xx_wdt {
     18 	void __iomem *sel_reg;
     19 	void __iomem *reg;
     20 	ulong clk_rate;
     21 	u64 timeout;
     22 };
     23 
     24 /*
     25  * We use Counter 1 for watchdog timer, because so does Marvell's Linux by
     26  * default.
     27  */
     28 
     29 #define CNTR_CTRL			0x10
     30 #define CNTR_CTRL_ENABLE		0x0001
     31 #define CNTR_CTRL_ACTIVE		0x0002
     32 #define CNTR_CTRL_MODE_MASK		0x000c
     33 #define CNTR_CTRL_MODE_ONESHOT		0x0000
     34 #define CNTR_CTRL_PRESCALE_MASK		0xff00
     35 #define CNTR_CTRL_PRESCALE_MIN		2
     36 #define CNTR_CTRL_PRESCALE_SHIFT	8
     37 
     38 #define CNTR_COUNT_LOW			0x14
     39 #define CNTR_COUNT_HIGH			0x18
     40 
     41 static void set_counter_value(struct a37xx_wdt *priv)
     42 {
     43 	writel(priv->timeout & 0xffffffff, priv->reg + CNTR_COUNT_LOW);
     44 	writel(priv->timeout >> 32, priv->reg + CNTR_COUNT_HIGH);
     45 }
     46 
     47 static void a37xx_wdt_enable(struct a37xx_wdt *priv)
     48 {
     49 	u32 reg = readl(priv->reg + CNTR_CTRL);
     50 
     51 	reg |= CNTR_CTRL_ENABLE;
     52 	writel(reg, priv->reg + CNTR_CTRL);
     53 }
     54 
     55 static void a37xx_wdt_disable(struct a37xx_wdt *priv)
     56 {
     57 	u32 reg = readl(priv->reg + CNTR_CTRL);
     58 
     59 	reg &= ~CNTR_CTRL_ENABLE;
     60 	writel(reg, priv->reg + CNTR_CTRL);
     61 }
     62 
     63 static int a37xx_wdt_reset(struct udevice *dev)
     64 {
     65 	struct a37xx_wdt *priv = dev_get_priv(dev);
     66 
     67 	if (!priv->timeout)
     68 		return -EINVAL;
     69 
     70 	a37xx_wdt_disable(priv);
     71 	set_counter_value(priv);
     72 	a37xx_wdt_enable(priv);
     73 
     74 	return 0;
     75 }
     76 
     77 static int a37xx_wdt_expire_now(struct udevice *dev, ulong flags)
     78 {
     79 	struct a37xx_wdt *priv = dev_get_priv(dev);
     80 
     81 	a37xx_wdt_disable(priv);
     82 	priv->timeout = 0;
     83 	set_counter_value(priv);
     84 	a37xx_wdt_enable(priv);
     85 
     86 	return 0;
     87 }
     88 
     89 static int a37xx_wdt_start(struct udevice *dev, u64 ms, ulong flags)
     90 {
     91 	struct a37xx_wdt *priv = dev_get_priv(dev);
     92 	u32 reg;
     93 
     94 	reg = readl(priv->reg + CNTR_CTRL);
     95 
     96 	if (reg & CNTR_CTRL_ACTIVE)
     97 		return -EBUSY;
     98 
     99 	/* set mode */
    100 	reg = (reg & ~CNTR_CTRL_MODE_MASK) | CNTR_CTRL_MODE_ONESHOT;
    101 
    102 	/* set prescaler to the min value */
    103 	reg &= ~CNTR_CTRL_PRESCALE_MASK;
    104 	reg |= CNTR_CTRL_PRESCALE_MIN << CNTR_CTRL_PRESCALE_SHIFT;
    105 
    106 	priv->timeout = ms * priv->clk_rate / 1000 / CNTR_CTRL_PRESCALE_MIN;
    107 
    108 	writel(reg, priv->reg + CNTR_CTRL);
    109 
    110 	set_counter_value(priv);
    111 	a37xx_wdt_enable(priv);
    112 
    113 	return 0;
    114 }
    115 
    116 static int a37xx_wdt_stop(struct udevice *dev)
    117 {
    118 	struct a37xx_wdt *priv = dev_get_priv(dev);
    119 
    120 	a37xx_wdt_disable(priv);
    121 
    122 	return 0;
    123 }
    124 
    125 static int a37xx_wdt_probe(struct udevice *dev)
    126 {
    127 	struct a37xx_wdt *priv = dev_get_priv(dev);
    128 	fdt_addr_t addr;
    129 
    130 	addr = dev_read_addr_index(dev, 0);
    131 	if (addr == FDT_ADDR_T_NONE)
    132 		goto err;
    133 	priv->sel_reg = (void __iomem *)addr;
    134 
    135 	addr = dev_read_addr_index(dev, 1);
    136 	if (addr == FDT_ADDR_T_NONE)
    137 		goto err;
    138 	priv->reg = (void __iomem *)addr;
    139 
    140 	priv->clk_rate = (ulong)get_ref_clk() * 1000000;
    141 
    142 	a37xx_wdt_disable(priv);
    143 
    144 	/*
    145 	 * We use timer 1 as watchdog timer (because Marvell's Linux uses that
    146 	 * timer as default), therefore we only set bit TIMER1_IS_WCHDOG_TIMER.
    147 	 */
    148 	writel(1 << 1, priv->sel_reg);
    149 
    150 	return 0;
    151 err:
    152 	dev_err(dev, "no io address\n");
    153 	return -ENODEV;
    154 }
    155 
    156 static const struct wdt_ops a37xx_wdt_ops = {
    157 	.start = a37xx_wdt_start,
    158 	.reset = a37xx_wdt_reset,
    159 	.stop = a37xx_wdt_stop,
    160 	.expire_now = a37xx_wdt_expire_now,
    161 };
    162 
    163 static const struct udevice_id a37xx_wdt_ids[] = {
    164 	{ .compatible = "marvell,armada-3700-wdt" },
    165 	{}
    166 };
    167 
    168 U_BOOT_DRIVER(a37xx_wdt) = {
    169 	.name = "armada_37xx_wdt",
    170 	.id = UCLASS_WDT,
    171 	.of_match = a37xx_wdt_ids,
    172 	.probe = a37xx_wdt_probe,
    173 	.priv_auto_alloc_size = sizeof(struct a37xx_wdt),
    174 	.ops = &a37xx_wdt_ops,
    175 };
    176