Skip to content

Commit 450e248

Browse files
TroyMitchell911linusw
authored andcommitted
pinctrl: spacemit: support I/O power domain configuration
Dual-voltage GPIO banks default to 3.3V operation. Even when a bank is externally supplied with 1.8V, the internal logic remains in the 3.3V domain, leading to functional failures. Add support for programming the IO domain power control registers to allow explicit configuration for 1.8V operation. These registers are secure due to hardware safety constraints. Specifically, configuring the domain for 1.8V while externally supplying 3.3V causes back-powering and potential pin damage. Consequently, access requires unlocking the AIB Secure Access Register (ASAR) in the APBC block before any read or write operation. Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com> Signed-off-by: Linus Walleij <linusw@kernel.org>
1 parent e817f02 commit 450e248

1 file changed

Lines changed: 126 additions & 3 deletions

File tree

drivers/pinctrl/spacemit/pinctrl-k1.c

Lines changed: 126 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
#include <linux/io.h>
88
#include <linux/of.h>
99
#include <linux/platform_device.h>
10+
#include <linux/regmap.h>
1011
#include <linux/seq_file.h>
1112
#include <linux/spinlock.h>
13+
#include <linux/mfd/syscon.h>
1214
#include <linux/module.h>
1315
#include <linux/mutex.h>
1416

@@ -47,6 +49,27 @@
4749
#define PAD_PULLUP BIT(14)
4850
#define PAD_PULL_EN BIT(15)
4951

52+
#define IO_PWR_DOMAIN_OFFSET 0x800
53+
54+
#define IO_PWR_DOMAIN_GPIO2_Kx 0x0c
55+
#define IO_PWR_DOMAIN_MMC_Kx 0x1c
56+
57+
#define IO_PWR_DOMAIN_GPIO3_K1 0x10
58+
#define IO_PWR_DOMAIN_QSPI_K1 0x20
59+
60+
#define IO_PWR_DOMAIN_GPIO1_K3 0x04
61+
#define IO_PWR_DOMAIN_GPIO5_K3 0x10
62+
#define IO_PWR_DOMAIN_GPIO4_K3 0x20
63+
#define IO_PWR_DOMAIN_QSPI_K3 0x2c
64+
65+
#define IO_PWR_DOMAIN_V18EN BIT(2)
66+
67+
#define APBC_ASFAR 0x50
68+
#define APBC_ASSAR 0x54
69+
70+
#define APBC_ASFAR_AKEY 0xbaba
71+
#define APBC_ASSAR_AKEY 0xeb10
72+
5073
struct spacemit_pin_drv_strength {
5174
u8 val;
5275
u32 mA;
@@ -78,13 +101,16 @@ struct spacemit_pinctrl {
78101
raw_spinlock_t lock;
79102

80103
void __iomem *regs;
104+
105+
struct regmap *regmap_apbc;
81106
};
82107

83108
struct spacemit_pinctrl_data {
84109
const struct pinctrl_pin_desc *pins;
85110
const struct spacemit_pin *data;
86111
u16 npins;
87112
unsigned int (*pin_to_offset)(unsigned int pin);
113+
unsigned int (*pin_to_io_pd_offset)(unsigned int pin);
88114
const struct spacemit_pinctrl_dconf *dconf;
89115
};
90116

@@ -146,6 +172,56 @@ static unsigned int spacemit_k3_pin_to_offset(unsigned int pin)
146172
return offset << 2;
147173
}
148174

