blob: 27f2092b9882aef307763e214eb74c69f4db2af5 [file] [log] [blame]
Sudeep Holla5179c522017-06-06 11:38:10 +01001// SPDX-License-Identifier: GPL-2.0
2/*
3 * System Control and Management Interface (SCMI) Sensor Protocol
4 *
5 * Copyright (C) 2018 ARM Ltd.
6 */
7
8#include "common.h"
9
10enum scmi_sensor_protocol_cmd {
11 SENSOR_DESCRIPTION_GET = 0x3,
12 SENSOR_CONFIG_SET = 0x4,
13 SENSOR_TRIP_POINT_SET = 0x5,
14 SENSOR_READING_GET = 0x6,
15};
16
17struct scmi_msg_resp_sensor_attributes {
18 __le16 num_sensors;
19 u8 max_requests;
20 u8 reserved;
21 __le32 reg_addr_low;
22 __le32 reg_addr_high;
23 __le32 reg_size;
24};
25
26struct scmi_msg_resp_sensor_description {
27 __le16 num_returned;
28 __le16 num_remaining;
29 struct {
30 __le32 id;
31 __le32 attributes_low;
32#define SUPPORTS_ASYNC_READ(x) ((x) & BIT(31))
33#define NUM_TRIP_POINTS(x) (((x) >> 4) & 0xff)
34 __le32 attributes_high;
35#define SENSOR_TYPE(x) ((x) & 0xff)
36#define SENSOR_SCALE(x) (((x) >> 11) & 0x3f)
37#define SENSOR_UPDATE_SCALE(x) (((x) >> 22) & 0x1f)
38#define SENSOR_UPDATE_BASE(x) (((x) >> 27) & 0x1f)
39 u8 name[SCMI_MAX_STR_SIZE];
40 } desc[0];
41};
42
43struct scmi_msg_set_sensor_config {
44 __le32 id;
45 __le32 event_control;
46};
47
48struct scmi_msg_set_sensor_trip_point {
49 __le32 id;
50 __le32 event_control;
51#define SENSOR_TP_EVENT_MASK (0x3)
52#define SENSOR_TP_DISABLED 0x0
53#define SENSOR_TP_POSITIVE 0x1
54#define SENSOR_TP_NEGATIVE 0x2
55#define SENSOR_TP_BOTH 0x3
56#define SENSOR_TP_ID(x) (((x) & 0xff) << 4)
57 __le32 value_low;
58 __le32 value_high;
59};
60
61struct scmi_msg_sensor_reading_get {
62 __le32 id;
63 __le32 flags;
64#define SENSOR_READ_ASYNC BIT(0)
65};
66
67struct sensors_info {
68 int num_sensors;
69 int max_requests;
70 u64 reg_addr;
71 u32 reg_size;
72 struct scmi_sensor_info *sensors;
73};
74
75static int scmi_sensor_attributes_get(const struct scmi_handle *handle,
76 struct sensors_info *si)
77{
78 int ret;
79 struct scmi_xfer *t;
80 struct scmi_msg_resp_sensor_attributes *attr;
81
Sudeep Holla14e297b2018-05-09 17:52:06 +010082 ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
Sudeep Holla5179c522017-06-06 11:38:10 +010083 SCMI_PROTOCOL_SENSOR, 0, sizeof(*attr), &t);
84 if (ret)
85 return ret;
86
87 attr = t->rx.buf;
88
89 ret = scmi_do_xfer(handle, t);
90 if (!ret) {
91 si->num_sensors = le16_to_cpu(attr->num_sensors);
92 si->max_requests = attr->max_requests;
93 si->reg_addr = le32_to_cpu(attr->reg_addr_low) |
94 (u64)le32_to_cpu(attr->reg_addr_high) << 32;
95 si->reg_size = le32_to_cpu(attr->reg_size);
96 }
97
Sudeep Holla14e297b2018-05-09 17:52:06 +010098 scmi_xfer_put(handle, t);
Sudeep Holla5179c522017-06-06 11:38:10 +010099 return ret;
100}
101
102static int scmi_sensor_description_get(const struct scmi_handle *handle,
103 struct sensors_info *si)
104{
105 int ret, cnt;
106 u32 desc_index = 0;
107 u16 num_returned, num_remaining;
108 struct scmi_xfer *t;
109 struct scmi_msg_resp_sensor_description *buf;
110
Sudeep Holla14e297b2018-05-09 17:52:06 +0100111 ret = scmi_xfer_get_init(handle, SENSOR_DESCRIPTION_GET,
Sudeep Holla5179c522017-06-06 11:38:10 +0100112 SCMI_PROTOCOL_SENSOR, sizeof(__le32), 0, &t);
113 if (ret)
114 return ret;
115
116 buf = t->rx.buf;
117
118 do {
119 /* Set the number of sensors to be skipped/already read */
120 *(__le32 *)t->tx.buf = cpu_to_le32(desc_index);
121
122 ret = scmi_do_xfer(handle, t);
123 if (ret)
124 break;
125
126 num_returned = le16_to_cpu(buf->num_returned);
127 num_remaining = le16_to_cpu(buf->num_remaining);
128
129 if (desc_index + num_returned > si->num_sensors) {
130 dev_err(handle->dev, "No. of sensors can't exceed %d",
131 si->num_sensors);
132 break;
133 }
134
135 for (cnt = 0; cnt < num_returned; cnt++) {
136 u32 attrh;
137 struct scmi_sensor_info *s;
138
139 attrh = le32_to_cpu(buf->desc[cnt].attributes_high);
140 s = &si->sensors[desc_index + cnt];
141 s->id = le32_to_cpu(buf->desc[cnt].id);
142 s->type = SENSOR_TYPE(attrh);
143 memcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE);
144 }
145
146 desc_index += num_returned;
147 /*
148 * check for both returned and remaining to avoid infinite
149 * loop due to buggy firmware
150 */
151 } while (num_returned && num_remaining);
152
Sudeep Holla14e297b2018-05-09 17:52:06 +0100153 scmi_xfer_put(handle, t);
Sudeep Holla5179c522017-06-06 11:38:10 +0100154 return ret;
155}
156
157static int
158scmi_sensor_configuration_set(const struct scmi_handle *handle, u32 sensor_id)
159{
160 int ret;
161 u32 evt_cntl = BIT(0);
162 struct scmi_xfer *t;
163 struct scmi_msg_set_sensor_config *cfg;
164
Sudeep Holla14e297b2018-05-09 17:52:06 +0100165 ret = scmi_xfer_get_init(handle, SENSOR_CONFIG_SET,
Sudeep Holla5179c522017-06-06 11:38:10 +0100166 SCMI_PROTOCOL_SENSOR, sizeof(*cfg), 0, &t);
167 if (ret)
168 return ret;
169
170 cfg = t->tx.buf;
171 cfg->id = cpu_to_le32(sensor_id);
172 cfg->event_control = cpu_to_le32(evt_cntl);
173
174 ret = scmi_do_xfer(handle, t);
175
Sudeep Holla14e297b2018-05-09 17:52:06 +0100176 scmi_xfer_put(handle, t);
Sudeep Holla5179c522017-06-06 11:38:10 +0100177 return ret;
178}
179
180static int scmi_sensor_trip_point_set(const struct scmi_handle *handle,
181 u32 sensor_id, u8 trip_id, u64 trip_value)
182{
183 int ret;
184 u32 evt_cntl = SENSOR_TP_BOTH;
185 struct scmi_xfer *t;
186 struct scmi_msg_set_sensor_trip_point *trip;
187
Sudeep Holla14e297b2018-05-09 17:52:06 +0100188 ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_SET,
Sudeep Holla5179c522017-06-06 11:38:10 +0100189 SCMI_PROTOCOL_SENSOR, sizeof(*trip), 0, &t);
190 if (ret)
191 return ret;
192
193 trip = t->tx.buf;
194 trip->id = cpu_to_le32(sensor_id);
195 trip->event_control = cpu_to_le32(evt_cntl | SENSOR_TP_ID(trip_id));
196 trip->value_low = cpu_to_le32(trip_value & 0xffffffff);
197 trip->value_high = cpu_to_le32(trip_value >> 32);
198
199 ret = scmi_do_xfer(handle, t);
200
Sudeep Holla14e297b2018-05-09 17:52:06 +0100201 scmi_xfer_put(handle, t);
Sudeep Holla5179c522017-06-06 11:38:10 +0100202 return ret;
203}
204
205static int scmi_sensor_reading_get(const struct scmi_handle *handle,
206 u32 sensor_id, bool async, u64 *value)
207{
208 int ret;
209 struct scmi_xfer *t;
210 struct scmi_msg_sensor_reading_get *sensor;
211
Sudeep Holla14e297b2018-05-09 17:52:06 +0100212 ret = scmi_xfer_get_init(handle, SENSOR_READING_GET,
Sudeep Holla5179c522017-06-06 11:38:10 +0100213 SCMI_PROTOCOL_SENSOR, sizeof(*sensor),
214 sizeof(u64), &t);
215 if (ret)
216 return ret;
217
218 sensor = t->tx.buf;
219 sensor->id = cpu_to_le32(sensor_id);
220 sensor->flags = cpu_to_le32(async ? SENSOR_READ_ASYNC : 0);
221
222 ret = scmi_do_xfer(handle, t);
223 if (!ret) {
224 __le32 *pval = t->rx.buf;
225
226 *value = le32_to_cpu(*pval);
227 *value |= (u64)le32_to_cpu(*(pval + 1)) << 32;
228 }
229
Sudeep Holla14e297b2018-05-09 17:52:06 +0100230 scmi_xfer_put(handle, t);
Sudeep Holla5179c522017-06-06 11:38:10 +0100231 return ret;
232}
233
234static const struct scmi_sensor_info *
235scmi_sensor_info_get(const struct scmi_handle *handle, u32 sensor_id)
236{
237 struct sensors_info *si = handle->sensor_priv;
238
239 return si->sensors + sensor_id;
240}
241
242static int scmi_sensor_count_get(const struct scmi_handle *handle)
243{
244 struct sensors_info *si = handle->sensor_priv;
245
246 return si->num_sensors;
247}
248
249static struct scmi_sensor_ops sensor_ops = {
250 .count_get = scmi_sensor_count_get,
251 .info_get = scmi_sensor_info_get,
252 .configuration_set = scmi_sensor_configuration_set,
253 .trip_point_set = scmi_sensor_trip_point_set,
254 .reading_get = scmi_sensor_reading_get,
255};
256
257static int scmi_sensors_protocol_init(struct scmi_handle *handle)
258{
259 u32 version;
260 struct sensors_info *sinfo;
261
262 scmi_version_get(handle, SCMI_PROTOCOL_SENSOR, &version);
263
264 dev_dbg(handle->dev, "Sensor Version %d.%d\n",
265 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
266
267 sinfo = devm_kzalloc(handle->dev, sizeof(*sinfo), GFP_KERNEL);
268 if (!sinfo)
269 return -ENOMEM;
270
271 scmi_sensor_attributes_get(handle, sinfo);
272
273 sinfo->sensors = devm_kcalloc(handle->dev, sinfo->num_sensors,
274 sizeof(*sinfo->sensors), GFP_KERNEL);
275 if (!sinfo->sensors)
276 return -ENOMEM;
277
278 scmi_sensor_description_get(handle, sinfo);
279
280 handle->sensor_ops = &sensor_ops;
281 handle->sensor_priv = sinfo;
282
283 return 0;
284}
285
286static int __init scmi_sensors_init(void)
287{
288 return scmi_protocol_register(SCMI_PROTOCOL_SENSOR,
289 &scmi_sensors_protocol_init);
290}
291subsys_initcall(scmi_sensors_init);