1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (c) 2022 BayLibre, SAS. 4 */ 5 6#include <clk.h> 7#include <dm.h> 8#include <dm/device_compat.h> 9#include <reset.h> 10#include <wdt.h> 11#include <asm/io.h> 12#include <linux/bitops.h> 13 14#define GXBB_WDT_CTRL_REG 0x0 15#define GXBB_WDT_TCNT_REG 0x8 16#define GXBB_WDT_RSET_REG 0xc 17 18#define GXBB_WDT_CTRL_SYS_RESET_NOW BIT(26) 19#define GXBB_WDT_CTRL_CLKDIV_EN BIT(25) 20#define GXBB_WDT_CTRL_CLK_EN BIT(24) 21#define GXBB_WDT_CTRL_EE_RESET BIT(21) 22#define GXBB_WDT_CTRL_EN BIT(18) 23 24#define GXBB_WDT_CTRL_DIV_MASK GENMASK(17, 0) 25#define GXBB_WDT_TCNT_SETUP_MASK GENMASK(15, 0) 26 27 28struct amlogic_wdt_priv { 29 void __iomem *reg_base; 30}; 31 32static int amlogic_wdt_set_timeout(struct udevice *dev, u64 timeout_ms) 33{ 34 struct amlogic_wdt_priv *data = dev_get_priv(dev); 35 36 if (timeout_ms > GXBB_WDT_TCNT_SETUP_MASK) { 37 dev_warn(dev, "%s: timeout_ms=%llu: maximum watchdog timeout exceeded\n", 38 __func__, timeout_ms); 39 timeout_ms = GXBB_WDT_TCNT_SETUP_MASK; 40 } 41 42 writel(timeout_ms, data->reg_base + GXBB_WDT_TCNT_REG); 43 44 return 0; 45} 46 47static int amlogic_wdt_stop(struct udevice *dev) 48{ 49 struct amlogic_wdt_priv *data = dev_get_priv(dev); 50 51 writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) & ~GXBB_WDT_CTRL_EN, 52 data->reg_base + GXBB_WDT_CTRL_REG); 53 54 return 0; 55} 56 57static int amlogic_wdt_start(struct udevice *dev, u64 time_ms, ulong flags) 58{ 59 struct amlogic_wdt_priv *data = dev_get_priv(dev); 60 61 writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) | GXBB_WDT_CTRL_EN, 62 data->reg_base + GXBB_WDT_CTRL_REG); 63 64 return amlogic_wdt_set_timeout(dev, time_ms); 65} 66 67static int amlogic_wdt_reset(struct udevice *dev) 68{ 69 struct amlogic_wdt_priv *data = dev_get_priv(dev); 70 71 writel(0, data->reg_base + GXBB_WDT_RSET_REG); 72 73 return 0; 74} 75 76static int amlogic_wdt_expire_now(struct udevice *dev, ulong flags) 77{ 78 struct amlogic_wdt_priv *data = dev_get_priv(dev); 79 80 writel(0, data->reg_base + GXBB_WDT_CTRL_SYS_RESET_NOW); 81 82 return 0; 83} 84 85static int amlogic_wdt_probe(struct udevice *dev) 86{ 87 struct amlogic_wdt_priv *data = dev_get_priv(dev); 88 int ret; 89 90 data->reg_base = dev_remap_addr(dev); 91 if (!data->reg_base) 92 return -EINVAL; 93 94 struct clk clk; 95 96 ret = clk_get_by_index(dev, 0, &clk); 97 if (ret) 98 return ret; 99 100 ret = clk_enable(&clk); 101 if (ret) 102 return ret; 103 104 /* Setup with 1ms timebase */ 105 writel(((clk_get_rate(&clk) / 1000) & GXBB_WDT_CTRL_DIV_MASK) | 106 GXBB_WDT_CTRL_EE_RESET | 107 GXBB_WDT_CTRL_CLK_EN | 108 GXBB_WDT_CTRL_CLKDIV_EN, 109 data->reg_base + GXBB_WDT_CTRL_REG); 110 111 return 0; 112} 113 114static const struct wdt_ops amlogic_wdt_ops = { 115 .start = amlogic_wdt_start, 116 .reset = amlogic_wdt_reset, 117 .stop = amlogic_wdt_stop, 118 .expire_now = amlogic_wdt_expire_now, 119}; 120 121static const struct udevice_id amlogic_wdt_ids[] = { 122 { .compatible = "amlogic,meson-gxbb-wdt" }, 123 {} 124}; 125 126U_BOOT_DRIVER(amlogic_wdt) = { 127 .name = "amlogic_wdt", 128 .id = UCLASS_WDT, 129 .of_match = amlogic_wdt_ids, 130 .priv_auto = sizeof(struct amlogic_wdt_priv), 131 .probe = amlogic_wdt_probe, 132 .ops = &amlogic_wdt_ops, 133 .flags = DM_FLAG_PRE_RELOC, 134}; 135