Message ID | 20210222072747.7514-1-etienne.carriere@linaro.org |
---|---|
State | Superseded |
Headers | show |
Series | [v3,1/4] firmware: scmi: voltage regulator | expand |
Hi Etienne, On 2/22/21 8:27 AM, 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://github.com/torvalds/linux/commit/0f80fcec08e9c50b8d2992cf26495673765ebaba > Link: [2] https://github.com/torvalds/linux/commit/2add5cacff3531e54c50b0832128299faa9f0563 > Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org> > Reviewed-by: Simon Glass <sjg@chromium.org> > Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com> > --- > Changes in v3: > - applied review tags > > 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; > +} > + This parsing function can be moved in drivers/power/regulator/scmi_regulator.c By adding the intermediate NOP uclass, binded with "protocol@17" node for example U_BOOT_DRIVER(scmi_regulator) = { .name = "scmi_regulator", .id = UCLASS_REGULATOR, .ops = &scmi_voltd_ops, .probe = scmi_regulator_probe, .ofdata_to_platdata = scmi_regulator_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct scmi_regulator_platdata), }; static int scmi_regulator_bind(struct udevice *dev) { struct driver *drv; ofnode node, subnode; int ret; node = dev_read_subnode(dev, "regulators"); if (!ofnode_valid(node)) { dev_err(dev, "regulators subnode not found\n"); return -ENXIO; } drv = DM_GET_DRIVER(scmi_regulator); ofnode_for_each_subnode(subnode, node) { ret = device_bind_ofnode(dev, drv, ofnode_get_name(subnode), NULL, subnode, NULL); if (ret) return ret; } return 0; } U_BOOT_DRIVER(scmi_voltage_domain) = { .name = "scmi_voltage_domain", .id = UCLASS_NOP, .bind = scmi_regulator_bind, }; I think it is more elegant and more alligned with existing behavior for SCMI clock and SCMI reset. warning: in this case the tree of U-Boot class change, SCMI agent become the grand-father of each regulator and all the call "devm_scmi_process_msg(dev->parent, &msg);" should be replaced by "devm_scmi_process_msg(dev->parent->parent, &msg);" > /* > * 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; > } (...) Regards Patrick
Hello Patrick, On Wed, 3 Mar 2021 at 11:09, Patrick DELAUNAY <patrick.delaunay@foss.st.com> wrote: > > Hi Etienne, > > On 2/22/21 8:27 AM, 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://github.com/torvalds/linux/commit/0f80fcec08e9c50b8d2992cf26495673765ebaba > > Link: [2] https://github.com/torvalds/linux/commit/2add5cacff3531e54c50b0832128299faa9f0563 > > Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org> > > Reviewed-by: Simon Glass <sjg@chromium.org> > > Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com> > > --- > > Changes in v3: > > - applied review tags > > > > 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; > > +} > > + > > This parsing function can be moved in > > drivers/power/regulator/scmi_regulator.c That is a good idea. I didn't want to export a function from regu/scmi-regu.c to scmi/... The intermediate device would nicely do the binding job. At the cost of an additional udevice. Is that worth it? I agree it would reflect more the DT since scmi regus are in a child node of the SCMI protocol. But should the software align with the DT? That said, if no one argues against your id, i'll make the proposed changes and post a v4. Thanks, etienne > > By adding the intermediate NOP uclass, binded with "protocol@17" node > > for example > > > U_BOOT_DRIVER(scmi_regulator) = { > .name = "scmi_regulator", > .id = UCLASS_REGULATOR, > .ops = &scmi_voltd_ops, > .probe = scmi_regulator_probe, > .ofdata_to_platdata = scmi_regulator_ofdata_to_platdata, > .platdata_auto_alloc_size = sizeof(struct scmi_regulator_platdata), > }; > > static int scmi_regulator_bind(struct udevice *dev) > { > struct driver *drv; > ofnode node, subnode; > int ret; > > node = dev_read_subnode(dev, "regulators"); > if (!ofnode_valid(node)) { > dev_err(dev, "regulators subnode not found\n"); > return -ENXIO; > } > > drv = DM_GET_DRIVER(scmi_regulator); > ofnode_for_each_subnode(subnode, node) { > ret = device_bind_ofnode(dev, drv, ofnode_get_name(subnode), > NULL, subnode, NULL); > if (ret) > return ret; > } > > return 0; > } > > U_BOOT_DRIVER(scmi_voltage_domain) = { > .name = "scmi_voltage_domain", > .id = UCLASS_NOP, > .bind = scmi_regulator_bind, > }; > > > I think it is more elegant and more alligned with existing behavior > > for SCMI clock and SCMI reset. SCMI clocks and reset are different in that there is only 1 device per clock grapes and reset controller series. For SCMI regulator, there is 1 device per regulator. > > > warning: > > in this case the tree of U-Boot class change, SCMI agent become the > grand-father > > of each regulator and all the call "devm_scmi_process_msg(dev->parent, > &msg);" > > should be replaced by "devm_scmi_process_msg(dev->parent->parent, &msg);" > > > /* > > * 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; > > } > > (...) > > Regards > > Patrick >
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 */