Commit 270a5bbb authored by Arnd Bergmann's avatar Arnd Bergmann

Merge tag 'scmi-updates-5.11' of...

Merge tag 'scmi-updates-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into arm/drivers

ARM SCMI updates for v5.11

Two main additions this time:
1. Support for SCMI v3.0 sensor extensions
2. Support for voltage domain management protocol added newly to SCMI v3.0

* tag 'scmi-updates-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux:
  firmware: arm_scmi: Remove residual _le structs naming
  firmware: arm_scmi: Add SCMI v3.0 sensor notifications
  firmware: arm_scmi: Add SCMI v3.0 sensor configuration support
  firmware: arm_scmi: Add SCMI v3.0 sensors timestamped reads
  hwmon: (scmi) Update hwmon internal scale data type
  firmware: arm_scmi: Add support to enumerated SCMI voltage domain device
  firmware: arm_scmi: Add voltage domain management protocol support
  dt-bindings: arm: Add support for SCMI Regulators
  firmware: arm_scmi: Add SCMI v3.0 sensors descriptors extensions
  firmware: arm_scmi: Add full list of sensor type enumeration
  firmware: arm_scmi: Rework scmi_sensors_protocol_init
  firmware: arm_scmi: Fix missing destroy_workqueue()

Link: https://lore.kernel.org/r/20201124122412.22386-1-sudeep.holla@arm.comSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 718e43b5 f83eb664
...@@ -62,6 +62,29 @@ Required properties: ...@@ -62,6 +62,29 @@ 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] but, due to SCMI abstractions,
supports only a subset of its properties as specified below amongst Optional
properties.
Required properties:
- reg : shall identify an existent SCMI Voltage Domain.
Optional properties:
- regulator-name
- regulator-min-microvolt / regulator-max-microvolt
- regulator-always-on / regulator-boot-on
- regulator-max-step-microvolt
- regulator-coupled-with / regulator-coupled-max-spread
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 +128,7 @@ Required sub-node properties: ...@@ -105,6 +128,7 @@ Required sub-node properties:
[3] Documentation/devicetree/bindings/thermal/thermal*.yaml [3] Documentation/devicetree/bindings/thermal/thermal*.yaml
[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 +193,25 @@ firmware { ...@@ -169,6 +193,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>;
};
...
};
};
}; };
}; };
......
...@@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o ...@@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o
scmi-transport-y = shmem.o scmi-transport-y = shmem.o
scmi-transport-$(CONFIG_MAILBOX) += mailbox.o scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o
scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \ scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
$(scmi-transport-y) $(scmi-transport-y)
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
......
...@@ -169,6 +169,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(perf); ...@@ -169,6 +169,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(perf);
DECLARE_SCMI_REGISTER_UNREGISTER(power); DECLARE_SCMI_REGISTER_UNREGISTER(power);
DECLARE_SCMI_REGISTER_UNREGISTER(reset); DECLARE_SCMI_REGISTER_UNREGISTER(reset);
DECLARE_SCMI_REGISTER_UNREGISTER(sensors); DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
DECLARE_SCMI_REGISTER_UNREGISTER(voltage);
DECLARE_SCMI_REGISTER_UNREGISTER(system); DECLARE_SCMI_REGISTER_UNREGISTER(system);
#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \ #define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \
......
...@@ -743,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = { ...@@ -743,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = {
{ SCMI_PROTOCOL_CLOCK, { "clocks" },}, { SCMI_PROTOCOL_CLOCK, { "clocks" },},
{ SCMI_PROTOCOL_SENSOR, { "hwmon" },}, { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
{ SCMI_PROTOCOL_RESET, { "reset" },}, { SCMI_PROTOCOL_RESET, { "reset" },},
{ SCMI_PROTOCOL_VOLTAGE, { "regulator" },},
}; };
static inline void static inline void
...@@ -946,6 +947,7 @@ static int __init scmi_driver_init(void) ...@@ -946,6 +947,7 @@ static int __init scmi_driver_init(void)
scmi_power_register(); scmi_power_register();
scmi_reset_register(); scmi_reset_register();
scmi_sensors_register(); scmi_sensors_register();
scmi_voltage_register();
scmi_system_register(); scmi_system_register();
return platform_driver_register(&scmi_driver); return platform_driver_register(&scmi_driver);
...@@ -961,6 +963,7 @@ static void __exit scmi_driver_exit(void) ...@@ -961,6 +963,7 @@ static void __exit scmi_driver_exit(void)
scmi_power_unregister(); scmi_power_unregister();
scmi_reset_unregister(); scmi_reset_unregister();
scmi_sensors_unregister(); scmi_sensors_unregister();
scmi_voltage_unregister();
scmi_system_unregister(); scmi_system_unregister();
platform_driver_unregister(&scmi_driver); platform_driver_unregister(&scmi_driver);
......
...@@ -1474,17 +1474,17 @@ int scmi_notification_init(struct scmi_handle *handle) ...@@ -1474,17 +1474,17 @@ int scmi_notification_init(struct scmi_handle *handle)
ni->gid = gid; ni->gid = gid;
ni->handle = handle; ni->handle = handle;
ni->registered_protocols = devm_kcalloc(handle->dev, SCMI_MAX_PROTO,
sizeof(char *), GFP_KERNEL);
if (!ni->registered_protocols)
goto err;
ni->notify_wq = alloc_workqueue(dev_name(handle->dev), ni->notify_wq = alloc_workqueue(dev_name(handle->dev),
WQ_UNBOUND | WQ_FREEZABLE | WQ_SYSFS, WQ_UNBOUND | WQ_FREEZABLE | WQ_SYSFS,
0); 0);
if (!ni->notify_wq) if (!ni->notify_wq)
goto err; goto err;
ni->registered_protocols = devm_kcalloc(handle->dev, SCMI_MAX_PROTO,
sizeof(char *), GFP_KERNEL);
if (!ni->registered_protocols)
goto err;
mutex_init(&ni->pending_mtx); mutex_init(&ni->pending_mtx);
hash_init(ni->pending_events_handlers); hash_init(ni->pending_events_handlers);
......
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/*
* System Control and Management Interface (SCMI) Voltage Protocol
*
* Copyright (C) 2020 ARM Ltd.
*/
#include <linux/scmi_protocol.h>
#include "common.h"
#define VOLTAGE_DOMS_NUM_MASK GENMASK(15, 0)
#define REMAINING_LEVELS_MASK GENMASK(31, 16)
#define RETURNED_LEVELS_MASK GENMASK(11, 0)
enum scmi_voltage_protocol_cmd {
VOLTAGE_DOMAIN_ATTRIBUTES = 0x3,
VOLTAGE_DESCRIBE_LEVELS = 0x4,
VOLTAGE_CONFIG_SET = 0x5,
VOLTAGE_CONFIG_GET = 0x6,
VOLTAGE_LEVEL_SET = 0x7,
VOLTAGE_LEVEL_GET = 0x8,
};
#define NUM_VOLTAGE_DOMAINS(x) ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x))))
struct scmi_msg_resp_domain_attributes {
__le32 attr;
u8 name[SCMI_MAX_STR_SIZE];
};
struct scmi_msg_cmd_describe_levels {
__le32 domain_id;
__le32 level_index;
};
struct scmi_msg_resp_describe_levels {
__le32 flags;
#define NUM_REMAINING_LEVELS(f) ((u16)(FIELD_GET(REMAINING_LEVELS_MASK, (f))))
#define NUM_RETURNED_LEVELS(f) ((u16)(FIELD_GET(RETURNED_LEVELS_MASK, (f))))
#define SUPPORTS_SEGMENTED_LEVELS(f) ((f) & BIT(12))
__le32 voltage[];
};
struct scmi_msg_cmd_config_set {
__le32 domain_id;
__le32 config;
};
struct scmi_msg_cmd_level_set {
__le32 domain_id;
__le32 flags;
__le32 voltage_level;
};
struct voltage_info {
unsigned int version;
unsigned int num_domains;
struct scmi_voltage_info *domains;
};
static int scmi_protocol_attributes_get(const struct scmi_handle *handle,
struct voltage_info *vinfo)
{
int ret;
struct scmi_xfer *t;
ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
SCMI_PROTOCOL_VOLTAGE, 0, sizeof(__le32), &t);
if (ret)
return ret;
ret = scmi_do_xfer(handle, t);
if (!ret)
vinfo->num_domains =
NUM_VOLTAGE_DOMAINS(get_unaligned_le32(t->rx.buf));
scmi_xfer_put(handle, t);
return ret;
}
static int scmi_init_voltage_levels(struct device *dev,
struct scmi_voltage_info *v,
u32 num_returned, u32 num_remaining,
bool segmented)
{
u32 num_levels;
num_levels = num_returned + num_remaining;
/*
* segmented levels entries are represented by a single triplet
* returned all in one go.
*/
if (!num_levels ||
(segmented && (num_remaining || num_returned != 3))) {
dev_err(dev,
"Invalid level descriptor(%d/%d/%d) for voltage dom %d\n",
num_levels, num_returned, num_remaining, v->id);
return -EINVAL;
}
v->levels_uv = devm_kcalloc(dev, num_levels, sizeof(u32), GFP_KERNEL);
if (!v->levels_uv)
return -ENOMEM;
v->num_levels = num_levels;
v->segmented = segmented;
return 0;
}
static int scmi_voltage_descriptors_get(const struct scmi_handle *handle,
struct voltage_info *vinfo)
{
int ret, dom;
struct scmi_xfer *td, *tl;
struct device *dev = handle->dev;
struct scmi_msg_resp_domain_attributes *resp_dom;
struct scmi_msg_resp_describe_levels *resp_levels;
ret = scmi_xfer_get_init(handle, VOLTAGE_DOMAIN_ATTRIBUTES,
SCMI_PROTOCOL_VOLTAGE, sizeof(__le32),
sizeof(*resp_dom), &td);
if (ret)
return ret;
resp_dom = td->rx.buf;
ret = scmi_xfer_get_init(handle, VOLTAGE_DESCRIBE_LEVELS,
SCMI_PROTOCOL_VOLTAGE, sizeof(__le64), 0, &tl);
if (ret)
goto outd;
resp_levels = tl->rx.buf;
for (dom = 0; dom < vinfo->num_domains; dom++) {
u32 desc_index = 0;
u16 num_returned = 0, num_remaining = 0;
struct scmi_msg_cmd_describe_levels *cmd;
struct scmi_voltage_info *v;
/* Retrieve domain attributes at first ... */
put_unaligned_le32(dom, td->tx.buf);
ret = scmi_do_xfer(handle, td);
/* Skip domain on comms error */
if (ret)
continue;
v = vinfo->domains + dom;
v->id = dom;
v->attributes = le32_to_cpu(resp_dom->attr);
strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE);
cmd = tl->tx.buf;
/* ...then retrieve domain levels descriptions */
do {
u32 flags;
int cnt;
cmd->domain_id = cpu_to_le32(v->id);
cmd->level_index = desc_index;
ret = scmi_do_xfer(handle, tl);
if (ret)
break;
flags = le32_to_cpu(resp_levels->flags);
num_returned = NUM_RETURNED_LEVELS(flags);
num_remaining = NUM_REMAINING_LEVELS(flags);
/* Allocate space for num_levels if not already done */
if (!v->num_levels) {
ret = scmi_init_voltage_levels(dev, v,
num_returned,
num_remaining,
SUPPORTS_SEGMENTED_LEVELS(flags));
if (ret)
break;
}
if (desc_index + num_returned > v->num_levels) {
dev_err(handle->dev,
"No. of voltage levels can't exceed %d\n",
v->num_levels);
ret = -EINVAL;
break;
}
for (cnt = 0; cnt < num_returned; cnt++) {
s32 val;
val =
(s32)le32_to_cpu(resp_levels->voltage[cnt]);
v->levels_uv[desc_index + cnt] = val;
if (val < 0)
v->negative_volts_allowed = true;
}
desc_index += num_returned;
scmi_reset_rx_to_maxsz(handle, tl);
/* check both to avoid infinite loop due to buggy fw */
} while (num_returned && num_remaining);
if (ret) {
v->num_levels = 0;
devm_kfree(dev, v->levels_uv);
}
scmi_reset_rx_to_maxsz(handle, td);
}
scmi_xfer_put(handle, tl);
outd:
scmi_xfer_put(handle, td);
return ret;
}
static int __scmi_voltage_get_u32(const struct scmi_handle *handle,
u8 cmd_id, u32 domain_id, u32 *value)
{
int ret;
struct scmi_xfer *t;
struct voltage_info *vinfo = handle->voltage_priv;
if (domain_id >= vinfo->num_domains)
return -EINVAL;
ret = scmi_xfer_get_init(handle, cmd_id,
SCMI_PROTOCOL_VOLTAGE,
sizeof(__le32), 0, &t);
if (ret)
return ret;
put_unaligned_le32(domain_id, t->tx.buf);
ret = scmi_do_xfer(handle, t);
if (!ret)
*value = get_unaligned_le32(t->rx.buf);
scmi_xfer_put(handle, t);
return ret;
}
static int scmi_voltage_config_set(const struct scmi_handle *handle,
u32 domain_id, u32 config)
{
int ret;
struct scmi_xfer *t;
struct voltage_info *vinfo = handle->voltage_priv;
struct scmi_msg_cmd_config_set *cmd;
if (domain_id >= vinfo->num_domains)
return -EINVAL;
ret = scmi_xfer_get_init(handle, VOLTAGE_CONFIG_SET,
SCMI_PROTOCOL_VOLTAGE,
sizeof(*cmd), 0, &t);
if (ret)
return ret;
cmd = t->tx.buf;
cmd->domain_id = cpu_to_le32(domain_id);
cmd->config = cpu_to_le32(config & GENMASK(3, 0));
ret = scmi_do_xfer(handle, t);
scmi_xfer_put(handle, t);
return ret;
}
static int scmi_voltage_config_get(const struct scmi_handle *handle,
u32 domain_id, u32 *config)
{
return __scmi_voltage_get_u32(handle, VOLTAGE_CONFIG_GET,
domain_id, config);
}
static int scmi_voltage_level_set(const struct scmi_handle *handle,
u32 domain_id, u32 flags, s32 volt_uV)
{
int ret;
struct scmi_xfer *t;
struct voltage_info *vinfo = handle->voltage_priv;
struct scmi_msg_cmd_level_set *cmd;
if (domain_id >= vinfo->num_domains)
return -EINVAL;
ret = scmi_xfer_get_init(handle, VOLTAGE_LEVEL_SET,
SCMI_PROTOCOL_VOLTAGE,
sizeof(*cmd), 0, &t);
if (ret)
return ret;
cmd = t->tx.buf;
cmd->domain_id = cpu_to_le32(domain_id);
cmd->flags = cpu_to_le32(flags);
cmd->voltage_level = cpu_to_le32(volt_uV);
ret = scmi_do_xfer(handle, t);
scmi_xfer_put(handle, t);
return ret;
}
static int scmi_voltage_level_get(const struct scmi_handle *handle,
u32 domain_id, s32 *volt_uV)
{
return __scmi_voltage_get_u32(handle, VOLTAGE_LEVEL_GET,
domain_id, (u32 *)volt_uV);
}
static const struct scmi_voltage_info * __must_check
scmi_voltage_info_get(const struct scmi_handle *handle, u32 domain_id)
{
struct voltage_info *vinfo = handle->voltage_priv;
if (domain_id >= vinfo->num_domains ||
!vinfo->domains[domain_id].num_levels)
return NULL;
return vinfo->domains + domain_id;
}
static int scmi_voltage_domains_num_get(const struct scmi_handle *handle)
{
struct voltage_info *vinfo = handle->voltage_priv;
return vinfo->num_domains;
}
static struct scmi_voltage_ops voltage_ops = {
.num_domains_get = scmi_voltage_domains_num_get,
.info_get = scmi_voltage_info_get,
.config_set = scmi_voltage_config_set,
.config_get = scmi_voltage_config_get,
.level_set = scmi_voltage_level_set,
.level_get = scmi_voltage_level_get,
};
static int scmi_voltage_protocol_init(struct scmi_handle *handle)
{
int ret;
u32 version;
struct voltage_info *vinfo;
ret = scmi_version_get(handle, SCMI_PROTOCOL_VOLTAGE, &version);
if (ret)
return ret;
dev_dbg(handle->dev, "Voltage Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
vinfo = devm_kzalloc(handle->dev, sizeof(*vinfo), GFP_KERNEL);
if (!vinfo)
return -ENOMEM;
vinfo->version = version;
ret = scmi_protocol_attributes_get(handle, vinfo);
if (ret)
return ret;
if (vinfo->num_domains) {
vinfo->domains = devm_kcalloc(handle->dev, vinfo->num_domains,
sizeof(*vinfo->domains),
GFP_KERNEL);
if (!vinfo->domains)
return -ENOMEM;
ret = scmi_voltage_descriptors_get(handle, vinfo);
if (ret)
return ret;
} else {
dev_warn(handle->dev, "No Voltage domains found.\n");
}
handle->voltage_ops = &voltage_ops;
handle->voltage_priv = vinfo;
return 0;
}
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_VOLTAGE, voltage)
...@@ -30,7 +30,7 @@ static inline u64 __pow10(u8 x) ...@@ -30,7 +30,7 @@ static inline u64 __pow10(u8 x)
static int scmi_hwmon_scale(const struct scmi_sensor_info *sensor, u64 *value) static int scmi_hwmon_scale(const struct scmi_sensor_info *sensor, u64 *value)
{ {
s8 scale = sensor->scale; int scale = sensor->scale;
u64 f; u64 f;
switch (sensor->type) { switch (sensor->type) {
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment