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
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+
5073struct 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
83108struct 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+
149225static 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+
368480static 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
796910static 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