Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Troy Mitchell and committed by
Linus Walleij
450e2487 e817f022

+126 -3
+126 -3
drivers/pinctrl/spacemit/pinctrl-k1.c
··· 7 7 #include <linux/io.h> 8 8 #include <linux/of.h> 9 9 #include <linux/platform_device.h> 10 + #include <linux/regmap.h> 10 11 #include <linux/seq_file.h> 11 12 #include <linux/spinlock.h> 13 + #include <linux/mfd/syscon.h> 12 14 #include <linux/module.h> 13 15 #include <linux/mutex.h> 14 16 ··· 49 47 #define PAD_PULLUP BIT(14) 50 48 #define PAD_PULL_EN BIT(15) 51 49 50 + #define IO_PWR_DOMAIN_OFFSET 0x800 51 + 52 + #define IO_PWR_DOMAIN_GPIO2_Kx 0x0c 53 + #define IO_PWR_DOMAIN_MMC_Kx 0x1c 54 + 55 + #define IO_PWR_DOMAIN_GPIO3_K1 0x10 56 + #define IO_PWR_DOMAIN_QSPI_K1 0x20 57 + 58 + #define IO_PWR_DOMAIN_GPIO1_K3 0x04 59 + #define IO_PWR_DOMAIN_GPIO5_K3 0x10 60 + #define IO_PWR_DOMAIN_GPIO4_K3 0x20 61 + #define IO_PWR_DOMAIN_QSPI_K3 0x2c 62 + 63 + #define IO_PWR_DOMAIN_V18EN BIT(2) 64 + 65 + #define APBC_ASFAR 0x50 66 + #define APBC_ASSAR 0x54 67 + 68 + #define APBC_ASFAR_AKEY 0xbaba 69 + #define APBC_ASSAR_AKEY 0xeb10 70 + 52 71 struct spacemit_pin_drv_strength { 53 72 u8 val; 54 73 u32 mA; ··· 101 78 raw_spinlock_t lock; 102 79 103 80 void __iomem *regs; 81 + 82 + struct regmap *regmap_apbc; 104 83 }; 105 84 106 85 struct spacemit_pinctrl_data { ··· 110 85 const struct spacemit_pin *data; 111 86 u16 npins; 112 87 unsigned int (*pin_to_offset)(unsigned int pin); 88 + unsigned int (*pin_to_io_pd_offset)(unsigned int pin); 113 89 const struct spacemit_pinctrl_dconf *dconf; 114 90 }; 115 91 ··· 170 144 unsigned int offset = pin > 130 ? (pin + 2) : pin; 171 145 172 146 return offset << 2; 147 + } 148 + 149 + static unsigned int spacemit_k1_pin_to_io_pd_offset(unsigned int pin) 150 + { 151 + unsigned int offset = 0; 152 + 153 + switch (pin) { 154 + case 47 ... 52: 155 + offset = IO_PWR_DOMAIN_GPIO3_K1; 156 + break; 157 + case 75 ... 80: 158 + offset = IO_PWR_DOMAIN_GPIO2_Kx; 159 + break; 160 + case 98 ... 103: 161 + offset = IO_PWR_DOMAIN_QSPI_K1; 162 + break; 163 + case 104 ... 109: 164 + offset = IO_PWR_DOMAIN_MMC_Kx; 165 + break; 166 + } 167 + 168 + return offset; 169 + } 170 + 171 + static unsigned int spacemit_k3_pin_to_io_pd_offset(unsigned int pin) 172 + { 173 + unsigned int offset = 0; 174 + 175 + switch (pin) { 176 + case 0 ... 20: 177 + offset = IO_PWR_DOMAIN_GPIO1_K3; 178 + break; 179 + case 21 ... 41: 180 + offset = IO_PWR_DOMAIN_GPIO2_Kx; 181 + break; 182 + case 76 ... 98: 183 + offset = IO_PWR_DOMAIN_GPIO4_K3; 184 + break; 185 + case 99 ... 127: 186 + offset = IO_PWR_DOMAIN_GPIO5_K3; 187 + break; 188 + case 132 ... 137: 189 + offset = IO_PWR_DOMAIN_MMC_Kx; 190 + break; 191 + case 138 ... 144: 192 + offset = IO_PWR_DOMAIN_QSPI_K3; 193 + break; 194 + } 195 + 196 + return offset; 173 197 } 174 198 175 199 static inline void __iomem *spacemit_pin_to_reg(struct spacemit_pinctrl *pctrl, ··· 441 365 return 0; 442 366 } 443 367 368 + static void spacemit_set_io_pwr_domain(struct spacemit_pinctrl *pctrl, 369 + const struct spacemit_pin *spin, 370 + const enum spacemit_pin_io_type type) 371 + { 372 + u32 offset, val = 0; 373 + 374 + if (!pctrl->regmap_apbc) 375 + return; 376 + 377 + offset = pctrl->data->pin_to_io_pd_offset(spin->pin); 378 + 379 + /* Other bits are reserved so don't need to save them */ 380 + if (type == IO_TYPE_1V8) 381 + val = IO_PWR_DOMAIN_V18EN; 382 + 383 + /* 384 + * IO power domain registers are protected and cannot be accessed 385 + * directly. Before performing any read or write to the IO power 386 + * domain registers, an explicit unlock sequence must be issued 387 + * via the AIB Secure Access Register (ASAR). 388 + * 389 + * The unlock sequence allows exactly one subsequent access to the 390 + * IO power domain registers. After that access completes, the ASAR 391 + * keys are automatically cleared, and the registers become locked 392 + * again. 393 + * 394 + * This mechanism ensures that IO power domain configuration is 395 + * performed intentionally, as incorrect voltage settings may 396 + * result in functional failures or hardware damage. 397 + */ 398 + regmap_write(pctrl->regmap_apbc, APBC_ASFAR, APBC_ASFAR_AKEY); 399 + regmap_write(pctrl->regmap_apbc, APBC_ASSAR, APBC_ASSAR_AKEY); 400 + 401 + writel_relaxed(val, pctrl->regs + IO_PWR_DOMAIN_OFFSET + offset); 402 + } 403 + 444 404 static int spacemit_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, 445 405 struct device_node *np, 446 406 struct pinctrl_map **maps, ··· 684 572 685 573 #define ENABLE_DRV_STRENGTH BIT(1) 686 574 #define ENABLE_SLEW_RATE BIT(2) 687 - static int spacemit_pinconf_generate_config(const struct spacemit_pin *spin, 575 + static int spacemit_pinconf_generate_config(struct spacemit_pinctrl *pctrl, 576 + const struct spacemit_pin *spin, 688 577 const struct spacemit_pinctrl_dconf *dconf, 689 578 unsigned long *configs, 690 579 unsigned int num_configs, ··· 759 646 default: 760 647 return -EINVAL; 761 648 } 649 + spacemit_set_io_pwr_domain(pctrl, spin, type); 762 650 } 763 651 764 652 val = spacemit_get_driver_strength(type, dconf, drv_strength); ··· 815 701 const struct spacemit_pin *spin = spacemit_get_pin(pctrl, pin); 816 702 u32 value; 817 703 818 - if (spacemit_pinconf_generate_config(spin, pctrl->data->dconf, 704 + if (spacemit_pinconf_generate_config(pctrl, spin, pctrl->data->dconf, 819 705 configs, num_configs, &value)) 820 706 return -EINVAL; 821 707 ··· 838 724 return -EINVAL; 839 725 840 726 spin = spacemit_get_pin(pctrl, group->grp.pins[0]); 841 - if (spacemit_pinconf_generate_config(spin, pctrl->data->dconf, 727 + if (spacemit_pinconf_generate_config(pctrl, spin, pctrl->data->dconf, 842 728 configs, num_configs, &value)) 843 729 return -EINVAL; 844 730 ··· 909 795 910 796 static int spacemit_pinctrl_probe(struct platform_device *pdev) 911 797 { 798 + struct device_node *np = pdev->dev.of_node; 912 799 struct device *dev = &pdev->dev; 913 800 struct spacemit_pinctrl *pctrl; 914 801 struct clk *func_clk, *bus_clk; ··· 930 815 pctrl->regs = devm_platform_ioremap_resource(pdev, 0); 931 816 if (IS_ERR(pctrl->regs)) 932 817 return PTR_ERR(pctrl->regs); 818 + 819 + pctrl->regmap_apbc = syscon_regmap_lookup_by_phandle(np, "spacemit,apbc"); 820 + if (IS_ERR(pctrl->regmap_apbc)) { 821 + dev_warn(dev, "no syscon found, disable power voltage switch functionality\n"); 822 + pctrl->regmap_apbc = NULL; 823 + } 933 824 934 825 func_clk = devm_clk_get_enabled(dev, "func"); 935 826 if (IS_ERR(func_clk)) ··· 1239 1118 .data = k1_pin_data, 1240 1119 .npins = ARRAY_SIZE(k1_pin_desc), 1241 1120 .pin_to_offset = spacemit_k1_pin_to_offset, 1121 + .pin_to_io_pd_offset = spacemit_k1_pin_to_io_pd_offset, 1242 1122 .dconf = &k1_drive_conf, 1243 1123 }; 1244 1124 ··· 1577 1455 .data = k3_pin_data, 1578 1456 .npins = ARRAY_SIZE(k3_pin_desc), 1579 1457 .pin_to_offset = spacemit_k3_pin_to_offset, 1458 + .pin_to_io_pd_offset = spacemit_k3_pin_to_io_pd_offset, 1580 1459 .dconf = &k3_drive_conf, 1581 1460 }; 1582 1461