1 /* 2 * drivers/watchdog/orion_wdt.c 3 * 4 * Watchdog driver for Orion/Kirkwood processors 5 * 6 * Authors: Tomas Hlavacek <tmshlvck@gmail.com> 7 * Sylver Bruneau <sylver.bruneau@googlemail.com> 8 * Marek Behun <marek.behun@nic.cz> 9 * 10 * This file is licensed under the terms of the GNU General Public 11 * License version 2. This program is licensed "as is" without any 12 * warranty of any kind, whether express or implied. 13 */ 14 15 #include <common.h> 16 #include <dm.h> 17 #include <wdt.h> 18 #include <asm/io.h> 19 #include <asm/arch/cpu.h> 20 #include <asm/arch/soc.h> 21 22 DECLARE_GLOBAL_DATA_PTR; 23 24 struct orion_wdt_priv { 25 void __iomem *reg; 26 int wdt_counter_offset; 27 void __iomem *rstout; 28 void __iomem *rstout_mask; 29 u32 timeout; 30 }; 31 32 #define RSTOUT_ENABLE_BIT BIT(8) 33 #define RSTOUT_MASK_BIT BIT(10) 34 #define WDT_ENABLE_BIT BIT(8) 35 36 #define TIMER_CTRL 0x0000 37 #define TIMER_A370_STATUS 0x04 38 39 #define WDT_AXP_FIXED_ENABLE_BIT BIT(10) 40 #define WDT_A370_EXPIRED BIT(31) 41 42 static int orion_wdt_reset(struct udevice *dev) 43 { 44 struct orion_wdt_priv *priv = dev_get_priv(dev); 45 46 /* Reload watchdog duration */ 47 writel(priv->timeout, priv->reg + priv->wdt_counter_offset); 48 49 return 0; 50 } 51 52 static int orion_wdt_start(struct udevice *dev, u64 timeout, ulong flags) 53 { 54 struct orion_wdt_priv *priv = dev_get_priv(dev); 55 u32 reg; 56 57 priv->timeout = (u32) timeout; 58 59 /* Enable the fixed watchdog clock input */ 60 reg = readl(priv->reg + TIMER_CTRL); 61 reg |= WDT_AXP_FIXED_ENABLE_BIT; 62 writel(reg, priv->reg + TIMER_CTRL); 63 64 /* Set watchdog duration */ 65 writel(priv->timeout, priv->reg + priv->wdt_counter_offset); 66 67 /* Clear the watchdog expiration bit */ 68 reg = readl(priv->reg + TIMER_A370_STATUS); 69 reg &= ~WDT_A370_EXPIRED; 70 writel(reg, priv->reg + TIMER_A370_STATUS); 71 72 /* Enable watchdog timer */ 73 reg = readl(priv->reg + TIMER_CTRL); 74 reg |= WDT_ENABLE_BIT; 75 writel(reg, priv->reg + TIMER_CTRL); 76 77 /* Enable reset on watchdog */ 78 reg = readl(priv->rstout); 79 reg |= RSTOUT_ENABLE_BIT; 80 writel(reg, priv->rstout); 81 82 reg = readl(priv->rstout_mask); 83 reg &= ~RSTOUT_MASK_BIT; 84 writel(reg, priv->rstout_mask); 85 86 return 0; 87 } 88 89 static int orion_wdt_stop(struct udevice *dev) 90 { 91 struct orion_wdt_priv *priv = dev_get_priv(dev); 92 u32 reg; 93 94 /* Disable reset on watchdog */ 95 reg = readl(priv->rstout_mask); 96 reg |= RSTOUT_MASK_BIT; 97 writel(reg, priv->rstout_mask); 98 99 reg = readl(priv->rstout); 100 reg &= ~RSTOUT_ENABLE_BIT; 101 writel(reg, priv->rstout); 102 103 /* Disable watchdog timer */ 104 reg = readl(priv->reg + TIMER_CTRL); 105 reg &= ~WDT_ENABLE_BIT; 106 writel(reg, priv->reg + TIMER_CTRL); 107 108 return 0; 109 } 110 111 static inline bool save_reg_from_ofdata(struct udevice *dev, int index, 112 void __iomem **reg, int *offset) 113 { 114 fdt_addr_t addr; 115 fdt_size_t off; 116 117 addr = fdtdec_get_addr_size_auto_noparent( 118 gd->fdt_blob, dev_of_offset(dev), "reg", index, &off, true); 119 120 if (addr == FDT_ADDR_T_NONE) 121 return false; 122 123 *reg = (void __iomem *) addr; 124 if (offset) 125 *offset = off; 126 127 return true; 128 } 129 130 static int orion_wdt_ofdata_to_platdata(struct udevice *dev) 131 { 132 struct orion_wdt_priv *priv = dev_get_priv(dev); 133 134 if (!save_reg_from_ofdata(dev, 0, &priv->reg, 135 &priv->wdt_counter_offset)) 136 goto err; 137 138 if (!save_reg_from_ofdata(dev, 1, &priv->rstout, NULL)) 139 goto err; 140 141 if (!save_reg_from_ofdata(dev, 2, &priv->rstout_mask, NULL)) 142 goto err; 143 144 return 0; 145 err: 146 debug("%s: Could not determine Orion wdt IO addresses\n", __func__); 147 return -ENXIO; 148 } 149 150 static int orion_wdt_probe(struct udevice *dev) 151 { 152 debug("%s: Probing wdt%u\n", __func__, dev->seq); 153 orion_wdt_stop(dev); 154 155 return 0; 156 } 157 158 static const struct wdt_ops orion_wdt_ops = { 159 .start = orion_wdt_start, 160 .reset = orion_wdt_reset, 161 .stop = orion_wdt_stop, 162 }; 163 164 static const struct udevice_id orion_wdt_ids[] = { 165 { .compatible = "marvell,armada-380-wdt" }, 166 {} 167 }; 168 169 U_BOOT_DRIVER(orion_wdt) = { 170 .name = "orion_wdt", 171 .id = UCLASS_WDT, 172 .of_match = orion_wdt_ids, 173 .probe = orion_wdt_probe, 174 .priv_auto_alloc_size = sizeof(struct orion_wdt_priv), 175 .ofdata_to_platdata = orion_wdt_ofdata_to_platdata, 176 .ops = &orion_wdt_ops, 177 }; 178