diff --git a/arch/arm/include/asm/arch-sunxi/ccu.h b/arch/arm/include/asm/arch-sunxi/ccu.h index 24efe0ab0a..5dd97ab227 100644 --- a/arch/arm/include/asm/arch-sunxi/ccu.h +++ b/arch/arm/include/asm/arch-sunxi/ccu.h @@ -8,12 +8,14 @@ #define _ASM_ARCH_CCU_H /** - * enum ccu_flags - ccu clock flags + * enum ccu_flags - ccu clock/reset flags * * @CCU_CLK_F_IS_VALID: is given clock gate is valid? + * @CCU_RST_F_IS_VALID: is given reset control is valid? */ enum ccu_flags { CCU_CLK_F_IS_VALID = BIT(0), + CCU_RST_F_IS_VALID = BIT(1), }; /** @@ -34,13 +36,33 @@ struct ccu_clk_gate { .flags = CCU_CLK_F_IS_VALID, \ } +/** + * struct ccu_reset - ccu reset + * @off: reset offset + * @bit: reset bit + * @flags: ccu reset control flags + */ +struct ccu_reset { + u16 off; + u32 bit; + enum ccu_flags flags; +}; + +#define RESET(_off, _bit) { \ + .off = _off, \ + .bit = _bit, \ + .flags = CCU_RST_F_IS_VALID, \ +} + /** * struct ccu_desc - clock control unit descriptor * * @gates: clock gates + * @resets: reset unit */ struct ccu_desc { const struct ccu_clk_gate *gates; + const struct ccu_reset *resets; }; /** @@ -62,4 +84,13 @@ int sunxi_clk_probe(struct udevice *dev); extern struct clk_ops sunxi_clk_ops; +/** + * sunxi_reset_bind() - reset binding + * + * @dev: reset device + * @count: reset count + * @return 0 success, or error value + */ +int sunxi_reset_bind(struct udevice *dev, ulong count); + #endif /* _ASM_ARCH_CCU_H */ diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig index bf5ecb3801..041d711e58 100644 --- a/drivers/clk/sunxi/Kconfig +++ b/drivers/clk/sunxi/Kconfig @@ -1,6 +1,7 @@ config CLK_SUNXI bool "Clock support for Allwinner SoCs" depends on CLK && ARCH_SUNXI + select DM_RESET default y help This enables support for common clock driver API on Allwinner diff --git a/drivers/clk/sunxi/clk_a64.c b/drivers/clk/sunxi/clk_a64.c index 803a2f711d..eb0a45d97f 100644 --- a/drivers/clk/sunxi/clk_a64.c +++ b/drivers/clk/sunxi/clk_a64.c @@ -10,6 +10,7 @@ #include #include #include +#include static const struct ccu_clk_gate a64_gates[] = { [CLK_BUS_OTG] = GATE(0x060, BIT(23)), @@ -26,10 +27,28 @@ static const struct ccu_clk_gate a64_gates[] = { [CLK_USB_OHCI1] = GATE(0x0cc, BIT(17)), }; +static const struct ccu_reset a64_resets[] = { + [RST_USB_PHY0] = RESET(0x0cc, BIT(0)), + [RST_USB_PHY1] = RESET(0x0cc, BIT(1)), + [RST_USB_HSIC] = RESET(0x0cc, BIT(2)), + + [RST_BUS_OTG] = RESET(0x2c0, BIT(23)), + [RST_BUS_EHCI0] = RESET(0x2c0, BIT(24)), + [RST_BUS_EHCI1] = RESET(0x2c0, BIT(25)), + [RST_BUS_OHCI0] = RESET(0x2c0, BIT(28)), + [RST_BUS_OHCI1] = RESET(0x2c0, BIT(29)), +}; + static const struct ccu_desc a64_ccu_desc = { .gates = a64_gates, + .resets = a64_resets, }; +static int a64_clk_bind(struct udevice *dev) +{ + return sunxi_reset_bind(dev, ARRAY_SIZE(a64_resets)); +} + static const struct udevice_id a64_ccu_ids[] = { { .compatible = "allwinner,sun50i-a64-ccu", .data = (ulong)&a64_ccu_desc }, @@ -43,4 +62,5 @@ U_BOOT_DRIVER(clk_sun50i_a64) = { .priv_auto_alloc_size = sizeof(struct ccu_priv), .ops = &sunxi_clk_ops, .probe = sunxi_clk_probe, + .bind = a64_clk_bind, }; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 3a6d61f440..a81e767696 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -113,4 +113,12 @@ config RESET_MEDIATEK help Support for reset controller on MediaTek SoCs. +config RESET_SUNXI + bool "RESET support for Allwinner SoCs" + depends on DM_RESET && ARCH_SUNXI + default y + help + This enables support for common reset driver for + Allwinner SoCs. + endmenu diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 8a4dcab8f6..4fad7d4129 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_RESET_ROCKCHIP) += reset-rockchip.o obj-$(CONFIG_RESET_MESON) += reset-meson.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_RESET_MEDIATEK) += reset-mediatek.o +obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c new file mode 100644 index 0000000000..364dc52fb7 --- /dev/null +++ b/drivers/reset/reset-sunxi.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2018 Amarula Solutions. + * Author: Jagan Teki + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct sunxi_reset_priv { + void *base; + ulong count; + const struct ccu_desc *desc; +}; + +static const struct ccu_reset *priv_to_reset(struct sunxi_reset_priv *priv, + unsigned long id) +{ + return &priv->desc->resets[id]; +} + +static int sunxi_reset_request(struct reset_ctl *reset_ctl) +{ + struct sunxi_reset_priv *priv = dev_get_priv(reset_ctl->dev); + + debug("%s: (RST#%ld)\n", __func__, reset_ctl->id); + + if (reset_ctl->id >= priv->count) + return -EINVAL; + + return 0; +} + +static int sunxi_reset_free(struct reset_ctl *reset_ctl) +{ + debug("%s: (RST#%ld)\n", __func__, reset_ctl->id); + + return 0; +} + +static int sunxi_set_reset(struct reset_ctl *reset_ctl, bool on) +{ + struct sunxi_reset_priv *priv = dev_get_priv(reset_ctl->dev); + const struct ccu_reset *reset = priv_to_reset(priv, reset_ctl->id); + u32 reg; + + if (!(reset->flags & CCU_RST_F_IS_VALID)) { + printf("%s: (RST#%ld) unhandled\n", __func__, reset_ctl->id); + return 0; + } + + debug("%s: (RST#%ld) off#0x%x, BIT(%d)\n", __func__, + reset_ctl->id, reset->off, ilog2(reset->bit)); + + reg = readl(priv->base + reset->off); + if (on) + reg |= reset->bit; + else + reg &= ~reset->bit; + + writel(reg, priv->base + reset->off); + + return 0; +} + +static int sunxi_reset_assert(struct reset_ctl *reset_ctl) +{ + return sunxi_set_reset(reset_ctl, false); +} + +static int sunxi_reset_deassert(struct reset_ctl *reset_ctl) +{ + return sunxi_set_reset(reset_ctl, true); +} + +struct reset_ops sunxi_reset_ops = { + .request = sunxi_reset_request, + .free = sunxi_reset_free, + .rst_assert = sunxi_reset_assert, + .rst_deassert = sunxi_reset_deassert, +}; + +static int sunxi_reset_probe(struct udevice *dev) +{ + struct sunxi_reset_priv *priv = dev_get_priv(dev); + + priv->base = dev_read_addr_ptr(dev); + + return 0; +} + +int sunxi_reset_bind(struct udevice *dev, ulong count) +{ + struct udevice *rst_dev; + struct sunxi_reset_priv *priv; + int ret; + + ret = device_bind_driver_to_node(dev, "sunxi_reset", "reset", + dev_ofnode(dev), &rst_dev); + if (ret) { + debug("failed to bind sunxi_reset driver (ret=%d)\n", ret); + return ret; + } + priv = malloc(sizeof(struct sunxi_reset_priv)); + priv->count = count; + priv->desc = (const struct ccu_desc *)dev_get_driver_data(dev); + rst_dev->priv = priv; + + return 0; +} + +U_BOOT_DRIVER(sunxi_reset) = { + .name = "sunxi_reset", + .id = UCLASS_RESET, + .ops = &sunxi_reset_ops, + .probe = sunxi_reset_probe, + .priv_auto_alloc_size = sizeof(struct sunxi_reset_priv), +};