759 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Rockchip IO Voltage Domain driver4 *5 * Copyright 2014 MundoReader S.L.6 * Copyright 2014 Google, Inc.7 */8 9#include <linux/kernel.h>10#include <linux/module.h>11#include <linux/err.h>12#include <linux/mfd/syscon.h>13#include <linux/of.h>14#include <linux/platform_device.h>15#include <linux/regmap.h>16#include <linux/regulator/consumer.h>17 18#define MAX_SUPPLIES 1619 20/*21 * The max voltage for 1.8V and 3.3V come from the Rockchip datasheet under22 * "Recommended Operating Conditions" for "Digital GPIO". When the typical23 * is 3.3V the max is 3.6V. When the typical is 1.8V the max is 1.98V.24 *25 * They are used like this:26 * - If the voltage on a rail is above the "1.8" voltage (1.98V) we'll tell the27 * SoC we're at 3.3.28 * - If the voltage on a rail is above the "3.3" voltage (3.6V) we'll consider29 * that to be an error.30 */31#define MAX_VOLTAGE_1_8 198000032#define MAX_VOLTAGE_3_3 360000033 34#define PX30_IO_VSEL 0x18035#define PX30_IO_VSEL_VCCIO6_SRC BIT(0)36#define PX30_IO_VSEL_VCCIO6_SUPPLY_NUM 137 38#define RK3288_SOC_CON2 0x24c39#define RK3288_SOC_CON2_FLASH0 BIT(7)40#define RK3288_SOC_FLASH_SUPPLY_NUM 241 42#define RK3308_SOC_CON0 0x30043#define RK3308_SOC_CON0_VCCIO3 BIT(8)44#define RK3308_SOC_VCCIO3_SUPPLY_NUM 345 46#define RK3328_SOC_CON4 0x41047#define RK3328_SOC_CON4_VCCIO2 BIT(7)48#define RK3328_SOC_VCCIO2_SUPPLY_NUM 149 50#define RK3368_SOC_CON15 0x43c51#define RK3368_SOC_CON15_FLASH0 BIT(14)52#define RK3368_SOC_FLASH_SUPPLY_NUM 253 54#define RK3399_PMUGRF_CON0 0x18055#define RK3399_PMUGRF_CON0_VSEL BIT(8)56#define RK3399_PMUGRF_VSEL_SUPPLY_NUM 957 58#define RK3568_PMU_GRF_IO_VSEL0 (0x0140)59#define RK3568_PMU_GRF_IO_VSEL1 (0x0144)60#define RK3568_PMU_GRF_IO_VSEL2 (0x0148)61 62struct rockchip_iodomain;63 64struct rockchip_iodomain_supply {65 struct rockchip_iodomain *iod;66 struct regulator *reg;67 struct notifier_block nb;68 int idx;69};70 71struct rockchip_iodomain_soc_data {72 int grf_offset;73 const char *supply_names[MAX_SUPPLIES];74 void (*init)(struct rockchip_iodomain *iod);75 int (*write)(struct rockchip_iodomain_supply *supply, int uV);76};77 78struct rockchip_iodomain {79 struct device *dev;80 struct regmap *grf;81 const struct rockchip_iodomain_soc_data *soc_data;82 struct rockchip_iodomain_supply supplies[MAX_SUPPLIES];83 int (*write)(struct rockchip_iodomain_supply *supply, int uV);84};85 86static int rk3568_iodomain_write(struct rockchip_iodomain_supply *supply, int uV)87{88 struct rockchip_iodomain *iod = supply->iod;89 u32 is_3v3 = uV > MAX_VOLTAGE_1_8;90 u32 val0, val1;91 int b;92 93 switch (supply->idx) {94 case 0: /* pmuio1 */95 break;96 case 1: /* pmuio2 */97 b = supply->idx;98 val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b));99 b = supply->idx + 4;100 val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0);101 102 regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val0);103 regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val1);104 break;105 case 3: /* vccio2 */106 break;107 case 2: /* vccio1 */108 case 4: /* vccio3 */109 case 5: /* vccio4 */110 case 6: /* vccio5 */111 case 7: /* vccio6 */112 case 8: /* vccio7 */113 b = supply->idx - 1;114 val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b));115 val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0);116 117 regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL0, val0);118 regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL1, val1);119 break;120 default:121 return -EINVAL;122 }123 124 return 0;125}126 127static int rockchip_iodomain_write(struct rockchip_iodomain_supply *supply,128 int uV)129{130 struct rockchip_iodomain *iod = supply->iod;131 u32 val;132 int ret;133 134 /* set value bit */135 val = (uV > MAX_VOLTAGE_1_8) ? 0 : 1;136 val <<= supply->idx;137 138 /* apply hiword-mask */139 val |= (BIT(supply->idx) << 16);140 141 ret = regmap_write(iod->grf, iod->soc_data->grf_offset, val);142 if (ret)143 dev_err(iod->dev, "Couldn't write to GRF\n");144 145 return ret;146}147 148static int rockchip_iodomain_notify(struct notifier_block *nb,149 unsigned long event,150 void *data)151{152 struct rockchip_iodomain_supply *supply =153 container_of(nb, struct rockchip_iodomain_supply, nb);154 int uV;155 int ret;156 157 /*158 * According to Rockchip it's important to keep the SoC IO domain159 * higher than (or equal to) the external voltage. That means we need160 * to change it before external voltage changes happen in the case161 * of an increase.162 *163 * Note that in the "pre" change we pick the max possible voltage that164 * the regulator might end up at (the client requests a range and we165 * don't know for certain the exact voltage). Right now we rely on the166 * slop in MAX_VOLTAGE_1_8 and MAX_VOLTAGE_3_3 to save us if clients167 * request something like a max of 3.6V when they really want 3.3V.168 * We could attempt to come up with better rules if this fails.169 */170 if (event & REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) {171 struct pre_voltage_change_data *pvc_data = data;172 173 uV = max_t(unsigned long, pvc_data->old_uV, pvc_data->max_uV);174 } else if (event & (REGULATOR_EVENT_VOLTAGE_CHANGE |175 REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE)) {176 uV = (unsigned long)data;177 } else {178 return NOTIFY_OK;179 }180 181 dev_dbg(supply->iod->dev, "Setting to %d\n", uV);182 183 if (uV > MAX_VOLTAGE_3_3) {184 dev_err(supply->iod->dev, "Voltage too high: %d\n", uV);185 186 if (event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)187 return NOTIFY_BAD;188 }189 190 ret = supply->iod->write(supply, uV);191 if (ret && event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)192 return NOTIFY_BAD;193 194 dev_dbg(supply->iod->dev, "Setting to %d done\n", uV);195 return NOTIFY_OK;196}197 198static void px30_iodomain_init(struct rockchip_iodomain *iod)199{200 int ret;201 u32 val;202 203 /* if no VCCIO6 supply we should leave things alone */204 if (!iod->supplies[PX30_IO_VSEL_VCCIO6_SUPPLY_NUM].reg)205 return;206 207 /*208 * set vccio6 iodomain to also use this framework209 * instead of a special gpio.210 */211 val = PX30_IO_VSEL_VCCIO6_SRC | (PX30_IO_VSEL_VCCIO6_SRC << 16);212 ret = regmap_write(iod->grf, PX30_IO_VSEL, val);213 if (ret < 0)214 dev_warn(iod->dev, "couldn't update vccio6 ctrl\n");215}216 217static void rk3288_iodomain_init(struct rockchip_iodomain *iod)218{219 int ret;220 u32 val;221 222 /* if no flash supply we should leave things alone */223 if (!iod->supplies[RK3288_SOC_FLASH_SUPPLY_NUM].reg)224 return;225 226 /*227 * set flash0 iodomain to also use this framework228 * instead of a special gpio.229 */230 val = RK3288_SOC_CON2_FLASH0 | (RK3288_SOC_CON2_FLASH0 << 16);231 ret = regmap_write(iod->grf, RK3288_SOC_CON2, val);232 if (ret < 0)233 dev_warn(iod->dev, "couldn't update flash0 ctrl\n");234}235 236static void rk3308_iodomain_init(struct rockchip_iodomain *iod)237{238 int ret;239 u32 val;240 241 /* if no vccio3 supply we should leave things alone */242 if (!iod->supplies[RK3308_SOC_VCCIO3_SUPPLY_NUM].reg)243 return;244 245 /*246 * set vccio3 iodomain to also use this framework247 * instead of a special gpio.248 */249 val = RK3308_SOC_CON0_VCCIO3 | (RK3308_SOC_CON0_VCCIO3 << 16);250 ret = regmap_write(iod->grf, RK3308_SOC_CON0, val);251 if (ret < 0)252 dev_warn(iod->dev, "couldn't update vccio3 vsel ctrl\n");253}254 255static void rk3328_iodomain_init(struct rockchip_iodomain *iod)256{257 int ret;258 u32 val;259 260 /* if no vccio2 supply we should leave things alone */261 if (!iod->supplies[RK3328_SOC_VCCIO2_SUPPLY_NUM].reg)262 return;263 264 /*265 * set vccio2 iodomain to also use this framework266 * instead of a special gpio.267 */268 val = RK3328_SOC_CON4_VCCIO2 | (RK3328_SOC_CON4_VCCIO2 << 16);269 ret = regmap_write(iod->grf, RK3328_SOC_CON4, val);270 if (ret < 0)271 dev_warn(iod->dev, "couldn't update vccio2 vsel ctrl\n");272}273 274static void rk3368_iodomain_init(struct rockchip_iodomain *iod)275{276 int ret;277 u32 val;278 279 /* if no flash supply we should leave things alone */280 if (!iod->supplies[RK3368_SOC_FLASH_SUPPLY_NUM].reg)281 return;282 283 /*284 * set flash0 iodomain to also use this framework285 * instead of a special gpio.286 */287 val = RK3368_SOC_CON15_FLASH0 | (RK3368_SOC_CON15_FLASH0 << 16);288 ret = regmap_write(iod->grf, RK3368_SOC_CON15, val);289 if (ret < 0)290 dev_warn(iod->dev, "couldn't update flash0 ctrl\n");291}292 293static void rk3399_pmu_iodomain_init(struct rockchip_iodomain *iod)294{295 int ret;296 u32 val;297 298 /* if no pmu io supply we should leave things alone */299 if (!iod->supplies[RK3399_PMUGRF_VSEL_SUPPLY_NUM].reg)300 return;301 302 /*303 * set pmu io iodomain to also use this framework304 * instead of a special gpio.305 */306 val = RK3399_PMUGRF_CON0_VSEL | (RK3399_PMUGRF_CON0_VSEL << 16);307 ret = regmap_write(iod->grf, RK3399_PMUGRF_CON0, val);308 if (ret < 0)309 dev_warn(iod->dev, "couldn't update pmu io iodomain ctrl\n");310}311 312static const struct rockchip_iodomain_soc_data soc_data_px30 = {313 .grf_offset = 0x180,314 .supply_names = {315 NULL,316 "vccio6",317 "vccio1",318 "vccio2",319 "vccio3",320 "vccio4",321 "vccio5",322 "vccio-oscgpi",323 },324 .init = px30_iodomain_init,325};326 327static const struct rockchip_iodomain_soc_data soc_data_px30_pmu = {328 .grf_offset = 0x100,329 .supply_names = {330 NULL,331 NULL,332 NULL,333 NULL,334 NULL,335 NULL,336 NULL,337 NULL,338 NULL,339 NULL,340 NULL,341 NULL,342 NULL,343 NULL,344 "pmuio1",345 "pmuio2",346 },347};348 349/*350 * On the rk3188 the io-domains are handled by a shared register with the351 * lower 8 bits being still being continuing drive-strength settings.352 */353static const struct rockchip_iodomain_soc_data soc_data_rk3188 = {354 .grf_offset = 0x104,355 .supply_names = {356 NULL,357 NULL,358 NULL,359 NULL,360 NULL,361 NULL,362 NULL,363 NULL,364 "ap0",365 "ap1",366 "cif",367 "flash",368 "vccio0",369 "vccio1",370 "lcdc0",371 "lcdc1",372 },373};374 375static const struct rockchip_iodomain_soc_data soc_data_rk3228 = {376 .grf_offset = 0x418,377 .supply_names = {378 "vccio1",379 "vccio2",380 "vccio3",381 "vccio4",382 },383};384 385static const struct rockchip_iodomain_soc_data soc_data_rk3288 = {386 .grf_offset = 0x380,387 .supply_names = {388 "lcdc", /* LCDC_VDD */389 "dvp", /* DVPIO_VDD */390 "flash0", /* FLASH0_VDD (emmc) */391 "flash1", /* FLASH1_VDD (sdio1) */392 "wifi", /* APIO3_VDD (sdio0) */393 "bb", /* APIO5_VDD */394 "audio", /* APIO4_VDD */395 "sdcard", /* SDMMC0_VDD (sdmmc) */396 "gpio30", /* APIO1_VDD */397 "gpio1830", /* APIO2_VDD */398 },399 .init = rk3288_iodomain_init,400};401 402static const struct rockchip_iodomain_soc_data soc_data_rk3308 = {403 .grf_offset = 0x300,404 .supply_names = {405 "vccio0",406 "vccio1",407 "vccio2",408 "vccio3",409 "vccio4",410 "vccio5",411 },412 .init = rk3308_iodomain_init,413};414 415static const struct rockchip_iodomain_soc_data soc_data_rk3328 = {416 .grf_offset = 0x410,417 .supply_names = {418 "vccio1",419 "vccio2",420 "vccio3",421 "vccio4",422 "vccio5",423 "vccio6",424 "pmuio",425 },426 .init = rk3328_iodomain_init,427};428 429static const struct rockchip_iodomain_soc_data soc_data_rk3368 = {430 .grf_offset = 0x900,431 .supply_names = {432 NULL, /* reserved */433 "dvp", /* DVPIO_VDD */434 "flash0", /* FLASH0_VDD (emmc) */435 "wifi", /* APIO2_VDD (sdio0) */436 NULL,437 "audio", /* APIO3_VDD */438 "sdcard", /* SDMMC0_VDD (sdmmc) */439 "gpio30", /* APIO1_VDD */440 "gpio1830", /* APIO4_VDD (gpujtag) */441 },442 .init = rk3368_iodomain_init,443};444 445static const struct rockchip_iodomain_soc_data soc_data_rk3368_pmu = {446 .grf_offset = 0x100,447 .supply_names = {448 NULL,449 NULL,450 NULL,451 NULL,452 "pmu", /*PMU IO domain*/453 "vop", /*LCDC IO domain*/454 },455};456 457static const struct rockchip_iodomain_soc_data soc_data_rk3399 = {458 .grf_offset = 0xe640,459 .supply_names = {460 "bt656", /* APIO2_VDD */461 "audio", /* APIO5_VDD */462 "sdmmc", /* SDMMC0_VDD */463 "gpio1830", /* APIO4_VDD */464 },465};466 467static const struct rockchip_iodomain_soc_data soc_data_rk3399_pmu = {468 .grf_offset = 0x180,469 .supply_names = {470 NULL,471 NULL,472 NULL,473 NULL,474 NULL,475 NULL,476 NULL,477 NULL,478 NULL,479 "pmu1830", /* PMUIO2_VDD */480 },481 .init = rk3399_pmu_iodomain_init,482};483 484static const struct rockchip_iodomain_soc_data soc_data_rk3568_pmu = {485 .grf_offset = 0x140,486 .supply_names = {487 "pmuio1",488 "pmuio2",489 "vccio1",490 "vccio2",491 "vccio3",492 "vccio4",493 "vccio5",494 "vccio6",495 "vccio7",496 },497 .write = rk3568_iodomain_write,498};499 500static const struct rockchip_iodomain_soc_data soc_data_rv1108 = {501 .grf_offset = 0x404,502 .supply_names = {503 NULL,504 NULL,505 NULL,506 NULL,507 NULL,508 NULL,509 NULL,510 NULL,511 NULL,512 NULL,513 NULL,514 "vccio1",515 "vccio2",516 "vccio3",517 "vccio5",518 "vccio6",519 },520 521};522 523static const struct rockchip_iodomain_soc_data soc_data_rv1108_pmu = {524 .grf_offset = 0x104,525 .supply_names = {526 "pmu",527 },528};529 530static const struct rockchip_iodomain_soc_data soc_data_rv1126_pmu = {531 .grf_offset = 0x140,532 .supply_names = {533 NULL,534 "vccio1",535 "vccio2",536 "vccio3",537 "vccio4",538 "vccio5",539 "vccio6",540 "vccio7",541 "pmuio0",542 "pmuio1",543 },544};545 546static const struct of_device_id rockchip_iodomain_match[] = {547 {548 .compatible = "rockchip,px30-io-voltage-domain",549 .data = (void *)&soc_data_px30550 },551 {552 .compatible = "rockchip,px30-pmu-io-voltage-domain",553 .data = (void *)&soc_data_px30_pmu554 },555 {556 .compatible = "rockchip,rk3188-io-voltage-domain",557 .data = &soc_data_rk3188558 },559 {560 .compatible = "rockchip,rk3228-io-voltage-domain",561 .data = &soc_data_rk3228562 },563 {564 .compatible = "rockchip,rk3288-io-voltage-domain",565 .data = &soc_data_rk3288566 },567 {568 .compatible = "rockchip,rk3308-io-voltage-domain",569 .data = &soc_data_rk3308570 },571 {572 .compatible = "rockchip,rk3328-io-voltage-domain",573 .data = &soc_data_rk3328574 },575 {576 .compatible = "rockchip,rk3368-io-voltage-domain",577 .data = &soc_data_rk3368578 },579 {580 .compatible = "rockchip,rk3368-pmu-io-voltage-domain",581 .data = &soc_data_rk3368_pmu582 },583 {584 .compatible = "rockchip,rk3399-io-voltage-domain",585 .data = &soc_data_rk3399586 },587 {588 .compatible = "rockchip,rk3399-pmu-io-voltage-domain",589 .data = &soc_data_rk3399_pmu590 },591 {592 .compatible = "rockchip,rk3568-pmu-io-voltage-domain",593 .data = &soc_data_rk3568_pmu594 },595 {596 .compatible = "rockchip,rv1108-io-voltage-domain",597 .data = &soc_data_rv1108598 },599 {600 .compatible = "rockchip,rv1108-pmu-io-voltage-domain",601 .data = &soc_data_rv1108_pmu602 },603 {604 .compatible = "rockchip,rv1126-pmu-io-voltage-domain",605 .data = &soc_data_rv1126_pmu606 },607 { /* sentinel */ },608};609MODULE_DEVICE_TABLE(of, rockchip_iodomain_match);610 611static int rockchip_iodomain_probe(struct platform_device *pdev)612{613 struct device_node *np = pdev->dev.of_node;614 const struct of_device_id *match;615 struct rockchip_iodomain *iod;616 struct device *parent;617 int i, ret = 0;618 619 if (!np)620 return -ENODEV;621 622 iod = devm_kzalloc(&pdev->dev, sizeof(*iod), GFP_KERNEL);623 if (!iod)624 return -ENOMEM;625 626 iod->dev = &pdev->dev;627 platform_set_drvdata(pdev, iod);628 629 match = of_match_node(rockchip_iodomain_match, np);630 iod->soc_data = match->data;631 632 if (iod->soc_data->write)633 iod->write = iod->soc_data->write;634 else635 iod->write = rockchip_iodomain_write;636 637 parent = pdev->dev.parent;638 if (parent && parent->of_node) {639 iod->grf = syscon_node_to_regmap(parent->of_node);640 } else {641 dev_dbg(&pdev->dev, "falling back to old binding\n");642 iod->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");643 }644 645 if (IS_ERR(iod->grf)) {646 dev_err(&pdev->dev, "couldn't find grf regmap\n");647 return PTR_ERR(iod->grf);648 }649 650 for (i = 0; i < MAX_SUPPLIES; i++) {651 const char *supply_name = iod->soc_data->supply_names[i];652 struct rockchip_iodomain_supply *supply = &iod->supplies[i];653 struct regulator *reg;654 int uV;655 656 if (!supply_name)657 continue;658 659 reg = devm_regulator_get_optional(iod->dev, supply_name);660 if (IS_ERR(reg)) {661 ret = PTR_ERR(reg);662 663 /* If a supply wasn't specified, that's OK */664 if (ret == -ENODEV)665 continue;666 else if (ret != -EPROBE_DEFER)667 dev_err(iod->dev, "couldn't get regulator %s\n",668 supply_name);669 goto unreg_notify;670 }671 672 /* set initial correct value */673 uV = regulator_get_voltage(reg);674 675 /* must be a regulator we can get the voltage of */676 if (uV < 0) {677 dev_err(iod->dev, "Can't determine voltage: %s\n",678 supply_name);679 ret = uV;680 goto unreg_notify;681 }682 683 if (uV > MAX_VOLTAGE_3_3) {684 dev_crit(iod->dev,685 "%d uV is too high. May damage SoC!\n",686 uV);687 ret = -EINVAL;688 goto unreg_notify;689 }690 691 /* setup our supply */692 supply->idx = i;693 supply->iod = iod;694 supply->reg = reg;695 supply->nb.notifier_call = rockchip_iodomain_notify;696 697 ret = iod->write(supply, uV);698 if (ret) {699 supply->reg = NULL;700 goto unreg_notify;701 }702 703 /* register regulator notifier */704 ret = regulator_register_notifier(reg, &supply->nb);705 if (ret) {706 dev_err(&pdev->dev,707 "regulator notifier request failed\n");708 supply->reg = NULL;709 goto unreg_notify;710 }711 }712 713 if (iod->soc_data->init)714 iod->soc_data->init(iod);715 716 return 0;717 718unreg_notify:719 for (i = MAX_SUPPLIES - 1; i >= 0; i--) {720 struct rockchip_iodomain_supply *io_supply = &iod->supplies[i];721 722 if (io_supply->reg)723 regulator_unregister_notifier(io_supply->reg,724 &io_supply->nb);725 }726 727 return ret;728}729 730static void rockchip_iodomain_remove(struct platform_device *pdev)731{732 struct rockchip_iodomain *iod = platform_get_drvdata(pdev);733 int i;734 735 for (i = MAX_SUPPLIES - 1; i >= 0; i--) {736 struct rockchip_iodomain_supply *io_supply = &iod->supplies[i];737 738 if (io_supply->reg)739 regulator_unregister_notifier(io_supply->reg,740 &io_supply->nb);741 }742}743 744static struct platform_driver rockchip_iodomain_driver = {745 .probe = rockchip_iodomain_probe,746 .remove_new = rockchip_iodomain_remove,747 .driver = {748 .name = "rockchip-iodomain",749 .of_match_table = rockchip_iodomain_match,750 },751};752 753module_platform_driver(rockchip_iodomain_driver);754 755MODULE_DESCRIPTION("Rockchip IO-domain driver");756MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");757MODULE_AUTHOR("Doug Anderson <dianders@chromium.org>");758MODULE_LICENSE("GPL v2");759