summaryrefslogtreecommitdiff
path: root/drivers/reset
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/reset')
-rw-r--r--drivers/reset/Kconfig35
-rw-r--r--drivers/reset/Makefile4
-rw-r--r--drivers/reset/gpio-reset.c217
-rw-r--r--drivers/reset/reset-dispmix.c399
-rw-r--r--drivers/reset/reset-imx-audiomix.c123
-rw-r--r--drivers/reset/reset-imx-hdmimix.c164
-rw-r--r--drivers/reset/reset-imx7.c14
7 files changed, 953 insertions, 3 deletions
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 7b07281aa0ae..6e976729213b 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -49,6 +49,15 @@ config RESET_BRCMSTB
This enables the reset controller driver for Broadcom STB SoCs using
a SUN_TOP_CTRL_SW_INIT style controller.
+config RESET_DISPMIX
+ tristate "IMX Display Mix reset support"
+ default y
+ select REGMAP_MMIO
+ depends on ARCH_MXC
+ help
+ This driver provides support for Display Mix reset that is controlled
+ by dispmix GPR registers.
+
config RESET_HSDK
bool "Synopsys HSDK Reset Driver"
depends on HAS_IOMEM
@@ -57,19 +66,41 @@ config RESET_HSDK
This enables the reset controller driver for HSDK board.
config RESET_IMX7
- bool "i.MX7/8 Reset Driver" if COMPILE_TEST
- depends on HAS_IOMEM
+ tristate "i.MX7/8 Reset Driver"
+ depends on HAS_IOMEM || COMPILE_TEST
default SOC_IMX7D || (ARM64 && ARCH_MXC)
select MFD_SYSCON
help
This enables the reset controller driver for i.MX7 SoCs.
+config RESET_IMX_HDMIMIX
+ tristate "i.MX HDMIMIX Reset Driver"
+ depends on HAS_IOMEM || COMPILE_TEST
+ default ARCH_MXC
+ help
+ This enables the hdmimix reset controller driver for i.MX8MP.
+
+config RESET_IMX_AUDIOMIX
+ tristate "i.MX Audiomix Reset Driver"
+ depends on HAS_IOMEM || COMPILE_TEST
+ default ARCH_MXC
+ help
+ This enables the audiomix reset controller driver for i.MX SoCs.
+
config RESET_LANTIQ
bool "Lantiq XWAY Reset Driver" if COMPILE_TEST
default SOC_TYPE_XWAY
help
This enables the reset controller driver for Lantiq / Intel XWAY SoCs.
+config RESET_GPIO
+ tristate "GPIO reset controller support"
+ default y
+ depends on GPIOLIB && OF
+ help
+ This driver provides support for reset lines that are controlled
+ directly by GPIOs.
+
config RESET_LPC18XX
bool "LPC18xx/43xx Reset Driver" if COMPILE_TEST
default ARCH_LPC18XX
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index cf60ce526064..9367c7aed2f7 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -8,9 +8,13 @@ obj-$(CONFIG_RESET_ATH79) += reset-ath79.o
obj-$(CONFIG_RESET_AXS10X) += reset-axs10x.o
obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o
obj-$(CONFIG_RESET_BRCMSTB) += reset-brcmstb.o
+obj-$(CONFIG_RESET_DISPMIX) += reset-dispmix.o
obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
+obj-$(CONFIG_RESET_IMX_HDMIMIX) += reset-imx-hdmimix.o
+obj-$(CONFIG_RESET_IMX_AUDIOMIX) += reset-imx-audiomix.o
obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o
+obj-$(CONFIG_RESET_GPIO) += gpio-reset.o
obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
obj-$(CONFIG_RESET_MESON) += reset-meson.o
obj-$(CONFIG_RESET_MESON_AUDIO_ARB) += reset-meson-audio-arb.o
diff --git a/drivers/reset/gpio-reset.c b/drivers/reset/gpio-reset.c
new file mode 100644
index 000000000000..cfd3fe02611b
--- /dev/null
+++ b/drivers/reset/gpio-reset.c
@@ -0,0 +1,217 @@
+/*
+ * GPIO Reset Controller driver
+ *
+ * Copyright 2013 Philipp Zabel, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/reset-controller.h>
+
+struct gpio_reset_data {
+ struct reset_controller_dev rcdev;
+ unsigned int gpio;
+ bool active_low;
+ s32 delay_us;
+ s32 post_delay_ms;
+};
+
+static void gpio_reset_set(struct reset_controller_dev *rcdev, int asserted)
+{
+ struct gpio_reset_data *drvdata = container_of(rcdev,
+ struct gpio_reset_data, rcdev);
+ int value = asserted;
+
+ if (drvdata->active_low)
+ value = !value;
+
+ gpio_set_value_cansleep(drvdata->gpio, value);
+}
+
+static int gpio_reset(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct gpio_reset_data *drvdata = container_of(rcdev,
+ struct gpio_reset_data, rcdev);
+
+ if (drvdata->delay_us < 0)
+ return -ENOSYS;
+
+ gpio_reset_set(rcdev, 1);
+ udelay(drvdata->delay_us);
+ gpio_reset_set(rcdev, 0);
+
+ if (drvdata->post_delay_ms < 0)
+ return 0;
+
+ msleep(drvdata->post_delay_ms);
+ return 0;
+}
+
+static int gpio_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ gpio_reset_set(rcdev, 1);
+
+ return 0;
+}
+
+static int gpio_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ gpio_reset_set(rcdev, 0);
+
+ return 0;
+}
+
+static struct reset_control_ops gpio_reset_ops = {
+ .reset = gpio_reset,
+ .assert = gpio_reset_assert,
+ .deassert = gpio_reset_deassert,
+};
+
+static int of_gpio_reset_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ if (WARN_ON(reset_spec->args_count != 0))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int gpio_reset_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct gpio_reset_data *drvdata;
+ enum of_gpio_flags flags;
+ unsigned long gpio_flags;
+ bool initially_in_reset;
+ int ret;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (drvdata == NULL)
+ return -ENOMEM;
+
+ if (of_gpio_named_count(np, "reset-gpios") != 1) {
+ dev_err(&pdev->dev,
+ "reset-gpios property missing, or not a single gpio\n");
+ return -EINVAL;
+ }
+
+ drvdata->gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &flags);
+ if (drvdata->gpio == -EPROBE_DEFER) {
+ return drvdata->gpio;
+ } else if (!gpio_is_valid(drvdata->gpio)) {
+ dev_err(&pdev->dev, "invalid reset gpio: %d\n", drvdata->gpio);
+ return drvdata->gpio;
+ }
+
+ drvdata->active_low = flags & OF_GPIO_ACTIVE_LOW;
+
+ ret = of_property_read_u32(np, "reset-delay-us", &drvdata->delay_us);
+ if (ret < 0)
+ drvdata->delay_us = -1;
+ else if (drvdata->delay_us < 0)
+ dev_warn(&pdev->dev, "reset delay too high\n");
+
+ /* It is optional.
+ * Some devices need some milliseconds to wait after reset.
+ */
+ ret = of_property_read_u32(np, "reset-post-delay-ms", &drvdata->post_delay_ms);
+ if (ret < 0)
+ drvdata->post_delay_ms = -1;
+
+ initially_in_reset = of_property_read_bool(np, "initially-in-reset");
+ if (drvdata->active_low ^ initially_in_reset)
+ gpio_flags = GPIOF_OUT_INIT_HIGH;
+ else
+ gpio_flags = GPIOF_OUT_INIT_LOW;
+
+ ret = devm_gpio_request_one(&pdev->dev, drvdata->gpio, gpio_flags, NULL);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request gpio %d: %d\n",
+ drvdata->gpio, ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, drvdata);
+
+ drvdata->rcdev.of_node = np;
+ drvdata->rcdev.owner = THIS_MODULE;
+ drvdata->rcdev.nr_resets = 1;
+ drvdata->rcdev.ops = &gpio_reset_ops;
+ drvdata->rcdev.of_xlate = of_gpio_reset_xlate;
+ reset_controller_register(&drvdata->rcdev);
+
+ return 0;
+}
+
+static int gpio_reset_remove(struct platform_device *pdev)
+{
+ struct gpio_reset_data *drvdata = platform_get_drvdata(pdev);
+
+ reset_controller_unregister(&drvdata->rcdev);
+
+ return 0;
+}
+
+static struct of_device_id gpio_reset_dt_ids[] = {
+ { .compatible = "gpio-reset" },
+ { }
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int gpio_reset_suspend(struct device *dev)
+{
+ pinctrl_pm_select_sleep_state(dev);
+
+ return 0;
+}
+static int gpio_reset_resume(struct device *dev)
+{
+ pinctrl_pm_select_default_state(dev);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops gpio_reset_pm_ops = {
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(gpio_reset_suspend, gpio_reset_resume)
+};
+
+static struct platform_driver gpio_reset_driver = {
+ .probe = gpio_reset_probe,
+ .remove = gpio_reset_remove,
+ .driver = {
+ .name = "gpio-reset",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(gpio_reset_dt_ids),
+ .pm = &gpio_reset_pm_ops,
+ },
+};
+
+static int __init gpio_reset_init(void)
+{
+ return platform_driver_register(&gpio_reset_driver);
+}
+arch_initcall(gpio_reset_init);
+
+static void __exit gpio_reset_exit(void)
+{
+ platform_driver_unregister(&gpio_reset_driver);
+}
+module_exit(gpio_reset_exit);
+
+MODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>");
+MODULE_DESCRIPTION("gpio reset controller");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpio-reset");
+MODULE_DEVICE_TABLE(of, gpio_reset_dt_ids);
diff --git a/drivers/reset/reset-dispmix.c b/drivers/reset/reset-dispmix.c
new file mode 100644
index 000000000000..60daf7109d0b
--- /dev/null
+++ b/drivers/reset/reset-dispmix.c
@@ -0,0 +1,399 @@
+/*
+ * IMX Display Mix GPR reset driver
+ *
+ * Copyright 2019 NXP
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <dt-bindings/reset/imx8mm-dispmix.h>
+#include <dt-bindings/reset/imx8mn-dispmix.h>
+
+#define DRIVER_NAME "dispmix_reset_drv"
+
+/* DISPMIX GPR registers */
+#define DISPLAY_MIX_SFT_RSTN_CSR 0x00
+#define DISPLAY_MIX_CLK_EN_CSR 0x00
+#define GPR_MIPI_RESET_DIV 0x00
+
+struct dispmix_reset_controller {
+ struct reset_controller_dev rcdev;
+ struct device *dev;
+ struct regmap *rstcon;
+ struct clk *ipg_clk;
+ bool active_low;
+};
+
+struct dispmix_reset_entry {
+ uint32_t reg_off;
+ uint32_t bit_off;
+};
+
+struct dispmix_reset_pdata {
+ const struct dispmix_reset_entry *resets;
+ uint32_t nr_resets;
+ const struct regmap_config *config;
+};
+
+#define RESET_ENTRY(id, reg, bit) \
+ [id] = { .reg_off = (reg), .bit_off = (bit) }
+
+static const struct dispmix_reset_entry imx8mm_sft_rstn[] = {
+ /* dispmix reset entry */
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_CHIP_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 0),
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_IPG_HARD_ASYNC_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 1),
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_CSI_HRESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 2),
+ RESET_ENTRY(IMX8MM_CAMERA_PIXEL_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 3),
+ RESET_ENTRY(IMX8MM_MIPI_CSI_I_PRESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 4),
+ RESET_ENTRY(IMX8MM_MIPI_DSI_I_PRESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 5),
+ RESET_ENTRY(IMX8MM_BUS_RSTN_BLK_SYNC,
+ DISPLAY_MIX_SFT_RSTN_CSR, 6),
+};
+
+static const struct dispmix_reset_entry imx8mm_clk_en[] = {
+ /* dispmix clock enable entry */
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_CSI_HCLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 0),
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_SPU_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 1),
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_MEM_WRAPPER_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 2),
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_IPG_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 3),
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_IPG_CLK_S_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 4),
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_IPG_CLK_S_RAW_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 5),
+ RESET_ENTRY(IMX8MM_LCDIF_APB_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 6),
+ RESET_ENTRY(IMX8MM_LCDIF_PIXEL_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 7),
+ RESET_ENTRY(IMX8MM_MIPI_DSI_PCLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 8),
+ RESET_ENTRY(IMX8MM_MIPI_DSI_CLKREF_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 9),
+ RESET_ENTRY(IMX8MM_MIPI_CSI_ACLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 10),
+ RESET_ENTRY(IMX8MM_MIPI_CSI_PCLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 11),
+ RESET_ENTRY(IMX8MM_BUS_BLK_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 12),
+};
+
+static const struct dispmix_reset_entry imx8mm_mipi_rst[] = {
+ /* mipi lanes reset entry */
+ RESET_ENTRY(IMX8MM_MIPI_S_RESET,
+ GPR_MIPI_RESET_DIV, 16),
+ RESET_ENTRY(IMX8MM_MIPI_M_RESET,
+ GPR_MIPI_RESET_DIV, 17),
+};
+
+static const struct dispmix_reset_entry imx8mn_sft_rstn[] = {
+ /* dispmix reset entry */
+ RESET_ENTRY(IMX8MN_MIPI_DSI_PCLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 0),
+ RESET_ENTRY(IMX8MN_MIPI_DSI_CLKREF_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 1),
+ RESET_ENTRY(IMX8MN_MIPI_CSI_PCLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 2),
+ RESET_ENTRY(IMX8MN_MIPI_CSI_ACLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 3),
+ RESET_ENTRY(IMX8MN_LCDIF_PIXEL_CLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 4),
+ RESET_ENTRY(IMX8MN_LCDIF_APB_CLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 5),
+ RESET_ENTRY(IMX8MN_ISI_PROC_CLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 6),
+ RESET_ENTRY(IMX8MN_ISI_APB_CLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 7),
+ RESET_ENTRY(IMX8MN_BUS_BLK_CLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 8),
+};
+
+static const struct dispmix_reset_entry imx8mn_clk_en[] = {
+ /* dispmix clock enable entry */
+ RESET_ENTRY(IMX8MN_MIPI_DSI_PCLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 0),
+ RESET_ENTRY(IMX8MN_MIPI_DSI_CLKREF_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 1),
+ RESET_ENTRY(IMX8MN_MIPI_CSI_PCLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 2),
+ RESET_ENTRY(IMX8MN_MIPI_CSI_ACLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 3),
+ RESET_ENTRY(IMX8MN_LCDIF_PIXEL_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 4),
+ RESET_ENTRY(IMX8MN_LCDIF_APB_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 5),
+ RESET_ENTRY(IMX8MN_ISI_PROC_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 6),
+ RESET_ENTRY(IMX8MN_ISI_APB_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 7),
+ RESET_ENTRY(IMX8MN_BUS_BLK_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 8),
+};
+
+static const struct dispmix_reset_entry imx8mn_mipi_rst[] = {
+ /* mipi lanes reset entry */
+ RESET_ENTRY(IMX8MN_MIPI_S_RESET,
+ GPR_MIPI_RESET_DIV, 16),
+ RESET_ENTRY(IMX8MN_MIPI_M_RESET,
+ GPR_MIPI_RESET_DIV, 17),
+};
+
+static const struct regmap_config sft_rstn_config = {
+ .name = "sft_rstn",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x00,
+};
+
+static const struct regmap_config clk_en_config = {
+ .name = "clk_en",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x00,
+};
+
+static const struct regmap_config mipi_rst_config = {
+ .name = "mipi_rst",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x00,
+};
+
+static const struct dispmix_reset_pdata imx8mm_sft_rstn_pdata = {
+ .resets = imx8mm_sft_rstn,
+ .nr_resets = IMX8MM_DISPMIX_SFT_RSTN_NUM,
+ .config = &sft_rstn_config,
+};
+
+static const struct dispmix_reset_pdata imx8mm_clk_en_pdata = {
+ .resets = imx8mm_clk_en,
+ .nr_resets = IMX8MM_DISPMIX_CLK_EN_NUM,
+ .config = &clk_en_config,
+};
+
+static const struct dispmix_reset_pdata imx8mm_mipi_rst_pdata = {
+ .resets = imx8mm_mipi_rst,
+ .nr_resets = IMX8MM_MIPI_RESET_NUM,
+ .config = &mipi_rst_config,
+};
+
+static const struct dispmix_reset_pdata imx8mn_sft_rstn_pdata = {
+ .resets = imx8mn_sft_rstn,
+ .nr_resets = IMX8MN_DISPMIX_SFT_RSTN_NUM,
+ .config = &sft_rstn_config,
+};
+
+static const struct dispmix_reset_pdata imx8mn_clk_en_pdata = {
+ .resets = imx8mn_clk_en,
+ .nr_resets = IMX8MN_DISPMIX_CLK_EN_NUM,
+ .config = &clk_en_config,
+};
+
+static const struct dispmix_reset_pdata imx8mn_mipi_rst_pdata = {
+ .resets = imx8mn_mipi_rst,
+ .nr_resets = IMX8MN_MIPI_RESET_NUM,
+ .config = &mipi_rst_config,
+};
+
+static const struct of_device_id dispmix_reset_dt_ids[] = {
+ {
+ .compatible = "fsl,imx8mm-dispmix-sft-rstn",
+ .data = &imx8mm_sft_rstn_pdata,
+ },
+ {
+ .compatible = "fsl,imx8mm-dispmix-clk-en",
+ .data = &imx8mm_clk_en_pdata,
+ },
+ {
+ .compatible = "fsl,imx8mm-dispmix-mipi-rst",
+ .data = &imx8mm_mipi_rst_pdata,
+ },
+ {
+ .compatible = "fsl,imx8mn-dispmix-sft-rstn",
+ .data = &imx8mn_sft_rstn_pdata,
+ },
+ {
+ .compatible = "fsl,imx8mn-dispmix-clk-en",
+ .data = &imx8mn_clk_en_pdata,
+ },
+ {
+ .compatible = "fsl,imx8mn-dispmix-mipi-rst",
+ .data = &imx8mn_mipi_rst_pdata,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dispmix_reset_dt_ids);
+
+static int dispmix_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct dispmix_reset_controller *drcdev;
+ const struct of_device_id *of_id;
+ const struct dispmix_reset_pdata *pdata;
+ const struct dispmix_reset_entry *rstent;
+ struct regmap *rstcon;
+
+ if (id >= rcdev->nr_resets) {
+ pr_info("dispmix reset: %lu is not a valid line\n", id);
+ return -EINVAL;
+ }
+
+ drcdev = container_of(rcdev, struct dispmix_reset_controller, rcdev);
+ of_id = of_match_device(dispmix_reset_dt_ids, drcdev->dev);
+ pdata = of_id->data;
+
+ rstcon = drcdev->rstcon;
+ rstent = &pdata->resets[id];
+
+ pm_runtime_get_sync(drcdev->dev);
+ regmap_update_bits(rstcon, rstent->reg_off,
+ 1 << rstent->bit_off,
+ !drcdev->active_low << rstent->bit_off);
+ pm_runtime_put(drcdev->dev);
+
+ return 0;
+}
+
+static int dispmix_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct dispmix_reset_controller *drcdev;
+ const struct of_device_id *of_id;
+ const struct dispmix_reset_pdata *pdata;
+ const struct dispmix_reset_entry *rstent;
+ struct regmap *rstcon;
+
+ if (id >= rcdev->nr_resets) {
+ pr_info("dispmix reset: %lu is not a valid line\n", id);
+ return -EINVAL;
+ }
+
+ drcdev = container_of(rcdev, struct dispmix_reset_controller, rcdev);
+ of_id = of_match_device(dispmix_reset_dt_ids, drcdev->dev);
+ pdata = of_id->data;
+
+ rstcon = drcdev->rstcon;
+ rstent = &pdata->resets[id];
+
+ pm_runtime_get_sync(drcdev->dev);
+ regmap_update_bits(rstcon, rstent->reg_off,
+ 1 << rstent->bit_off,
+ !!drcdev->active_low << rstent->bit_off);
+ pm_runtime_put(drcdev->dev);
+
+ return 0;
+}
+
+static const struct reset_control_ops dispmix_reset_ops = {
+ .assert = dispmix_reset_assert,
+ .deassert = dispmix_reset_deassert,
+};
+
+static int dispmix_reset_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ const struct of_device_id *of_id;
+ struct dispmix_reset_controller *drcdev;
+ const struct dispmix_reset_pdata *pdata;
+ struct resource *res;
+ void __iomem *regs;
+ struct regmap *regmap;
+ struct clk *apb_clk;
+
+ drcdev = devm_kzalloc(dev, sizeof(*drcdev), GFP_KERNEL);
+ if (!drcdev)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ apb_clk = devm_clk_get(dev, "disp_apb_root_clk");
+ if (IS_ERR(apb_clk)) {
+ dev_err(dev, "Unable to get disp apb clock\n");
+ return PTR_ERR(apb_clk);
+ }
+
+ drcdev->active_low = of_property_read_bool(np, "active_low");
+
+ of_id = of_match_device(dispmix_reset_dt_ids, dev);
+ pdata = of_id->data;
+
+ /* init mmio regmap */
+ regmap = regmap_init_mmio_clk(NULL, NULL,
+ regs, pdata->config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Failed to init mmio regmap: %ld\n",
+ PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+ drcdev->rstcon = regmap;
+
+ platform_set_drvdata(pdev, drcdev);
+ pm_runtime_enable(dev);
+
+ /* register reset controller */
+ drcdev->dev = dev;
+ drcdev->rcdev.of_node = dev->of_node;
+ drcdev->rcdev.owner = THIS_MODULE;
+ drcdev->rcdev.nr_resets = pdata->nr_resets;
+ drcdev->rcdev.ops = &dispmix_reset_ops;
+
+ return devm_reset_controller_register(dev, &drcdev->rcdev);
+}
+
+static int dispmix_reset_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver dispmix_reset_driver = {
+ .probe = dispmix_reset_probe,
+ .remove = dispmix_reset_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(dispmix_reset_dt_ids),
+ },
+};
+
+builtin_platform_driver(dispmix_reset_driver);
+
+MODULE_DESCRIPTION("IMX Display Mix reset driver");
+MODULE_AUTHOR("Fancy Fang <chen.fang@nxp.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-imx-audiomix.c b/drivers/reset/reset-imx-audiomix.c
new file mode 100644
index 000000000000..25a950c09ccf
--- /dev/null
+++ b/drivers/reset/reset-imx-audiomix.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <dt-bindings/reset/imx-audiomix-reset.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset-controller.h>
+
+#define IMX_AUDIOMIX_EARC_CTRL_REG 0x200
+
+#define IMX_AUDIOMIX_EARC_RESET_BIT 0x0
+#define IMX_AUDIOMIX_EARC_PHY_RESET_BIT 0x1
+
+struct imx_audiomix_reset_data {
+ void __iomem *base;
+ struct reset_controller_dev rcdev;
+ spinlock_t lock;
+};
+
+static int imx_audiomix_reset_set(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct imx_audiomix_reset_data *drvdata = container_of(rcdev,
+ struct imx_audiomix_reset_data, rcdev);
+ void __iomem *reg_addr = drvdata->base;
+ unsigned long flags;
+ unsigned int offset;
+ u32 reg;
+
+ switch (id) {
+ case IMX_AUDIOMIX_EARC_PHY_RESET:
+ reg_addr += IMX_AUDIOMIX_EARC_CTRL_REG;
+ offset = IMX_AUDIOMIX_EARC_PHY_RESET_BIT;
+ break;
+ case IMX_AUDIOMIX_EARC_RESET:
+ reg_addr += IMX_AUDIOMIX_EARC_CTRL_REG;
+ offset = IMX_AUDIOMIX_EARC_RESET_BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (assert) {
+ pm_runtime_get_sync(rcdev->dev);
+ spin_lock_irqsave(&drvdata->lock, flags);
+ reg = readl(reg_addr);
+ writel(reg & ~BIT(offset), reg_addr);
+ spin_unlock_irqrestore(&drvdata->lock, flags);
+ } else {
+ spin_lock_irqsave(&drvdata->lock, flags);
+ reg = readl(reg_addr);
+ writel(reg | BIT(offset), reg_addr);
+ spin_unlock_irqrestore(&drvdata->lock, flags);
+ pm_runtime_put(rcdev->dev);
+ }
+
+ return 0;
+}
+
+static int imx_audiomix_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx_audiomix_reset_set(rcdev, id, true);
+}
+
+static int imx_audiomix_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx_audiomix_reset_set(rcdev, id, false);
+}
+
+static const struct reset_control_ops imx_audiomix_reset_ops = {
+ .assert = imx_audiomix_reset_assert,
+ .deassert = imx_audiomix_reset_deassert,
+};
+
+static int imx_audiomix_reset_probe(struct platform_device *pdev)
+{
+ struct imx_audiomix_reset_data *drvdata;
+ struct device *dev = &pdev->dev;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (drvdata == NULL)
+ return -ENOMEM;
+
+ drvdata->base = dev_get_drvdata(dev->parent);
+
+ platform_set_drvdata(pdev, drvdata);
+
+ pm_runtime_enable(dev);
+
+ spin_lock_init(&drvdata->lock);
+
+ drvdata->rcdev.owner = THIS_MODULE;
+ drvdata->rcdev.nr_resets = IMX_AUDIOMIX_RESET_NUM;
+ drvdata->rcdev.ops = &imx_audiomix_reset_ops;
+ drvdata->rcdev.of_node = dev->of_node;
+ drvdata->rcdev.dev = dev;
+
+ return devm_reset_controller_register(dev, &drvdata->rcdev);
+}
+
+static const struct of_device_id imx_audiomix_reset_dt_ids[] = {
+ { .compatible = "fsl,imx8mp-audiomix-reset", },
+ { /* sentinel */ },
+};
+
+static struct platform_driver imx_audiomix_reset_driver = {
+ .probe = imx_audiomix_reset_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = imx_audiomix_reset_dt_ids,
+ },
+};
+module_platform_driver(imx_audiomix_reset_driver);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/reset-imx-hdmimix.c b/drivers/reset/reset-imx-hdmimix.c
new file mode 100644
index 000000000000..79043b6e02c1
--- /dev/null
+++ b/drivers/reset/reset-imx-hdmimix.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright 2020 NXP
+ *
+ */
+
+#include <dt-bindings/reset/imx-hdmimix-reset.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset-controller.h>
+
+#define IMX_HDMIMIX_RESET_CTL0_REG 0x20
+
+#define IMX_HDMIMIX_RESET_CTL0_TX_TRNG_RESETN (1 << 20)
+#define IMX_HDMIMIX_RESET_CTL0_VID_LINK_SLV_RESETN (1 << 22)
+#define IMX_HDMIMIX_RESET_CTL0_PAI_RESETN (1 << 18)
+#define IMX_HDMIMIX_RESET_CTL0_IRQ_STEER_RESETN (1 << 16)
+#define IMX_HDMIMIX_RESET_CTL0_TX_KSV_MEM_RESETN (1 << 13)
+#define IMX_HDMIMIX_RESET_CTL0_TX_PHY_PRESETN (1 << 12)
+#define IMX_HDMIMIX_RESET_CTL0_TX_APBRSTZ (1 << 11)
+#define IMX_HDMIMIX_RESET_CTL0_TX_RSTZ (1 << 10)
+#define IMX_HDMIMIX_RESET_CTL0_FDCC_HDMI_RESETN (1 << 7)
+#define IMX_HDMIMIX_RESET_CTL0_FDCC_RESETN (1 << 6)
+#define IMX_HDMIMIX_RESET_CTL0_LCDIF_APB_RESETN (1 << 5)
+#define IMX_HDMIMIX_RESET_CTL0_LCDIF_ASYNC_RESETN (1 << 4)
+#define IMX_HDMIMIX_RESET_CTL0_NOC_RESETN (1 << 0)
+
+struct imx_hdmimix_reset_data {
+ void __iomem *base;
+ struct reset_controller_dev rcdev;
+ spinlock_t lock;
+};
+
+static int imx_hdmimix_reset_set(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct imx_hdmimix_reset_data *drvdata = container_of(rcdev,
+ struct imx_hdmimix_reset_data, rcdev);
+ void __iomem *reg_addr = drvdata->base + IMX_HDMIMIX_RESET_CTL0_REG;
+ unsigned long flags;
+ unsigned int val;
+ u32 reg;
+
+ switch (id) {
+ case IMX_HDMIMIX_HDMI_TX_RESET:
+ val = IMX_HDMIMIX_RESET_CTL0_TX_APBRSTZ |
+ IMX_HDMIMIX_RESET_CTL0_TX_RSTZ |
+ IMX_HDMIMIX_RESET_CTL0_FDCC_HDMI_RESETN |
+ IMX_HDMIMIX_RESET_CTL0_FDCC_RESETN;
+ break;
+ case IMX_HDMIMIX_HDMI_PHY_RESET:
+ val = IMX_HDMIMIX_RESET_CTL0_TX_PHY_PRESETN;
+ break;
+ case IMX_HDMIMIX_HDMI_PAI_RESET:
+ val = IMX_HDMIMIX_RESET_CTL0_PAI_RESETN;
+ break;
+ case IMX_HDMIMIX_HDMI_PVI_RESET:
+ val = IMX_HDMIMIX_RESET_CTL0_VID_LINK_SLV_RESETN;
+ break;
+ case IMX_HDMIMIX_HDMI_TRNG_RESET:
+ val = IMX_HDMIMIX_RESET_CTL0_TX_TRNG_RESETN;
+ break;
+ case IMX_HDMIMIX_IRQ_STEER_RESET:
+ val = IMX_HDMIMIX_RESET_CTL0_IRQ_STEER_RESETN;
+ break;
+ case IMX_HDMIMIX_HDMI_HDCP_RESET:
+ val = IMX_HDMIMIX_RESET_CTL0_TX_KSV_MEM_RESETN;
+ break;
+ case IMX_HDMIMIX_LCDIF_RESET:
+ val = IMX_HDMIMIX_RESET_CTL0_LCDIF_APB_RESETN |
+ IMX_HDMIMIX_RESET_CTL0_LCDIF_ASYNC_RESETN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (assert) {
+ pm_runtime_get_sync(rcdev->dev);
+ spin_lock_irqsave(&drvdata->lock, flags);
+ reg = readl(reg_addr);
+ writel(reg & ~val, reg_addr);
+ spin_unlock_irqrestore(&drvdata->lock, flags);
+ } else {
+ spin_lock_irqsave(&drvdata->lock, flags);
+ reg = readl(reg_addr);
+ writel(reg | val, reg_addr);
+ spin_unlock_irqrestore(&drvdata->lock, flags);
+ pm_runtime_put(rcdev->dev);
+ }
+ reg = readl(reg_addr);
+
+ return 0;
+}
+
+static int imx_hdmimix_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx_hdmimix_reset_set(rcdev, id, true);
+}
+
+static int imx_hdmimix_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx_hdmimix_reset_set(rcdev, id, false);
+}
+
+static int imx_hdmimix_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ imx_hdmimix_reset_set(rcdev, id, true);
+ return imx_hdmimix_reset_set(rcdev, id, false);
+}
+
+static const struct reset_control_ops imx_hdmimix_reset_ops = {
+ .reset = imx_hdmimix_reset,
+ .assert = imx_hdmimix_reset_assert,
+ .deassert = imx_hdmimix_reset_deassert,
+};
+
+static int imx_hdmimix_reset_probe(struct platform_device *pdev)
+{
+ struct imx_hdmimix_reset_data *drvdata;
+ struct device *dev = &pdev->dev;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (drvdata == NULL)
+ return -ENOMEM;
+
+ drvdata->base = dev_get_drvdata(dev->parent);
+
+ platform_set_drvdata(pdev, drvdata);
+
+ pm_runtime_enable(dev);
+
+ spin_lock_init(&drvdata->lock);
+
+ drvdata->rcdev.owner = THIS_MODULE;
+ drvdata->rcdev.nr_resets = IMX_HDMIMIX_RESET_NUM;
+ drvdata->rcdev.ops = &imx_hdmimix_reset_ops;
+ drvdata->rcdev.of_node = dev->of_node;
+ drvdata->rcdev.dev = dev;
+
+ return devm_reset_controller_register(dev, &drvdata->rcdev);
+}
+
+static const struct of_device_id imx_hdmimix_reset_dt_ids[] = {
+ { .compatible = "fsl,imx8mp-hdmimix-reset", },
+ { /* sentinel */ },
+};
+
+static struct platform_driver imx_hdmimix_reset_driver = {
+ .probe = imx_hdmimix_reset_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = imx_hdmimix_reset_dt_ids,
+ },
+};
+module_platform_driver(imx_hdmimix_reset_driver);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/reset-imx7.c b/drivers/reset/reset-imx7.c
index 1443a55a0c29..184792a5a18f 100644
--- a/drivers/reset/reset-imx7.c
+++ b/drivers/reset/reset-imx7.c
@@ -8,6 +8,7 @@
*/
#include <linux/mfd/syscon.h>
+#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
@@ -165,6 +166,9 @@ static const struct imx7_src_signal imx8mq_src_signals[IMX8MQ_RESET_NUM] = {
[IMX8MQ_RESET_A53_SOC_DBG_RESET] = { SRC_A53RCR0, BIT(20) },
[IMX8MQ_RESET_A53_L2RESET] = { SRC_A53RCR0, BIT(21) },
[IMX8MQ_RESET_SW_NON_SCLR_M4C_RST] = { SRC_M4RCR, BIT(0) },
+ [IMX8MQ_RESET_SW_M4C_RST] = { SRC_M4RCR, BIT(1) },
+ [IMX8MQ_RESET_SW_M4P_RST] = { SRC_M4RCR, BIT(2) },
+ [IMX8MQ_RESET_M4_ENABLE] = { SRC_M4RCR, BIT(3) },
[IMX8MQ_RESET_OTG1_PHY_RESET] = { SRC_USBOPHY1_RCR, BIT(0) },
[IMX8MQ_RESET_OTG2_PHY_RESET] = { SRC_USBOPHY2_RCR, BIT(0) },
[IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N] = { SRC_MIPIPHY_RCR, BIT(1) },
@@ -175,6 +179,7 @@ static const struct imx7_src_signal imx8mq_src_signals[IMX8MQ_RESET_NUM] = {
[IMX8MQ_RESET_PCIEPHY] = { SRC_PCIEPHY_RCR,
BIT(2) | BIT(1) },
[IMX8MQ_RESET_PCIEPHY_PERST] = { SRC_PCIEPHY_RCR, BIT(3) },
+ [IMX8MQ_RESET_PCIE_CTRL_APPS_CLK_REQ] = { SRC_PCIEPHY_RCR, BIT(4) },
[IMX8MQ_RESET_PCIE_CTRL_APPS_EN] = { SRC_PCIEPHY_RCR, BIT(6) },
[IMX8MQ_RESET_PCIE_CTRL_APPS_TURNOFF] = { SRC_PCIEPHY_RCR, BIT(11) },
[IMX8MQ_RESET_HDMI_PHY_APB_RESET] = { SRC_HDMI_RCR, BIT(0) },
@@ -184,6 +189,7 @@ static const struct imx7_src_signal imx8mq_src_signals[IMX8MQ_RESET_NUM] = {
[IMX8MQ_RESET_PCIEPHY2] = { SRC_PCIE2_RCR,
BIT(2) | BIT(1) },
[IMX8MQ_RESET_PCIEPHY2_PERST] = { SRC_PCIE2_RCR, BIT(3) },
+ [IMX8MQ_RESET_PCIE2_CTRL_APPS_CLK_REQ] = { SRC_PCIE2_RCR, BIT(4) },
[IMX8MQ_RESET_PCIE2_CTRL_APPS_EN] = { SRC_PCIE2_RCR, BIT(6) },
[IMX8MQ_RESET_PCIE2_CTRL_APPS_TURNOFF] = { SRC_PCIE2_RCR, BIT(11) },
[IMX8MQ_RESET_MIPI_CSI1_CORE_RESET] = { SRC_MIPIPHY1_RCR, BIT(0) },
@@ -218,13 +224,17 @@ static int imx8mq_reset_set(struct reset_controller_dev *rcdev,
udelay(10);
break;
+ case IMX8MQ_RESET_PCIEPHY_PERST:
case IMX8MQ_RESET_PCIE_CTRL_APPS_EN:
+ case IMX8MQ_RESET_PCIE_CTRL_APPS_CLK_REQ:
case IMX8MQ_RESET_PCIE2_CTRL_APPS_EN: /* fallthrough */
+ case IMX8MQ_RESET_PCIE2_CTRL_APPS_CLK_REQ:
case IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N: /* fallthrough */
case IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N: /* fallthrough */
case IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N: /* fallthrough */
case IMX8MQ_RESET_MIPI_DSI_RESET_N: /* fallthrough */
case IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N: /* fallthrough */
+ case IMX8MQ_RESET_M4_ENABLE:
value = assert ? 0 : bit;
break;
}
@@ -285,6 +295,7 @@ static const struct of_device_id imx7_reset_dt_ids[] = {
{ .compatible = "fsl,imx8mq-src", .data = &variant_imx8mq },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, imx7_reset_dt_ids);
static struct platform_driver imx7_reset_driver = {
.probe = imx7_reset_probe,
@@ -293,4 +304,5 @@ static struct platform_driver imx7_reset_driver = {
.of_match_table = imx7_reset_dt_ids,
},
};
-builtin_platform_driver(imx7_reset_driver);
+module_platform_driver(imx7_reset_driver);
+MODULE_LICENSE("GPL v2");