162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Rockchip IO Voltage Domain driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2014 MundoReader S.L. 662306a36Sopenharmony_ci * Copyright 2014 Google, Inc. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/regmap.h> 1662306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define MAX_SUPPLIES 16 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * The max voltage for 1.8V and 3.3V come from the Rockchip datasheet under 2262306a36Sopenharmony_ci * "Recommended Operating Conditions" for "Digital GPIO". When the typical 2362306a36Sopenharmony_ci * is 3.3V the max is 3.6V. When the typical is 1.8V the max is 1.98V. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * They are used like this: 2662306a36Sopenharmony_ci * - If the voltage on a rail is above the "1.8" voltage (1.98V) we'll tell the 2762306a36Sopenharmony_ci * SoC we're at 3.3. 2862306a36Sopenharmony_ci * - If the voltage on a rail is above the "3.3" voltage (3.6V) we'll consider 2962306a36Sopenharmony_ci * that to be an error. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci#define MAX_VOLTAGE_1_8 1980000 3262306a36Sopenharmony_ci#define MAX_VOLTAGE_3_3 3600000 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define PX30_IO_VSEL 0x180 3562306a36Sopenharmony_ci#define PX30_IO_VSEL_VCCIO6_SRC BIT(0) 3662306a36Sopenharmony_ci#define PX30_IO_VSEL_VCCIO6_SUPPLY_NUM 1 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define RK3288_SOC_CON2 0x24c 3962306a36Sopenharmony_ci#define RK3288_SOC_CON2_FLASH0 BIT(7) 4062306a36Sopenharmony_ci#define RK3288_SOC_FLASH_SUPPLY_NUM 2 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define RK3328_SOC_CON4 0x410 4362306a36Sopenharmony_ci#define RK3328_SOC_CON4_VCCIO2 BIT(7) 4462306a36Sopenharmony_ci#define RK3328_SOC_VCCIO2_SUPPLY_NUM 1 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define RK3368_SOC_CON15 0x43c 4762306a36Sopenharmony_ci#define RK3368_SOC_CON15_FLASH0 BIT(14) 4862306a36Sopenharmony_ci#define RK3368_SOC_FLASH_SUPPLY_NUM 2 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define RK3399_PMUGRF_CON0 0x180 5162306a36Sopenharmony_ci#define RK3399_PMUGRF_CON0_VSEL BIT(8) 5262306a36Sopenharmony_ci#define RK3399_PMUGRF_VSEL_SUPPLY_NUM 9 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define RK3568_PMU_GRF_IO_VSEL0 (0x0140) 5562306a36Sopenharmony_ci#define RK3568_PMU_GRF_IO_VSEL1 (0x0144) 5662306a36Sopenharmony_ci#define RK3568_PMU_GRF_IO_VSEL2 (0x0148) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct rockchip_iodomain; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct rockchip_iodomain_supply { 6162306a36Sopenharmony_ci struct rockchip_iodomain *iod; 6262306a36Sopenharmony_ci struct regulator *reg; 6362306a36Sopenharmony_ci struct notifier_block nb; 6462306a36Sopenharmony_ci int idx; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct rockchip_iodomain_soc_data { 6862306a36Sopenharmony_ci int grf_offset; 6962306a36Sopenharmony_ci const char *supply_names[MAX_SUPPLIES]; 7062306a36Sopenharmony_ci void (*init)(struct rockchip_iodomain *iod); 7162306a36Sopenharmony_ci int (*write)(struct rockchip_iodomain_supply *supply, int uV); 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistruct rockchip_iodomain { 7562306a36Sopenharmony_ci struct device *dev; 7662306a36Sopenharmony_ci struct regmap *grf; 7762306a36Sopenharmony_ci const struct rockchip_iodomain_soc_data *soc_data; 7862306a36Sopenharmony_ci struct rockchip_iodomain_supply supplies[MAX_SUPPLIES]; 7962306a36Sopenharmony_ci int (*write)(struct rockchip_iodomain_supply *supply, int uV); 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int rk3568_iodomain_write(struct rockchip_iodomain_supply *supply, int uV) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct rockchip_iodomain *iod = supply->iod; 8562306a36Sopenharmony_ci u32 is_3v3 = uV > MAX_VOLTAGE_1_8; 8662306a36Sopenharmony_ci u32 val0, val1; 8762306a36Sopenharmony_ci int b; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci switch (supply->idx) { 9062306a36Sopenharmony_ci case 0: /* pmuio1 */ 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci case 1: /* pmuio2 */ 9362306a36Sopenharmony_ci b = supply->idx; 9462306a36Sopenharmony_ci val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b)); 9562306a36Sopenharmony_ci b = supply->idx + 4; 9662306a36Sopenharmony_ci val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val0); 9962306a36Sopenharmony_ci regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val1); 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci case 3: /* vccio2 */ 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci case 2: /* vccio1 */ 10462306a36Sopenharmony_ci case 4: /* vccio3 */ 10562306a36Sopenharmony_ci case 5: /* vccio4 */ 10662306a36Sopenharmony_ci case 6: /* vccio5 */ 10762306a36Sopenharmony_ci case 7: /* vccio6 */ 10862306a36Sopenharmony_ci case 8: /* vccio7 */ 10962306a36Sopenharmony_ci b = supply->idx - 1; 11062306a36Sopenharmony_ci val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b)); 11162306a36Sopenharmony_ci val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL0, val0); 11462306a36Sopenharmony_ci regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL1, val1); 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci default: 11762306a36Sopenharmony_ci return -EINVAL; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int rockchip_iodomain_write(struct rockchip_iodomain_supply *supply, 12462306a36Sopenharmony_ci int uV) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct rockchip_iodomain *iod = supply->iod; 12762306a36Sopenharmony_ci u32 val; 12862306a36Sopenharmony_ci int ret; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* set value bit */ 13162306a36Sopenharmony_ci val = (uV > MAX_VOLTAGE_1_8) ? 0 : 1; 13262306a36Sopenharmony_ci val <<= supply->idx; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* apply hiword-mask */ 13562306a36Sopenharmony_ci val |= (BIT(supply->idx) << 16); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ret = regmap_write(iod->grf, iod->soc_data->grf_offset, val); 13862306a36Sopenharmony_ci if (ret) 13962306a36Sopenharmony_ci dev_err(iod->dev, "Couldn't write to GRF\n"); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return ret; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int rockchip_iodomain_notify(struct notifier_block *nb, 14562306a36Sopenharmony_ci unsigned long event, 14662306a36Sopenharmony_ci void *data) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct rockchip_iodomain_supply *supply = 14962306a36Sopenharmony_ci container_of(nb, struct rockchip_iodomain_supply, nb); 15062306a36Sopenharmony_ci int uV; 15162306a36Sopenharmony_ci int ret; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* 15462306a36Sopenharmony_ci * According to Rockchip it's important to keep the SoC IO domain 15562306a36Sopenharmony_ci * higher than (or equal to) the external voltage. That means we need 15662306a36Sopenharmony_ci * to change it before external voltage changes happen in the case 15762306a36Sopenharmony_ci * of an increase. 15862306a36Sopenharmony_ci * 15962306a36Sopenharmony_ci * Note that in the "pre" change we pick the max possible voltage that 16062306a36Sopenharmony_ci * the regulator might end up at (the client requests a range and we 16162306a36Sopenharmony_ci * don't know for certain the exact voltage). Right now we rely on the 16262306a36Sopenharmony_ci * slop in MAX_VOLTAGE_1_8 and MAX_VOLTAGE_3_3 to save us if clients 16362306a36Sopenharmony_ci * request something like a max of 3.6V when they really want 3.3V. 16462306a36Sopenharmony_ci * We could attempt to come up with better rules if this fails. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci if (event & REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) { 16762306a36Sopenharmony_ci struct pre_voltage_change_data *pvc_data = data; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci uV = max_t(unsigned long, pvc_data->old_uV, pvc_data->max_uV); 17062306a36Sopenharmony_ci } else if (event & (REGULATOR_EVENT_VOLTAGE_CHANGE | 17162306a36Sopenharmony_ci REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE)) { 17262306a36Sopenharmony_ci uV = (unsigned long)data; 17362306a36Sopenharmony_ci } else { 17462306a36Sopenharmony_ci return NOTIFY_OK; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci dev_dbg(supply->iod->dev, "Setting to %d\n", uV); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (uV > MAX_VOLTAGE_3_3) { 18062306a36Sopenharmony_ci dev_err(supply->iod->dev, "Voltage too high: %d\n", uV); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) 18362306a36Sopenharmony_ci return NOTIFY_BAD; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci ret = supply->iod->write(supply, uV); 18762306a36Sopenharmony_ci if (ret && event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) 18862306a36Sopenharmony_ci return NOTIFY_BAD; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci dev_dbg(supply->iod->dev, "Setting to %d done\n", uV); 19162306a36Sopenharmony_ci return NOTIFY_OK; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void px30_iodomain_init(struct rockchip_iodomain *iod) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci int ret; 19762306a36Sopenharmony_ci u32 val; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* if no VCCIO6 supply we should leave things alone */ 20062306a36Sopenharmony_ci if (!iod->supplies[PX30_IO_VSEL_VCCIO6_SUPPLY_NUM].reg) 20162306a36Sopenharmony_ci return; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* 20462306a36Sopenharmony_ci * set vccio6 iodomain to also use this framework 20562306a36Sopenharmony_ci * instead of a special gpio. 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci val = PX30_IO_VSEL_VCCIO6_SRC | (PX30_IO_VSEL_VCCIO6_SRC << 16); 20862306a36Sopenharmony_ci ret = regmap_write(iod->grf, PX30_IO_VSEL, val); 20962306a36Sopenharmony_ci if (ret < 0) 21062306a36Sopenharmony_ci dev_warn(iod->dev, "couldn't update vccio6 ctrl\n"); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic void rk3288_iodomain_init(struct rockchip_iodomain *iod) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci int ret; 21662306a36Sopenharmony_ci u32 val; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* if no flash supply we should leave things alone */ 21962306a36Sopenharmony_ci if (!iod->supplies[RK3288_SOC_FLASH_SUPPLY_NUM].reg) 22062306a36Sopenharmony_ci return; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* 22362306a36Sopenharmony_ci * set flash0 iodomain to also use this framework 22462306a36Sopenharmony_ci * instead of a special gpio. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci val = RK3288_SOC_CON2_FLASH0 | (RK3288_SOC_CON2_FLASH0 << 16); 22762306a36Sopenharmony_ci ret = regmap_write(iod->grf, RK3288_SOC_CON2, val); 22862306a36Sopenharmony_ci if (ret < 0) 22962306a36Sopenharmony_ci dev_warn(iod->dev, "couldn't update flash0 ctrl\n"); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic void rk3328_iodomain_init(struct rockchip_iodomain *iod) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci int ret; 23562306a36Sopenharmony_ci u32 val; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* if no vccio2 supply we should leave things alone */ 23862306a36Sopenharmony_ci if (!iod->supplies[RK3328_SOC_VCCIO2_SUPPLY_NUM].reg) 23962306a36Sopenharmony_ci return; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* 24262306a36Sopenharmony_ci * set vccio2 iodomain to also use this framework 24362306a36Sopenharmony_ci * instead of a special gpio. 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ci val = RK3328_SOC_CON4_VCCIO2 | (RK3328_SOC_CON4_VCCIO2 << 16); 24662306a36Sopenharmony_ci ret = regmap_write(iod->grf, RK3328_SOC_CON4, val); 24762306a36Sopenharmony_ci if (ret < 0) 24862306a36Sopenharmony_ci dev_warn(iod->dev, "couldn't update vccio2 vsel ctrl\n"); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic void rk3368_iodomain_init(struct rockchip_iodomain *iod) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci int ret; 25462306a36Sopenharmony_ci u32 val; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* if no flash supply we should leave things alone */ 25762306a36Sopenharmony_ci if (!iod->supplies[RK3368_SOC_FLASH_SUPPLY_NUM].reg) 25862306a36Sopenharmony_ci return; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * set flash0 iodomain to also use this framework 26262306a36Sopenharmony_ci * instead of a special gpio. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci val = RK3368_SOC_CON15_FLASH0 | (RK3368_SOC_CON15_FLASH0 << 16); 26562306a36Sopenharmony_ci ret = regmap_write(iod->grf, RK3368_SOC_CON15, val); 26662306a36Sopenharmony_ci if (ret < 0) 26762306a36Sopenharmony_ci dev_warn(iod->dev, "couldn't update flash0 ctrl\n"); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void rk3399_pmu_iodomain_init(struct rockchip_iodomain *iod) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci int ret; 27362306a36Sopenharmony_ci u32 val; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* if no pmu io supply we should leave things alone */ 27662306a36Sopenharmony_ci if (!iod->supplies[RK3399_PMUGRF_VSEL_SUPPLY_NUM].reg) 27762306a36Sopenharmony_ci return; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* 28062306a36Sopenharmony_ci * set pmu io iodomain to also use this framework 28162306a36Sopenharmony_ci * instead of a special gpio. 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_ci val = RK3399_PMUGRF_CON0_VSEL | (RK3399_PMUGRF_CON0_VSEL << 16); 28462306a36Sopenharmony_ci ret = regmap_write(iod->grf, RK3399_PMUGRF_CON0, val); 28562306a36Sopenharmony_ci if (ret < 0) 28662306a36Sopenharmony_ci dev_warn(iod->dev, "couldn't update pmu io iodomain ctrl\n"); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_px30 = { 29062306a36Sopenharmony_ci .grf_offset = 0x180, 29162306a36Sopenharmony_ci .supply_names = { 29262306a36Sopenharmony_ci NULL, 29362306a36Sopenharmony_ci "vccio6", 29462306a36Sopenharmony_ci "vccio1", 29562306a36Sopenharmony_ci "vccio2", 29662306a36Sopenharmony_ci "vccio3", 29762306a36Sopenharmony_ci "vccio4", 29862306a36Sopenharmony_ci "vccio5", 29962306a36Sopenharmony_ci "vccio-oscgpi", 30062306a36Sopenharmony_ci }, 30162306a36Sopenharmony_ci .init = px30_iodomain_init, 30262306a36Sopenharmony_ci}; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_px30_pmu = { 30562306a36Sopenharmony_ci .grf_offset = 0x100, 30662306a36Sopenharmony_ci .supply_names = { 30762306a36Sopenharmony_ci NULL, 30862306a36Sopenharmony_ci NULL, 30962306a36Sopenharmony_ci NULL, 31062306a36Sopenharmony_ci NULL, 31162306a36Sopenharmony_ci NULL, 31262306a36Sopenharmony_ci NULL, 31362306a36Sopenharmony_ci NULL, 31462306a36Sopenharmony_ci NULL, 31562306a36Sopenharmony_ci NULL, 31662306a36Sopenharmony_ci NULL, 31762306a36Sopenharmony_ci NULL, 31862306a36Sopenharmony_ci NULL, 31962306a36Sopenharmony_ci NULL, 32062306a36Sopenharmony_ci NULL, 32162306a36Sopenharmony_ci "pmuio1", 32262306a36Sopenharmony_ci "pmuio2", 32362306a36Sopenharmony_ci }, 32462306a36Sopenharmony_ci}; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci/* 32762306a36Sopenharmony_ci * On the rk3188 the io-domains are handled by a shared register with the 32862306a36Sopenharmony_ci * lower 8 bits being still being continuing drive-strength settings. 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3188 = { 33162306a36Sopenharmony_ci .grf_offset = 0x104, 33262306a36Sopenharmony_ci .supply_names = { 33362306a36Sopenharmony_ci NULL, 33462306a36Sopenharmony_ci NULL, 33562306a36Sopenharmony_ci NULL, 33662306a36Sopenharmony_ci NULL, 33762306a36Sopenharmony_ci NULL, 33862306a36Sopenharmony_ci NULL, 33962306a36Sopenharmony_ci NULL, 34062306a36Sopenharmony_ci NULL, 34162306a36Sopenharmony_ci "ap0", 34262306a36Sopenharmony_ci "ap1", 34362306a36Sopenharmony_ci "cif", 34462306a36Sopenharmony_ci "flash", 34562306a36Sopenharmony_ci "vccio0", 34662306a36Sopenharmony_ci "vccio1", 34762306a36Sopenharmony_ci "lcdc0", 34862306a36Sopenharmony_ci "lcdc1", 34962306a36Sopenharmony_ci }, 35062306a36Sopenharmony_ci}; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3228 = { 35362306a36Sopenharmony_ci .grf_offset = 0x418, 35462306a36Sopenharmony_ci .supply_names = { 35562306a36Sopenharmony_ci "vccio1", 35662306a36Sopenharmony_ci "vccio2", 35762306a36Sopenharmony_ci "vccio3", 35862306a36Sopenharmony_ci "vccio4", 35962306a36Sopenharmony_ci }, 36062306a36Sopenharmony_ci}; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3288 = { 36362306a36Sopenharmony_ci .grf_offset = 0x380, 36462306a36Sopenharmony_ci .supply_names = { 36562306a36Sopenharmony_ci "lcdc", /* LCDC_VDD */ 36662306a36Sopenharmony_ci "dvp", /* DVPIO_VDD */ 36762306a36Sopenharmony_ci "flash0", /* FLASH0_VDD (emmc) */ 36862306a36Sopenharmony_ci "flash1", /* FLASH1_VDD (sdio1) */ 36962306a36Sopenharmony_ci "wifi", /* APIO3_VDD (sdio0) */ 37062306a36Sopenharmony_ci "bb", /* APIO5_VDD */ 37162306a36Sopenharmony_ci "audio", /* APIO4_VDD */ 37262306a36Sopenharmony_ci "sdcard", /* SDMMC0_VDD (sdmmc) */ 37362306a36Sopenharmony_ci "gpio30", /* APIO1_VDD */ 37462306a36Sopenharmony_ci "gpio1830", /* APIO2_VDD */ 37562306a36Sopenharmony_ci }, 37662306a36Sopenharmony_ci .init = rk3288_iodomain_init, 37762306a36Sopenharmony_ci}; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3328 = { 38062306a36Sopenharmony_ci .grf_offset = 0x410, 38162306a36Sopenharmony_ci .supply_names = { 38262306a36Sopenharmony_ci "vccio1", 38362306a36Sopenharmony_ci "vccio2", 38462306a36Sopenharmony_ci "vccio3", 38562306a36Sopenharmony_ci "vccio4", 38662306a36Sopenharmony_ci "vccio5", 38762306a36Sopenharmony_ci "vccio6", 38862306a36Sopenharmony_ci "pmuio", 38962306a36Sopenharmony_ci }, 39062306a36Sopenharmony_ci .init = rk3328_iodomain_init, 39162306a36Sopenharmony_ci}; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3368 = { 39462306a36Sopenharmony_ci .grf_offset = 0x900, 39562306a36Sopenharmony_ci .supply_names = { 39662306a36Sopenharmony_ci NULL, /* reserved */ 39762306a36Sopenharmony_ci "dvp", /* DVPIO_VDD */ 39862306a36Sopenharmony_ci "flash0", /* FLASH0_VDD (emmc) */ 39962306a36Sopenharmony_ci "wifi", /* APIO2_VDD (sdio0) */ 40062306a36Sopenharmony_ci NULL, 40162306a36Sopenharmony_ci "audio", /* APIO3_VDD */ 40262306a36Sopenharmony_ci "sdcard", /* SDMMC0_VDD (sdmmc) */ 40362306a36Sopenharmony_ci "gpio30", /* APIO1_VDD */ 40462306a36Sopenharmony_ci "gpio1830", /* APIO4_VDD (gpujtag) */ 40562306a36Sopenharmony_ci }, 40662306a36Sopenharmony_ci .init = rk3368_iodomain_init, 40762306a36Sopenharmony_ci}; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3368_pmu = { 41062306a36Sopenharmony_ci .grf_offset = 0x100, 41162306a36Sopenharmony_ci .supply_names = { 41262306a36Sopenharmony_ci NULL, 41362306a36Sopenharmony_ci NULL, 41462306a36Sopenharmony_ci NULL, 41562306a36Sopenharmony_ci NULL, 41662306a36Sopenharmony_ci "pmu", /*PMU IO domain*/ 41762306a36Sopenharmony_ci "vop", /*LCDC IO domain*/ 41862306a36Sopenharmony_ci }, 41962306a36Sopenharmony_ci}; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3399 = { 42262306a36Sopenharmony_ci .grf_offset = 0xe640, 42362306a36Sopenharmony_ci .supply_names = { 42462306a36Sopenharmony_ci "bt656", /* APIO2_VDD */ 42562306a36Sopenharmony_ci "audio", /* APIO5_VDD */ 42662306a36Sopenharmony_ci "sdmmc", /* SDMMC0_VDD */ 42762306a36Sopenharmony_ci "gpio1830", /* APIO4_VDD */ 42862306a36Sopenharmony_ci }, 42962306a36Sopenharmony_ci}; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3399_pmu = { 43262306a36Sopenharmony_ci .grf_offset = 0x180, 43362306a36Sopenharmony_ci .supply_names = { 43462306a36Sopenharmony_ci NULL, 43562306a36Sopenharmony_ci NULL, 43662306a36Sopenharmony_ci NULL, 43762306a36Sopenharmony_ci NULL, 43862306a36Sopenharmony_ci NULL, 43962306a36Sopenharmony_ci NULL, 44062306a36Sopenharmony_ci NULL, 44162306a36Sopenharmony_ci NULL, 44262306a36Sopenharmony_ci NULL, 44362306a36Sopenharmony_ci "pmu1830", /* PMUIO2_VDD */ 44462306a36Sopenharmony_ci }, 44562306a36Sopenharmony_ci .init = rk3399_pmu_iodomain_init, 44662306a36Sopenharmony_ci}; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3568_pmu = { 44962306a36Sopenharmony_ci .grf_offset = 0x140, 45062306a36Sopenharmony_ci .supply_names = { 45162306a36Sopenharmony_ci "pmuio1", 45262306a36Sopenharmony_ci "pmuio2", 45362306a36Sopenharmony_ci "vccio1", 45462306a36Sopenharmony_ci "vccio2", 45562306a36Sopenharmony_ci "vccio3", 45662306a36Sopenharmony_ci "vccio4", 45762306a36Sopenharmony_ci "vccio5", 45862306a36Sopenharmony_ci "vccio6", 45962306a36Sopenharmony_ci "vccio7", 46062306a36Sopenharmony_ci }, 46162306a36Sopenharmony_ci .write = rk3568_iodomain_write, 46262306a36Sopenharmony_ci}; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rv1108 = { 46562306a36Sopenharmony_ci .grf_offset = 0x404, 46662306a36Sopenharmony_ci .supply_names = { 46762306a36Sopenharmony_ci NULL, 46862306a36Sopenharmony_ci NULL, 46962306a36Sopenharmony_ci NULL, 47062306a36Sopenharmony_ci NULL, 47162306a36Sopenharmony_ci NULL, 47262306a36Sopenharmony_ci NULL, 47362306a36Sopenharmony_ci NULL, 47462306a36Sopenharmony_ci NULL, 47562306a36Sopenharmony_ci NULL, 47662306a36Sopenharmony_ci NULL, 47762306a36Sopenharmony_ci NULL, 47862306a36Sopenharmony_ci "vccio1", 47962306a36Sopenharmony_ci "vccio2", 48062306a36Sopenharmony_ci "vccio3", 48162306a36Sopenharmony_ci "vccio5", 48262306a36Sopenharmony_ci "vccio6", 48362306a36Sopenharmony_ci }, 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci}; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rv1108_pmu = { 48862306a36Sopenharmony_ci .grf_offset = 0x104, 48962306a36Sopenharmony_ci .supply_names = { 49062306a36Sopenharmony_ci "pmu", 49162306a36Sopenharmony_ci }, 49262306a36Sopenharmony_ci}; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rv1126_pmu = { 49562306a36Sopenharmony_ci .grf_offset = 0x140, 49662306a36Sopenharmony_ci .supply_names = { 49762306a36Sopenharmony_ci NULL, 49862306a36Sopenharmony_ci "vccio1", 49962306a36Sopenharmony_ci "vccio2", 50062306a36Sopenharmony_ci "vccio3", 50162306a36Sopenharmony_ci "vccio4", 50262306a36Sopenharmony_ci "vccio5", 50362306a36Sopenharmony_ci "vccio6", 50462306a36Sopenharmony_ci "vccio7", 50562306a36Sopenharmony_ci "pmuio0", 50662306a36Sopenharmony_ci "pmuio1", 50762306a36Sopenharmony_ci }, 50862306a36Sopenharmony_ci}; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic const struct of_device_id rockchip_iodomain_match[] = { 51162306a36Sopenharmony_ci { 51262306a36Sopenharmony_ci .compatible = "rockchip,px30-io-voltage-domain", 51362306a36Sopenharmony_ci .data = (void *)&soc_data_px30 51462306a36Sopenharmony_ci }, 51562306a36Sopenharmony_ci { 51662306a36Sopenharmony_ci .compatible = "rockchip,px30-pmu-io-voltage-domain", 51762306a36Sopenharmony_ci .data = (void *)&soc_data_px30_pmu 51862306a36Sopenharmony_ci }, 51962306a36Sopenharmony_ci { 52062306a36Sopenharmony_ci .compatible = "rockchip,rk3188-io-voltage-domain", 52162306a36Sopenharmony_ci .data = &soc_data_rk3188 52262306a36Sopenharmony_ci }, 52362306a36Sopenharmony_ci { 52462306a36Sopenharmony_ci .compatible = "rockchip,rk3228-io-voltage-domain", 52562306a36Sopenharmony_ci .data = &soc_data_rk3228 52662306a36Sopenharmony_ci }, 52762306a36Sopenharmony_ci { 52862306a36Sopenharmony_ci .compatible = "rockchip,rk3288-io-voltage-domain", 52962306a36Sopenharmony_ci .data = &soc_data_rk3288 53062306a36Sopenharmony_ci }, 53162306a36Sopenharmony_ci { 53262306a36Sopenharmony_ci .compatible = "rockchip,rk3328-io-voltage-domain", 53362306a36Sopenharmony_ci .data = &soc_data_rk3328 53462306a36Sopenharmony_ci }, 53562306a36Sopenharmony_ci { 53662306a36Sopenharmony_ci .compatible = "rockchip,rk3368-io-voltage-domain", 53762306a36Sopenharmony_ci .data = &soc_data_rk3368 53862306a36Sopenharmony_ci }, 53962306a36Sopenharmony_ci { 54062306a36Sopenharmony_ci .compatible = "rockchip,rk3368-pmu-io-voltage-domain", 54162306a36Sopenharmony_ci .data = &soc_data_rk3368_pmu 54262306a36Sopenharmony_ci }, 54362306a36Sopenharmony_ci { 54462306a36Sopenharmony_ci .compatible = "rockchip,rk3399-io-voltage-domain", 54562306a36Sopenharmony_ci .data = &soc_data_rk3399 54662306a36Sopenharmony_ci }, 54762306a36Sopenharmony_ci { 54862306a36Sopenharmony_ci .compatible = "rockchip,rk3399-pmu-io-voltage-domain", 54962306a36Sopenharmony_ci .data = &soc_data_rk3399_pmu 55062306a36Sopenharmony_ci }, 55162306a36Sopenharmony_ci { 55262306a36Sopenharmony_ci .compatible = "rockchip,rk3568-pmu-io-voltage-domain", 55362306a36Sopenharmony_ci .data = &soc_data_rk3568_pmu 55462306a36Sopenharmony_ci }, 55562306a36Sopenharmony_ci { 55662306a36Sopenharmony_ci .compatible = "rockchip,rv1108-io-voltage-domain", 55762306a36Sopenharmony_ci .data = &soc_data_rv1108 55862306a36Sopenharmony_ci }, 55962306a36Sopenharmony_ci { 56062306a36Sopenharmony_ci .compatible = "rockchip,rv1108-pmu-io-voltage-domain", 56162306a36Sopenharmony_ci .data = &soc_data_rv1108_pmu 56262306a36Sopenharmony_ci }, 56362306a36Sopenharmony_ci { 56462306a36Sopenharmony_ci .compatible = "rockchip,rv1126-pmu-io-voltage-domain", 56562306a36Sopenharmony_ci .data = &soc_data_rv1126_pmu 56662306a36Sopenharmony_ci }, 56762306a36Sopenharmony_ci { /* sentinel */ }, 56862306a36Sopenharmony_ci}; 56962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_iodomain_match); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int rockchip_iodomain_probe(struct platform_device *pdev) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 57462306a36Sopenharmony_ci const struct of_device_id *match; 57562306a36Sopenharmony_ci struct rockchip_iodomain *iod; 57662306a36Sopenharmony_ci struct device *parent; 57762306a36Sopenharmony_ci int i, ret = 0; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (!np) 58062306a36Sopenharmony_ci return -ENODEV; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci iod = devm_kzalloc(&pdev->dev, sizeof(*iod), GFP_KERNEL); 58362306a36Sopenharmony_ci if (!iod) 58462306a36Sopenharmony_ci return -ENOMEM; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci iod->dev = &pdev->dev; 58762306a36Sopenharmony_ci platform_set_drvdata(pdev, iod); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci match = of_match_node(rockchip_iodomain_match, np); 59062306a36Sopenharmony_ci iod->soc_data = match->data; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (iod->soc_data->write) 59362306a36Sopenharmony_ci iod->write = iod->soc_data->write; 59462306a36Sopenharmony_ci else 59562306a36Sopenharmony_ci iod->write = rockchip_iodomain_write; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci parent = pdev->dev.parent; 59862306a36Sopenharmony_ci if (parent && parent->of_node) { 59962306a36Sopenharmony_ci iod->grf = syscon_node_to_regmap(parent->of_node); 60062306a36Sopenharmony_ci } else { 60162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "falling back to old binding\n"); 60262306a36Sopenharmony_ci iod->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (IS_ERR(iod->grf)) { 60662306a36Sopenharmony_ci dev_err(&pdev->dev, "couldn't find grf regmap\n"); 60762306a36Sopenharmony_ci return PTR_ERR(iod->grf); 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci for (i = 0; i < MAX_SUPPLIES; i++) { 61162306a36Sopenharmony_ci const char *supply_name = iod->soc_data->supply_names[i]; 61262306a36Sopenharmony_ci struct rockchip_iodomain_supply *supply = &iod->supplies[i]; 61362306a36Sopenharmony_ci struct regulator *reg; 61462306a36Sopenharmony_ci int uV; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (!supply_name) 61762306a36Sopenharmony_ci continue; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci reg = devm_regulator_get_optional(iod->dev, supply_name); 62062306a36Sopenharmony_ci if (IS_ERR(reg)) { 62162306a36Sopenharmony_ci ret = PTR_ERR(reg); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* If a supply wasn't specified, that's OK */ 62462306a36Sopenharmony_ci if (ret == -ENODEV) 62562306a36Sopenharmony_ci continue; 62662306a36Sopenharmony_ci else if (ret != -EPROBE_DEFER) 62762306a36Sopenharmony_ci dev_err(iod->dev, "couldn't get regulator %s\n", 62862306a36Sopenharmony_ci supply_name); 62962306a36Sopenharmony_ci goto unreg_notify; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* set initial correct value */ 63362306a36Sopenharmony_ci uV = regulator_get_voltage(reg); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* must be a regulator we can get the voltage of */ 63662306a36Sopenharmony_ci if (uV < 0) { 63762306a36Sopenharmony_ci dev_err(iod->dev, "Can't determine voltage: %s\n", 63862306a36Sopenharmony_ci supply_name); 63962306a36Sopenharmony_ci ret = uV; 64062306a36Sopenharmony_ci goto unreg_notify; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (uV > MAX_VOLTAGE_3_3) { 64462306a36Sopenharmony_ci dev_crit(iod->dev, 64562306a36Sopenharmony_ci "%d uV is too high. May damage SoC!\n", 64662306a36Sopenharmony_ci uV); 64762306a36Sopenharmony_ci ret = -EINVAL; 64862306a36Sopenharmony_ci goto unreg_notify; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* setup our supply */ 65262306a36Sopenharmony_ci supply->idx = i; 65362306a36Sopenharmony_ci supply->iod = iod; 65462306a36Sopenharmony_ci supply->reg = reg; 65562306a36Sopenharmony_ci supply->nb.notifier_call = rockchip_iodomain_notify; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci ret = iod->write(supply, uV); 65862306a36Sopenharmony_ci if (ret) { 65962306a36Sopenharmony_ci supply->reg = NULL; 66062306a36Sopenharmony_ci goto unreg_notify; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* register regulator notifier */ 66462306a36Sopenharmony_ci ret = regulator_register_notifier(reg, &supply->nb); 66562306a36Sopenharmony_ci if (ret) { 66662306a36Sopenharmony_ci dev_err(&pdev->dev, 66762306a36Sopenharmony_ci "regulator notifier request failed\n"); 66862306a36Sopenharmony_ci supply->reg = NULL; 66962306a36Sopenharmony_ci goto unreg_notify; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (iod->soc_data->init) 67462306a36Sopenharmony_ci iod->soc_data->init(iod); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci return 0; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ciunreg_notify: 67962306a36Sopenharmony_ci for (i = MAX_SUPPLIES - 1; i >= 0; i--) { 68062306a36Sopenharmony_ci struct rockchip_iodomain_supply *io_supply = &iod->supplies[i]; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (io_supply->reg) 68362306a36Sopenharmony_ci regulator_unregister_notifier(io_supply->reg, 68462306a36Sopenharmony_ci &io_supply->nb); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci return ret; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic int rockchip_iodomain_remove(struct platform_device *pdev) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci struct rockchip_iodomain *iod = platform_get_drvdata(pdev); 69362306a36Sopenharmony_ci int i; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci for (i = MAX_SUPPLIES - 1; i >= 0; i--) { 69662306a36Sopenharmony_ci struct rockchip_iodomain_supply *io_supply = &iod->supplies[i]; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (io_supply->reg) 69962306a36Sopenharmony_ci regulator_unregister_notifier(io_supply->reg, 70062306a36Sopenharmony_ci &io_supply->nb); 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci return 0; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic struct platform_driver rockchip_iodomain_driver = { 70762306a36Sopenharmony_ci .probe = rockchip_iodomain_probe, 70862306a36Sopenharmony_ci .remove = rockchip_iodomain_remove, 70962306a36Sopenharmony_ci .driver = { 71062306a36Sopenharmony_ci .name = "rockchip-iodomain", 71162306a36Sopenharmony_ci .of_match_table = rockchip_iodomain_match, 71262306a36Sopenharmony_ci }, 71362306a36Sopenharmony_ci}; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cimodule_platform_driver(rockchip_iodomain_driver); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ciMODULE_DESCRIPTION("Rockchip IO-domain driver"); 71862306a36Sopenharmony_ciMODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); 71962306a36Sopenharmony_ciMODULE_AUTHOR("Doug Anderson <dianders@chromium.org>"); 72062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 721