Add Reset Management Unit (RMU) support for Actions Semi S900 SoC of the Owl family series. RMU belongs to the Owl SoCs system-controller which also includes CMU (Clock Management Unit). Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@xxxxxxxxxx> --- drivers/reset/Kconfig | 6 ++ drivers/reset/Makefile | 1 + drivers/reset/reset-owl.c | 192 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 drivers/reset/reset-owl.c diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index c0b292be1b72..90627430569b 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -73,6 +73,12 @@ config RESET_MESON help This enables the reset driver for Amlogic Meson SoCs. +config RESET_OWL + bool "Actions Semi Owl SoCs Reset Driver" if COMPILE_TEST + default ARCH_ACTIONS + help + This enables the reset controller driver for Actions Semi Owl SoCs. + config RESET_OXNAS bool diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index c1261dcfe9ad..fa655319cf17 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_RESET_IMX7) += reset-imx7.o obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o obj-$(CONFIG_RESET_MESON) += reset-meson.o +obj-$(CONFIG_RESET_OWL) += reset-owl.o obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o diff --git a/drivers/reset/reset-owl.c b/drivers/reset/reset-owl.c new file mode 100644 index 000000000000..c4f07691fb36 --- /dev/null +++ b/drivers/reset/reset-owl.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Actions Semi Owl SoCs Reset Management Unit driver +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam <manivannan.sadhasivam@xxxxxxxxxx> + +#include <linux/delay.h> +#include <linux/mfd/syscon.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> + +#include <dt-bindings/reset/actions,s900-rmu.h> + +#define CMU_DEVRST0 0x00a8 +#define CMU_DEVRST1 0x00ac + +struct owl_reset_map { + u32 reg; + u32 bit; +}; + +struct owl_reset_hw { + const struct owl_reset_map *resets; + u32 num_resets; +}; + +struct owl_reset { + struct reset_controller_dev rcdev; + const struct owl_reset_hw *hw; + struct regmap *regmap; +}; + +static const struct owl_reset_map s900_resets[] = { + [S900_RESET_DMAC] = { CMU_DEVRST0, BIT(0) }, + [S900_RESET_SRAMI] = { CMU_DEVRST0, BIT(1) }, + [S900_RESET_DDR_CTL_PHY] = { CMU_DEVRST0, BIT(2) }, + [S900_RESET_NANDC0] = { CMU_DEVRST0, BIT(3) }, + [S900_RESET_SD0] = { CMU_DEVRST0, BIT(4) }, + [S900_RESET_SD1] = { CMU_DEVRST0, BIT(5) }, + [S900_RESET_PCM1] = { CMU_DEVRST0, BIT(6) }, + [S900_RESET_DE] = { CMU_DEVRST0, BIT(7) }, + [S900_RESET_LVDS] = { CMU_DEVRST0, BIT(8) }, + [S900_RESET_SD2] = { CMU_DEVRST0, BIT(9) }, + [S900_RESET_DSI] = { CMU_DEVRST0, BIT(10) }, + [S900_RESET_CSI0] = { CMU_DEVRST0, BIT(11) }, + [S900_RESET_BISP_AXI] = { CMU_DEVRST0, BIT(12) }, + [S900_RESET_CSI1] = { CMU_DEVRST0, BIT(13) }, + [S900_RESET_GPIO] = { CMU_DEVRST0, BIT(15) }, + [S900_RESET_EDP] = { CMU_DEVRST0, BIT(16) }, + [S900_RESET_AUDIO] = { CMU_DEVRST0, BIT(17) }, + [S900_RESET_PCM0] = { CMU_DEVRST0, BIT(18) }, + [S900_RESET_HDE] = { CMU_DEVRST0, BIT(21) }, + [S900_RESET_GPU3D_PA] = { CMU_DEVRST0, BIT(22) }, + [S900_RESET_IMX] = { CMU_DEVRST0, BIT(23) }, + [S900_RESET_SE] = { CMU_DEVRST0, BIT(24) }, + [S900_RESET_NANDC1] = { CMU_DEVRST0, BIT(25) }, + [S900_RESET_SD3] = { CMU_DEVRST0, BIT(26) }, + [S900_RESET_GIC] = { CMU_DEVRST0, BIT(27) }, + [S900_RESET_GPU3D_PB] = { CMU_DEVRST0, BIT(28) }, + [S900_RESET_DDR_CTL_PHY_AXI] = { CMU_DEVRST0, BIT(29) }, + [S900_RESET_CMU_DDR] = { CMU_DEVRST0, BIT(30) }, + [S900_RESET_DMM] = { CMU_DEVRST0, BIT(31) }, + [S900_RESET_USB2HUB] = { CMU_DEVRST1, BIT(0) }, + [S900_RESET_USB2HSIC] = { CMU_DEVRST1, BIT(1) }, + [S900_RESET_HDMI] = { CMU_DEVRST1, BIT(2) }, + [S900_RESET_HDCP2TX] = { CMU_DEVRST1, BIT(3) }, + [S900_RESET_UART6] = { CMU_DEVRST1, BIT(4) }, + [S900_RESET_UART0] = { CMU_DEVRST1, BIT(5) }, + [S900_RESET_UART1] = { CMU_DEVRST1, BIT(6) }, + [S900_RESET_UART2] = { CMU_DEVRST1, BIT(7) }, + [S900_RESET_SPI0] = { CMU_DEVRST1, BIT(8) }, + [S900_RESET_SPI1] = { CMU_DEVRST1, BIT(9) }, + [S900_RESET_SPI2] = { CMU_DEVRST1, BIT(10) }, + [S900_RESET_SPI3] = { CMU_DEVRST1, BIT(11) }, + [S900_RESET_I2C0] = { CMU_DEVRST1, BIT(12) }, + [S900_RESET_I2C1] = { CMU_DEVRST1, BIT(13) }, + [S900_RESET_USB3] = { CMU_DEVRST1, BIT(14) }, + [S900_RESET_UART3] = { CMU_DEVRST1, BIT(15) }, + [S900_RESET_UART4] = { CMU_DEVRST1, BIT(16) }, + [S900_RESET_UART5] = { CMU_DEVRST1, BIT(17) }, + [S900_RESET_I2C2] = { CMU_DEVRST1, BIT(18) }, + [S900_RESET_I2C3] = { CMU_DEVRST1, BIT(19) }, +}; + +static const struct owl_reset_hw s900_reset_hw = { + .resets = s900_resets, + .num_resets = ARRAY_SIZE(s900_resets), +}; + +static inline struct owl_reset *to_owl_reset(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct owl_reset, rcdev); +} + +static int owl_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct owl_reset *reset = to_owl_reset(rcdev); + const struct owl_reset_map *map = &reset->hw->resets[id]; + + return regmap_update_bits(reset->regmap, map->reg, map->bit, 0); +} + +static int owl_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct owl_reset *reset = to_owl_reset(rcdev); + const struct owl_reset_map *map = &reset->hw->resets[id]; + + return regmap_update_bits(reset->regmap, map->reg, map->bit, map->bit); +} + +static int owl_reset_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + owl_reset_assert(rcdev, id); + udelay(1); + owl_reset_deassert(rcdev, id); + + return 0; +} + +static int owl_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct owl_reset *reset = to_owl_reset(rcdev); + const struct owl_reset_map *map = &reset->hw->resets[id]; + u32 reg; + int ret; + + ret = regmap_read(reset->regmap, map->reg, ®); + if (ret) + return ret; + + /* + * The reset control API expects 0 if reset is not asserted, + * which is the opposite of what our hardware uses. + */ + return !(map->bit & reg); +} + +static const struct reset_control_ops owl_reset_ops = { + .assert = owl_reset_assert, + .deassert = owl_reset_deassert, + .reset = owl_reset_reset, + .status = owl_reset_status, +}; + +static int owl_reset_probe(struct platform_device *pdev) +{ + struct owl_reset *reset; + struct regmap *regmap; + const struct owl_reset_hw *hw; + + hw = of_device_get_match_data(&pdev->dev); + if (!hw) + return -EINVAL; + + reset = devm_kzalloc(&pdev->dev, sizeof(*reset), GFP_KERNEL); + if (!reset) + return -ENOMEM; + + regmap = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node)); + if (IS_ERR(regmap)) { + dev_err(&pdev->dev, "failed to get regmap\n"); + return PTR_ERR(regmap); + } + + reset->rcdev.of_node = pdev->dev.of_node; + reset->rcdev.ops = &owl_reset_ops; + reset->rcdev.nr_resets = hw->num_resets; + reset->hw = hw; + reset->regmap = regmap; + + return devm_reset_controller_register(&pdev->dev, &reset->rcdev); +} + +static const struct of_device_id owl_reset_of_match[] = { + { .compatible = "actions,s900-rmu", .data = &s900_reset_hw }, + { /* sentinel */ } +}; + +static struct platform_driver owl_reset_driver = { + .probe = owl_reset_probe, + .driver = { + .name = "owl-reset", + .of_match_table = owl_reset_of_match, + }, +}; +builtin_platform_driver(owl_reset_driver); -- 2.17.1 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html