summaryrefslogtreecommitdiff
path: root/drivers/thermal/scmi_thermal.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thermal/scmi_thermal.c')
-rw-r--r--drivers/thermal/scmi_thermal.c176
1 files changed, 176 insertions, 0 deletions
diff --git a/drivers/thermal/scmi_thermal.c b/drivers/thermal/scmi_thermal.c
new file mode 100644
index 0000000000..bccfd28c2f
--- /dev/null
+++ b/drivers/thermal/scmi_thermal.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2022 NXP
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <thermal.h>
+#include <scmi_agent.h>
+#include <scmi_protocols.h>
+#include <asm/types.h>
+#include <dm/device-internal.h>
+#include <dm/device.h>
+
+struct scmi_thermal_priv {
+ s16 num_sensors;
+ s16 thermal_id;
+ struct scmi_sensor_descrition_get_p2a *desc_buf;
+ size_t desc_buf_size;
+};
+
+static int scmi_thermal_get_temp(struct udevice *dev, int *temp)
+{
+ struct scmi_thermal_priv *priv = dev_get_priv(dev);
+ struct scmi_sensor_reading_get_a2p in = { priv->thermal_id };
+ struct scmi_sensor_reading_get_p2a out = { 0 };
+ struct scmi_msg msg = {
+ .protocol_id = SCMI_PROTOCOL_ID_SENSOR,
+ .message_id = SCMI_SENSOR_READING_GET,
+ .in_msg = (u8 *)&in,
+ .in_msg_sz = sizeof(in),
+ .out_msg = (u8 *)&out,
+ .out_msg_sz = sizeof(out),
+ };
+ int ret;
+
+ ret = devm_scmi_process_msg(dev->parent, &msg);
+ if (ret)
+ return ret;
+
+ ret = scmi_to_linux_errno(out.status);
+ if (ret < 0)
+ return ret;
+
+ *temp = out.val.value_low;
+
+ return ret;
+}
+
+static int scmi_sensor_attributes_get(struct udevice *dev)
+{
+ struct scmi_protocol_attributes_p2a_sensor out = { 0 };
+ struct scmi_msg msg = {
+ .protocol_id = SCMI_PROTOCOL_ID_SENSOR,
+ .message_id = SCMI_PROTOCOL_ATTRIBUTES,
+ .in_msg = NULL,
+ .in_msg_sz = 0,
+ .out_msg = (u8 *)&out,
+ .out_msg_sz = sizeof(out),
+ };
+ int ret;
+ struct scmi_thermal_priv *priv = dev_get_priv(dev);
+
+ ret = devm_scmi_process_msg(dev->parent, &msg);
+ if (ret)
+ return ret;
+
+ ret = scmi_to_linux_errno(out.status);
+ if (ret < 0)
+ return ret;
+
+ priv->num_sensors = out.num_sensors;
+
+ dev_dbg(dev,"num_sensors %d\n", priv->num_sensors);
+
+ return 0;
+}
+
+static int scmi_sensor_description_get(struct udevice *dev, u32 start_ind,
+ struct scmi_sensor_descrition_get_p2a *desc_buf, size_t desc_buf_size)
+{
+ struct scmi_sensor_description_get_a2p in = { start_ind };
+ struct scmi_msg msg = {
+ .protocol_id = SCMI_PROTOCOL_ID_SENSOR,
+ .message_id = SCMI_SENSOR_DESCRIPTION_GET,
+ .in_msg = (u8 *)&in,
+ .in_msg_sz = sizeof(in),
+ .out_msg = (u8 *)desc_buf,
+ .out_msg_sz = desc_buf_size,
+ };
+ int ret;
+
+ ret = devm_scmi_process_msg(dev->parent, &msg);
+ if (ret)
+ return ret;
+
+ ret = scmi_to_linux_errno(desc_buf->status);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static const struct dm_thermal_ops scmi_thermal_ops = {
+ .get_temp = scmi_thermal_get_temp,
+};
+
+static int scmi_thermal_probe(struct udevice *dev)
+{
+ int ret;
+ u16 num_returned, num_remaining, cnt;
+ u32 index = 0;
+ bool find = false;
+ struct scmi_sensor_desc *desc;
+ struct scmi_thermal_priv *priv = dev_get_priv(dev);
+
+ ret = scmi_sensor_attributes_get(dev);
+ if (ret){
+ dev_err(dev, "scmi_sensor_attributes_get failure %d\n", ret);
+ return ret;
+ }
+
+ priv->desc_buf_size = sizeof(struct scmi_sensor_descrition_get_p2a) +
+ (priv->num_sensors - 1) * sizeof(struct scmi_sensor_desc);
+ priv->desc_buf = (struct scmi_sensor_descrition_get_p2a *)calloc(1, priv->desc_buf_size);
+ if (!priv->desc_buf) {
+ dev_err(dev, "allocate desc_buffer failure\n");
+ return -ENOMEM;
+ }
+
+ do {
+ ret = scmi_sensor_description_get(dev, index, priv->desc_buf, priv->desc_buf_size);
+ if (ret){
+ dev_err(dev, "scmi_sensor_description_get failure %d\n", ret);
+ return ret;
+ }
+
+ num_returned = priv->desc_buf->num_sensor_flags & 0xffff;
+ num_remaining = (priv->desc_buf->num_sensor_flags >> 16) & 0xffff;
+
+ if (index + num_returned > priv->num_sensors) {
+ dev_err(dev, "Num of sensors can't exceed %d",
+ priv->num_sensors);
+ return -EINVAL;
+ }
+
+ for (cnt = 0; cnt < num_returned; cnt++) {
+ desc = &priv->desc_buf->desc[cnt];
+ if ((desc->attr_high & 0xff) == 0x2) {
+ priv->thermal_id = desc->id; /* Only get one thermal sensor */
+ dev_dbg(dev, "thermal id %u\n", priv->thermal_id);
+ find = true;
+ break;
+ }
+ }
+
+ index += num_returned;
+ } while (num_returned && num_remaining && !find);
+
+ if (!find) {
+ dev_err(dev, "Can't find thermal sensor device\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+U_BOOT_DRIVER(scmi_thermal) = {
+ .name = "scmi_thermal",
+ .id = UCLASS_THERMAL,
+ .ops = &scmi_thermal_ops,
+ .probe = scmi_thermal_probe,
+ .flags = DM_FLAG_PRE_RELOC,
+ .priv_auto = sizeof(struct scmi_thermal_priv),
+};