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 Beh��n <kabel@kernel.org> 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 <clk.h> 18#include <log.h> 19#include <wdt.h> 20#include <asm/global_data.h> 21#include <linux/bitops.h> 22#include <linux/kernel.h> 23#include <asm/io.h> 24#include <asm/arch/cpu.h> 25#include <asm/arch/soc.h> 26 27DECLARE_GLOBAL_DATA_PTR; 28 29struct orion_wdt_priv { 30 void __iomem *reg; 31 int wdt_counter_offset; 32 void __iomem *rstout; 33 void __iomem *rstout_mask; 34 u32 timeout; 35 unsigned long clk_rate; 36 struct clk clk; 37}; 38 39#define RSTOUT_ENABLE_BIT BIT(8) 40#define RSTOUT_MASK_BIT BIT(10) 41#define WDT_ENABLE_BIT BIT(8) 42 43#define TIMER_CTRL 0x0000 44#define TIMER_A370_STATUS 0x04 45 46#define WDT_AXP_FIXED_ENABLE_BIT BIT(10) 47#define WDT_A370_EXPIRED BIT(31) 48 49static int orion_wdt_reset(struct udevice *dev) 50{ 51 struct orion_wdt_priv *priv = dev_get_priv(dev); 52 53 /* Reload watchdog duration */ 54 writel(priv->clk_rate * priv->timeout, 55 priv->reg + priv->wdt_counter_offset); 56 57 return 0; 58} 59 60static int orion_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) 61{ 62 struct orion_wdt_priv *priv = dev_get_priv(dev); 63 u32 reg; 64 65 priv->timeout = DIV_ROUND_UP(timeout_ms, 1000); 66 67 /* Enable the fixed watchdog clock input */ 68 reg = readl(priv->reg + TIMER_CTRL); 69 reg |= WDT_AXP_FIXED_ENABLE_BIT; 70 writel(reg, priv->reg + TIMER_CTRL); 71 72 /* Set watchdog duration */ 73 writel(priv->clk_rate * priv->timeout, 74 priv->reg + priv->wdt_counter_offset); 75 76 /* Clear the watchdog expiration bit */ 77 reg = readl(priv->reg + TIMER_A370_STATUS); 78 reg &= ~WDT_A370_EXPIRED; 79 writel(reg, priv->reg + TIMER_A370_STATUS); 80 81 /* Enable watchdog timer */ 82 reg = readl(priv->reg + TIMER_CTRL); 83 reg |= WDT_ENABLE_BIT; 84 writel(reg, priv->reg + TIMER_CTRL); 85 86 /* Enable reset on watchdog */ 87 reg = readl(priv->rstout); 88 reg |= RSTOUT_ENABLE_BIT; 89 writel(reg, priv->rstout); 90 91 reg = readl(priv->rstout_mask); 92 reg &= ~RSTOUT_MASK_BIT; 93 writel(reg, priv->rstout_mask); 94 95 return 0; 96} 97 98static int orion_wdt_stop(struct udevice *dev) 99{ 100 struct orion_wdt_priv *priv = dev_get_priv(dev); 101 u32 reg; 102 103 /* Disable reset on watchdog */ 104 reg = readl(priv->rstout_mask); 105 reg |= RSTOUT_MASK_BIT; 106 writel(reg, priv->rstout_mask); 107 108 reg = readl(priv->rstout); 109 reg &= ~RSTOUT_ENABLE_BIT; 110 writel(reg, priv->rstout); 111 112 /* Disable watchdog timer */ 113 reg = readl(priv->reg + TIMER_CTRL); 114 reg &= ~WDT_ENABLE_BIT; 115 writel(reg, priv->reg + TIMER_CTRL); 116 117 return 0; 118} 119 120static inline bool save_reg_from_ofdata(struct udevice *dev, int index, 121 void __iomem **reg, int *offset) 122{ 123 fdt_addr_t addr; 124 fdt_size_t off; 125 126 addr = devfdt_get_addr_size_index(dev, index, &off); 127 if (addr == FDT_ADDR_T_NONE) 128 return false; 129 130 *reg = (void __iomem *) addr; 131 if (offset) 132 *offset = off; 133 134 return true; 135} 136 137static int orion_wdt_of_to_plat(struct udevice *dev) 138{ 139 struct orion_wdt_priv *priv = dev_get_priv(dev); 140 141 if (!save_reg_from_ofdata(dev, 0, &priv->reg, 142 &priv->wdt_counter_offset)) 143 goto err; 144 145 if (!save_reg_from_ofdata(dev, 1, &priv->rstout, NULL)) 146 goto err; 147 148 if (!save_reg_from_ofdata(dev, 2, &priv->rstout_mask, NULL)) 149 goto err; 150 151 return 0; 152err: 153 debug("%s: Could not determine Orion wdt IO addresses\n", __func__); 154 return -ENXIO; 155} 156 157static int orion_wdt_probe(struct udevice *dev) 158{ 159 struct orion_wdt_priv *priv = dev_get_priv(dev); 160 int ret; 161 162 debug("%s: Probing wdt%u\n", __func__, dev_seq(dev)); 163 orion_wdt_stop(dev); 164 165 ret = clk_get_by_name(dev, "fixed", &priv->clk); 166 if (!ret) 167 priv->clk_rate = clk_get_rate(&priv->clk); 168 else 169 priv->clk_rate = 25000000; 170 171 return 0; 172} 173 174static const struct wdt_ops orion_wdt_ops = { 175 .start = orion_wdt_start, 176 .reset = orion_wdt_reset, 177 .stop = orion_wdt_stop, 178}; 179 180static const struct udevice_id orion_wdt_ids[] = { 181 { .compatible = "marvell,armada-380-wdt" }, 182 {} 183}; 184 185U_BOOT_DRIVER(orion_wdt) = { 186 .name = "orion_wdt", 187 .id = UCLASS_WDT, 188 .of_match = orion_wdt_ids, 189 .probe = orion_wdt_probe, 190 .priv_auto = sizeof(struct orion_wdt_priv), 191 .of_to_plat = orion_wdt_of_to_plat, 192 .ops = &orion_wdt_ops, 193}; 194