From 4f43646e3628de1a420479d1c38d0f9e1544fb94 Mon Sep 17 00:00:00 2001 From: Jianqun Xu Date: Fri, 14 Dec 2018 11:55:36 +0800 Subject: [PATCH] power: rockchip: add io-domain driver The io-domains on Rockchip SoCs will be supplied with 1.8V or 3.3V(3.0V) depends on hardware design. The driver needs to config them through grf register. Change-Id: Ic896b94bfadd0b808da54992e58e2cf4a8fee950 Signed-off-by: Jianqun Xu --- Makefile | 3 +- drivers/power/Kconfig | 2 + drivers/power/io-domain/Kconfig | 19 + drivers/power/io-domain/Makefile | 6 + drivers/power/io-domain/io-domain-uclass.c | 23 + drivers/power/io-domain/rockchip-io-domain.c | 620 +++++++++++++++++++ include/dm/uclass-id.h | 1 + include/io-domain.h | 16 + 8 files changed, 689 insertions(+), 1 deletion(-) create mode 100644 drivers/power/io-domain/Kconfig create mode 100644 drivers/power/io-domain/Makefile create mode 100644 drivers/power/io-domain/io-domain-uclass.c create mode 100644 drivers/power/io-domain/rockchip-io-domain.c create mode 100644 include/io-domain.h diff --git a/Makefile b/Makefile index 7060da1c5f..efedc288d4 100644 --- a/Makefile +++ b/Makefile @@ -672,7 +672,8 @@ libs-y += drivers/power/ \ drivers/power/pmic/ \ drivers/power/battery/ \ drivers/power/regulator/ \ - drivers/power/dvfs/ + drivers/power/dvfs/ \ + drivers/power/io-domain/ libs-y += drivers/spi/ libs-$(CONFIG_FMAN_ENET) += drivers/net/fm/ libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/ diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index adf5ff58bf..608df55cca 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -4,6 +4,8 @@ source "drivers/power/domain/Kconfig" source "drivers/power/fuel_gauge/Kconfig" +source "drivers/power/io-domain/Kconfig" + source "drivers/power/pmic/Kconfig" source "drivers/power/regulator/Kconfig" diff --git a/drivers/power/io-domain/Kconfig b/drivers/power/io-domain/Kconfig new file mode 100644 index 0000000000..1c71e0a9a2 --- /dev/null +++ b/drivers/power/io-domain/Kconfig @@ -0,0 +1,19 @@ +menu "IO Domain Support" + +config IO_DOMAIN + bool "Enable io domain support using Driver Model" + depends on DM && OF_CONTROL + help + Enable support for the io domain driver class. Many SoCs allow + power to be applied to or removed from portions of the SoC (io + domains). This may be used to save power. This API provides the + means to control such power management hardware. + +config ROCKCHIP_IO_DOMAIN + bool "Enable the rockchip io domain driver" + depends on IO_DOMAIN && ARCH_ROCKCHIP + help + Enable support for manipulating ROCKCHIP io domains via MMIO mapped + registers. + +endmenu diff --git a/drivers/power/io-domain/Makefile b/drivers/power/io-domain/Makefile new file mode 100644 index 0000000000..f1e7af71d2 --- /dev/null +++ b/drivers/power/io-domain/Makefile @@ -0,0 +1,6 @@ +# Copyright (c) 2018 Rockchip Electronics Co., Ltd +# +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_IO_DOMAIN) += io-domain-uclass.o +obj-$(CONFIG_ROCKCHIP_IO_DOMAIN) += rockchip-io-domain.o diff --git a/drivers/power/io-domain/io-domain-uclass.c b/drivers/power/io-domain/io-domain-uclass.c new file mode 100644 index 0000000000..7d5139e3c1 --- /dev/null +++ b/drivers/power/io-domain/io-domain-uclass.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 Rockchip Electronics Co., Ltd + */ + +#include +#include +#include + +void io_domain_init(void) +{ + int ret; + struct udevice *dev; + + ret = uclass_get_device(UCLASS_IO_DOMAIN, 0, &dev); + if (ret) + printf("Can't find UCLASS_IO_DOMAIN driver %d\n", ret); +} + +UCLASS_DRIVER(io_domain) = { + .id = UCLASS_IO_DOMAIN, + .name = "io_domain", +}; diff --git a/drivers/power/io-domain/rockchip-io-domain.c b/drivers/power/io-domain/rockchip-io-domain.c new file mode 100644 index 0000000000..ced9fd4f49 --- /dev/null +++ b/drivers/power/io-domain/rockchip-io-domain.c @@ -0,0 +1,620 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2018 Rockchip Electronics Co., Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_SUPPLIES 16 + +/* + * The max voltage for 1.8V and 3.3V come from the Rockchip datasheet under + * "Recommended Operating Conditions" for "Digital GPIO". When the typical + * is 3.3V the max is 3.6V. When the typical is 1.8V the max is 1.98V. + * + * They are used like this: + * - If the voltage on a rail is above the "1.8" voltage (1.98V) we'll tell the + * SoC we're at 3.3. + * - If the voltage on a rail is above the "3.3" voltage (3.6V) we'll consider + * that to be an error. + */ +#define MAX_VOLTAGE_1_8 1980000 +#define MAX_VOLTAGE_3_3 3600000 + +#define PX30_IO_VSEL 0x180 +#define PX30_IO_VSEL_VCCIO6_SRC BIT(0) +#define PX30_IO_VSEL_VCCIO6_SUPPLY_NUM 1 + +#define RK3288_SOC_CON2 0x24c +#define RK3288_SOC_CON2_FLASH0 BIT(7) +#define RK3288_SOC_FLASH_SUPPLY_NUM 2 + +#define RK3308_SOC_CON0 0x300 +#define RK3308_SOC_CON0_VCCIO3 BIT(8) +#define RK3308_SOC_VCCIO3_SUPPLY_NUM 3 + +#define RK3328_SOC_CON4 0x410 +#define RK3328_SOC_CON4_VCCIO2 BIT(7) +#define RK3328_SOC_VCCIO2_SUPPLY_NUM 1 + +#define RK3366_SOC_CON6 0x418 +#define RK3366_SOC_CON6_FLASH0 BIT(14) +#define RK3366_SOC_FLASH_SUPPLY_NUM 2 + +#define RK3368_SOC_CON15 0x43c +#define RK3368_SOC_CON15_FLASH0 BIT(14) +#define RK3368_SOC_FLASH_SUPPLY_NUM 2 + +#define RK3399_PMUGRF_CON0 0x180 +#define RK3399_PMUGRF_CON0_VSEL BIT(8) +#define RK3399_PMUGRF_VSEL_SUPPLY_NUM 9 + +struct rockchip_iodomain_priv; + +/** + * @supplies: voltage settings matching the register bits. + */ +struct rockchip_iodomain_soc_data { + int grf_offset; + const char *supply_names[MAX_SUPPLIES]; + void (*init)(struct rockchip_iodomain_priv *iod); +}; + +struct rockchip_iodomain_supply { + struct rockchip_iodomain_priv *iod; + struct udevice *reg; + int idx; +}; + +struct rockchip_iodomain_priv { + struct regmap *regmap_base; + struct rockchip_iodomain_soc_data *sdata; + struct rockchip_iodomain_supply supplies[MAX_SUPPLIES]; +}; + +static int rockchip_ofdata_to_platdata(struct udevice *dev) +{ + struct rockchip_iodomain_priv *priv = dev_get_priv(dev); + struct regmap *regmap; + + /* get grf-reg base address */ + regmap = syscon_get_regmap_by_driver_data(ROCKCHIP_SYSCON_GRF); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + priv->regmap_base = regmap; + + return 0; +} + +static int rockchip_iodomain_write(struct rockchip_iodomain_supply *supply, + int uV) +{ + struct rockchip_iodomain_priv *priv = supply->iod; + struct regmap *regmap = priv->regmap_base; + u32 val; + int ret; + + /* set value bit */ + val = (uV > MAX_VOLTAGE_1_8) ? 0 : 1; + val <<= supply->idx; + + /* apply hiword-mask */ + val |= (BIT(supply->idx) << 16); + + ret = regmap_write(regmap, priv->sdata->grf_offset, val); + if (ret) { + dev_err(priv->dev, "Couldn't write to GRF\n"); + return ret; + } + + return 0; +} + +static void px30_iodomain_init(struct rockchip_iodomain_priv *iod) +{ + int ret; + u32 val; + + /* if no VCCIO0 supply we should leave things alone */ + if (!iod->supplies[PX30_IO_VSEL_VCCIO6_SUPPLY_NUM].reg) + return; + + /* + * set vccio0 iodomain to also use this framework + * instead of a special gpio. + */ + val = PX30_IO_VSEL_VCCIO6_SRC | (PX30_IO_VSEL_VCCIO6_SRC << 16); + ret = regmap_write(iod->regmap_base, PX30_IO_VSEL, val); + if (ret < 0) + dev_warn(iod->dev, "couldn't update vccio0 ctrl\n"); +} + +static void rk3288_iodomain_init(struct rockchip_iodomain_priv *iod) +{ + int ret; + u32 val; + + /* if no flash supply we should leave things alone */ + if (!iod->supplies[RK3288_SOC_FLASH_SUPPLY_NUM].reg) + return; + + /* + * set flash0 iodomain to also use this framework + * instead of a special gpio. + */ + val = RK3288_SOC_CON2_FLASH0 | (RK3288_SOC_CON2_FLASH0 << 16); + ret = regmap_write(iod->regmap_base, RK3288_SOC_CON2, val); + if (ret < 0) + dev_warn(iod->dev, "couldn't update flash0 ctrl\n"); +} + +static void rk3308_iodomain_init(struct rockchip_iodomain_priv *iod) +{ + int ret; + u32 val; + + /* if no vccio3 supply we should leave things alone */ + if (!iod->supplies[RK3308_SOC_VCCIO3_SUPPLY_NUM].reg) + return; + + /* + * set vccio3 iodomain to also use this framework + * instead of a special gpio. + */ + val = RK3308_SOC_CON0_VCCIO3 | (RK3308_SOC_CON0_VCCIO3 << 16); + ret = regmap_write(iod->regmap_base, RK3308_SOC_CON0, val); + if (ret < 0) + dev_warn(iod->dev, "couldn't update vccio3 vsel ctrl\n"); +} + +static void rk3328_iodomain_init(struct rockchip_iodomain_priv *iod) +{ + int ret; + u32 val; + + /* if no vccio2 supply we should leave things alone */ + if (!iod->supplies[RK3328_SOC_VCCIO2_SUPPLY_NUM].reg) + return; + + /* + * set vccio2 iodomain to also use this framework + * instead of a special gpio. + */ + val = RK3328_SOC_CON4_VCCIO2 | (RK3328_SOC_CON4_VCCIO2 << 16); + ret = regmap_write(iod->regmap_base, RK3328_SOC_CON4, val); + if (ret < 0) + dev_warn(iod->dev, "couldn't update vccio2 vsel ctrl\n"); +} + +static void rk3366_iodomain_init(struct rockchip_iodomain_priv *iod) +{ + int ret; + u32 val; + + /* if no flash supply we should leave things alone */ + if (!iod->supplies[RK3366_SOC_FLASH_SUPPLY_NUM].reg) + return; + + /* + * set flash0 iodomain to also use this framework + * instead of a special gpio. + */ + val = RK3366_SOC_CON6_FLASH0 | (RK3366_SOC_CON6_FLASH0 << 16); + ret = regmap_write(iod->regmap_base, RK3368_SOC_CON15, val); + if (ret < 0) + dev_warn(iod->dev, "couldn't update flash0 ctrl\n"); +} + +static void rk3368_iodomain_init(struct rockchip_iodomain_priv *iod) +{ + int ret; + u32 val; + + /* if no flash supply we should leave things alone */ + if (!iod->supplies[RK3368_SOC_FLASH_SUPPLY_NUM].reg) + return; + + /* + * set flash0 iodomain to also use this framework + * instead of a special gpio. + */ + val = RK3368_SOC_CON15_FLASH0 | (RK3368_SOC_CON15_FLASH0 << 16); + ret = regmap_write(iod->regmap_base, RK3368_SOC_CON15, val); + if (ret < 0) + dev_warn(iod->dev, "couldn't update flash0 ctrl\n"); +} + +static void rk3399_pmu_iodomain_init(struct rockchip_iodomain_priv *iod) +{ + int ret; + u32 val; + + /* if no pmu io supply we should leave things alone */ + if (!iod->supplies[RK3399_PMUGRF_VSEL_SUPPLY_NUM].reg) + return; + + /* + * set pmu io iodomain to also use this framework + * instead of a special gpio. + */ + val = RK3399_PMUGRF_CON0_VSEL | (RK3399_PMUGRF_CON0_VSEL << 16); + ret = regmap_write(iod->regmap_base, RK3399_PMUGRF_CON0, val); + if (ret < 0) + dev_warn(iod->dev, "couldn't update pmu io iodomain ctrl\n"); +} + +static const struct rockchip_iodomain_soc_data soc_data_px30 = { + .grf_offset = 0x180, + .supply_names = { + NULL, + "vccio6", + "vccio1", + "vccio2", + "vccio3", + "vccio4", + "vccio5", + "vccio-oscgpi", + }, + .init = px30_iodomain_init, +}; + +static const struct rockchip_iodomain_soc_data soc_data_px30_pmu = { + .grf_offset = 0x100, + .supply_names = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "pmuio1", + "pmuio2", + }, +}; + +/* + * On the rk3188 the io-domains are handled by a shared register with the + * lower 8 bits being still being continuing drive-strength settings. + */ +static const struct rockchip_iodomain_soc_data soc_data_rk3188 = { + .grf_offset = 0x104, + .supply_names = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "ap0", + "ap1", + "cif", + "flash", + "vccio0", + "vccio1", + "lcdc0", + "lcdc1", + }, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rk322x = { + .grf_offset = 0x418, + .supply_names = { + "vccio1", + "vccio2", + "vccio3", + "vccio4", + }, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rk3288 = { + .grf_offset = 0x380, + .supply_names = { + "lcdc", /* LCDC_VDD */ + "dvp", /* DVPIO_VDD */ + "flash0", /* FLASH0_VDD (emmc) */ + "flash1", /* FLASH1_VDD (sdio1) */ + "wifi", /* APIO3_VDD (sdio0) */ + "bb", /* APIO5_VDD */ + "audio", /* APIO4_VDD */ + "sdcard", /* SDMMC0_VDD (sdmmc) */ + "gpio30", /* APIO1_VDD */ + "gpio1830", /* APIO2_VDD */ + }, + .init = rk3288_iodomain_init, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rk3308 = { + .grf_offset = 0x300, + .supply_names = { + "vccio0", + "vccio1", + "vccio2", + "vccio3", + "vccio4", + "vccio5", + }, + .init = rk3308_iodomain_init, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rk3328 = { + .grf_offset = 0x410, + .supply_names = { + "vccio1", + "vccio2", + "vccio3", + "vccio4", + "vccio5", + "vccio6", + "pmuio", + }, + .init = rk3328_iodomain_init, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rk3366 = { + .grf_offset = 0x900, + .supply_names = { + "lcdc", /* LCDC_IOVDD */ + "dvpts", /* DVP_IOVDD */ + "flash", /* FLASH_IOVDD (emmc) */ + "wifibt", /* APIO1_IOVDD */ + NULL, + "audio", /* AUDIO_IODVDD */ + "sdcard", /* SDMMC_IOVDD (sdmmc) */ + "tphdsor", /* APIO2_IOVDD */ + }, + .init = rk3366_iodomain_init, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rk3368 = { + .grf_offset = 0x900, + .supply_names = { + NULL, /* reserved */ + "dvp", /* DVPIO_VDD */ + "flash0", /* FLASH0_VDD (emmc) */ + "wifi", /* APIO2_VDD (sdio0) */ + NULL, + "audio", /* APIO3_VDD */ + "sdcard", /* SDMMC0_VDD (sdmmc) */ + "gpio30", /* APIO1_VDD */ + "gpio1830", /* APIO4_VDD (gpujtag) */ + }, + .init = rk3368_iodomain_init, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rk3368_pmu = { + .grf_offset = 0x100, + .supply_names = { + NULL, + NULL, + NULL, + NULL, + "pmu", /*PMU IO domain*/ + "vop", /*LCDC IO domain*/ + }, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rk3399 = { + .grf_offset = 0xe640, + .supply_names = { + "bt656", /* APIO2_VDD */ + "audio", /* APIO5_VDD */ + "sdmmc", /* SDMMC0_VDD */ + "gpio1830", /* APIO4_VDD */ + }, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rk3399_pmu = { + .grf_offset = 0x180, + .supply_names = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "pmu1830", /* PMUIO2_VDD */ + }, + .init = rk3399_pmu_iodomain_init, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rv1108 = { + .grf_offset = 0x404, + .supply_names = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "vccio1", + "vccio2", + "vccio3", + "vccio5", + "vccio6", + }, + +}; + +static const struct rockchip_iodomain_soc_data soc_data_rv1108_pmu = { + .grf_offset = 0x104, + .supply_names = { + "pmu", + }, +}; + +static struct udevice *of_get_regulator(ofnode node, const char *supply) +{ + char sname[32]; /* 32 is max size of property name */ + struct udevice *sudev; + ofnode snode; + u32 phandle; + int ret; + + snprintf(sname, 32, "%s-supply", supply); + + /* Get regulator and clk */ + if (!ofnode_read_u32(node, sname, &phandle)) { + snode = ofnode_get_by_phandle(phandle); + ret = regulator_get_by_devname(snode.np->name, &sudev); + if (ret) { + printf("%s: %s: Get supply(%s) failed, ret=%d", + __func__, + sudev->name, snode.np->full_name, ret); + return NULL; + } + debug("IO-DOMAIN: supply: %s\n", snode.np->full_name); + } + + return sudev; +} + +static int rockchip_iodomain_probe(struct udevice *dev) +{ + struct rockchip_iodomain_priv *priv = dev_get_priv(dev); + struct rockchip_iodomain_soc_data *sdata; + int i, ret; + + sdata = (struct rockchip_iodomain_soc_data *)dev_get_driver_data(dev); + priv->sdata = sdata; + + if (!priv->regmap_base) + return -1; + + for (i = 0; i < MAX_SUPPLIES; i++) { + const char *supply_name = priv->sdata->supply_names[i]; + struct rockchip_iodomain_supply *supply = &priv->supplies[i]; + struct udevice *reg; + u32 uV; + + if (!supply_name) + continue; + + reg = of_get_regulator(dev_ofnode(dev), supply_name); + if (!reg) { + printf("could not find regulator %s\n", supply_name); + return -1; + } + + uV = regulator_get_value(reg); + if (uV < 0) { + printf("could not get voltage from %s\n", reg->name); + return -1; + } + + if (uV > MAX_VOLTAGE_3_3) { + printf("%d uV is too high from %s\n", uV, reg->name); + return -1; + } + + /* setup our supply */ + supply->idx = i; + supply->iod = priv; + supply->reg = reg; + + ret = rockchip_iodomain_write(supply, uV); + if (ret) + supply->reg = NULL; + } + + if (priv->sdata->init) + priv->sdata->init(priv); + + return 0; +} + +static const struct udevice_id rockchip_iodomain_match[] = { + { + .compatible = "rockchip,px30-io-voltage-domain", + .data = (ulong)&soc_data_px30 + }, + { + .compatible = "rockchip,px30-pmu-io-voltage-domain", + .data = (ulong)&soc_data_px30_pmu + }, + { + .compatible = "rockchip,rk3188-io-voltage-domain", + .data = (ulong)&soc_data_rk3188 + }, + { + .compatible = "rockchip,rk322x-io-voltage-domain", + .data = (ulong)&soc_data_rk322x + }, + { + .compatible = "rockchip,rk3288-io-voltage-domain", + .data = (ulong)&soc_data_rk3288 + }, + { + .compatible = "rockchip,rk3308-io-voltage-domain", + .data = (ulong)&soc_data_rk3308 + }, + { + .compatible = "rockchip,rk3328-io-voltage-domain", + .data = (ulong)&soc_data_rk3328 + }, + { + .compatible = "rockchip,rk3366-io-voltage-domain", + .data = (ulong)&soc_data_rk3366 + }, + { + .compatible = "rockchip,rk3368-io-voltage-domain", + .data = (ulong)&soc_data_rk3368 + }, + { + .compatible = "rockchip,rk3368-pmu-io-voltage-domain", + .data = (ulong)&soc_data_rk3368_pmu + }, + { + .compatible = "rockchip,rk3399-io-voltage-domain", + .data = (ulong)&soc_data_rk3399 + }, + { + .compatible = "rockchip,rk3399-pmu-io-voltage-domain", + .data = (ulong)&soc_data_rk3399_pmu + }, + { + .compatible = "rockchip,rv1108-io-voltage-domain", + .data = (ulong)&soc_data_rv1108 + }, + { + .compatible = "rockchip,rv1108-pmu-io-voltage-domain", + .data = (ulong)&soc_data_rv1108_pmu + }, + { /* sentinel */ }, +}; + +U_BOOT_DRIVER(io_domain) = { + .name = "io_domain", + .id = UCLASS_IO_DOMAIN, + .of_match = rockchip_iodomain_match, + .priv_auto_alloc_size = sizeof(struct rockchip_iodomain_priv), + .ofdata_to_platdata = rockchip_ofdata_to_platdata, + .probe = rockchip_iodomain_probe, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index addf27639b..76c5eb7d88 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -99,6 +99,7 @@ enum uclass_id { UCLASS_RC, /* Remote Controller */ UCLASS_CHARGE_DISPLAY, /* Charge display */ UCLASS_DVFS, /* DVFS policy */ + UCLASS_IO_DOMAIN, /* IO domain */ UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/io-domain.h b/include/io-domain.h new file mode 100644 index 0000000000..a52b9a7e37 --- /dev/null +++ b/include/io-domain.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2018 Rockchip Electronics Co., Ltd + */ + +#ifndef _IO_DOMAIN_H_ +#define _IO_DOMAIN_H_ + +#include + +/** + * io_domain_init() - init io-domain driver + */ +void io_domain_init(void); + +#endif /* _IO_DOMAIN_H_ */