Message ID | 20210219133341.3097-1-etienne.carriere@linaro.org |
---|---|
State | Superseded |
Headers | show |
Series | [v2,1/3] firmware: scmi: voltage regulator | expand |
On Fri, 19 Feb 2021 at 06:34, Etienne Carriere <etienne.carriere@linaro.org> wrote: > > Implement voltage regulators interfaced by the SCMI voltage domain > protocol. The DT bindings are defined in the Linux kernel since > SCMI voltage domain and regulators patches [1] and [2] integration > in v5.11-rc7. > > Link: [1] https://github.com/torvalds/linux/commit/0f80fcec08e9c50b8d2992cf26495673765ebaba > Link: [2] https://github.com/torvalds/linux/commit/2add5cacff3531e54c50b0832128299faa9f0563 > > Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org> > --- > Changes in v2: > - no change > --- > doc/device-tree-bindings/arm/arm,scmi.txt | 34 +++++ > drivers/firmware/scmi/scmi_agent-uclass.c | 35 ++++- > drivers/power/regulator/Kconfig | 8 + > drivers/power/regulator/Makefile | 1 + > drivers/power/regulator/scmi_regulator.c | 170 ++++++++++++++++++++++ > include/scmi_protocols.h | 113 ++++++++++++++ > 6 files changed, 359 insertions(+), 2 deletions(-) > create mode 100644 drivers/power/regulator/scmi_regulator.c Reviewed-by: Simon Glass <sjg@chromium.org>
On 2/19/21 10:33 PM, Etienne Carriere wrote: > Implement voltage regulators interfaced by the SCMI voltage domain > protocol. The DT bindings are defined in the Linux kernel since > SCMI voltage domain and regulators patches [1] and [2] integration > in v5.11-rc7. > > Link: [1] https://protect2.fireeye.com/v1/url?k=472c44bf-18b77dae-472dcff0-0cc47a31cdbc-c857593954d155c5&q=1&e=117141f8-c98a-4b9c-83e8-c7bbf1ffbd72&u=https%3A%2F%2Fgithub.com%2Ftorvalds%2Flinux%2Fcommit%2F0f80fcec08e9c50b8d2992cf26495673765ebaba > Link: [2] https://protect2.fireeye.com/v1/url?k=8c3fa7e5-d3a49ef4-8c3e2caa-0cc47a31cdbc-5d6f0aa01e7bcbb1&q=1&e=117141f8-c98a-4b9c-83e8-c7bbf1ffbd72&u=https%3A%2F%2Fgithub.com%2Ftorvalds%2Flinux%2Fcommit%2F2add5cacff3531e54c50b0832128299faa9f0563 > > Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org> Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com> Best Regards, Jaehoon Chung > --- > Changes in v2: > - no change > --- > doc/device-tree-bindings/arm/arm,scmi.txt | 34 +++++ > drivers/firmware/scmi/scmi_agent-uclass.c | 35 ++++- > drivers/power/regulator/Kconfig | 8 + > drivers/power/regulator/Makefile | 1 + > drivers/power/regulator/scmi_regulator.c | 170 ++++++++++++++++++++++ > include/scmi_protocols.h | 113 ++++++++++++++ > 6 files changed, 359 insertions(+), 2 deletions(-) > create mode 100644 drivers/power/regulator/scmi_regulator.c > > diff --git a/doc/device-tree-bindings/arm/arm,scmi.txt b/doc/device-tree-bindings/arm/arm,scmi.txt > index 1f293ea24..a76124f4a 100644 > --- a/doc/device-tree-bindings/arm/arm,scmi.txt > +++ b/doc/device-tree-bindings/arm/arm,scmi.txt > @@ -62,6 +62,20 @@ Required properties: > - #power-domain-cells : Should be 1. Contains the device or the power > domain ID value used by SCMI commands. > > +Regulator bindings for the SCMI Regulator based on SCMI Message Protocol > +------------------------------------------------------------ > +An SCMI Regulator is permanently bound to a well defined SCMI Voltage Domain, > +and should be always positioned as a root regulator. > +It does not support any current operation. > + > +SCMI Regulators are grouped under a 'regulators' node which in turn is a child > +of the SCMI Voltage protocol node inside the desired SCMI instance node. > + > +This binding uses the common regulator binding[6]. > + > +Required properties: > + - reg : shall identify an existent SCMI Voltage Domain. > + > Sensor bindings for the sensors based on SCMI Message Protocol > -------------------------------------------------------------- > SCMI provides an API to access the various sensors on the SoC. > @@ -105,6 +119,7 @@ Required sub-node properties: > [3] Documentation/devicetree/bindings/thermal/thermal.txt > [4] Documentation/devicetree/bindings/sram/sram.yaml > [5] Documentation/devicetree/bindings/reset/reset.txt > +[6] Documentation/devicetree/bindings/regulator/regulator.yaml > > Example: > > @@ -169,6 +184,25 @@ firmware { > reg = <0x16>; > #reset-cells = <1>; > }; > + > + scmi_voltage: protocol@17 { > + reg = <0x17>; > + > + regulators { > + regulator_devX: regulator@0 { > + reg = <0x0>; > + regulator-max-microvolt = <3300000>; > + }; > + > + regulator_devY: regulator@9 { > + reg = <0x9>; > + regulator-min-microvolt = <500000>; > + regulator-max-microvolt = <4200000>; > + }; > + > + ... > + }; > + }; > }; > }; > > diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c > index 516e690ac..03d236426 100644 > --- a/drivers/firmware/scmi/scmi_agent-uclass.c > +++ b/drivers/firmware/scmi/scmi_agent-uclass.c > @@ -50,6 +50,29 @@ int scmi_to_linux_errno(s32 scmi_code) > return -EPROTO; > } > > +static int regulator_devices_bind(struct udevice *dev, struct driver *drv, > + ofnode protocol_node) > +{ > + ofnode regulators_node; > + ofnode node; > + int ret; > + > + regulators_node = ofnode_find_subnode(protocol_node, "regulators"); > + if (!ofnode_valid(regulators_node)) { > + dev_err(dev, "regulators subnode not found\n"); > + return -ENXIO; > + } > + > + ofnode_for_each_subnode(node, regulators_node) { > + ret = device_bind(dev, drv, ofnode_get_name(node), NULL, node, > + NULL); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > /* > * SCMI agent devices binds devices of various uclasses depeding on > * the FDT description. scmi_bind_protocol() is a generic bind sequence > @@ -79,6 +102,10 @@ static int scmi_bind_protocols(struct udevice *dev) > if (IS_ENABLED(CONFIG_RESET_SCMI)) > drv = DM_DRIVER_GET(scmi_reset_domain); > break; > + case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: > + if (IS_ENABLED(CONFIG_DM_REGULATOR_SCMI)) > + drv = DM_DRIVER_GET(scmi_voltage_domain); > + break; > default: > break; > } > @@ -89,8 +116,12 @@ static int scmi_bind_protocols(struct udevice *dev) > continue; > } > > - ret = device_bind(dev, drv, ofnode_get_name(node), NULL, node, > - NULL); > + if (protocol_id == SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN) > + ret = regulator_devices_bind(dev, drv, node); > + else > + ret = device_bind(dev, drv, ofnode_get_name(node), > + NULL, node, NULL); > + > if (ret) > break; > } > diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig > index fbbea18c7..5d6180229 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 9d58112dc..b2f5972ea 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 000000000..072a978a8 > --- /dev/null > +++ b/drivers/power/regulator/scmi_regulator.c > @@ -0,0 +1,170 @@ > +// 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 <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, &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, &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, &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, &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, &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_voltage_domain) = { > + .name = "scmi_voltage_domain", > + .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), > +}; > diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h > index ccab97c96..2db71697e 100644 > --- a/include/scmi_protocols.h > +++ b/include/scmi_protocols.h > @@ -23,6 +23,7 @@ enum scmi_std_protocol { > SCMI_PROTOCOL_ID_CLOCK = 0x14, > SCMI_PROTOCOL_ID_SENSOR = 0x15, > SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16, > + SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN = 0x17, > }; > > enum scmi_status_code { > @@ -176,4 +177,116 @@ struct scmi_rd_reset_out { > s32 status; > }; > > +/* > + * SCMI Voltage Domain Protocol > + */ > + > +enum scmi_voltage_domain_message_id { > + SCMI_VOLTAGE_DOMAIN_ATTRIBUTES = 0x3, > + SCMI_VOLTAGE_DOMAIN_CONFIG_SET = 0x5, > + SCMI_VOLTAGE_DOMAIN_CONFIG_GET = 0x6, > + SCMI_VOLTAGE_DOMAIN_LEVEL_SET = 0x7, > + SCMI_VOLTAGE_DOMAIN_LEVEL_GET = 0x8, > +}; > + > +#define SCMI_VOLTD_NAME_LEN 16 > + > +#define SCMI_VOLTD_CONFIG_MASK GENMASK(3, 0) > +#define SCMI_VOLTD_CONFIG_OFF 0 > +#define SCMI_VOLTD_CONFIG_ON 0x7 > + > +/** > + * struct scmi_voltd_attr_in - Payload for VOLTAGE_DOMAIN_ATTRIBUTES message > + * @domain_id: SCMI voltage domain ID > + */ > +struct scmi_voltd_attr_in { > + u32 domain_id; > +}; > + > +/** > + * struct scmi_voltd_attr_out - Payload for VOLTAGE_DOMAIN_ATTRIBUTES response > + * @status: SCMI command status > + * @attributes: Retrieved attributes of the voltage domain > + * @name: Voltage domain name > + */ > +struct scmi_voltd_attr_out { > + s32 status; > + u32 attributes; > + char name[SCMI_VOLTD_NAME_LEN]; > +}; > + > +/** > + * struct scmi_voltd_config_set_in - Message payload for VOLTAGE_CONFIG_SET cmd > + * @domain_id: SCMI voltage domain ID > + * @config: Configuration data of the voltage domain > + */ > +struct scmi_voltd_config_set_in { > + u32 domain_id; > + u32 config; > +}; > + > +/** > + * struct scmi_voltd_config_set_out - Response for VOLTAGE_CONFIG_SET command > + * @status: SCMI command status > + */ > +struct scmi_voltd_config_set_out { > + s32 status; > +}; > + > +/** > + * struct scmi_voltd_config_get_in - Message payload for VOLTAGE_CONFIG_GET cmd > + * @domain_id: SCMI voltage domain ID > + */ > +struct scmi_voltd_config_get_in { > + u32 domain_id; > +}; > + > +/** > + * struct scmi_voltd_config_get_out - Response for VOLTAGE_CONFIG_GET command > + * @status: SCMI command status > + * @config: Configuration data of the voltage domain > + */ > +struct scmi_voltd_config_get_out { > + s32 status; > + u32 config; > +}; > + > +/** > + * struct scmi_voltd_level_set_in - Message payload for VOLTAGE_LEVEL_SET cmd > + * @domain_id: SCMI voltage domain ID > + * @flags: Parameter flags for configuring target level > + * @voltage_level: Target voltage level in microvolts (uV) > + */ > +struct scmi_voltd_level_set_in { > + u32 domain_id; > + u32 flags; > + s32 voltage_level; > +}; > + > +/** > + * struct scmi_voltd_level_set_out - Response for VOLTAGE_LEVEL_SET command > + * @status: SCMI command status > + */ > +struct scmi_voltd_level_set_out { > + s32 status; > +}; > + > +/** > + * struct scmi_voltd_level_get_in - Message payload for VOLTAGE_LEVEL_GET cmd > + * @domain_id: SCMI voltage domain ID > + */ > +struct scmi_voltd_level_get_in { > + u32 domain_id; > +}; > + > +/** > + * struct scmi_voltd_level_get_out - Response for VOLTAGE_LEVEL_GET command > + * @status: SCMI command status > + * @voltage_level: Voltage level in microvolts (uV) > + */ > +struct scmi_voltd_level_get_out { > + s32 status; > + s32 voltage_level; > +}; > + > #endif /* _SCMI_PROTOCOLS_H */ >
diff --git a/doc/device-tree-bindings/arm/arm,scmi.txt b/doc/device-tree-bindings/arm/arm,scmi.txt index 1f293ea24..a76124f4a 100644 --- a/doc/device-tree-bindings/arm/arm,scmi.txt +++ b/doc/device-tree-bindings/arm/arm,scmi.txt @@ -62,6 +62,20 @@ Required properties: - #power-domain-cells : Should be 1. Contains the device or the power domain ID value used by SCMI commands. +Regulator bindings for the SCMI Regulator based on SCMI Message Protocol +------------------------------------------------------------ +An SCMI Regulator is permanently bound to a well defined SCMI Voltage Domain, +and should be always positioned as a root regulator. +It does not support any current operation. + +SCMI Regulators are grouped under a 'regulators' node which in turn is a child +of the SCMI Voltage protocol node inside the desired SCMI instance node. + +This binding uses the common regulator binding[6]. + +Required properties: + - reg : shall identify an existent SCMI Voltage Domain. + Sensor bindings for the sensors based on SCMI Message Protocol -------------------------------------------------------------- SCMI provides an API to access the various sensors on the SoC. @@ -105,6 +119,7 @@ Required sub-node properties: [3] Documentation/devicetree/bindings/thermal/thermal.txt [4] Documentation/devicetree/bindings/sram/sram.yaml [5] Documentation/devicetree/bindings/reset/reset.txt +[6] Documentation/devicetree/bindings/regulator/regulator.yaml Example: @@ -169,6 +184,25 @@ firmware { reg = <0x16>; #reset-cells = <1>; }; + + scmi_voltage: protocol@17 { + reg = <0x17>; + + regulators { + regulator_devX: regulator@0 { + reg = <0x0>; + regulator-max-microvolt = <3300000>; + }; + + regulator_devY: regulator@9 { + reg = <0x9>; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <4200000>; + }; + + ... + }; + }; }; }; diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index 516e690ac..03d236426 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -50,6 +50,29 @@ int scmi_to_linux_errno(s32 scmi_code) return -EPROTO; } +static int regulator_devices_bind(struct udevice *dev, struct driver *drv, + ofnode protocol_node) +{ + ofnode regulators_node; + ofnode node; + int ret; + + regulators_node = ofnode_find_subnode(protocol_node, "regulators"); + if (!ofnode_valid(regulators_node)) { + dev_err(dev, "regulators subnode not found\n"); + return -ENXIO; + } + + ofnode_for_each_subnode(node, regulators_node) { + ret = device_bind(dev, drv, ofnode_get_name(node), NULL, node, + NULL); + if (ret) + return ret; + } + + return 0; +} + /* * SCMI agent devices binds devices of various uclasses depeding on * the FDT description. scmi_bind_protocol() is a generic bind sequence @@ -79,6 +102,10 @@ static int scmi_bind_protocols(struct udevice *dev) if (IS_ENABLED(CONFIG_RESET_SCMI)) drv = DM_DRIVER_GET(scmi_reset_domain); break; + case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: + if (IS_ENABLED(CONFIG_DM_REGULATOR_SCMI)) + drv = DM_DRIVER_GET(scmi_voltage_domain); + break; default: break; } @@ -89,8 +116,12 @@ static int scmi_bind_protocols(struct udevice *dev) continue; } - ret = device_bind(dev, drv, ofnode_get_name(node), NULL, node, - NULL); + if (protocol_id == SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN) + ret = regulator_devices_bind(dev, drv, node); + else + ret = device_bind(dev, drv, ofnode_get_name(node), + NULL, node, NULL); + if (ret) break; } diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index fbbea18c7..5d6180229 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 9d58112dc..b2f5972ea 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 000000000..072a978a8 --- /dev/null +++ b/drivers/power/regulator/scmi_regulator.c @@ -0,0 +1,170 @@ +// 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 <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, &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, &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, &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, &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, &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_voltage_domain) = { + .name = "scmi_voltage_domain", + .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), +}; diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index ccab97c96..2db71697e 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -23,6 +23,7 @@ enum scmi_std_protocol { SCMI_PROTOCOL_ID_CLOCK = 0x14, SCMI_PROTOCOL_ID_SENSOR = 0x15, SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16, + SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN = 0x17, }; enum scmi_status_code { @@ -176,4 +177,116 @@ struct scmi_rd_reset_out { s32 status; }; +/* + * SCMI Voltage Domain Protocol + */ + +enum scmi_voltage_domain_message_id { + SCMI_VOLTAGE_DOMAIN_ATTRIBUTES = 0x3, + SCMI_VOLTAGE_DOMAIN_CONFIG_SET = 0x5, + SCMI_VOLTAGE_DOMAIN_CONFIG_GET = 0x6, + SCMI_VOLTAGE_DOMAIN_LEVEL_SET = 0x7, + SCMI_VOLTAGE_DOMAIN_LEVEL_GET = 0x8, +}; + +#define SCMI_VOLTD_NAME_LEN 16 + +#define SCMI_VOLTD_CONFIG_MASK GENMASK(3, 0) +#define SCMI_VOLTD_CONFIG_OFF 0 +#define SCMI_VOLTD_CONFIG_ON 0x7 + +/** + * struct scmi_voltd_attr_in - Payload for VOLTAGE_DOMAIN_ATTRIBUTES message + * @domain_id: SCMI voltage domain ID + */ +struct scmi_voltd_attr_in { + u32 domain_id; +}; + +/** + * struct scmi_voltd_attr_out - Payload for VOLTAGE_DOMAIN_ATTRIBUTES response + * @status: SCMI command status + * @attributes: Retrieved attributes of the voltage domain + * @name: Voltage domain name + */ +struct scmi_voltd_attr_out { + s32 status; + u32 attributes; + char name[SCMI_VOLTD_NAME_LEN]; +}; + +/** + * struct scmi_voltd_config_set_in - Message payload for VOLTAGE_CONFIG_SET cmd + * @domain_id: SCMI voltage domain ID + * @config: Configuration data of the voltage domain + */ +struct scmi_voltd_config_set_in { + u32 domain_id; + u32 config; +}; + +/** + * struct scmi_voltd_config_set_out - Response for VOLTAGE_CONFIG_SET command + * @status: SCMI command status + */ +struct scmi_voltd_config_set_out { + s32 status; +}; + +/** + * struct scmi_voltd_config_get_in - Message payload for VOLTAGE_CONFIG_GET cmd + * @domain_id: SCMI voltage domain ID + */ +struct scmi_voltd_config_get_in { + u32 domain_id; +}; + +/** + * struct scmi_voltd_config_get_out - Response for VOLTAGE_CONFIG_GET command + * @status: SCMI command status + * @config: Configuration data of the voltage domain + */ +struct scmi_voltd_config_get_out { + s32 status; + u32 config; +}; + +/** + * struct scmi_voltd_level_set_in - Message payload for VOLTAGE_LEVEL_SET cmd + * @domain_id: SCMI voltage domain ID + * @flags: Parameter flags for configuring target level + * @voltage_level: Target voltage level in microvolts (uV) + */ +struct scmi_voltd_level_set_in { + u32 domain_id; + u32 flags; + s32 voltage_level; +}; + +/** + * struct scmi_voltd_level_set_out - Response for VOLTAGE_LEVEL_SET command + * @status: SCMI command status + */ +struct scmi_voltd_level_set_out { + s32 status; +}; + +/** + * struct scmi_voltd_level_get_in - Message payload for VOLTAGE_LEVEL_GET cmd + * @domain_id: SCMI voltage domain ID + */ +struct scmi_voltd_level_get_in { + u32 domain_id; +}; + +/** + * struct scmi_voltd_level_get_out - Response for VOLTAGE_LEVEL_GET command + * @status: SCMI command status + * @voltage_level: Voltage level in microvolts (uV) + */ +struct scmi_voltd_level_get_out { + s32 status; + s32 voltage_level; +}; + #endif /* _SCMI_PROTOCOLS_H */
Implement voltage regulators interfaced by the SCMI voltage domain protocol. The DT bindings are defined in the Linux kernel since SCMI voltage domain and regulators patches [1] and [2] integration in v5.11-rc7. Link: [1] https://github.com/torvalds/linux/commit/0f80fcec08e9c50b8d2992cf26495673765ebaba Link: [2] https://github.com/torvalds/linux/commit/2add5cacff3531e54c50b0832128299faa9f0563 Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org> --- Changes in v2: - no change --- doc/device-tree-bindings/arm/arm,scmi.txt | 34 +++++ drivers/firmware/scmi/scmi_agent-uclass.c | 35 ++++- drivers/power/regulator/Kconfig | 8 + drivers/power/regulator/Makefile | 1 + drivers/power/regulator/scmi_regulator.c | 170 ++++++++++++++++++++++ include/scmi_protocols.h | 113 ++++++++++++++ 6 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 drivers/power/regulator/scmi_regulator.c -- 2.17.1