Add basic Power Management Unit (PMU) driver for Exynos SoCs. For now
it's only capable of changing UART path in PMU, which is needed for
E850-96 board. The driver's structure resembles the exynos-pmu driver
from Linux kernel, and although it's very basic and slim at the moment,
it can be easily extended in future if the need arises.

UCLASS_NOP is used, as there are no benefits in using more elaborate
classes like UCLASS_MISC in this case. The DM_FLAG_PROBE_AFTER_BIND flag
is added in bind function, as the probe function must be always called
for this driver.

Signed-off-by: Sam Protsenko <semen.protse...@linaro.org>
---
 drivers/soc/samsung/Kconfig      |  10 +++
 drivers/soc/samsung/Makefile     |   1 +
 drivers/soc/samsung/exynos-pmu.c | 102 +++++++++++++++++++++++++++++++
 3 files changed, 113 insertions(+)
 create mode 100644 drivers/soc/samsung/exynos-pmu.c

diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig
index ffb87fe79316..737b7ca8cd19 100644
--- a/drivers/soc/samsung/Kconfig
+++ b/drivers/soc/samsung/Kconfig
@@ -5,6 +5,16 @@ menuconfig SOC_SAMSUNG
 
 if SOC_SAMSUNG
 
+config EXYNOS_PMU
+       bool "Exynos PMU controller driver"
+       depends on ARCH_EXYNOS
+       select REGMAP
+       select SYSCON
+       help
+         Enable support for system controller configuration driver. It allows
+         one to configure system controller registers (e.g. some register in
+         PMU syscon) by providing register's offset, mask and value.
+
 config EXYNOS_USI
        bool "Exynos USI (Universal Serial Interface) driver"
        depends on ARCH_EXYNOS
diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
index 833ac073fbfa..0eb3ed8353b0 100644
--- a/drivers/soc/samsung/Makefile
+++ b/drivers/soc/samsung/Makefile
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0+
 
+obj-$(CONFIG_EXYNOS_PMU)       += exynos-pmu.o
 obj-$(CONFIG_EXYNOS_USI)       += exynos-usi.o
diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
new file mode 100644
index 000000000000..233ad4a908f5
--- /dev/null
+++ b/drivers/soc/samsung/exynos-pmu.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 Linaro Ltd.
+ * Author: Sam Protsenko <semen.protse...@linaro.org>
+ *
+ * Exynos PMU (Power Management Unit) driver.
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+
+#define EXYNOS850_UART_IO_SHARE_CTRL   0x0760
+#define SEL_RXD_AP_UART_SHIFT          16
+#define SEL_RXD_AP_UART_MASK           GENMASK(17, 16)
+#define SEL_TXD_GPIO_1_SHIFT           20
+#define SEL_TXD_GPIO_1_MASK            GENMASK(21, 20)
+#define RXD_GPIO_1                     0x3
+#define TXD_AP_UART                    0x0
+
+struct exynos_pmu {
+       struct udevice *dev;
+       const struct exynos_pmu_data *pmu_data;
+       struct regmap *regmap;
+};
+
+struct exynos_pmu_data {
+       int (*pmu_init)(struct exynos_pmu *priv);
+};
+
+static int exynos850_pmu_init(struct exynos_pmu *priv)
+{
+       ofnode node;
+       bool uart_debug_1;
+       unsigned int offset, mask, value;
+
+       node = dev_ofnode(priv->dev);
+       uart_debug_1 = ofnode_read_bool(node, "samsung,uart-debug-1");
+       if (!uart_debug_1)
+               return 0;
+
+       /*
+        * If uart1_pins are used for serial, AP UART lines have to be muxed
+        * in PMU block to UART_DEBUG_1 path (GPIO_1). By default (reset value)
+        * UART_DEBUG_0 path (uart0_pins) is connected to AP UART lines.
+        */
+       offset = EXYNOS850_UART_IO_SHARE_CTRL;
+       mask = SEL_RXD_AP_UART_MASK | SEL_TXD_GPIO_1_MASK;
+       value = RXD_GPIO_1 << SEL_RXD_AP_UART_SHIFT |
+               TXD_AP_UART << SEL_TXD_GPIO_1_SHIFT;
+       return regmap_update_bits(priv->regmap, offset, mask, value);
+}
+
+static const struct exynos_pmu_data exynos850_pmu_data = {
+       .pmu_init = exynos850_pmu_init,
+};
+
+static int exynos_pmu_bind(struct udevice *dev)
+{
+       dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND);
+       return 0;
+}
+
+static int exynos_pmu_probe(struct udevice *dev)
+{
+       ofnode node;
+       struct exynos_pmu *priv;
+
+       priv = dev_get_priv(dev);
+       priv->dev = dev;
+
+       node = dev_ofnode(dev);
+       priv->regmap = syscon_node_to_regmap(node);
+       if (IS_ERR(priv->regmap))
+               return PTR_ERR(priv->regmap);
+
+       priv->pmu_data = (struct exynos_pmu_data *)dev_get_driver_data(dev);
+       if (priv->pmu_data && priv->pmu_data->pmu_init)
+               return priv->pmu_data->pmu_init(priv);
+
+       return 0;
+}
+
+static const struct udevice_id exynos_pmu_ids[] = {
+       {
+               .compatible = "samsung,exynos850-pmu",
+               .data = (ulong)&exynos850_pmu_data
+       },
+       { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(exynos_pmu) = {
+       .name           = "exynos-pmu",
+       .id             = UCLASS_NOP,
+       .of_match       = exynos_pmu_ids,
+       .bind           = exynos_pmu_bind,
+       .probe          = exynos_pmu_probe,
+       .priv_auto      = sizeof(struct exynos_pmu),
+};
-- 
2.39.2

Reply via email to