]> Git Repo - linux.git/blob - drivers/firmware/arm_scmi/sensors.c
efi/libstub: Optimize for size instead of speed
[linux.git] / drivers / firmware / arm_scmi / sensors.c
1 // 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
10 enum scmi_sensor_protocol_cmd {
11         SENSOR_DESCRIPTION_GET = 0x3,
12         SENSOR_TRIP_POINT_NOTIFY = 0x4,
13         SENSOR_TRIP_POINT_CONFIG = 0x5,
14         SENSOR_READING_GET = 0x6,
15 };
16
17 struct 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
26 struct 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) & 0xff)
34                 __le32 attributes_high;
35 #define SENSOR_TYPE(x)          ((x) & 0xff)
36 #define SENSOR_SCALE(x)         (((x) >> 11) & 0x1f)
37 #define SENSOR_SCALE_SIGN       BIT(4)
38 #define SENSOR_SCALE_EXTEND     GENMASK(7, 5)
39 #define SENSOR_UPDATE_SCALE(x)  (((x) >> 22) & 0x1f)
40 #define SENSOR_UPDATE_BASE(x)   (((x) >> 27) & 0x1f)
41                     u8 name[SCMI_MAX_STR_SIZE];
42         } desc[0];
43 };
44
45 struct scmi_msg_sensor_trip_point_notify {
46         __le32 id;
47         __le32 event_control;
48 #define SENSOR_TP_NOTIFY_ALL    BIT(0)
49 };
50
51 struct scmi_msg_set_sensor_trip_point {
52         __le32 id;
53         __le32 event_control;
54 #define SENSOR_TP_EVENT_MASK    (0x3)
55 #define SENSOR_TP_DISABLED      0x0
56 #define SENSOR_TP_POSITIVE      0x1
57 #define SENSOR_TP_NEGATIVE      0x2
58 #define SENSOR_TP_BOTH          0x3
59 #define SENSOR_TP_ID(x)         (((x) & 0xff) << 4)
60         __le32 value_low;
61         __le32 value_high;
62 };
63
64 struct scmi_msg_sensor_reading_get {
65         __le32 id;
66         __le32 flags;
67 #define SENSOR_READ_ASYNC       BIT(0)
68 };
69
70 struct sensors_info {
71         u32 version;
72         int num_sensors;
73         int max_requests;
74         u64 reg_addr;
75         u32 reg_size;
76         struct scmi_sensor_info *sensors;
77 };
78
79 static int scmi_sensor_attributes_get(const struct scmi_handle *handle,
80                                       struct sensors_info *si)
81 {
82         int ret;
83         struct scmi_xfer *t;
84         struct scmi_msg_resp_sensor_attributes *attr;
85
86         ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
87                                  SCMI_PROTOCOL_SENSOR, 0, sizeof(*attr), &t);
88         if (ret)
89                 return ret;
90
91         attr = t->rx.buf;
92
93         ret = scmi_do_xfer(handle, t);
94         if (!ret) {
95                 si->num_sensors = le16_to_cpu(attr->num_sensors);
96                 si->max_requests = attr->max_requests;
97                 si->reg_addr = le32_to_cpu(attr->reg_addr_low) |
98                                 (u64)le32_to_cpu(attr->reg_addr_high) << 32;
99                 si->reg_size = le32_to_cpu(attr->reg_size);
100         }
101
102         scmi_xfer_put(handle, t);
103         return ret;
104 }
105
106 static int scmi_sensor_description_get(const struct scmi_handle *handle,
107                                        struct sensors_info *si)
108 {
109         int ret, cnt;
110         u32 desc_index = 0;
111         u16 num_returned, num_remaining;
112         struct scmi_xfer *t;
113         struct scmi_msg_resp_sensor_description *buf;
114
115         ret = scmi_xfer_get_init(handle, SENSOR_DESCRIPTION_GET,
116                                  SCMI_PROTOCOL_SENSOR, sizeof(__le32), 0, &t);
117         if (ret)
118                 return ret;
119
120         buf = t->rx.buf;
121
122         do {
123                 /* Set the number of sensors to be skipped/already read */
124                 put_unaligned_le32(desc_index, t->tx.buf);
125
126                 ret = scmi_do_xfer(handle, t);
127                 if (ret)
128                         break;
129
130                 num_returned = le16_to_cpu(buf->num_returned);
131                 num_remaining = le16_to_cpu(buf->num_remaining);
132
133                 if (desc_index + num_returned > si->num_sensors) {
134                         dev_err(handle->dev, "No. of sensors can't exceed %d",
135                                 si->num_sensors);
136                         break;
137                 }
138
139                 for (cnt = 0; cnt < num_returned; cnt++) {
140                         u32 attrh, attrl;
141                         struct scmi_sensor_info *s;
142
143                         attrl = le32_to_cpu(buf->desc[cnt].attributes_low);
144                         attrh = le32_to_cpu(buf->desc[cnt].attributes_high);
145                         s = &si->sensors[desc_index + cnt];
146                         s->id = le32_to_cpu(buf->desc[cnt].id);
147                         s->type = SENSOR_TYPE(attrh);
148                         s->scale = SENSOR_SCALE(attrh);
149                         /* Sign extend to a full s8 */
150                         if (s->scale & SENSOR_SCALE_SIGN)
151                                 s->scale |= SENSOR_SCALE_EXTEND;
152                         s->async = SUPPORTS_ASYNC_READ(attrl);
153                         s->num_trip_points = NUM_TRIP_POINTS(attrl);
154                         strlcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE);
155                 }
156
157                 desc_index += num_returned;
158                 /*
159                  * check for both returned and remaining to avoid infinite
160                  * loop due to buggy firmware
161                  */
162         } while (num_returned && num_remaining);
163
164         scmi_xfer_put(handle, t);
165         return ret;
166 }
167
168 static int scmi_sensor_trip_point_notify(const struct scmi_handle *handle,
169                                          u32 sensor_id, bool enable)
170 {
171         int ret;
172         u32 evt_cntl = enable ? SENSOR_TP_NOTIFY_ALL : 0;
173         struct scmi_xfer *t;
174         struct scmi_msg_sensor_trip_point_notify *cfg;
175
176         ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_NOTIFY,
177                                  SCMI_PROTOCOL_SENSOR, sizeof(*cfg), 0, &t);
178         if (ret)
179                 return ret;
180
181         cfg = t->tx.buf;
182         cfg->id = cpu_to_le32(sensor_id);
183         cfg->event_control = cpu_to_le32(evt_cntl);
184
185         ret = scmi_do_xfer(handle, t);
186
187         scmi_xfer_put(handle, t);
188         return ret;
189 }
190
191 static int
192 scmi_sensor_trip_point_config(const struct scmi_handle *handle, u32 sensor_id,
193                               u8 trip_id, u64 trip_value)
194 {
195         int ret;
196         u32 evt_cntl = SENSOR_TP_BOTH;
197         struct scmi_xfer *t;
198         struct scmi_msg_set_sensor_trip_point *trip;
199
200         ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_CONFIG,
201                                  SCMI_PROTOCOL_SENSOR, sizeof(*trip), 0, &t);
202         if (ret)
203                 return ret;
204
205         trip = t->tx.buf;
206         trip->id = cpu_to_le32(sensor_id);
207         trip->event_control = cpu_to_le32(evt_cntl | SENSOR_TP_ID(trip_id));
208         trip->value_low = cpu_to_le32(trip_value & 0xffffffff);
209         trip->value_high = cpu_to_le32(trip_value >> 32);
210
211         ret = scmi_do_xfer(handle, t);
212
213         scmi_xfer_put(handle, t);
214         return ret;
215 }
216
217 static int scmi_sensor_reading_get(const struct scmi_handle *handle,
218                                    u32 sensor_id, u64 *value)
219 {
220         int ret;
221         struct scmi_xfer *t;
222         struct scmi_msg_sensor_reading_get *sensor;
223         struct sensors_info *si = handle->sensor_priv;
224         struct scmi_sensor_info *s = si->sensors + sensor_id;
225
226         ret = scmi_xfer_get_init(handle, SENSOR_READING_GET,
227                                  SCMI_PROTOCOL_SENSOR, sizeof(*sensor),
228                                  sizeof(u64), &t);
229         if (ret)
230                 return ret;
231
232         sensor = t->tx.buf;
233         sensor->id = cpu_to_le32(sensor_id);
234
235         if (s->async) {
236                 sensor->flags = cpu_to_le32(SENSOR_READ_ASYNC);
237                 ret = scmi_do_xfer_with_response(handle, t);
238                 if (!ret)
239                         *value = get_unaligned_le64((void *)
240                                                     ((__le32 *)t->rx.buf + 1));
241         } else {
242                 sensor->flags = cpu_to_le32(0);
243                 ret = scmi_do_xfer(handle, t);
244                 if (!ret)
245                         *value = get_unaligned_le64(t->rx.buf);
246         }
247
248         scmi_xfer_put(handle, t);
249         return ret;
250 }
251
252 static const struct scmi_sensor_info *
253 scmi_sensor_info_get(const struct scmi_handle *handle, u32 sensor_id)
254 {
255         struct sensors_info *si = handle->sensor_priv;
256
257         return si->sensors + sensor_id;
258 }
259
260 static int scmi_sensor_count_get(const struct scmi_handle *handle)
261 {
262         struct sensors_info *si = handle->sensor_priv;
263
264         return si->num_sensors;
265 }
266
267 static struct scmi_sensor_ops sensor_ops = {
268         .count_get = scmi_sensor_count_get,
269         .info_get = scmi_sensor_info_get,
270         .trip_point_notify = scmi_sensor_trip_point_notify,
271         .trip_point_config = scmi_sensor_trip_point_config,
272         .reading_get = scmi_sensor_reading_get,
273 };
274
275 static int scmi_sensors_protocol_init(struct scmi_handle *handle)
276 {
277         u32 version;
278         struct sensors_info *sinfo;
279
280         scmi_version_get(handle, SCMI_PROTOCOL_SENSOR, &version);
281
282         dev_dbg(handle->dev, "Sensor Version %d.%d\n",
283                 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
284
285         sinfo = devm_kzalloc(handle->dev, sizeof(*sinfo), GFP_KERNEL);
286         if (!sinfo)
287                 return -ENOMEM;
288
289         scmi_sensor_attributes_get(handle, sinfo);
290
291         sinfo->sensors = devm_kcalloc(handle->dev, sinfo->num_sensors,
292                                       sizeof(*sinfo->sensors), GFP_KERNEL);
293         if (!sinfo->sensors)
294                 return -ENOMEM;
295
296         scmi_sensor_description_get(handle, sinfo);
297
298         sinfo->version = version;
299         handle->sensor_ops = &sensor_ops;
300         handle->sensor_priv = sinfo;
301
302         return 0;
303 }
304
305 static int __init scmi_sensors_init(void)
306 {
307         return scmi_protocol_register(SCMI_PROTOCOL_SENSOR,
308                                       &scmi_sensors_protocol_init);
309 }
310 subsys_initcall(scmi_sensors_init);
This page took 0.050443 seconds and 4 git commands to generate.