firmware: scmi: voltage regulator

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] 0f80fcec08
Link: [2] 2add5cacff
Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org>
Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com>
This commit is contained in:
Etienne Carriere 2021-03-08 22:38:06 +01:00 committed by Tom Rini
parent d46933839f
commit 1f213ee4db
6 changed files with 361 additions and 0 deletions

View File

@ -62,6 +62,20 @@ Required properties:
- #power-domain-cells : Should be 1. Contains the device or the power - #power-domain-cells : Should be 1. Contains the device or the power
domain ID value used by SCMI commands. 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 Sensor bindings for the sensors based on SCMI Message Protocol
-------------------------------------------------------------- --------------------------------------------------------------
SCMI provides an API to access the various sensors on the SoC. 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 [3] Documentation/devicetree/bindings/thermal/thermal.txt
[4] Documentation/devicetree/bindings/sram/sram.yaml [4] Documentation/devicetree/bindings/sram/sram.yaml
[5] Documentation/devicetree/bindings/reset/reset.txt [5] Documentation/devicetree/bindings/reset/reset.txt
[6] Documentation/devicetree/bindings/regulator/regulator.yaml
Example: Example:
@ -169,6 +184,25 @@ firmware {
reg = <0x16>; reg = <0x16>;
#reset-cells = <1>; #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>;
};
...
};
};
}; };
}; };

View File

@ -80,6 +80,16 @@ static int scmi_bind_protocols(struct udevice *dev)
if (IS_ENABLED(CONFIG_RESET_SCMI)) if (IS_ENABLED(CONFIG_RESET_SCMI))
drv = DM_DRIVER_GET(scmi_reset_domain); drv = DM_DRIVER_GET(scmi_reset_domain);
break; break;
case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN:
if (IS_ENABLED(CONFIG_DM_REGULATOR_SCMI)) {
node = ofnode_find_subnode(node, "regulators");
if (!ofnode_valid(node)) {
dev_err(dev, "no regulators node\n");
return -ENXIO;
}
drv = DM_DRIVER_GET(scmi_voltage_domain);
}
break;
default: default:
break; break;
} }

View File

@ -353,3 +353,11 @@ config DM_REGULATOR_TPS65941
TPS65941 series of PMICs have 5 single phase BUCKs that can also TPS65941 series of PMICs have 5 single phase BUCKs that can also
be configured in multi phase modes & 4 LDOs. The driver implements be configured in multi phase modes & 4 LDOs. The driver implements
get/set api for value and enable. 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.

View File

@ -30,3 +30,4 @@ obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o
obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o
obj-$(CONFIG_$(SPL_)DM_REGULATOR_STPMIC1) += stpmic1.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STPMIC1) += stpmic1.o
obj-$(CONFIG_DM_REGULATOR_TPS65941) += tps65941_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS65941) += tps65941_regulator.o
obj-$(CONFIG_DM_REGULATOR_SCMI) += scmi_regulator.o

View File

@ -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,
};

View File

@ -23,6 +23,7 @@ enum scmi_std_protocol {
SCMI_PROTOCOL_ID_CLOCK = 0x14, SCMI_PROTOCOL_ID_CLOCK = 0x14,
SCMI_PROTOCOL_ID_SENSOR = 0x15, SCMI_PROTOCOL_ID_SENSOR = 0x15,
SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16, SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16,
SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN = 0x17,
}; };
enum scmi_status_code { enum scmi_status_code {
@ -176,4 +177,116 @@ struct scmi_rd_reset_out {
s32 status; 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 */ #endif /* _SCMI_PROTOCOLS_H */