blob: f0c37a4ecddf797ae1ae6b33127121ddbf470455 [file] [log] [blame]
Sudeep Holla8cb7cf52015-03-30 10:59:52 +01001/*
2 * System Control and Power Interface (SCPI) Message Protocol driver
3 *
4 * SCPI Message Protocol is used between the System Control Processor(SCP)
5 * and the Application Processors(AP). The Message Handling Unit(MHU)
6 * provides a mechanism for inter-processor communication between SCP's
7 * Cortex M3 and AP.
8 *
9 * SCP offers control and management of the core/cluster power states,
10 * various power domain DVFS including the core/cluster, certain system
11 * clocks configuration, thermal sensors and many others.
12 *
13 * Copyright (C) 2015 ARM Ltd.
14 *
15 * This program is free software; you can redistribute it and/or modify it
16 * under the terms and conditions of the GNU General Public License,
17 * version 2, as published by the Free Software Foundation.
18 *
19 * This program is distributed in the hope it will be useful, but WITHOUT
20 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
22 * more details.
23 *
24 * You should have received a copy of the GNU General Public License along
25 * with this program. If not, see <http://www.gnu.org/licenses/>.
26 */
27
28#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
29
30#include <linux/bitmap.h>
Heiner Kallweit4864dca2017-10-04 21:00:39 +020031#include <linux/bitfield.h>
Sudeep Holla8cb7cf52015-03-30 10:59:52 +010032#include <linux/device.h>
33#include <linux/err.h>
34#include <linux/export.h>
35#include <linux/io.h>
36#include <linux/kernel.h>
37#include <linux/list.h>
38#include <linux/mailbox_client.h>
39#include <linux/module.h>
40#include <linux/of_address.h>
41#include <linux/of_platform.h>
42#include <linux/printk.h>
Sudeep Holla45ca7df2017-04-27 15:08:51 +010043#include <linux/pm_opp.h>
Sudeep Holla8cb7cf52015-03-30 10:59:52 +010044#include <linux/scpi_protocol.h>
45#include <linux/slab.h>
46#include <linux/sort.h>
47#include <linux/spinlock.h>
48
49#define CMD_ID_SHIFT 0
50#define CMD_ID_MASK 0x7f
51#define CMD_TOKEN_ID_SHIFT 8
52#define CMD_TOKEN_ID_MASK 0xff
53#define CMD_DATA_SIZE_SHIFT 16
54#define CMD_DATA_SIZE_MASK 0x1ff
Neil Armstrong4dfe32d2016-10-19 14:51:08 +020055#define CMD_LEGACY_DATA_SIZE_SHIFT 20
56#define CMD_LEGACY_DATA_SIZE_MASK 0x1ff
Sudeep Holla8cb7cf52015-03-30 10:59:52 +010057#define PACK_SCPI_CMD(cmd_id, tx_sz) \
58 ((((cmd_id) & CMD_ID_MASK) << CMD_ID_SHIFT) | \
59 (((tx_sz) & CMD_DATA_SIZE_MASK) << CMD_DATA_SIZE_SHIFT))
60#define ADD_SCPI_TOKEN(cmd, token) \
61 ((cmd) |= (((token) & CMD_TOKEN_ID_MASK) << CMD_TOKEN_ID_SHIFT))
Neil Armstrong4dfe32d2016-10-19 14:51:08 +020062#define PACK_LEGACY_SCPI_CMD(cmd_id, tx_sz) \
63 ((((cmd_id) & CMD_ID_MASK) << CMD_ID_SHIFT) | \
64 (((tx_sz) & CMD_LEGACY_DATA_SIZE_MASK) << CMD_LEGACY_DATA_SIZE_SHIFT))
Sudeep Holla8cb7cf52015-03-30 10:59:52 +010065
66#define CMD_SIZE(cmd) (((cmd) >> CMD_DATA_SIZE_SHIFT) & CMD_DATA_SIZE_MASK)
Neil Armstrong4dfe32d2016-10-19 14:51:08 +020067#define CMD_LEGACY_SIZE(cmd) (((cmd) >> CMD_LEGACY_DATA_SIZE_SHIFT) & \
68 CMD_LEGACY_DATA_SIZE_MASK)
Sudeep Holla8cb7cf52015-03-30 10:59:52 +010069#define CMD_UNIQ_MASK (CMD_TOKEN_ID_MASK << CMD_TOKEN_ID_SHIFT | CMD_ID_MASK)
70#define CMD_XTRACT_UNIQ(cmd) ((cmd) & CMD_UNIQ_MASK)
71
72#define SCPI_SLOT 0
73
74#define MAX_DVFS_DOMAINS 8
Neil Armstrongbb789cd2016-10-05 09:33:31 +020075#define MAX_DVFS_OPPS 16
Sudeep Holla8cb7cf52015-03-30 10:59:52 +010076
Heiner Kallweit4864dca2017-10-04 21:00:39 +020077#define PROTO_REV_MAJOR_MASK GENMASK(31, 16)
78#define PROTO_REV_MINOR_MASK GENMASK(15, 0)
Sudeep Holla8cb7cf52015-03-30 10:59:52 +010079
Heiner Kallweit4864dca2017-10-04 21:00:39 +020080#define FW_REV_MAJOR_MASK GENMASK(31, 24)
81#define FW_REV_MINOR_MASK GENMASK(23, 16)
82#define FW_REV_PATCH_MASK GENMASK(15, 0)
Sudeep Holla8cb7cf52015-03-30 10:59:52 +010083
Sudeep Holla3bdd8842016-01-15 11:57:38 +000084#define MAX_RX_TIMEOUT (msecs_to_jiffies(30))
Sudeep Holla8cb7cf52015-03-30 10:59:52 +010085
86enum scpi_error_codes {
87 SCPI_SUCCESS = 0, /* Success */
88 SCPI_ERR_PARAM = 1, /* Invalid parameter(s) */
89 SCPI_ERR_ALIGN = 2, /* Invalid alignment */
90 SCPI_ERR_SIZE = 3, /* Invalid size */
91 SCPI_ERR_HANDLER = 4, /* Invalid handler/callback */
92 SCPI_ERR_ACCESS = 5, /* Invalid access/permission denied */
93 SCPI_ERR_RANGE = 6, /* Value out of range */
94 SCPI_ERR_TIMEOUT = 7, /* Timeout has occurred */
95 SCPI_ERR_NOMEM = 8, /* Invalid memory area or pointer */
96 SCPI_ERR_PWRSTATE = 9, /* Invalid power state */
97 SCPI_ERR_SUPPORT = 10, /* Not supported or disabled */
98 SCPI_ERR_DEVICE = 11, /* Device error */
99 SCPI_ERR_BUSY = 12, /* Device busy */
100 SCPI_ERR_MAX
101};
102
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200103/* SCPI Standard commands */
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100104enum scpi_std_cmd {
105 SCPI_CMD_INVALID = 0x00,
106 SCPI_CMD_SCPI_READY = 0x01,
107 SCPI_CMD_SCPI_CAPABILITIES = 0x02,
108 SCPI_CMD_SET_CSS_PWR_STATE = 0x03,
109 SCPI_CMD_GET_CSS_PWR_STATE = 0x04,
110 SCPI_CMD_SET_SYS_PWR_STATE = 0x05,
111 SCPI_CMD_SET_CPU_TIMER = 0x06,
112 SCPI_CMD_CANCEL_CPU_TIMER = 0x07,
113 SCPI_CMD_DVFS_CAPABILITIES = 0x08,
114 SCPI_CMD_GET_DVFS_INFO = 0x09,
115 SCPI_CMD_SET_DVFS = 0x0a,
116 SCPI_CMD_GET_DVFS = 0x0b,
117 SCPI_CMD_GET_DVFS_STAT = 0x0c,
118 SCPI_CMD_CLOCK_CAPABILITIES = 0x0d,
119 SCPI_CMD_GET_CLOCK_INFO = 0x0e,
120 SCPI_CMD_SET_CLOCK_VALUE = 0x0f,
121 SCPI_CMD_GET_CLOCK_VALUE = 0x10,
122 SCPI_CMD_PSU_CAPABILITIES = 0x11,
123 SCPI_CMD_GET_PSU_INFO = 0x12,
124 SCPI_CMD_SET_PSU = 0x13,
125 SCPI_CMD_GET_PSU = 0x14,
126 SCPI_CMD_SENSOR_CAPABILITIES = 0x15,
127 SCPI_CMD_SENSOR_INFO = 0x16,
128 SCPI_CMD_SENSOR_VALUE = 0x17,
129 SCPI_CMD_SENSOR_CFG_PERIODIC = 0x18,
130 SCPI_CMD_SENSOR_CFG_BOUNDS = 0x19,
131 SCPI_CMD_SENSOR_ASYNC_VALUE = 0x1a,
132 SCPI_CMD_SET_DEVICE_PWR_STATE = 0x1b,
133 SCPI_CMD_GET_DEVICE_PWR_STATE = 0x1c,
134 SCPI_CMD_COUNT
135};
136
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200137/* SCPI Legacy Commands */
138enum legacy_scpi_std_cmd {
139 LEGACY_SCPI_CMD_INVALID = 0x00,
140 LEGACY_SCPI_CMD_SCPI_READY = 0x01,
141 LEGACY_SCPI_CMD_SCPI_CAPABILITIES = 0x02,
142 LEGACY_SCPI_CMD_EVENT = 0x03,
143 LEGACY_SCPI_CMD_SET_CSS_PWR_STATE = 0x04,
144 LEGACY_SCPI_CMD_GET_CSS_PWR_STATE = 0x05,
145 LEGACY_SCPI_CMD_CFG_PWR_STATE_STAT = 0x06,
146 LEGACY_SCPI_CMD_GET_PWR_STATE_STAT = 0x07,
147 LEGACY_SCPI_CMD_SYS_PWR_STATE = 0x08,
148 LEGACY_SCPI_CMD_L2_READY = 0x09,
149 LEGACY_SCPI_CMD_SET_AP_TIMER = 0x0a,
150 LEGACY_SCPI_CMD_CANCEL_AP_TIME = 0x0b,
151 LEGACY_SCPI_CMD_DVFS_CAPABILITIES = 0x0c,
152 LEGACY_SCPI_CMD_GET_DVFS_INFO = 0x0d,
153 LEGACY_SCPI_CMD_SET_DVFS = 0x0e,
154 LEGACY_SCPI_CMD_GET_DVFS = 0x0f,
155 LEGACY_SCPI_CMD_GET_DVFS_STAT = 0x10,
156 LEGACY_SCPI_CMD_SET_RTC = 0x11,
157 LEGACY_SCPI_CMD_GET_RTC = 0x12,
158 LEGACY_SCPI_CMD_CLOCK_CAPABILITIES = 0x13,
159 LEGACY_SCPI_CMD_SET_CLOCK_INDEX = 0x14,
160 LEGACY_SCPI_CMD_SET_CLOCK_VALUE = 0x15,
161 LEGACY_SCPI_CMD_GET_CLOCK_VALUE = 0x16,
162 LEGACY_SCPI_CMD_PSU_CAPABILITIES = 0x17,
163 LEGACY_SCPI_CMD_SET_PSU = 0x18,
164 LEGACY_SCPI_CMD_GET_PSU = 0x19,
165 LEGACY_SCPI_CMD_SENSOR_CAPABILITIES = 0x1a,
166 LEGACY_SCPI_CMD_SENSOR_INFO = 0x1b,
167 LEGACY_SCPI_CMD_SENSOR_VALUE = 0x1c,
168 LEGACY_SCPI_CMD_SENSOR_CFG_PERIODIC = 0x1d,
169 LEGACY_SCPI_CMD_SENSOR_CFG_BOUNDS = 0x1e,
170 LEGACY_SCPI_CMD_SENSOR_ASYNC_VALUE = 0x1f,
171 LEGACY_SCPI_CMD_COUNT
172};
173
174/* List all commands that are required to go through the high priority link */
175static int legacy_hpriority_cmds[] = {
176 LEGACY_SCPI_CMD_GET_CSS_PWR_STATE,
177 LEGACY_SCPI_CMD_CFG_PWR_STATE_STAT,
178 LEGACY_SCPI_CMD_GET_PWR_STATE_STAT,
179 LEGACY_SCPI_CMD_SET_DVFS,
180 LEGACY_SCPI_CMD_GET_DVFS,
181 LEGACY_SCPI_CMD_SET_RTC,
182 LEGACY_SCPI_CMD_GET_RTC,
183 LEGACY_SCPI_CMD_SET_CLOCK_INDEX,
184 LEGACY_SCPI_CMD_SET_CLOCK_VALUE,
185 LEGACY_SCPI_CMD_GET_CLOCK_VALUE,
186 LEGACY_SCPI_CMD_SET_PSU,
187 LEGACY_SCPI_CMD_GET_PSU,
188 LEGACY_SCPI_CMD_SENSOR_CFG_PERIODIC,
189 LEGACY_SCPI_CMD_SENSOR_CFG_BOUNDS,
190};
191
192/* List all commands used by this driver, used as indexes */
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200193enum scpi_drv_cmds {
194 CMD_SCPI_CAPABILITIES = 0,
195 CMD_GET_CLOCK_INFO,
196 CMD_GET_CLOCK_VALUE,
197 CMD_SET_CLOCK_VALUE,
198 CMD_GET_DVFS,
199 CMD_SET_DVFS,
200 CMD_GET_DVFS_INFO,
201 CMD_SENSOR_CAPABILITIES,
202 CMD_SENSOR_INFO,
203 CMD_SENSOR_VALUE,
204 CMD_SET_DEVICE_PWR_STATE,
205 CMD_GET_DEVICE_PWR_STATE,
206 CMD_MAX_COUNT,
207};
208
209static int scpi_std_commands[CMD_MAX_COUNT] = {
210 SCPI_CMD_SCPI_CAPABILITIES,
211 SCPI_CMD_GET_CLOCK_INFO,
212 SCPI_CMD_GET_CLOCK_VALUE,
213 SCPI_CMD_SET_CLOCK_VALUE,
214 SCPI_CMD_GET_DVFS,
215 SCPI_CMD_SET_DVFS,
216 SCPI_CMD_GET_DVFS_INFO,
217 SCPI_CMD_SENSOR_CAPABILITIES,
218 SCPI_CMD_SENSOR_INFO,
219 SCPI_CMD_SENSOR_VALUE,
220 SCPI_CMD_SET_DEVICE_PWR_STATE,
221 SCPI_CMD_GET_DEVICE_PWR_STATE,
222};
223
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200224static int scpi_legacy_commands[CMD_MAX_COUNT] = {
225 LEGACY_SCPI_CMD_SCPI_CAPABILITIES,
226 -1, /* GET_CLOCK_INFO */
227 LEGACY_SCPI_CMD_GET_CLOCK_VALUE,
228 LEGACY_SCPI_CMD_SET_CLOCK_VALUE,
229 LEGACY_SCPI_CMD_GET_DVFS,
230 LEGACY_SCPI_CMD_SET_DVFS,
231 LEGACY_SCPI_CMD_GET_DVFS_INFO,
232 LEGACY_SCPI_CMD_SENSOR_CAPABILITIES,
233 LEGACY_SCPI_CMD_SENSOR_INFO,
234 LEGACY_SCPI_CMD_SENSOR_VALUE,
235 -1, /* SET_DEVICE_PWR_STATE */
236 -1, /* GET_DEVICE_PWR_STATE */
237};
238
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100239struct scpi_xfer {
240 u32 slot; /* has to be first element */
241 u32 cmd;
242 u32 status;
243 const void *tx_buf;
244 void *rx_buf;
245 unsigned int tx_len;
246 unsigned int rx_len;
247 struct list_head node;
248 struct completion done;
249};
250
251struct scpi_chan {
252 struct mbox_client cl;
253 struct mbox_chan *chan;
254 void __iomem *tx_payload;
255 void __iomem *rx_payload;
256 struct list_head rx_pending;
257 struct list_head xfers_list;
258 struct scpi_xfer *xfers;
259 spinlock_t rx_lock; /* locking for the rx pending list */
260 struct mutex xfers_lock;
261 u8 token;
262};
263
264struct scpi_drvinfo {
265 u32 protocol_version;
266 u32 firmware_version;
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200267 bool is_legacy;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100268 int num_chans;
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200269 int *commands;
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200270 DECLARE_BITMAP(cmd_priority, LEGACY_SCPI_CMD_COUNT);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100271 atomic_t next_chan;
272 struct scpi_ops *scpi_ops;
273 struct scpi_chan *channels;
274 struct scpi_dvfs_info *dvfs[MAX_DVFS_DOMAINS];
275};
276
277/*
278 * The SCP firmware only executes in little-endian mode, so any buffers
279 * shared through SCPI should have their contents converted to little-endian
280 */
281struct scpi_shared_mem {
282 __le32 command;
283 __le32 status;
284 u8 payload[0];
285} __packed;
286
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200287struct legacy_scpi_shared_mem {
288 __le32 status;
289 u8 payload[0];
290} __packed;
291
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100292struct scp_capabilities {
293 __le32 protocol_version;
294 __le32 event_version;
295 __le32 platform_version;
296 __le32 commands[4];
297} __packed;
298
299struct clk_get_info {
300 __le16 id;
301 __le16 flags;
302 __le32 min_rate;
303 __le32 max_rate;
304 u8 name[20];
305} __packed;
306
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100307struct clk_set_value {
308 __le16 id;
309 __le16 reserved;
310 __le32 rate;
311} __packed;
312
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200313struct legacy_clk_set_value {
314 __le32 rate;
315 __le16 id;
316 __le16 reserved;
317} __packed;
318
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100319struct dvfs_info {
Heiner Kallweitd9e324e2017-10-04 20:56:44 +0200320 u8 domain;
321 u8 opp_count;
322 __le16 latency;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100323 struct {
324 __le32 freq;
325 __le32 m_volt;
326 } opps[MAX_DVFS_OPPS];
327} __packed;
328
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100329struct dvfs_set {
330 u8 domain;
331 u8 index;
332} __packed;
333
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100334struct sensor_capabilities {
335 __le16 sensors;
336} __packed;
337
338struct _scpi_sensor_info {
339 __le16 sensor_id;
340 u8 class;
341 u8 trigger_type;
342 char name[20];
343};
344
Sudeep Holla37a441d2016-04-20 14:05:14 +0100345struct dev_pstate_set {
Sudeep Holla0d301762017-08-18 15:39:28 +0100346 __le16 dev_id;
Sudeep Holla37a441d2016-04-20 14:05:14 +0100347 u8 pstate;
348} __packed;
349
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100350static struct scpi_drvinfo *scpi_info;
351
352static int scpi_linux_errmap[SCPI_ERR_MAX] = {
353 /* better than switch case as long as return value is continuous */
354 0, /* SCPI_SUCCESS */
355 -EINVAL, /* SCPI_ERR_PARAM */
356 -ENOEXEC, /* SCPI_ERR_ALIGN */
357 -EMSGSIZE, /* SCPI_ERR_SIZE */
358 -EINVAL, /* SCPI_ERR_HANDLER */
359 -EACCES, /* SCPI_ERR_ACCESS */
360 -ERANGE, /* SCPI_ERR_RANGE */
361 -ETIMEDOUT, /* SCPI_ERR_TIMEOUT */
362 -ENOMEM, /* SCPI_ERR_NOMEM */
363 -EINVAL, /* SCPI_ERR_PWRSTATE */
364 -EOPNOTSUPP, /* SCPI_ERR_SUPPORT */
365 -EIO, /* SCPI_ERR_DEVICE */
366 -EBUSY, /* SCPI_ERR_BUSY */
367};
368
369static inline int scpi_to_linux_errno(int errno)
370{
371 if (errno >= SCPI_SUCCESS && errno < SCPI_ERR_MAX)
372 return scpi_linux_errmap[errno];
373 return -EIO;
374}
375
376static void scpi_process_cmd(struct scpi_chan *ch, u32 cmd)
377{
378 unsigned long flags;
379 struct scpi_xfer *t, *match = NULL;
380
381 spin_lock_irqsave(&ch->rx_lock, flags);
382 if (list_empty(&ch->rx_pending)) {
383 spin_unlock_irqrestore(&ch->rx_lock, flags);
384 return;
385 }
386
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200387 /* Command type is not replied by the SCP Firmware in legacy Mode
388 * We should consider that command is the head of pending RX commands
389 * if the list is not empty. In TX only mode, the list would be empty.
390 */
391 if (scpi_info->is_legacy) {
392 match = list_first_entry(&ch->rx_pending, struct scpi_xfer,
393 node);
394 list_del(&match->node);
395 } else {
396 list_for_each_entry(t, &ch->rx_pending, node)
397 if (CMD_XTRACT_UNIQ(t->cmd) == CMD_XTRACT_UNIQ(cmd)) {
398 list_del(&t->node);
399 match = t;
400 break;
401 }
402 }
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100403 /* check if wait_for_completion is in progress or timed-out */
404 if (match && !completion_done(&match->done)) {
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200405 unsigned int len;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100406
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200407 if (scpi_info->is_legacy) {
408 struct legacy_scpi_shared_mem *mem = ch->rx_payload;
409
410 /* RX Length is not replied by the legacy Firmware */
411 len = match->rx_len;
412
413 match->status = le32_to_cpu(mem->status);
414 memcpy_fromio(match->rx_buf, mem->payload, len);
415 } else {
416 struct scpi_shared_mem *mem = ch->rx_payload;
417
418 len = min(match->rx_len, CMD_SIZE(cmd));
419
420 match->status = le32_to_cpu(mem->status);
421 memcpy_fromio(match->rx_buf, mem->payload, len);
422 }
423
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100424 if (match->rx_len > len)
425 memset(match->rx_buf + len, 0, match->rx_len - len);
426 complete(&match->done);
427 }
428 spin_unlock_irqrestore(&ch->rx_lock, flags);
429}
430
431static void scpi_handle_remote_msg(struct mbox_client *c, void *msg)
432{
433 struct scpi_chan *ch = container_of(c, struct scpi_chan, cl);
434 struct scpi_shared_mem *mem = ch->rx_payload;
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200435 u32 cmd = 0;
436
437 if (!scpi_info->is_legacy)
438 cmd = le32_to_cpu(mem->command);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100439
440 scpi_process_cmd(ch, cmd);
441}
442
443static void scpi_tx_prepare(struct mbox_client *c, void *msg)
444{
445 unsigned long flags;
446 struct scpi_xfer *t = msg;
447 struct scpi_chan *ch = container_of(c, struct scpi_chan, cl);
Sudeep Holla1b366332017-10-05 11:31:36 +0100448 struct scpi_shared_mem *mem = ch->tx_payload;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100449
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200450 if (t->tx_buf) {
451 if (scpi_info->is_legacy)
452 memcpy_toio(ch->tx_payload, t->tx_buf, t->tx_len);
453 else
454 memcpy_toio(mem->payload, t->tx_buf, t->tx_len);
455 }
456
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100457 if (t->rx_buf) {
458 if (!(++ch->token))
459 ++ch->token;
460 ADD_SCPI_TOKEN(t->cmd, ch->token);
461 spin_lock_irqsave(&ch->rx_lock, flags);
462 list_add_tail(&t->node, &ch->rx_pending);
463 spin_unlock_irqrestore(&ch->rx_lock, flags);
464 }
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200465
466 if (!scpi_info->is_legacy)
467 mem->command = cpu_to_le32(t->cmd);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100468}
469
470static struct scpi_xfer *get_scpi_xfer(struct scpi_chan *ch)
471{
472 struct scpi_xfer *t;
473
474 mutex_lock(&ch->xfers_lock);
475 if (list_empty(&ch->xfers_list)) {
476 mutex_unlock(&ch->xfers_lock);
477 return NULL;
478 }
479 t = list_first_entry(&ch->xfers_list, struct scpi_xfer, node);
480 list_del(&t->node);
481 mutex_unlock(&ch->xfers_lock);
482 return t;
483}
484
485static void put_scpi_xfer(struct scpi_xfer *t, struct scpi_chan *ch)
486{
487 mutex_lock(&ch->xfers_lock);
488 list_add_tail(&t->node, &ch->xfers_list);
489 mutex_unlock(&ch->xfers_lock);
490}
491
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200492static int scpi_send_message(u8 idx, void *tx_buf, unsigned int tx_len,
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100493 void *rx_buf, unsigned int rx_len)
494{
495 int ret;
496 u8 chan;
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200497 u8 cmd;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100498 struct scpi_xfer *msg;
499 struct scpi_chan *scpi_chan;
500
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200501 if (scpi_info->commands[idx] < 0)
502 return -EOPNOTSUPP;
503
504 cmd = scpi_info->commands[idx];
505
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200506 if (scpi_info->is_legacy)
507 chan = test_bit(cmd, scpi_info->cmd_priority) ? 1 : 0;
508 else
509 chan = atomic_inc_return(&scpi_info->next_chan) %
510 scpi_info->num_chans;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100511 scpi_chan = scpi_info->channels + chan;
512
513 msg = get_scpi_xfer(scpi_chan);
514 if (!msg)
515 return -ENOMEM;
516
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200517 if (scpi_info->is_legacy) {
518 msg->cmd = PACK_LEGACY_SCPI_CMD(cmd, tx_len);
519 msg->slot = msg->cmd;
520 } else {
521 msg->slot = BIT(SCPI_SLOT);
522 msg->cmd = PACK_SCPI_CMD(cmd, tx_len);
523 }
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100524 msg->tx_buf = tx_buf;
525 msg->tx_len = tx_len;
526 msg->rx_buf = rx_buf;
527 msg->rx_len = rx_len;
Alexey Klimovc511fa3f2017-03-29 19:16:27 +0100528 reinit_completion(&msg->done);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100529
530 ret = mbox_send_message(scpi_chan->chan, msg);
531 if (ret < 0 || !rx_buf)
532 goto out;
533
534 if (!wait_for_completion_timeout(&msg->done, MAX_RX_TIMEOUT))
535 ret = -ETIMEDOUT;
536 else
537 /* first status word */
Sudeep Holladd9a1d62016-01-29 13:35:44 +0000538 ret = msg->status;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100539out:
540 if (ret < 0 && rx_buf) /* remove entry from the list if timed-out */
541 scpi_process_cmd(scpi_chan, msg->cmd);
542
543 put_scpi_xfer(msg, scpi_chan);
544 /* SCPI error codes > 0, translate them to Linux scale*/
545 return ret > 0 ? scpi_to_linux_errno(ret) : ret;
546}
547
548static u32 scpi_get_version(void)
549{
550 return scpi_info->protocol_version;
551}
552
553static int
554scpi_clk_get_range(u16 clk_id, unsigned long *min, unsigned long *max)
555{
556 int ret;
557 struct clk_get_info clk;
558 __le16 le_clk_id = cpu_to_le16(clk_id);
559
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200560 ret = scpi_send_message(CMD_GET_CLOCK_INFO, &le_clk_id,
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100561 sizeof(le_clk_id), &clk, sizeof(clk));
562 if (!ret) {
563 *min = le32_to_cpu(clk.min_rate);
564 *max = le32_to_cpu(clk.max_rate);
565 }
566 return ret;
567}
568
569static unsigned long scpi_clk_get_val(u16 clk_id)
570{
571 int ret;
Sudeep Holla48bee742017-10-05 11:40:49 +0100572 __le32 rate;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100573 __le16 le_clk_id = cpu_to_le16(clk_id);
574
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200575 ret = scpi_send_message(CMD_GET_CLOCK_VALUE, &le_clk_id,
Sudeep Holla48bee742017-10-05 11:40:49 +0100576 sizeof(le_clk_id), &rate, sizeof(rate));
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200577
Sudeep Holla48bee742017-10-05 11:40:49 +0100578 return ret ? ret : le32_to_cpu(rate);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100579}
580
581static int scpi_clk_set_val(u16 clk_id, unsigned long rate)
582{
583 int stat;
584 struct clk_set_value clk = {
585 .id = cpu_to_le16(clk_id),
586 .rate = cpu_to_le32(rate)
587 };
588
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200589 return scpi_send_message(CMD_SET_CLOCK_VALUE, &clk, sizeof(clk),
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100590 &stat, sizeof(stat));
591}
592
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200593static int legacy_scpi_clk_set_val(u16 clk_id, unsigned long rate)
594{
595 int stat;
596 struct legacy_clk_set_value clk = {
597 .id = cpu_to_le16(clk_id),
598 .rate = cpu_to_le32(rate)
599 };
600
601 return scpi_send_message(CMD_SET_CLOCK_VALUE, &clk, sizeof(clk),
602 &stat, sizeof(stat));
603}
604
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100605static int scpi_dvfs_get_idx(u8 domain)
606{
607 int ret;
Sudeep Hollaf9d91de2016-04-20 13:25:01 +0100608 u8 dvfs_idx;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100609
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200610 ret = scpi_send_message(CMD_GET_DVFS, &domain, sizeof(domain),
Sudeep Hollaf9d91de2016-04-20 13:25:01 +0100611 &dvfs_idx, sizeof(dvfs_idx));
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200612
Sudeep Hollaf9d91de2016-04-20 13:25:01 +0100613 return ret ? ret : dvfs_idx;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100614}
615
616static int scpi_dvfs_set_idx(u8 domain, u8 index)
617{
618 int stat;
619 struct dvfs_set dvfs = {domain, index};
620
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200621 return scpi_send_message(CMD_SET_DVFS, &dvfs, sizeof(dvfs),
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100622 &stat, sizeof(stat));
623}
624
625static int opp_cmp_func(const void *opp1, const void *opp2)
626{
627 const struct scpi_opp *t1 = opp1, *t2 = opp2;
628
629 return t1->freq - t2->freq;
630}
631
632static struct scpi_dvfs_info *scpi_dvfs_get_info(u8 domain)
633{
Heiner Kallweit931cf0c2017-09-29 23:44:05 +0200634 if (domain >= MAX_DVFS_DOMAINS)
635 return ERR_PTR(-EINVAL);
636
637 return scpi_info->dvfs[domain] ?: ERR_PTR(-EINVAL);
638}
639
640static int scpi_dvfs_populate_info(struct device *dev, u8 domain)
641{
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100642 struct scpi_dvfs_info *info;
643 struct scpi_opp *opp;
644 struct dvfs_info buf;
645 int ret, i;
646
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200647 ret = scpi_send_message(CMD_GET_DVFS_INFO, &domain, sizeof(domain),
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100648 &buf, sizeof(buf));
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100649 if (ret)
Heiner Kallweit931cf0c2017-09-29 23:44:05 +0200650 return ret;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100651
Heiner Kallweit931cf0c2017-09-29 23:44:05 +0200652 info = devm_kmalloc(dev, sizeof(*info), GFP_KERNEL);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100653 if (!info)
Heiner Kallweit931cf0c2017-09-29 23:44:05 +0200654 return -ENOMEM;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100655
Heiner Kallweitd9e324e2017-10-04 20:56:44 +0200656 info->count = buf.opp_count;
657 info->latency = le16_to_cpu(buf.latency) * 1000; /* uS to nS */
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100658
Heiner Kallweit931cf0c2017-09-29 23:44:05 +0200659 info->opps = devm_kcalloc(dev, info->count, sizeof(*opp), GFP_KERNEL);
660 if (!info->opps)
661 return -ENOMEM;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100662
663 for (i = 0, opp = info->opps; i < info->count; i++, opp++) {
664 opp->freq = le32_to_cpu(buf.opps[i].freq);
665 opp->m_volt = le32_to_cpu(buf.opps[i].m_volt);
666 }
667
668 sort(info->opps, info->count, sizeof(*opp), opp_cmp_func, NULL);
669
670 scpi_info->dvfs[domain] = info;
Heiner Kallweit931cf0c2017-09-29 23:44:05 +0200671 return 0;
672}
673
674static void scpi_dvfs_populate(struct device *dev)
675{
676 int domain;
677
678 for (domain = 0; domain < MAX_DVFS_DOMAINS; domain++)
679 scpi_dvfs_populate_info(dev, domain);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100680}
681
Sudeep Holla45ca7df2017-04-27 15:08:51 +0100682static int scpi_dev_domain_id(struct device *dev)
683{
684 struct of_phandle_args clkspec;
685
686 if (of_parse_phandle_with_args(dev->of_node, "clocks", "#clock-cells",
687 0, &clkspec))
688 return -EINVAL;
689
690 return clkspec.args[0];
691}
692
693static struct scpi_dvfs_info *scpi_dvfs_info(struct device *dev)
694{
695 int domain = scpi_dev_domain_id(dev);
696
697 if (domain < 0)
698 return ERR_PTR(domain);
699
700 return scpi_dvfs_get_info(domain);
701}
702
703static int scpi_dvfs_get_transition_latency(struct device *dev)
704{
705 struct scpi_dvfs_info *info = scpi_dvfs_info(dev);
706
707 if (IS_ERR(info))
708 return PTR_ERR(info);
709
Sudeep Holla45ca7df2017-04-27 15:08:51 +0100710 return info->latency;
711}
712
713static int scpi_dvfs_add_opps_to_device(struct device *dev)
714{
715 int idx, ret;
716 struct scpi_opp *opp;
717 struct scpi_dvfs_info *info = scpi_dvfs_info(dev);
718
719 if (IS_ERR(info))
720 return PTR_ERR(info);
721
722 if (!info->opps)
723 return -EIO;
724
725 for (opp = info->opps, idx = 0; idx < info->count; idx++, opp++) {
726 ret = dev_pm_opp_add(dev, opp->freq, opp->m_volt * 1000);
727 if (ret) {
728 dev_warn(dev, "failed to add opp %uHz %umV\n",
729 opp->freq, opp->m_volt);
730 while (idx-- > 0)
731 dev_pm_opp_remove(dev, (--opp)->freq);
732 return ret;
733 }
734 }
735 return 0;
736}
737
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100738static int scpi_sensor_get_capability(u16 *sensors)
739{
740 struct sensor_capabilities cap_buf;
741 int ret;
742
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200743 ret = scpi_send_message(CMD_SENSOR_CAPABILITIES, NULL, 0, &cap_buf,
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100744 sizeof(cap_buf));
745 if (!ret)
746 *sensors = le16_to_cpu(cap_buf.sensors);
747
748 return ret;
749}
750
751static int scpi_sensor_get_info(u16 sensor_id, struct scpi_sensor_info *info)
752{
753 __le16 id = cpu_to_le16(sensor_id);
754 struct _scpi_sensor_info _info;
755 int ret;
756
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200757 ret = scpi_send_message(CMD_SENSOR_INFO, &id, sizeof(id),
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100758 &_info, sizeof(_info));
759 if (!ret) {
760 memcpy(info, &_info, sizeof(*info));
761 info->sensor_id = le16_to_cpu(_info.sensor_id);
762 }
763
764 return ret;
765}
766
Sudeep Holla3678b982016-02-23 16:21:16 +0000767static int scpi_sensor_get_value(u16 sensor, u64 *val)
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100768{
Sudeep Holladd9a1d62016-01-29 13:35:44 +0000769 __le16 id = cpu_to_le16(sensor);
Sudeep Holla48bee742017-10-05 11:40:49 +0100770 __le64 value;
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100771 int ret;
772
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200773 ret = scpi_send_message(CMD_SENSOR_VALUE, &id, sizeof(id),
Sudeep Holla48bee742017-10-05 11:40:49 +0100774 &value, sizeof(value));
Martin Blumenstingla7663472016-12-11 22:14:32 +0100775 if (ret)
776 return ret;
777
778 if (scpi_info->is_legacy)
Heiner Kallweit5c7ae642017-10-04 21:05:17 +0200779 /* only 32-bits supported, upper 32 bits can be junk */
Sudeep Holla48bee742017-10-05 11:40:49 +0100780 *val = le32_to_cpup((__le32 *)&value);
Martin Blumenstingla7663472016-12-11 22:14:32 +0100781 else
Sudeep Holla48bee742017-10-05 11:40:49 +0100782 *val = le64_to_cpu(value);
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100783
Martin Blumenstingla7663472016-12-11 22:14:32 +0100784 return 0;
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100785}
786
Sudeep Holla37a441d2016-04-20 14:05:14 +0100787static int scpi_device_get_power_state(u16 dev_id)
788{
789 int ret;
790 u8 pstate;
791 __le16 id = cpu_to_le16(dev_id);
792
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200793 ret = scpi_send_message(CMD_GET_DEVICE_PWR_STATE, &id,
Sudeep Holla37a441d2016-04-20 14:05:14 +0100794 sizeof(id), &pstate, sizeof(pstate));
795 return ret ? ret : pstate;
796}
797
798static int scpi_device_set_power_state(u16 dev_id, u8 pstate)
799{
800 int stat;
801 struct dev_pstate_set dev_set = {
802 .dev_id = cpu_to_le16(dev_id),
803 .pstate = pstate,
804 };
805
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200806 return scpi_send_message(CMD_SET_DEVICE_PWR_STATE, &dev_set,
Sudeep Holla37a441d2016-04-20 14:05:14 +0100807 sizeof(dev_set), &stat, sizeof(stat));
808}
809
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100810static struct scpi_ops scpi_ops = {
811 .get_version = scpi_get_version,
812 .clk_get_range = scpi_clk_get_range,
813 .clk_get_val = scpi_clk_get_val,
814 .clk_set_val = scpi_clk_set_val,
815 .dvfs_get_idx = scpi_dvfs_get_idx,
816 .dvfs_set_idx = scpi_dvfs_set_idx,
817 .dvfs_get_info = scpi_dvfs_get_info,
Sudeep Holla45ca7df2017-04-27 15:08:51 +0100818 .device_domain_id = scpi_dev_domain_id,
819 .get_transition_latency = scpi_dvfs_get_transition_latency,
820 .add_opps_to_device = scpi_dvfs_add_opps_to_device,
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100821 .sensor_get_capability = scpi_sensor_get_capability,
822 .sensor_get_info = scpi_sensor_get_info,
823 .sensor_get_value = scpi_sensor_get_value,
Sudeep Holla37a441d2016-04-20 14:05:14 +0100824 .device_get_power_state = scpi_device_get_power_state,
825 .device_set_power_state = scpi_device_set_power_state,
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100826};
827
828struct scpi_ops *get_scpi_ops(void)
829{
830 return scpi_info ? scpi_info->scpi_ops : NULL;
831}
832EXPORT_SYMBOL_GPL(get_scpi_ops);
833
834static int scpi_init_versions(struct scpi_drvinfo *info)
835{
836 int ret;
837 struct scp_capabilities caps;
838
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200839 ret = scpi_send_message(CMD_SCPI_CAPABILITIES, NULL, 0,
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100840 &caps, sizeof(caps));
841 if (!ret) {
842 info->protocol_version = le32_to_cpu(caps.protocol_version);
843 info->firmware_version = le32_to_cpu(caps.platform_version);
844 }
Neil Armstrongabd3e802016-10-19 14:51:09 +0200845 /* Ignore error if not implemented */
846 if (scpi_info->is_legacy && ret == -EOPNOTSUPP)
847 return 0;
848
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100849 return ret;
850}
851
852static ssize_t protocol_version_show(struct device *dev,
853 struct device_attribute *attr, char *buf)
854{
Heiner Kallweit4864dca2017-10-04 21:00:39 +0200855 return sprintf(buf, "%lu.%lu\n",
856 FIELD_GET(PROTO_REV_MAJOR_MASK, scpi_info->protocol_version),
857 FIELD_GET(PROTO_REV_MINOR_MASK, scpi_info->protocol_version));
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100858}
859static DEVICE_ATTR_RO(protocol_version);
860
861static ssize_t firmware_version_show(struct device *dev,
862 struct device_attribute *attr, char *buf)
863{
Heiner Kallweit4864dca2017-10-04 21:00:39 +0200864 return sprintf(buf, "%lu.%lu.%lu\n",
865 FIELD_GET(FW_REV_MAJOR_MASK, scpi_info->firmware_version),
866 FIELD_GET(FW_REV_MINOR_MASK, scpi_info->firmware_version),
867 FIELD_GET(FW_REV_PATCH_MASK, scpi_info->firmware_version));
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100868}
869static DEVICE_ATTR_RO(firmware_version);
870
871static struct attribute *versions_attrs[] = {
872 &dev_attr_firmware_version.attr,
873 &dev_attr_protocol_version.attr,
874 NULL,
875};
876ATTRIBUTE_GROUPS(versions);
877
Heiner Kallweit27c54cd2017-09-29 23:44:09 +0200878static void scpi_free_channels(void *data)
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100879{
Heiner Kallweit27c54cd2017-09-29 23:44:09 +0200880 struct scpi_drvinfo *info = data;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100881 int i;
882
Heiner Kallweit27c54cd2017-09-29 23:44:09 +0200883 for (i = 0; i < info->num_chans; i++)
884 mbox_free_channel(info->channels[i].chan);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100885}
886
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100887#define MAX_SCPI_XFERS 10
888static int scpi_alloc_xfer_list(struct device *dev, struct scpi_chan *ch)
889{
890 int i;
891 struct scpi_xfer *xfers;
892
893 xfers = devm_kzalloc(dev, MAX_SCPI_XFERS * sizeof(*xfers), GFP_KERNEL);
894 if (!xfers)
895 return -ENOMEM;
896
897 ch->xfers = xfers;
Alexey Klimovc511fa3f2017-03-29 19:16:27 +0100898 for (i = 0; i < MAX_SCPI_XFERS; i++, xfers++) {
899 init_completion(&xfers->done);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100900 list_add_tail(&xfers->node, &ch->xfers_list);
Alexey Klimovc511fa3f2017-03-29 19:16:27 +0100901 }
902
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100903 return 0;
904}
905
Sudeep Holla8358c6b2016-10-19 14:51:10 +0200906static const struct of_device_id legacy_scpi_of_match[] = {
907 {.compatible = "arm,scpi-pre-1.0"},
908 {},
909};
910
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100911static int scpi_probe(struct platform_device *pdev)
912{
913 int count, idx, ret;
914 struct resource res;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100915 struct device *dev = &pdev->dev;
916 struct device_node *np = dev->of_node;
917
918 scpi_info = devm_kzalloc(dev, sizeof(*scpi_info), GFP_KERNEL);
919 if (!scpi_info)
920 return -ENOMEM;
921
Sudeep Holla8358c6b2016-10-19 14:51:10 +0200922 if (of_match_device(legacy_scpi_of_match, &pdev->dev))
923 scpi_info->is_legacy = true;
924
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100925 count = of_count_phandle_with_args(np, "mboxes", "#mbox-cells");
926 if (count < 0) {
Rob Herring9deee312017-07-18 16:43:01 -0500927 dev_err(dev, "no mboxes property in '%pOF'\n", np);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100928 return -ENODEV;
929 }
930
Heiner Kallweit27c54cd2017-09-29 23:44:09 +0200931 scpi_info->channels = devm_kcalloc(dev, count, sizeof(struct scpi_chan),
932 GFP_KERNEL);
933 if (!scpi_info->channels)
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100934 return -ENOMEM;
935
Heiner Kallweit27c54cd2017-09-29 23:44:09 +0200936 ret = devm_add_action(dev, scpi_free_channels, scpi_info);
937 if (ret)
938 return ret;
939
940 for (; scpi_info->num_chans < count; scpi_info->num_chans++) {
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100941 resource_size_t size;
Heiner Kallweit27c54cd2017-09-29 23:44:09 +0200942 int idx = scpi_info->num_chans;
943 struct scpi_chan *pchan = scpi_info->channels + idx;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100944 struct mbox_client *cl = &pchan->cl;
945 struct device_node *shmem = of_parse_phandle(np, "shmem", idx);
946
Peter Chenb079bd52016-07-04 14:55:57 +0800947 ret = of_address_to_resource(shmem, 0, &res);
948 of_node_put(shmem);
949 if (ret) {
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100950 dev_err(dev, "failed to get SCPI payload mem resource\n");
Heiner Kallweit27c54cd2017-09-29 23:44:09 +0200951 return ret;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100952 }
953
954 size = resource_size(&res);
955 pchan->rx_payload = devm_ioremap(dev, res.start, size);
956 if (!pchan->rx_payload) {
957 dev_err(dev, "failed to ioremap SCPI payload\n");
Heiner Kallweit27c54cd2017-09-29 23:44:09 +0200958 return -EADDRNOTAVAIL;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100959 }
960 pchan->tx_payload = pchan->rx_payload + (size >> 1);
961
962 cl->dev = dev;
963 cl->rx_callback = scpi_handle_remote_msg;
964 cl->tx_prepare = scpi_tx_prepare;
965 cl->tx_block = true;
Sudeep Holla3bdd8842016-01-15 11:57:38 +0000966 cl->tx_tout = 20;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100967 cl->knows_txdone = false; /* controller can't ack */
968
969 INIT_LIST_HEAD(&pchan->rx_pending);
970 INIT_LIST_HEAD(&pchan->xfers_list);
971 spin_lock_init(&pchan->rx_lock);
972 mutex_init(&pchan->xfers_lock);
973
974 ret = scpi_alloc_xfer_list(dev, pchan);
975 if (!ret) {
976 pchan->chan = mbox_request_channel(cl, idx);
977 if (!IS_ERR(pchan->chan))
978 continue;
979 ret = PTR_ERR(pchan->chan);
980 if (ret != -EPROBE_DEFER)
981 dev_err(dev, "failed to get channel%d err %d\n",
982 idx, ret);
983 }
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100984 return ret;
985 }
986
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200987 scpi_info->commands = scpi_std_commands;
Heiner Kallweit931cf0c2017-09-29 23:44:05 +0200988 scpi_info->scpi_ops = &scpi_ops;
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200989
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200990 if (scpi_info->is_legacy) {
991 /* Replace with legacy variants */
992 scpi_ops.clk_set_val = legacy_scpi_clk_set_val;
993 scpi_info->commands = scpi_legacy_commands;
994
995 /* Fill priority bitmap */
996 for (idx = 0; idx < ARRAY_SIZE(legacy_hpriority_cmds); idx++)
997 set_bit(legacy_hpriority_cmds[idx],
998 scpi_info->cmd_priority);
999 }
1000
Sudeep Holla8cb7cf52015-03-30 10:59:52 +01001001 ret = scpi_init_versions(scpi_info);
1002 if (ret) {
1003 dev_err(dev, "incorrect or no SCP firmware found\n");
Sudeep Holla8cb7cf52015-03-30 10:59:52 +01001004 return ret;
1005 }
1006
Heiner Kallweit931cf0c2017-09-29 23:44:05 +02001007 scpi_dvfs_populate(dev);
1008
Heiner Kallweit4864dca2017-10-04 21:00:39 +02001009 _dev_info(dev, "SCP Protocol %lu.%lu Firmware %lu.%lu.%lu version\n",
1010 FIELD_GET(PROTO_REV_MAJOR_MASK, scpi_info->protocol_version),
1011 FIELD_GET(PROTO_REV_MINOR_MASK, scpi_info->protocol_version),
1012 FIELD_GET(FW_REV_MAJOR_MASK, scpi_info->firmware_version),
1013 FIELD_GET(FW_REV_MINOR_MASK, scpi_info->firmware_version),
1014 FIELD_GET(FW_REV_PATCH_MASK, scpi_info->firmware_version));
Sudeep Holla8cb7cf52015-03-30 10:59:52 +01001015
Heiner Kallweit95998c72017-09-29 23:44:15 +02001016 ret = devm_device_add_groups(dev, versions_groups);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +01001017 if (ret)
1018 dev_err(dev, "unable to create sysfs version group\n");
1019
Heiner Kallweit95998c72017-09-29 23:44:15 +02001020 return devm_of_platform_populate(dev);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +01001021}
1022
1023static const struct of_device_id scpi_of_match[] = {
1024 {.compatible = "arm,scpi"},
Sudeep Holla8358c6b2016-10-19 14:51:10 +02001025 {.compatible = "arm,scpi-pre-1.0"},
Sudeep Holla8cb7cf52015-03-30 10:59:52 +01001026 {},
1027};
1028
1029MODULE_DEVICE_TABLE(of, scpi_of_match);
1030
1031static struct platform_driver scpi_driver = {
1032 .driver = {
1033 .name = "scpi_protocol",
1034 .of_match_table = scpi_of_match,
1035 },
1036 .probe = scpi_probe,
Sudeep Holla8cb7cf52015-03-30 10:59:52 +01001037};
1038module_platform_driver(scpi_driver);
1039
1040MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
1041MODULE_DESCRIPTION("ARM SCPI mailbox protocol driver");
1042MODULE_LICENSE("GPL v2");