175+
static unsigned int spacemit_k1_pin_to_io_pd_offset(unsigned int pin)
176+
{
177+
unsigned int offset = 0;
178+
179+
switch (pin) {
180+
case 47 ... 52:
181+
offset = IO_PWR_DOMAIN_GPIO3_K1;
182+
break;
183+
case 75 ... 80:
184+
offset = IO_PWR_DOMAIN_GPIO2_Kx;
185+
break;
186+
case 98 ... 103:
187+
offset = IO_PWR_DOMAIN_QSPI_K1;
188+
break;
189+
case 104 ... 109:
190+
offset = IO_PWR_DOMAIN_MMC_Kx;
191+
break;
192+
}
193+
194+
return offset;
195+
}
196+
197+
static unsigned int spacemit_k3_pin_to_io_pd_offset(unsigned int pin)
198+
{
199+
unsigned int offset = 0;
200+
201+
switch (pin) {
202+
case 0 ... 20:
203+
offset = IO_PWR_DOMAIN_GPIO1_K3;
204+
break;
205+
case 21 ... 41:
206+
offset = IO_PWR_DOMAIN_GPIO2_Kx;
207+
break;
208+
case 76 ... 98:
209+
offset = IO_PWR_DOMAIN_GPIO4_K3;
210+
break;
211+
case 99 ... 127:
212+
offset = IO_PWR_DOMAIN_GPIO5_K3;
213+
break;
214+
case 132 ... 137:
215+
offset = IO_PWR_DOMAIN_MMC_Kx;
216+
break;
217+
case 138 ... 144:
218+
offset = IO_PWR_DOMAIN_QSPI_K3;
219+
break;
220+
}
221+
222+
return offset;
223+
}
224+
149225
static inline void __iomem *spacemit_pin_to_reg(struct spacemit_pinctrl *pctrl,
150226
unsigned int pin)
151227
{
@@ -365,6 +441,42 @@ static int spacemit_pctrl_check_power(struct pinctrl_dev *pctldev,
365441
return 0;
366442
}
367443

444+
static void spacemit_set_io_pwr_domain(struct spacemit_pinctrl *pctrl,
445+
const struct spacemit_pin *spin,
446+
const enum spacemit_pin_io_type type)
447+
{
448+
u32 offset, val = 0;
449+
450+
if (!pctrl->regmap_apbc)
451+
return;
452+
453+
offset = pctrl->data->pin_to_io_pd_offset(spin->pin);
454+
455+
/* Other bits are reserved so don't need to save them */
456+
if (type == IO_TYPE_1V8)
457+
val = IO_PWR_DOMAIN_V18EN;
458+
459+
/*
460+
* IO power domain registers are protected and cannot be accessed
461+
* directly. Before performing any read or write to the IO power
462+
* domain registers, an explicit unlock sequence must be issued
463+
* via the AIB Secure Access Register (ASAR).
464+
*
465+
* The unlock sequence allows exactly one subsequent access to the
466+
* IO power domain registers. After that access completes, the ASAR
467+
* keys are automatically cleared, and the registers become locked
468+
* again.
469+
*
470+
* This mechanism ensures that IO power domain configuration is
471+
* performed intentionally, as incorrect voltage settings may
472+
* result in functional failures or hardware damage.
473+
*/
474+
regmap_write(pctrl->regmap_apbc, APBC_ASFAR, APBC_ASFAR_AKEY);
475+
regmap_write(pctrl->regmap_apbc, APBC_ASSAR, APBC_ASSAR_AKEY);
476+
477+
writel_relaxed(val, pctrl->regs + IO_PWR_DOMAIN_OFFSET + offset);
478+
}
479+
368480
static int spacemit_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
369481
struct device_node *np,
370482
struct pinctrl_map **maps,
@@ -572,7 +684,8 @@ static int spacemit_pinconf_get(struct pinctrl_dev *pctldev,
572684

573685
#define ENABLE_DRV_STRENGTH BIT(1)
574686
#define ENABLE_SLEW_RATE BIT(2)
575-
static int spacemit_pinconf_generate_config(const struct spacemit_pin *spin,
687+
static int spacemit_pinconf_generate_config(struct spacemit_pinctrl *pctrl,
688+
const struct spacemit_pin *spin,
576689
const struct spacemit_pinctrl_dconf *dconf,
577690
unsigned long *configs,
578691
unsigned int num_configs,
@@ -646,6 +759,7 @@ static int spacemit_pinconf_generate_config(const struct spacemit_pin *spin,
646759
default:
647760
return -EINVAL;
648761
}
762+
spacemit_set_io_pwr_domain(pctrl, spin, type);
649763
}
650764

651765
val = spacemit_get_driver_strength(type, dconf, drv_strength);
@@ -701,7 +815,7 @@ static int spacemit_pinconf_set(struct pinctrl_dev *pctldev,
701815
const struct spacemit_pin *spin = spacemit_get_pin(pctrl, pin);
702816
u32 value;
703817

704-
if (spacemit_pinconf_generate_config(spin, pctrl->data->dconf,
818+
if (spacemit_pinconf_generate_config(pctrl, spin, pctrl->data->dconf,
705819
configs, num_configs, &value))
706820
return -EINVAL;
707821

@@ -724,7 +838,7 @@ static int spacemit_pinconf_group_set(struct pinctrl_dev *pctldev,
724838
return -EINVAL;
725839

726840
spin = spacemit_get_pin(pctrl, group->grp.pins[0]);
727-
if (spacemit_pinconf_generate_config(spin, pctrl->data->dconf,
841+
if (spacemit_pinconf_generate_config(pctrl, spin, pctrl->data->dconf,
728842
configs, num_configs, &value))
729843
return -EINVAL;
730844

@@ -795,6 +909,7 @@ static const struct pinconf_ops spacemit_pinconf_ops = {
795909

796910
static int spacemit_pinctrl_probe(struct platform_device *pdev)
797911
{
912+
struct device_node *np = pdev->dev.of_node;
798913
struct device *dev = &pdev->dev;
799914
struct spacemit_pinctrl *pctrl;
800915
struct clk *func_clk, *bus_clk;
@@ -816,6 +931,12 @@ static int spacemit_pinctrl_probe(struct platform_device *pdev)
816931
if (IS_ERR(pctrl->regs))
817932
return PTR_ERR(pctrl->regs);
818933

934+
pctrl->regmap_apbc = syscon_regmap_lookup_by_phandle(np, "spacemit,apbc");
935+
if (IS_ERR(pctrl->regmap_apbc)) {
936+
dev_warn(dev, "no syscon found, disable power voltage switch functionality\n");
937+
pctrl->regmap_apbc = NULL;
938+
}
939+
819940
func_clk = devm_clk_get_enabled(dev, "func");
820941
if (IS_ERR(func_clk))
821942
return dev_err_probe(dev, PTR_ERR(func_clk), "failed to get func clock\n");
@@ -1118,6 +1239,7 @@ static const struct spacemit_pinctrl_data k1_pinctrl_data = {
11181239
.data = k1_pin_data,
11191240
.npins = ARRAY_SIZE(k1_pin_desc),
11201241
.pin_to_offset = spacemit_k1_pin_to_offset,
1242+
.pin_to_io_pd_offset = spacemit_k1_pin_to_io_pd_offset,
11211243
.dconf = &k1_drive_conf,
11221244
};
11231245

@@ -1455,6 +1577,7 @@ static const struct spacemit_pinctrl_data k3_pinctrl_data = {
14551577
.data = k3_pin_data,
14561578
.npins = ARRAY_SIZE(k3_pin_desc),
14571579
.pin_to_offset = spacemit_k3_pin_to_offset,
1580+
.pin_to_io_pd_offset = spacemit_k3_pin_to_io_pd_offset,
14581581
.dconf = &k3_drive_conf,
14591582
};
14601583

0 commit comments

Comments
 (0)