Dual-voltage GPIO banks default to 3.3V, but when externally supplied
with 1.8V the internal logic must be explicitly reconfigured to match.

Add the ability to program IO power domain control registers through the
APBC block. These registers require unlocking the AIB Secure Access
Register (ASAR) before every read/write, since configuring a 1.8V domain
while 3.3V is externally supplied can cause back-powering and pin damage.

Signed-off-by: Eric Chung <[email protected]>

---
v3:
- Add SYSCON dependency in Kconfig.
- Fix not sorted issue in the driver.
---
 drivers/pinctrl/spacemit/Kconfig      |  2 +-
 drivers/pinctrl/spacemit/pinctrl-k1.c | 83 ++++++++++++++++++++++++++++++++++-
 2 files changed, 82 insertions(+), 3 deletions(-)

diff --git a/drivers/pinctrl/spacemit/Kconfig b/drivers/pinctrl/spacemit/Kconfig
index 6aab89e160c..ff754f5839c 100644
--- a/drivers/pinctrl/spacemit/Kconfig
+++ b/drivers/pinctrl/spacemit/Kconfig
@@ -1,6 +1,6 @@
 config PINCTRL_SPACEMIT_K1
        bool "Spacemit K1 SoC pinctrl driver"
-       depends on PINCTRL_GENERIC && DM
+       depends on PINCTRL_GENERIC && DM && SYSCON
        help
          Supports pin multiplexing control on Spacemit K1 SoCs.
 
diff --git a/drivers/pinctrl/spacemit/pinctrl-k1.c 
b/drivers/pinctrl/spacemit/pinctrl-k1.c
index 3ebc397213b..95344f4ec72 100644
--- a/drivers/pinctrl/spacemit/pinctrl-k1.c
+++ b/drivers/pinctrl/spacemit/pinctrl-k1.c
@@ -12,6 +12,8 @@
 #include <linux/bitops.h>
 #include <linux/errno.h>
 #include <linux/io.h>
+#include <regmap.h>
+#include <syscon.h>
 
 /*
  * +---------+----------+-----------+--------+--------+----------+--------+
@@ -34,8 +36,23 @@
 #define PAD_PULLUP             BIT(14)
 #define PAD_PULL_EN            BIT(15)
 
-#define PIN_POWER_STATE_1V8            1800
-#define PIN_POWER_STATE_3V3            3300
+#define IO_PWR_DOMAIN_OFFSET   0x800
+
+#define IO_PWR_DOMAIN_GPIO2_Kx  0x0c
+#define IO_PWR_DOMAIN_GPIO3_K1  0x10
+#define IO_PWR_DOMAIN_MMC_Kx    0x1c
+#define IO_PWR_DOMAIN_QSPI_K1   0x20
+
+#define IO_PWR_DOMAIN_V18EN    BIT(2)
+
+#define APBC_ASFAR             0x50
+#define APBC_ASSAR             0x54
+
+#define APBC_ASFAR_AKEY                0xbaba
+#define APBC_ASSAR_AKEY                0xeb10
+
+#define PIN_POWER_STATE_1V8    1800
+#define PIN_POWER_STATE_3V3    3300
 
 enum spacemit_pin_io_type {
        IO_TYPE_NONE = 0,
@@ -60,12 +77,14 @@ struct spacemit_pinctrl_data {
        int (*get_pins)(struct udevice *dev);
        int (*get_functions)(struct udevice *dev);
        int (*get_io_type)(struct udevice *dev, unsigned int pin);
+       unsigned int (*pin_to_io_pd_offset)(unsigned int pin);
 };
 
 struct spacemit_pinctrl_priv {
        void __iomem            *regs;
        struct spacemit_pin_io  *io_pins;
        int                     nr_io_pins;
+       struct regmap           *regmap;
 };
 
 struct spacemit_pin_mux_config {
@@ -196,6 +215,28 @@ static int k1_get_io_type(struct udevice *dev, unsigned 
int selector)
        return -EINVAL;
 }
 
+static unsigned int spacemit_k1_pin_to_io_pd_offset(unsigned int pin)
+{
+       unsigned int offset = 0;
+
+       switch (pin) {
+       case 47 ... 52:
+               offset = IO_PWR_DOMAIN_GPIO3_K1;
+               break;
+       case 75 ... 80:
+               offset = IO_PWR_DOMAIN_GPIO2_Kx;
+               break;
+       case 98 ... 103:
+               offset = IO_PWR_DOMAIN_QSPI_K1;
+               break;
+       case 104 ... 109:
+               offset = IO_PWR_DOMAIN_MMC_Kx;
+               break;
+       }
+
+       return offset;
+}
+
 /* use IO high level output current as the table */
 static struct spacemit_pin_drv_strength spacemit_ds_1v8_tbl[4] = {
        { 0, 11 },
@@ -400,6 +441,35 @@ static const struct pinconf_param 
spacemit_pinconf_params[] = {
        { "power-source",       PIN_CONFIG_POWER_SOURCE,        U32_MAX },
 };
 
+static void spacemit_set_io_power_domain(struct udevice *dev,
+                                        unsigned int pin,
+                                        unsigned int io_type)
+{
+       struct spacemit_pinctrl_priv *priv = dev_get_priv(dev);
+       struct spacemit_pinctrl_data *data;
+       unsigned int offset;
+       u32 val = 0;
+
+       if (!priv->regmap)
+               return;
+
+       data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+       if (!data || !data->pin_to_io_pd_offset)
+               return;
+
+       offset = data->pin_to_io_pd_offset(pin);
+       if (!offset)
+               return;
+
+       if (io_type == IO_TYPE_1V8)
+               val = IO_PWR_DOMAIN_V18EN;
+
+       regmap_write(priv->regmap, APBC_ASFAR, APBC_ASFAR_AKEY);
+       regmap_write(priv->regmap, APBC_ASSAR, APBC_ASSAR_AKEY);
+
+       writel(val, priv->regs + IO_PWR_DOMAIN_OFFSET + offset);
+}
+
 static int spacemit_pinconf_set(struct udevice *dev, unsigned int pin_selector,
                                unsigned int param, unsigned int argument)
 {
@@ -456,6 +526,9 @@ static int spacemit_pinconf_set(struct udevice *dev, 
unsigned int pin_selector,
                        dev_err(dev, "Invalid power source (%d)\n", argument);
                        return -EINVAL;
                }
+               if (found)
+                       spacemit_set_io_power_domain(dev, pin_selector,
+                                                    priv->io_pins[i].io_type);
                break;
        default:
                return -EOPNOTSUPP;
@@ -485,6 +558,11 @@ static int spacemit_pinctrl_probe(struct udevice *dev)
                dev_err(dev, "Fail to allocate memory\n");
                return -ENOMEM;
        }
+       priv->regmap = syscon_regmap_lookup_by_phandle(dev, "spacemit,apbc");
+       if (IS_ERR(priv->regmap)) {
+               dev_warn(dev, "no syscon found, disable IO power domain 
switching\n");
+               priv->regmap = NULL;
+       }
 
        ret = clk_get_bulk(dev, &clks);
        if (ret) {
@@ -512,6 +590,7 @@ static const struct spacemit_pinctrl_data k1_pinctrl_data = 
{
        .get_pins       = k1_get_pins,
        .get_functions  = k1_get_functions,
        .get_io_type    = k1_get_io_type,
+       .pin_to_io_pd_offset = spacemit_k1_pin_to_io_pd_offset,
 };
 
 static const struct udevice_id spacemit_pinctrl_ids[] = {

-- 
2.51.0

Reply via email to