Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 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
 82	ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
 83				 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
 98	scmi_one_xfer_put(handle, t);
 99	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
111	ret = scmi_one_xfer_init(handle, SENSOR_DESCRIPTION_GET,
112				 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
153	scmi_one_xfer_put(handle, t);
154	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
165	ret = scmi_one_xfer_init(handle, SENSOR_CONFIG_SET,
166				 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
176	scmi_one_xfer_put(handle, t);
177	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
188	ret = scmi_one_xfer_init(handle, SENSOR_TRIP_POINT_SET,
189				 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
201	scmi_one_xfer_put(handle, t);
202	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
212	ret = scmi_one_xfer_init(handle, SENSOR_READING_GET,
213				 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
230	scmi_one_xfer_put(handle, t);
231	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);