diff options
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/pmic/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/pmic/pmic_max77696.c | 31 | ||||
-rw-r--r-- | drivers/power/regulator/Kconfig | 8 | ||||
-rw-r--r-- | drivers/power/regulator/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/regulator/scmi_regulator.c | 195 |
5 files changed, 204 insertions, 32 deletions
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 7b4c0f02c60..89099fde573 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_$(SPL_)PMIC_LP87565) += lp87565.o obj-$(CONFIG_PMIC_STPMIC1) += stpmic1.o obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o -obj-$(CONFIG_POWER_MAX77696) += pmic_max77696.o obj-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o obj-$(CONFIG_POWER_PCA9450) += pmic_pca9450.o obj-$(CONFIG_POWER_PFUZE100) += pmic_pfuze100.o diff --git a/drivers/power/pmic/pmic_max77696.c b/drivers/power/pmic/pmic_max77696.c deleted file mode 100644 index f3a73d6405f..00000000000 --- a/drivers/power/pmic/pmic_max77696.c +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2015 Freescale Semiconductor, Inc. - * Fabio Estevam <fabio.estevam@freescale.com> - */ - -#include <common.h> -#include <errno.h> -#include <i2c.h> -#include <power/pmic.h> -#include <power/max77696_pmic.h> - -int power_max77696_init(unsigned char bus) -{ - static const char name[] = "MAX77696"; - struct pmic *p = pmic_alloc(); - - if (!p) { - printf("%s: POWER allocation error!\n", __func__); - return -ENOMEM; - } - - p->name = name; - p->interface = PMIC_I2C; - p->number_of_regs = PMIC_NUM_OF_REGS; - p->hw.i2c.addr = CONFIG_POWER_MAX77696_I2C_ADDR; - p->hw.i2c.tx_num = 1; - p->bus = bus; - - return 0; -} diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index fbbea18c7d1..5d6180229e9 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -353,3 +353,11 @@ config DM_REGULATOR_TPS65941 TPS65941 series of PMICs have 5 single phase BUCKs that can also be configured in multi phase modes & 4 LDOs. The driver implements get/set api for value and enable. + +config DM_REGULATOR_SCMI + bool "Enable driver for SCMI voltage domain regulators" + depends on DM_REGULATOR + select SCMI_AGENT + help + Enable this option if you want to support regulators exposed through + the SCMI voltage domain protocol by a SCMI server. diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 9d58112dcb1..b2f5972ea79 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STPMIC1) += stpmic1.o obj-$(CONFIG_DM_REGULATOR_TPS65941) += tps65941_regulator.o +obj-$(CONFIG_DM_REGULATOR_SCMI) += scmi_regulator.o diff --git a/drivers/power/regulator/scmi_regulator.c b/drivers/power/regulator/scmi_regulator.c new file mode 100644 index 00000000000..b3142bf4e1f --- /dev/null +++ b/drivers/power/regulator/scmi_regulator.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020-2021 Linaro Limited + */ +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include <asm/types.h> +#include <dm/device.h> +#include <dm/device_compat.h> +#include <dm/device-internal.h> +#include <linux/kernel.h> +#include <power/regulator.h> + +/** + * struct scmi_regulator_platdata - Platform data for a scmi voltage domain regulator + * @domain_id: ID representing the regulator for the related SCMI agent + */ +struct scmi_regulator_platdata { + u32 domain_id; +}; + +static int scmi_voltd_set_enable(struct udevice *dev, bool enable) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_config_set_in in = { + .domain_id = pdata->domain_id, + .config = enable ? SCMI_VOLTD_CONFIG_ON : SCMI_VOLTD_CONFIG_OFF, + }; + struct scmi_voltd_config_set_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_CONFIG_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret) + return ret; + + return ret; +} + +static int scmi_voltd_get_enable(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_config_get_in in = { + .domain_id = pdata->domain_id, + }; + struct scmi_voltd_config_get_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_CONFIG_GET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return out.config == SCMI_VOLTD_CONFIG_ON; +} + +static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_level_set_in in = { + .domain_id = pdata->domain_id, + .voltage_level = uV, + }; + struct scmi_voltd_level_set_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_LEVEL_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret < 0) + return ret; + + return scmi_to_linux_errno(out.status); +} + +static int scmi_voltd_get_voltage_level(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_level_get_in in = { + .domain_id = pdata->domain_id, + }; + struct scmi_voltd_level_get_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_LEVEL_GET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return out.voltage_level; +} + +static int scmi_regulator_of_to_plat(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + fdt_addr_t reg; + + reg = dev_read_addr(dev); + if (reg == FDT_ADDR_T_NONE) + return -EINVAL; + + pdata->domain_id = (u32)reg; + + return 0; +} + +static int scmi_regulator_probe(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_attr_in in = { 0 }; + struct scmi_voltd_attr_out out = { 0 }; + struct scmi_msg scmi_msg = { + .protocol_id = SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + .message_id = SCMI_VOLTAGE_DOMAIN_ATTRIBUTES, + .in_msg = (u8 *)&in, + .in_msg_sz = sizeof(in), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + /* Check voltage domain is known from SCMI server */ + in.domain_id = pdata->domain_id; + + ret = devm_scmi_process_msg(dev->parent->parent, &scmi_msg); + if (ret) { + dev_err(dev, "Failed to query voltage domain %u: %d\n", + pdata->domain_id, ret); + return -ENXIO; + } + + return 0; +} + +static const struct dm_regulator_ops scmi_voltd_ops = { + .get_value = scmi_voltd_get_voltage_level, + .set_value = scmi_voltd_set_voltage_level, + .get_enable = scmi_voltd_get_enable, + .set_enable = scmi_voltd_set_enable, +}; + +U_BOOT_DRIVER(scmi_regulator) = { + .name = "scmi_regulator", + .id = UCLASS_REGULATOR, + .ops = &scmi_voltd_ops, + .probe = scmi_regulator_probe, + .of_to_plat = scmi_regulator_of_to_plat, + .plat_auto = sizeof(struct scmi_regulator_platdata), +}; + +static int scmi_regulator_bind(struct udevice *dev) +{ + struct driver *drv; + ofnode node; + int ret; + + drv = DM_DRIVER_GET(scmi_regulator); + + ofnode_for_each_subnode(node, dev_ofnode(dev)) { + ret = device_bind(dev, drv, ofnode_get_name(node), + NULL, node, NULL); + if (ret) + return ret; + } + + return 0; +} + +U_BOOT_DRIVER(scmi_voltage_domain) = { + .name = "scmi_voltage_domain", + .id = UCLASS_NOP, + .bind = scmi_regulator_bind, +}; |