18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Rockchip IO Voltage Domain driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2014 MundoReader S.L. 68c2ecf20Sopenharmony_ci * Copyright 2014 Google, Inc. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/regmap.h> 168c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define MAX_SUPPLIES 16 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci * The max voltage for 1.8V and 3.3V come from the Rockchip datasheet under 228c2ecf20Sopenharmony_ci * "Recommended Operating Conditions" for "Digital GPIO". When the typical 238c2ecf20Sopenharmony_ci * is 3.3V the max is 3.6V. When the typical is 1.8V the max is 1.98V. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * They are used like this: 268c2ecf20Sopenharmony_ci * - If the voltage on a rail is above the "1.8" voltage (1.98V) we'll tell the 278c2ecf20Sopenharmony_ci * SoC we're at 3.3. 288c2ecf20Sopenharmony_ci * - If the voltage on a rail is above the "3.3" voltage (3.6V) we'll consider 298c2ecf20Sopenharmony_ci * that to be an error. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci#define MAX_VOLTAGE_1_8 1980000 328c2ecf20Sopenharmony_ci#define MAX_VOLTAGE_3_3 3600000 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define PX30_IO_VSEL 0x180 358c2ecf20Sopenharmony_ci#define PX30_IO_VSEL_VCCIO6_SRC BIT(0) 368c2ecf20Sopenharmony_ci#define PX30_IO_VSEL_VCCIO6_SUPPLY_NUM 1 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define RK3288_SOC_CON2 0x24c 398c2ecf20Sopenharmony_ci#define RK3288_SOC_CON2_FLASH0 BIT(7) 408c2ecf20Sopenharmony_ci#define RK3288_SOC_FLASH_SUPPLY_NUM 2 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define RK3328_SOC_CON4 0x410 438c2ecf20Sopenharmony_ci#define RK3328_SOC_CON4_VCCIO2 BIT(7) 448c2ecf20Sopenharmony_ci#define RK3328_SOC_VCCIO2_SUPPLY_NUM 1 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define RK3368_SOC_CON15 0x43c 478c2ecf20Sopenharmony_ci#define RK3368_SOC_CON15_FLASH0 BIT(14) 488c2ecf20Sopenharmony_ci#define RK3368_SOC_FLASH_SUPPLY_NUM 2 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define RK3399_PMUGRF_CON0 0x180 518c2ecf20Sopenharmony_ci#define RK3399_PMUGRF_CON0_VSEL BIT(8) 528c2ecf20Sopenharmony_ci#define RK3399_PMUGRF_VSEL_SUPPLY_NUM 9 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct rockchip_iodomain; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/** 578c2ecf20Sopenharmony_ci * @supplies: voltage settings matching the register bits. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_cistruct rockchip_iodomain_soc_data { 608c2ecf20Sopenharmony_ci int grf_offset; 618c2ecf20Sopenharmony_ci const char *supply_names[MAX_SUPPLIES]; 628c2ecf20Sopenharmony_ci void (*init)(struct rockchip_iodomain *iod); 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct rockchip_iodomain_supply { 668c2ecf20Sopenharmony_ci struct rockchip_iodomain *iod; 678c2ecf20Sopenharmony_ci struct regulator *reg; 688c2ecf20Sopenharmony_ci struct notifier_block nb; 698c2ecf20Sopenharmony_ci int idx; 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistruct rockchip_iodomain { 738c2ecf20Sopenharmony_ci struct device *dev; 748c2ecf20Sopenharmony_ci struct regmap *grf; 758c2ecf20Sopenharmony_ci const struct rockchip_iodomain_soc_data *soc_data; 768c2ecf20Sopenharmony_ci struct rockchip_iodomain_supply supplies[MAX_SUPPLIES]; 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int rockchip_iodomain_write(struct rockchip_iodomain_supply *supply, 808c2ecf20Sopenharmony_ci int uV) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct rockchip_iodomain *iod = supply->iod; 838c2ecf20Sopenharmony_ci u32 val; 848c2ecf20Sopenharmony_ci int ret; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* set value bit */ 878c2ecf20Sopenharmony_ci val = (uV > MAX_VOLTAGE_1_8) ? 0 : 1; 888c2ecf20Sopenharmony_ci val <<= supply->idx; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* apply hiword-mask */ 918c2ecf20Sopenharmony_ci val |= (BIT(supply->idx) << 16); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ret = regmap_write(iod->grf, iod->soc_data->grf_offset, val); 948c2ecf20Sopenharmony_ci if (ret) 958c2ecf20Sopenharmony_ci dev_err(iod->dev, "Couldn't write to GRF\n"); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return ret; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int rockchip_iodomain_notify(struct notifier_block *nb, 1018c2ecf20Sopenharmony_ci unsigned long event, 1028c2ecf20Sopenharmony_ci void *data) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct rockchip_iodomain_supply *supply = 1058c2ecf20Sopenharmony_ci container_of(nb, struct rockchip_iodomain_supply, nb); 1068c2ecf20Sopenharmony_ci int uV; 1078c2ecf20Sopenharmony_ci int ret; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* 1108c2ecf20Sopenharmony_ci * According to Rockchip it's important to keep the SoC IO domain 1118c2ecf20Sopenharmony_ci * higher than (or equal to) the external voltage. That means we need 1128c2ecf20Sopenharmony_ci * to change it before external voltage changes happen in the case 1138c2ecf20Sopenharmony_ci * of an increase. 1148c2ecf20Sopenharmony_ci * 1158c2ecf20Sopenharmony_ci * Note that in the "pre" change we pick the max possible voltage that 1168c2ecf20Sopenharmony_ci * the regulator might end up at (the client requests a range and we 1178c2ecf20Sopenharmony_ci * don't know for certain the exact voltage). Right now we rely on the 1188c2ecf20Sopenharmony_ci * slop in MAX_VOLTAGE_1_8 and MAX_VOLTAGE_3_3 to save us if clients 1198c2ecf20Sopenharmony_ci * request something like a max of 3.6V when they really want 3.3V. 1208c2ecf20Sopenharmony_ci * We could attempt to come up with better rules if this fails. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci if (event & REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) { 1238c2ecf20Sopenharmony_ci struct pre_voltage_change_data *pvc_data = data; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci uV = max_t(unsigned long, pvc_data->old_uV, pvc_data->max_uV); 1268c2ecf20Sopenharmony_ci } else if (event & (REGULATOR_EVENT_VOLTAGE_CHANGE | 1278c2ecf20Sopenharmony_ci REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE)) { 1288c2ecf20Sopenharmony_ci uV = (unsigned long)data; 1298c2ecf20Sopenharmony_ci } else { 1308c2ecf20Sopenharmony_ci return NOTIFY_OK; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci dev_dbg(supply->iod->dev, "Setting to %d\n", uV); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (uV > MAX_VOLTAGE_3_3) { 1368c2ecf20Sopenharmony_ci dev_err(supply->iod->dev, "Voltage too high: %d\n", uV); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) 1398c2ecf20Sopenharmony_ci return NOTIFY_BAD; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci ret = rockchip_iodomain_write(supply, uV); 1438c2ecf20Sopenharmony_ci if (ret && event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) 1448c2ecf20Sopenharmony_ci return NOTIFY_BAD; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci dev_dbg(supply->iod->dev, "Setting to %d done\n", uV); 1478c2ecf20Sopenharmony_ci return NOTIFY_OK; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void px30_iodomain_init(struct rockchip_iodomain *iod) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci int ret; 1538c2ecf20Sopenharmony_ci u32 val; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* if no VCCIO6 supply we should leave things alone */ 1568c2ecf20Sopenharmony_ci if (!iod->supplies[PX30_IO_VSEL_VCCIO6_SUPPLY_NUM].reg) 1578c2ecf20Sopenharmony_ci return; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* 1608c2ecf20Sopenharmony_ci * set vccio6 iodomain to also use this framework 1618c2ecf20Sopenharmony_ci * instead of a special gpio. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci val = PX30_IO_VSEL_VCCIO6_SRC | (PX30_IO_VSEL_VCCIO6_SRC << 16); 1648c2ecf20Sopenharmony_ci ret = regmap_write(iod->grf, PX30_IO_VSEL, val); 1658c2ecf20Sopenharmony_ci if (ret < 0) 1668c2ecf20Sopenharmony_ci dev_warn(iod->dev, "couldn't update vccio6 ctrl\n"); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void rk3288_iodomain_init(struct rockchip_iodomain *iod) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci int ret; 1728c2ecf20Sopenharmony_ci u32 val; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* if no flash supply we should leave things alone */ 1758c2ecf20Sopenharmony_ci if (!iod->supplies[RK3288_SOC_FLASH_SUPPLY_NUM].reg) 1768c2ecf20Sopenharmony_ci return; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* 1798c2ecf20Sopenharmony_ci * set flash0 iodomain to also use this framework 1808c2ecf20Sopenharmony_ci * instead of a special gpio. 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci val = RK3288_SOC_CON2_FLASH0 | (RK3288_SOC_CON2_FLASH0 << 16); 1838c2ecf20Sopenharmony_ci ret = regmap_write(iod->grf, RK3288_SOC_CON2, val); 1848c2ecf20Sopenharmony_ci if (ret < 0) 1858c2ecf20Sopenharmony_ci dev_warn(iod->dev, "couldn't update flash0 ctrl\n"); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic void rk3328_iodomain_init(struct rockchip_iodomain *iod) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci int ret; 1918c2ecf20Sopenharmony_ci u32 val; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* if no vccio2 supply we should leave things alone */ 1948c2ecf20Sopenharmony_ci if (!iod->supplies[RK3328_SOC_VCCIO2_SUPPLY_NUM].reg) 1958c2ecf20Sopenharmony_ci return; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* 1988c2ecf20Sopenharmony_ci * set vccio2 iodomain to also use this framework 1998c2ecf20Sopenharmony_ci * instead of a special gpio. 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_ci val = RK3328_SOC_CON4_VCCIO2 | (RK3328_SOC_CON4_VCCIO2 << 16); 2028c2ecf20Sopenharmony_ci ret = regmap_write(iod->grf, RK3328_SOC_CON4, val); 2038c2ecf20Sopenharmony_ci if (ret < 0) 2048c2ecf20Sopenharmony_ci dev_warn(iod->dev, "couldn't update vccio2 vsel ctrl\n"); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void rk3368_iodomain_init(struct rockchip_iodomain *iod) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci int ret; 2108c2ecf20Sopenharmony_ci u32 val; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* if no flash supply we should leave things alone */ 2138c2ecf20Sopenharmony_ci if (!iod->supplies[RK3368_SOC_FLASH_SUPPLY_NUM].reg) 2148c2ecf20Sopenharmony_ci return; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* 2178c2ecf20Sopenharmony_ci * set flash0 iodomain to also use this framework 2188c2ecf20Sopenharmony_ci * instead of a special gpio. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci val = RK3368_SOC_CON15_FLASH0 | (RK3368_SOC_CON15_FLASH0 << 16); 2218c2ecf20Sopenharmony_ci ret = regmap_write(iod->grf, RK3368_SOC_CON15, val); 2228c2ecf20Sopenharmony_ci if (ret < 0) 2238c2ecf20Sopenharmony_ci dev_warn(iod->dev, "couldn't update flash0 ctrl\n"); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void rk3399_pmu_iodomain_init(struct rockchip_iodomain *iod) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci int ret; 2298c2ecf20Sopenharmony_ci u32 val; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* if no pmu io supply we should leave things alone */ 2328c2ecf20Sopenharmony_ci if (!iod->supplies[RK3399_PMUGRF_VSEL_SUPPLY_NUM].reg) 2338c2ecf20Sopenharmony_ci return; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* 2368c2ecf20Sopenharmony_ci * set pmu io iodomain to also use this framework 2378c2ecf20Sopenharmony_ci * instead of a special gpio. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci val = RK3399_PMUGRF_CON0_VSEL | (RK3399_PMUGRF_CON0_VSEL << 16); 2408c2ecf20Sopenharmony_ci ret = regmap_write(iod->grf, RK3399_PMUGRF_CON0, val); 2418c2ecf20Sopenharmony_ci if (ret < 0) 2428c2ecf20Sopenharmony_ci dev_warn(iod->dev, "couldn't update pmu io iodomain ctrl\n"); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_px30 = { 2468c2ecf20Sopenharmony_ci .grf_offset = 0x180, 2478c2ecf20Sopenharmony_ci .supply_names = { 2488c2ecf20Sopenharmony_ci NULL, 2498c2ecf20Sopenharmony_ci "vccio6", 2508c2ecf20Sopenharmony_ci "vccio1", 2518c2ecf20Sopenharmony_ci "vccio2", 2528c2ecf20Sopenharmony_ci "vccio3", 2538c2ecf20Sopenharmony_ci "vccio4", 2548c2ecf20Sopenharmony_ci "vccio5", 2558c2ecf20Sopenharmony_ci "vccio-oscgpi", 2568c2ecf20Sopenharmony_ci }, 2578c2ecf20Sopenharmony_ci .init = px30_iodomain_init, 2588c2ecf20Sopenharmony_ci}; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_px30_pmu = { 2618c2ecf20Sopenharmony_ci .grf_offset = 0x100, 2628c2ecf20Sopenharmony_ci .supply_names = { 2638c2ecf20Sopenharmony_ci NULL, 2648c2ecf20Sopenharmony_ci NULL, 2658c2ecf20Sopenharmony_ci NULL, 2668c2ecf20Sopenharmony_ci NULL, 2678c2ecf20Sopenharmony_ci NULL, 2688c2ecf20Sopenharmony_ci NULL, 2698c2ecf20Sopenharmony_ci NULL, 2708c2ecf20Sopenharmony_ci NULL, 2718c2ecf20Sopenharmony_ci NULL, 2728c2ecf20Sopenharmony_ci NULL, 2738c2ecf20Sopenharmony_ci NULL, 2748c2ecf20Sopenharmony_ci NULL, 2758c2ecf20Sopenharmony_ci NULL, 2768c2ecf20Sopenharmony_ci NULL, 2778c2ecf20Sopenharmony_ci "pmuio1", 2788c2ecf20Sopenharmony_ci "pmuio2", 2798c2ecf20Sopenharmony_ci }, 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci/* 2838c2ecf20Sopenharmony_ci * On the rk3188 the io-domains are handled by a shared register with the 2848c2ecf20Sopenharmony_ci * lower 8 bits being still being continuing drive-strength settings. 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3188 = { 2878c2ecf20Sopenharmony_ci .grf_offset = 0x104, 2888c2ecf20Sopenharmony_ci .supply_names = { 2898c2ecf20Sopenharmony_ci NULL, 2908c2ecf20Sopenharmony_ci NULL, 2918c2ecf20Sopenharmony_ci NULL, 2928c2ecf20Sopenharmony_ci NULL, 2938c2ecf20Sopenharmony_ci NULL, 2948c2ecf20Sopenharmony_ci NULL, 2958c2ecf20Sopenharmony_ci NULL, 2968c2ecf20Sopenharmony_ci NULL, 2978c2ecf20Sopenharmony_ci "ap0", 2988c2ecf20Sopenharmony_ci "ap1", 2998c2ecf20Sopenharmony_ci "cif", 3008c2ecf20Sopenharmony_ci "flash", 3018c2ecf20Sopenharmony_ci "vccio0", 3028c2ecf20Sopenharmony_ci "vccio1", 3038c2ecf20Sopenharmony_ci "lcdc0", 3048c2ecf20Sopenharmony_ci "lcdc1", 3058c2ecf20Sopenharmony_ci }, 3068c2ecf20Sopenharmony_ci}; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3228 = { 3098c2ecf20Sopenharmony_ci .grf_offset = 0x418, 3108c2ecf20Sopenharmony_ci .supply_names = { 3118c2ecf20Sopenharmony_ci "vccio1", 3128c2ecf20Sopenharmony_ci "vccio2", 3138c2ecf20Sopenharmony_ci "vccio3", 3148c2ecf20Sopenharmony_ci "vccio4", 3158c2ecf20Sopenharmony_ci }, 3168c2ecf20Sopenharmony_ci}; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3288 = { 3198c2ecf20Sopenharmony_ci .grf_offset = 0x380, 3208c2ecf20Sopenharmony_ci .supply_names = { 3218c2ecf20Sopenharmony_ci "lcdc", /* LCDC_VDD */ 3228c2ecf20Sopenharmony_ci "dvp", /* DVPIO_VDD */ 3238c2ecf20Sopenharmony_ci "flash0", /* FLASH0_VDD (emmc) */ 3248c2ecf20Sopenharmony_ci "flash1", /* FLASH1_VDD (sdio1) */ 3258c2ecf20Sopenharmony_ci "wifi", /* APIO3_VDD (sdio0) */ 3268c2ecf20Sopenharmony_ci "bb", /* APIO5_VDD */ 3278c2ecf20Sopenharmony_ci "audio", /* APIO4_VDD */ 3288c2ecf20Sopenharmony_ci "sdcard", /* SDMMC0_VDD (sdmmc) */ 3298c2ecf20Sopenharmony_ci "gpio30", /* APIO1_VDD */ 3308c2ecf20Sopenharmony_ci "gpio1830", /* APIO2_VDD */ 3318c2ecf20Sopenharmony_ci }, 3328c2ecf20Sopenharmony_ci .init = rk3288_iodomain_init, 3338c2ecf20Sopenharmony_ci}; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3328 = { 3368c2ecf20Sopenharmony_ci .grf_offset = 0x410, 3378c2ecf20Sopenharmony_ci .supply_names = { 3388c2ecf20Sopenharmony_ci "vccio1", 3398c2ecf20Sopenharmony_ci "vccio2", 3408c2ecf20Sopenharmony_ci "vccio3", 3418c2ecf20Sopenharmony_ci "vccio4", 3428c2ecf20Sopenharmony_ci "vccio5", 3438c2ecf20Sopenharmony_ci "vccio6", 3448c2ecf20Sopenharmony_ci "pmuio", 3458c2ecf20Sopenharmony_ci }, 3468c2ecf20Sopenharmony_ci .init = rk3328_iodomain_init, 3478c2ecf20Sopenharmony_ci}; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3368 = { 3508c2ecf20Sopenharmony_ci .grf_offset = 0x900, 3518c2ecf20Sopenharmony_ci .supply_names = { 3528c2ecf20Sopenharmony_ci NULL, /* reserved */ 3538c2ecf20Sopenharmony_ci "dvp", /* DVPIO_VDD */ 3548c2ecf20Sopenharmony_ci "flash0", /* FLASH0_VDD (emmc) */ 3558c2ecf20Sopenharmony_ci "wifi", /* APIO2_VDD (sdio0) */ 3568c2ecf20Sopenharmony_ci NULL, 3578c2ecf20Sopenharmony_ci "audio", /* APIO3_VDD */ 3588c2ecf20Sopenharmony_ci "sdcard", /* SDMMC0_VDD (sdmmc) */ 3598c2ecf20Sopenharmony_ci "gpio30", /* APIO1_VDD */ 3608c2ecf20Sopenharmony_ci "gpio1830", /* APIO4_VDD (gpujtag) */ 3618c2ecf20Sopenharmony_ci }, 3628c2ecf20Sopenharmony_ci .init = rk3368_iodomain_init, 3638c2ecf20Sopenharmony_ci}; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3368_pmu = { 3668c2ecf20Sopenharmony_ci .grf_offset = 0x100, 3678c2ecf20Sopenharmony_ci .supply_names = { 3688c2ecf20Sopenharmony_ci NULL, 3698c2ecf20Sopenharmony_ci NULL, 3708c2ecf20Sopenharmony_ci NULL, 3718c2ecf20Sopenharmony_ci NULL, 3728c2ecf20Sopenharmony_ci "pmu", /*PMU IO domain*/ 3738c2ecf20Sopenharmony_ci "vop", /*LCDC IO domain*/ 3748c2ecf20Sopenharmony_ci }, 3758c2ecf20Sopenharmony_ci}; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3399 = { 3788c2ecf20Sopenharmony_ci .grf_offset = 0xe640, 3798c2ecf20Sopenharmony_ci .supply_names = { 3808c2ecf20Sopenharmony_ci "bt656", /* APIO2_VDD */ 3818c2ecf20Sopenharmony_ci "audio", /* APIO5_VDD */ 3828c2ecf20Sopenharmony_ci "sdmmc", /* SDMMC0_VDD */ 3838c2ecf20Sopenharmony_ci "gpio1830", /* APIO4_VDD */ 3848c2ecf20Sopenharmony_ci }, 3858c2ecf20Sopenharmony_ci}; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rk3399_pmu = { 3888c2ecf20Sopenharmony_ci .grf_offset = 0x180, 3898c2ecf20Sopenharmony_ci .supply_names = { 3908c2ecf20Sopenharmony_ci NULL, 3918c2ecf20Sopenharmony_ci NULL, 3928c2ecf20Sopenharmony_ci NULL, 3938c2ecf20Sopenharmony_ci NULL, 3948c2ecf20Sopenharmony_ci NULL, 3958c2ecf20Sopenharmony_ci NULL, 3968c2ecf20Sopenharmony_ci NULL, 3978c2ecf20Sopenharmony_ci NULL, 3988c2ecf20Sopenharmony_ci NULL, 3998c2ecf20Sopenharmony_ci "pmu1830", /* PMUIO2_VDD */ 4008c2ecf20Sopenharmony_ci }, 4018c2ecf20Sopenharmony_ci .init = rk3399_pmu_iodomain_init, 4028c2ecf20Sopenharmony_ci}; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rv1108 = { 4058c2ecf20Sopenharmony_ci .grf_offset = 0x404, 4068c2ecf20Sopenharmony_ci .supply_names = { 4078c2ecf20Sopenharmony_ci NULL, 4088c2ecf20Sopenharmony_ci NULL, 4098c2ecf20Sopenharmony_ci NULL, 4108c2ecf20Sopenharmony_ci NULL, 4118c2ecf20Sopenharmony_ci NULL, 4128c2ecf20Sopenharmony_ci NULL, 4138c2ecf20Sopenharmony_ci NULL, 4148c2ecf20Sopenharmony_ci NULL, 4158c2ecf20Sopenharmony_ci NULL, 4168c2ecf20Sopenharmony_ci NULL, 4178c2ecf20Sopenharmony_ci NULL, 4188c2ecf20Sopenharmony_ci "vccio1", 4198c2ecf20Sopenharmony_ci "vccio2", 4208c2ecf20Sopenharmony_ci "vccio3", 4218c2ecf20Sopenharmony_ci "vccio5", 4228c2ecf20Sopenharmony_ci "vccio6", 4238c2ecf20Sopenharmony_ci }, 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic const struct rockchip_iodomain_soc_data soc_data_rv1108_pmu = { 4288c2ecf20Sopenharmony_ci .grf_offset = 0x104, 4298c2ecf20Sopenharmony_ci .supply_names = { 4308c2ecf20Sopenharmony_ci "pmu", 4318c2ecf20Sopenharmony_ci }, 4328c2ecf20Sopenharmony_ci}; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic const struct of_device_id rockchip_iodomain_match[] = { 4358c2ecf20Sopenharmony_ci { 4368c2ecf20Sopenharmony_ci .compatible = "rockchip,px30-io-voltage-domain", 4378c2ecf20Sopenharmony_ci .data = (void *)&soc_data_px30 4388c2ecf20Sopenharmony_ci }, 4398c2ecf20Sopenharmony_ci { 4408c2ecf20Sopenharmony_ci .compatible = "rockchip,px30-pmu-io-voltage-domain", 4418c2ecf20Sopenharmony_ci .data = (void *)&soc_data_px30_pmu 4428c2ecf20Sopenharmony_ci }, 4438c2ecf20Sopenharmony_ci { 4448c2ecf20Sopenharmony_ci .compatible = "rockchip,rk3188-io-voltage-domain", 4458c2ecf20Sopenharmony_ci .data = &soc_data_rk3188 4468c2ecf20Sopenharmony_ci }, 4478c2ecf20Sopenharmony_ci { 4488c2ecf20Sopenharmony_ci .compatible = "rockchip,rk3228-io-voltage-domain", 4498c2ecf20Sopenharmony_ci .data = &soc_data_rk3228 4508c2ecf20Sopenharmony_ci }, 4518c2ecf20Sopenharmony_ci { 4528c2ecf20Sopenharmony_ci .compatible = "rockchip,rk3288-io-voltage-domain", 4538c2ecf20Sopenharmony_ci .data = &soc_data_rk3288 4548c2ecf20Sopenharmony_ci }, 4558c2ecf20Sopenharmony_ci { 4568c2ecf20Sopenharmony_ci .compatible = "rockchip,rk3328-io-voltage-domain", 4578c2ecf20Sopenharmony_ci .data = &soc_data_rk3328 4588c2ecf20Sopenharmony_ci }, 4598c2ecf20Sopenharmony_ci { 4608c2ecf20Sopenharmony_ci .compatible = "rockchip,rk3368-io-voltage-domain", 4618c2ecf20Sopenharmony_ci .data = &soc_data_rk3368 4628c2ecf20Sopenharmony_ci }, 4638c2ecf20Sopenharmony_ci { 4648c2ecf20Sopenharmony_ci .compatible = "rockchip,rk3368-pmu-io-voltage-domain", 4658c2ecf20Sopenharmony_ci .data = &soc_data_rk3368_pmu 4668c2ecf20Sopenharmony_ci }, 4678c2ecf20Sopenharmony_ci { 4688c2ecf20Sopenharmony_ci .compatible = "rockchip,rk3399-io-voltage-domain", 4698c2ecf20Sopenharmony_ci .data = &soc_data_rk3399 4708c2ecf20Sopenharmony_ci }, 4718c2ecf20Sopenharmony_ci { 4728c2ecf20Sopenharmony_ci .compatible = "rockchip,rk3399-pmu-io-voltage-domain", 4738c2ecf20Sopenharmony_ci .data = &soc_data_rk3399_pmu 4748c2ecf20Sopenharmony_ci }, 4758c2ecf20Sopenharmony_ci { 4768c2ecf20Sopenharmony_ci .compatible = "rockchip,rv1108-io-voltage-domain", 4778c2ecf20Sopenharmony_ci .data = &soc_data_rv1108 4788c2ecf20Sopenharmony_ci }, 4798c2ecf20Sopenharmony_ci { 4808c2ecf20Sopenharmony_ci .compatible = "rockchip,rv1108-pmu-io-voltage-domain", 4818c2ecf20Sopenharmony_ci .data = &soc_data_rv1108_pmu 4828c2ecf20Sopenharmony_ci }, 4838c2ecf20Sopenharmony_ci { /* sentinel */ }, 4848c2ecf20Sopenharmony_ci}; 4858c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_iodomain_match); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int rockchip_iodomain_probe(struct platform_device *pdev) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 4908c2ecf20Sopenharmony_ci const struct of_device_id *match; 4918c2ecf20Sopenharmony_ci struct rockchip_iodomain *iod; 4928c2ecf20Sopenharmony_ci struct device *parent; 4938c2ecf20Sopenharmony_ci int i, ret = 0; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (!np) 4968c2ecf20Sopenharmony_ci return -ENODEV; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci iod = devm_kzalloc(&pdev->dev, sizeof(*iod), GFP_KERNEL); 4998c2ecf20Sopenharmony_ci if (!iod) 5008c2ecf20Sopenharmony_ci return -ENOMEM; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci iod->dev = &pdev->dev; 5038c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, iod); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci match = of_match_node(rockchip_iodomain_match, np); 5068c2ecf20Sopenharmony_ci iod->soc_data = match->data; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci parent = pdev->dev.parent; 5098c2ecf20Sopenharmony_ci if (parent && parent->of_node) { 5108c2ecf20Sopenharmony_ci iod->grf = syscon_node_to_regmap(parent->of_node); 5118c2ecf20Sopenharmony_ci } else { 5128c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "falling back to old binding\n"); 5138c2ecf20Sopenharmony_ci iod->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (IS_ERR(iod->grf)) { 5178c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "couldn't find grf regmap\n"); 5188c2ecf20Sopenharmony_ci return PTR_ERR(iod->grf); 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci for (i = 0; i < MAX_SUPPLIES; i++) { 5228c2ecf20Sopenharmony_ci const char *supply_name = iod->soc_data->supply_names[i]; 5238c2ecf20Sopenharmony_ci struct rockchip_iodomain_supply *supply = &iod->supplies[i]; 5248c2ecf20Sopenharmony_ci struct regulator *reg; 5258c2ecf20Sopenharmony_ci int uV; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (!supply_name) 5288c2ecf20Sopenharmony_ci continue; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci reg = devm_regulator_get_optional(iod->dev, supply_name); 5318c2ecf20Sopenharmony_ci if (IS_ERR(reg)) { 5328c2ecf20Sopenharmony_ci ret = PTR_ERR(reg); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* If a supply wasn't specified, that's OK */ 5358c2ecf20Sopenharmony_ci if (ret == -ENODEV) 5368c2ecf20Sopenharmony_ci continue; 5378c2ecf20Sopenharmony_ci else if (ret != -EPROBE_DEFER) 5388c2ecf20Sopenharmony_ci dev_err(iod->dev, "couldn't get regulator %s\n", 5398c2ecf20Sopenharmony_ci supply_name); 5408c2ecf20Sopenharmony_ci goto unreg_notify; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* set initial correct value */ 5448c2ecf20Sopenharmony_ci uV = regulator_get_voltage(reg); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* must be a regulator we can get the voltage of */ 5478c2ecf20Sopenharmony_ci if (uV < 0) { 5488c2ecf20Sopenharmony_ci dev_err(iod->dev, "Can't determine voltage: %s\n", 5498c2ecf20Sopenharmony_ci supply_name); 5508c2ecf20Sopenharmony_ci ret = uV; 5518c2ecf20Sopenharmony_ci goto unreg_notify; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (uV > MAX_VOLTAGE_3_3) { 5558c2ecf20Sopenharmony_ci dev_crit(iod->dev, 5568c2ecf20Sopenharmony_ci "%d uV is too high. May damage SoC!\n", 5578c2ecf20Sopenharmony_ci uV); 5588c2ecf20Sopenharmony_ci ret = -EINVAL; 5598c2ecf20Sopenharmony_ci goto unreg_notify; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* setup our supply */ 5638c2ecf20Sopenharmony_ci supply->idx = i; 5648c2ecf20Sopenharmony_ci supply->iod = iod; 5658c2ecf20Sopenharmony_ci supply->reg = reg; 5668c2ecf20Sopenharmony_ci supply->nb.notifier_call = rockchip_iodomain_notify; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci ret = rockchip_iodomain_write(supply, uV); 5698c2ecf20Sopenharmony_ci if (ret) { 5708c2ecf20Sopenharmony_ci supply->reg = NULL; 5718c2ecf20Sopenharmony_ci goto unreg_notify; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* register regulator notifier */ 5758c2ecf20Sopenharmony_ci ret = regulator_register_notifier(reg, &supply->nb); 5768c2ecf20Sopenharmony_ci if (ret) { 5778c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 5788c2ecf20Sopenharmony_ci "regulator notifier request failed\n"); 5798c2ecf20Sopenharmony_ci supply->reg = NULL; 5808c2ecf20Sopenharmony_ci goto unreg_notify; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (iod->soc_data->init) 5858c2ecf20Sopenharmony_ci iod->soc_data->init(iod); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci return 0; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ciunreg_notify: 5908c2ecf20Sopenharmony_ci for (i = MAX_SUPPLIES - 1; i >= 0; i--) { 5918c2ecf20Sopenharmony_ci struct rockchip_iodomain_supply *io_supply = &iod->supplies[i]; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (io_supply->reg) 5948c2ecf20Sopenharmony_ci regulator_unregister_notifier(io_supply->reg, 5958c2ecf20Sopenharmony_ci &io_supply->nb); 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci return ret; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic int rockchip_iodomain_remove(struct platform_device *pdev) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct rockchip_iodomain *iod = platform_get_drvdata(pdev); 6048c2ecf20Sopenharmony_ci int i; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci for (i = MAX_SUPPLIES - 1; i >= 0; i--) { 6078c2ecf20Sopenharmony_ci struct rockchip_iodomain_supply *io_supply = &iod->supplies[i]; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (io_supply->reg) 6108c2ecf20Sopenharmony_ci regulator_unregister_notifier(io_supply->reg, 6118c2ecf20Sopenharmony_ci &io_supply->nb); 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return 0; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic struct platform_driver rockchip_iodomain_driver = { 6188c2ecf20Sopenharmony_ci .probe = rockchip_iodomain_probe, 6198c2ecf20Sopenharmony_ci .remove = rockchip_iodomain_remove, 6208c2ecf20Sopenharmony_ci .driver = { 6218c2ecf20Sopenharmony_ci .name = "rockchip-iodomain", 6228c2ecf20Sopenharmony_ci .of_match_table = rockchip_iodomain_match, 6238c2ecf20Sopenharmony_ci }, 6248c2ecf20Sopenharmony_ci}; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cimodule_platform_driver(rockchip_iodomain_driver); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Rockchip IO-domain driver"); 6298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); 6308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Doug Anderson <dianders@chromium.org>"); 6318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 632