Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watchdog: aspeed: Support configuration of external signal properties

Add support for configuring the drive strength and polarity on the
AST2500, and the pulse duration on both the AST2400 and AST2500.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Tested-by: Matt Spinler <mspinler@linux.vnet.ibm.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

authored by

Andrew Jeffery and committed by
Wim Van Sebroeck
012c0460 e654f191

+102 -3
+102 -3
drivers/watchdog/aspeed_wdt.c
··· 23 23 u32 ctrl; 24 24 }; 25 25 26 + struct aspeed_wdt_config { 27 + u32 ext_pulse_width_mask; 28 + }; 29 + 30 + static const struct aspeed_wdt_config ast2400_config = { 31 + .ext_pulse_width_mask = 0xff, 32 + }; 33 + 34 + static const struct aspeed_wdt_config ast2500_config = { 35 + .ext_pulse_width_mask = 0xfffff, 36 + }; 37 + 26 38 static const struct of_device_id aspeed_wdt_of_table[] = { 27 - { .compatible = "aspeed,ast2400-wdt" }, 28 - { .compatible = "aspeed,ast2500-wdt" }, 39 + { .compatible = "aspeed,ast2400-wdt", .data = &ast2400_config }, 40 + { .compatible = "aspeed,ast2500-wdt", .data = &ast2500_config }, 29 41 { }, 30 42 }; 31 43 MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table); ··· 54 42 #define WDT_CTRL_WDT_INTR BIT(2) 55 43 #define WDT_CTRL_RESET_SYSTEM BIT(1) 56 44 #define WDT_CTRL_ENABLE BIT(0) 45 + 46 + /* 47 + * WDT_RESET_WIDTH controls the characteristics of the external pulse (if 48 + * enabled), specifically: 49 + * 50 + * * Pulse duration 51 + * * Drive mode: push-pull vs open-drain 52 + * * Polarity: Active high or active low 53 + * 54 + * Pulse duration configuration is available on both the AST2400 and AST2500, 55 + * though the field changes between SoCs: 56 + * 57 + * AST2400: Bits 7:0 58 + * AST2500: Bits 19:0 59 + * 60 + * This difference is captured in struct aspeed_wdt_config. 61 + * 62 + * The AST2500 exposes the drive mode and polarity options, but not in a 63 + * regular fashion. For read purposes, bit 31 represents active high or low, 64 + * and bit 30 represents push-pull or open-drain. With respect to write, magic 65 + * values need to be written to the top byte to change the state of the drive 66 + * mode and polarity bits. Any other value written to the top byte has no 67 + * effect on the state of the drive mode or polarity bits. However, the pulse 68 + * width value must be preserved (as desired) if written. 69 + */ 70 + #define WDT_RESET_WIDTH 0x18 71 + #define WDT_RESET_WIDTH_ACTIVE_HIGH BIT(31) 72 + #define WDT_ACTIVE_HIGH_MAGIC (0xA5 << 24) 73 + #define WDT_ACTIVE_LOW_MAGIC (0x5A << 24) 74 + #define WDT_RESET_WIDTH_PUSH_PULL BIT(30) 75 + #define WDT_PUSH_PULL_MAGIC (0xA8 << 24) 76 + #define WDT_OPEN_DRAIN_MAGIC (0x8A << 24) 57 77 58 78 #define WDT_RESTART_MAGIC 0x4755 59 79 ··· 183 139 184 140 static int aspeed_wdt_probe(struct platform_device *pdev) 185 141 { 142 + const struct aspeed_wdt_config *config; 143 + const struct of_device_id *ofdid; 186 144 struct aspeed_wdt *wdt; 187 145 struct resource *res; 188 146 struct device_node *np; 189 147 const char *reset_type; 148 + u32 duration; 190 149 int ret; 191 150 192 151 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); ··· 214 167 wdt->wdd.timeout = WDT_DEFAULT_TIMEOUT; 215 168 watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev); 216 169 170 + np = pdev->dev.of_node; 171 + 172 + ofdid = of_match_node(aspeed_wdt_of_table, np); 173 + if (!ofdid) 174 + return -EINVAL; 175 + config = ofdid->data; 176 + 217 177 wdt->ctrl = WDT_CTRL_1MHZ_CLK; 218 178 219 179 /* 220 180 * Control reset on a per-device basis to ensure the 221 181 * host is not affected by a BMC reboot 222 182 */ 223 - np = pdev->dev.of_node; 224 183 ret = of_property_read_string(np, "aspeed,reset-type", &reset_type); 225 184 if (ret) { 226 185 wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC | WDT_CTRL_RESET_SYSTEM; ··· 248 195 if (readl(wdt->base + WDT_CTRL) & WDT_CTRL_ENABLE) { 249 196 aspeed_wdt_start(&wdt->wdd); 250 197 set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); 198 + } 199 + 200 + if (of_device_is_compatible(np, "aspeed,ast2500-wdt")) { 201 + u32 reg = readl(wdt->base + WDT_RESET_WIDTH); 202 + 203 + reg &= config->ext_pulse_width_mask; 204 + if (of_property_read_bool(np, "aspeed,ext-push-pull")) 205 + reg |= WDT_PUSH_PULL_MAGIC; 206 + else 207 + reg |= WDT_OPEN_DRAIN_MAGIC; 208 + 209 + writel(reg, wdt->base + WDT_RESET_WIDTH); 210 + 211 + reg &= config->ext_pulse_width_mask; 212 + if (of_property_read_bool(np, "aspeed,ext-active-high")) 213 + reg |= WDT_ACTIVE_HIGH_MAGIC; 214 + else 215 + reg |= WDT_ACTIVE_LOW_MAGIC; 216 + 217 + writel(reg, wdt->base + WDT_RESET_WIDTH); 218 + } 219 + 220 + if (!of_property_read_u32(np, "aspeed,ext-pulse-duration", &duration)) { 221 + u32 max_duration = config->ext_pulse_width_mask + 1; 222 + 223 + if (duration == 0 || duration > max_duration) { 224 + dev_err(&pdev->dev, "Invalid pulse duration: %uus\n", 225 + duration); 226 + duration = max(1U, min(max_duration, duration)); 227 + dev_info(&pdev->dev, "Pulse duration set to %uus\n", 228 + duration); 229 + } 230 + 231 + /* 232 + * The watchdog is always configured with a 1MHz source, so 233 + * there is no need to scale the microsecond value. However we 234 + * need to offset it - from the datasheet: 235 + * 236 + * "This register decides the asserting duration of wdt_ext and 237 + * wdt_rstarm signal. The default value is 0xFF. It means the 238 + * default asserting duration of wdt_ext and wdt_rstarm is 239 + * 256us." 240 + * 241 + * This implies a value of 0 gives a 1us pulse. 242 + */ 243 + writel(duration - 1, wdt->base + WDT_RESET_WIDTH); 251 244 } 252 245 253 246 ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdd);