blob: 3a722e5a6666e8d034f1a5b5d9050b55e781ddfa [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 Kallweit7cd49a22017-12-05 23:16:55 +010031#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 Kallweit7cd49a22017-12-05 23:16:55 +010077#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 Kallweit7cd49a22017-12-05 23:16:55 +010080#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 Kallweita963d7c2017-12-05 23:16:52 +0100320 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) {
Olof Johansson81faa552017-12-03 19:28:33 -0800408 struct legacy_scpi_shared_mem *mem = ch->rx_payload;
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200409
410 /* RX Length is not replied by the legacy Firmware */
411 len = match->rx_len;
412
Olof Johansson81faa552017-12-03 19:28:33 -0800413 match->status = le32_to_cpu(mem->status);
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200414 memcpy_fromio(match->rx_buf, mem->payload, len);
415 } else {
Olof Johansson81faa552017-12-03 19:28:33 -0800416 struct scpi_shared_mem *mem = ch->rx_payload;
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200417
418 len = min(match->rx_len, CMD_SIZE(cmd));
419
Olof Johansson81faa552017-12-03 19:28:33 -0800420 match->status = le32_to_cpu(mem->status);
Neil Armstrong4dfe32d2016-10-19 14:51:08 +0200421 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);
Olof Johansson81faa552017-12-03 19:28:33 -0800434 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)
Olof Johansson81faa552017-12-03 19:28:33 -0800438 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 Holla27901cc2017-12-05 23:17:03 +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)
Olof Johansson81faa552017-12-03 19:28:33 -0800467 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 Hollac10bd412017-12-05 23:17:09 +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 Hollac10bd412017-12-05 23:17:09 +0100576 sizeof(le_clk_id), &rate, sizeof(rate));
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200577
Sudeep Hollac10bd412017-12-05 23:17:09 +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{
634 struct scpi_dvfs_info *info;
635 struct scpi_opp *opp;
636 struct dvfs_info buf;
637 int ret, i;
638
Olof Johansson81faa552017-12-03 19:28:33 -0800639 if (domain >= MAX_DVFS_DOMAINS)
640 return ERR_PTR(-EINVAL);
641
642 if (scpi_info->dvfs[domain]) /* data already populated */
643 return scpi_info->dvfs[domain];
644
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200645 ret = scpi_send_message(CMD_GET_DVFS_INFO, &domain, sizeof(domain),
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100646 &buf, sizeof(buf));
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100647 if (ret)
Olof Johansson81faa552017-12-03 19:28:33 -0800648 return ERR_PTR(ret);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100649
Olof Johansson81faa552017-12-03 19:28:33 -0800650 info = kmalloc(sizeof(*info), GFP_KERNEL);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100651 if (!info)
Olof Johansson81faa552017-12-03 19:28:33 -0800652 return ERR_PTR(-ENOMEM);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100653
Heiner Kallweita963d7c2017-12-05 23:16:52 +0100654 info->count = buf.opp_count;
655 info->latency = le16_to_cpu(buf.latency) * 1000; /* uS to nS */
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100656
Olof Johansson81faa552017-12-03 19:28:33 -0800657 info->opps = kcalloc(info->count, sizeof(*opp), GFP_KERNEL);
658 if (!info->opps) {
659 kfree(info);
660 return ERR_PTR(-ENOMEM);
661 }
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;
Olof Johansson81faa552017-12-03 19:28:33 -0800671 return info;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100672}
673
Sudeep Holla45ca7df2017-04-27 15:08:51 +0100674static int scpi_dev_domain_id(struct device *dev)
675{
676 struct of_phandle_args clkspec;
677
678 if (of_parse_phandle_with_args(dev->of_node, "clocks", "#clock-cells",
679 0, &clkspec))
680 return -EINVAL;
681
682 return clkspec.args[0];
683}
684
685static struct scpi_dvfs_info *scpi_dvfs_info(struct device *dev)
686{
687 int domain = scpi_dev_domain_id(dev);
688
689 if (domain < 0)
690 return ERR_PTR(domain);
691
692 return scpi_dvfs_get_info(domain);
693}
694
695static int scpi_dvfs_get_transition_latency(struct device *dev)
696{
697 struct scpi_dvfs_info *info = scpi_dvfs_info(dev);
698
699 if (IS_ERR(info))
700 return PTR_ERR(info);
701
Sudeep Holla45ca7df2017-04-27 15:08:51 +0100702 return info->latency;
703}
704
705static int scpi_dvfs_add_opps_to_device(struct device *dev)
706{
707 int idx, ret;
708 struct scpi_opp *opp;
709 struct scpi_dvfs_info *info = scpi_dvfs_info(dev);
710
711 if (IS_ERR(info))
712 return PTR_ERR(info);
713
714 if (!info->opps)
715 return -EIO;
716
717 for (opp = info->opps, idx = 0; idx < info->count; idx++, opp++) {
718 ret = dev_pm_opp_add(dev, opp->freq, opp->m_volt * 1000);
719 if (ret) {
720 dev_warn(dev, "failed to add opp %uHz %umV\n",
721 opp->freq, opp->m_volt);
722 while (idx-- > 0)
723 dev_pm_opp_remove(dev, (--opp)->freq);
724 return ret;
725 }
726 }
727 return 0;
728}
729
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100730static int scpi_sensor_get_capability(u16 *sensors)
731{
732 struct sensor_capabilities cap_buf;
733 int ret;
734
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200735 ret = scpi_send_message(CMD_SENSOR_CAPABILITIES, NULL, 0, &cap_buf,
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100736 sizeof(cap_buf));
737 if (!ret)
738 *sensors = le16_to_cpu(cap_buf.sensors);
739
740 return ret;
741}
742
743static int scpi_sensor_get_info(u16 sensor_id, struct scpi_sensor_info *info)
744{
745 __le16 id = cpu_to_le16(sensor_id);
746 struct _scpi_sensor_info _info;
747 int ret;
748
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200749 ret = scpi_send_message(CMD_SENSOR_INFO, &id, sizeof(id),
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100750 &_info, sizeof(_info));
751 if (!ret) {
752 memcpy(info, &_info, sizeof(*info));
753 info->sensor_id = le16_to_cpu(_info.sensor_id);
754 }
755
756 return ret;
757}
758
Sudeep Holla3678b982016-02-23 16:21:16 +0000759static int scpi_sensor_get_value(u16 sensor, u64 *val)
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100760{
Sudeep Holladd9a1d62016-01-29 13:35:44 +0000761 __le16 id = cpu_to_le16(sensor);
Sudeep Hollac10bd412017-12-05 23:17:09 +0100762 __le64 value;
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100763 int ret;
764
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200765 ret = scpi_send_message(CMD_SENSOR_VALUE, &id, sizeof(id),
Sudeep Hollac10bd412017-12-05 23:17:09 +0100766 &value, sizeof(value));
Martin Blumenstingla7663472016-12-11 22:14:32 +0100767 if (ret)
768 return ret;
769
770 if (scpi_info->is_legacy)
Heiner Kallweit83a60602017-12-05 23:16:58 +0100771 /* only 32-bits supported, upper 32 bits can be junk */
Sudeep Hollac10bd412017-12-05 23:17:09 +0100772 *val = le32_to_cpup((__le32 *)&value);
Martin Blumenstingla7663472016-12-11 22:14:32 +0100773 else
Sudeep Hollac10bd412017-12-05 23:17:09 +0100774 *val = le64_to_cpu(value);
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100775
Martin Blumenstingla7663472016-12-11 22:14:32 +0100776 return 0;
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100777}
778
Sudeep Holla37a441d2016-04-20 14:05:14 +0100779static int scpi_device_get_power_state(u16 dev_id)
780{
781 int ret;
782 u8 pstate;
783 __le16 id = cpu_to_le16(dev_id);
784
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200785 ret = scpi_send_message(CMD_GET_DEVICE_PWR_STATE, &id,
Sudeep Holla37a441d2016-04-20 14:05:14 +0100786 sizeof(id), &pstate, sizeof(pstate));
787 return ret ? ret : pstate;
788}
789
790static int scpi_device_set_power_state(u16 dev_id, u8 pstate)
791{
792 int stat;
793 struct dev_pstate_set dev_set = {
794 .dev_id = cpu_to_le16(dev_id),
795 .pstate = pstate,
796 };
797
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200798 return scpi_send_message(CMD_SET_DEVICE_PWR_STATE, &dev_set,
Sudeep Holla37a441d2016-04-20 14:05:14 +0100799 sizeof(dev_set), &stat, sizeof(stat));
800}
801
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100802static struct scpi_ops scpi_ops = {
803 .get_version = scpi_get_version,
804 .clk_get_range = scpi_clk_get_range,
805 .clk_get_val = scpi_clk_get_val,
806 .clk_set_val = scpi_clk_set_val,
807 .dvfs_get_idx = scpi_dvfs_get_idx,
808 .dvfs_set_idx = scpi_dvfs_set_idx,
809 .dvfs_get_info = scpi_dvfs_get_info,
Sudeep Holla45ca7df2017-04-27 15:08:51 +0100810 .device_domain_id = scpi_dev_domain_id,
811 .get_transition_latency = scpi_dvfs_get_transition_latency,
812 .add_opps_to_device = scpi_dvfs_add_opps_to_device,
Punit Agrawal38a1bdc2015-06-19 15:31:46 +0100813 .sensor_get_capability = scpi_sensor_get_capability,
814 .sensor_get_info = scpi_sensor_get_info,
815 .sensor_get_value = scpi_sensor_get_value,
Sudeep Holla37a441d2016-04-20 14:05:14 +0100816 .device_get_power_state = scpi_device_get_power_state,
817 .device_set_power_state = scpi_device_set_power_state,
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100818};
819
820struct scpi_ops *get_scpi_ops(void)
821{
822 return scpi_info ? scpi_info->scpi_ops : NULL;
823}
824EXPORT_SYMBOL_GPL(get_scpi_ops);
825
826static int scpi_init_versions(struct scpi_drvinfo *info)
827{
828 int ret;
829 struct scp_capabilities caps;
830
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200831 ret = scpi_send_message(CMD_SCPI_CAPABILITIES, NULL, 0,
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100832 &caps, sizeof(caps));
833 if (!ret) {
834 info->protocol_version = le32_to_cpu(caps.protocol_version);
835 info->firmware_version = le32_to_cpu(caps.platform_version);
836 }
Neil Armstrongabd3e802016-10-19 14:51:09 +0200837 /* Ignore error if not implemented */
838 if (scpi_info->is_legacy && ret == -EOPNOTSUPP)
839 return 0;
840
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100841 return ret;
842}
843
844static ssize_t protocol_version_show(struct device *dev,
845 struct device_attribute *attr, char *buf)
846{
Olof Johansson81faa552017-12-03 19:28:33 -0800847 struct scpi_drvinfo *scpi_info = dev_get_drvdata(dev);
848
Heiner Kallweit7cd49a22017-12-05 23:16:55 +0100849 return sprintf(buf, "%lu.%lu\n",
850 FIELD_GET(PROTO_REV_MAJOR_MASK, scpi_info->protocol_version),
851 FIELD_GET(PROTO_REV_MINOR_MASK, scpi_info->protocol_version));
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100852}
853static DEVICE_ATTR_RO(protocol_version);
854
855static ssize_t firmware_version_show(struct device *dev,
856 struct device_attribute *attr, char *buf)
857{
Olof Johansson81faa552017-12-03 19:28:33 -0800858 struct scpi_drvinfo *scpi_info = dev_get_drvdata(dev);
859
Heiner Kallweit7cd49a22017-12-05 23:16:55 +0100860 return sprintf(buf, "%lu.%lu.%lu\n",
861 FIELD_GET(FW_REV_MAJOR_MASK, scpi_info->firmware_version),
862 FIELD_GET(FW_REV_MINOR_MASK, scpi_info->firmware_version),
863 FIELD_GET(FW_REV_PATCH_MASK, scpi_info->firmware_version));
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100864}
865static DEVICE_ATTR_RO(firmware_version);
866
867static struct attribute *versions_attrs[] = {
868 &dev_attr_firmware_version.attr,
869 &dev_attr_protocol_version.attr,
870 NULL,
871};
872ATTRIBUTE_GROUPS(versions);
873
Heiner Kallweitc14f1db2017-12-05 23:16:42 +0100874static void scpi_free_channels(void *data)
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100875{
Heiner Kallweitc14f1db2017-12-05 23:16:42 +0100876 struct scpi_drvinfo *info = data;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100877 int i;
878
Heiner Kallweitc14f1db2017-12-05 23:16:42 +0100879 for (i = 0; i < info->num_chans; i++)
880 mbox_free_channel(info->channels[i].chan);
Olof Johansson81faa552017-12-03 19:28:33 -0800881}
882
883static int scpi_remove(struct platform_device *pdev)
884{
885 int i;
Olof Johansson81faa552017-12-03 19:28:33 -0800886 struct scpi_drvinfo *info = platform_get_drvdata(pdev);
887
888 scpi_info = NULL; /* stop exporting SCPI ops through get_scpi_ops */
889
Olof Johansson81faa552017-12-03 19:28:33 -0800890 for (i = 0; i < MAX_DVFS_DOMAINS && info->dvfs[i]; i++) {
891 kfree(info->dvfs[i]->opps);
892 kfree(info->dvfs[i]);
893 }
Olof Johansson81faa552017-12-03 19:28:33 -0800894
895 return 0;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100896}
897
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100898#define MAX_SCPI_XFERS 10
899static int scpi_alloc_xfer_list(struct device *dev, struct scpi_chan *ch)
900{
901 int i;
902 struct scpi_xfer *xfers;
903
904 xfers = devm_kzalloc(dev, MAX_SCPI_XFERS * sizeof(*xfers), GFP_KERNEL);
905 if (!xfers)
906 return -ENOMEM;
907
908 ch->xfers = xfers;
Alexey Klimovc511fa3f2017-03-29 19:16:27 +0100909 for (i = 0; i < MAX_SCPI_XFERS; i++, xfers++) {
910 init_completion(&xfers->done);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100911 list_add_tail(&xfers->node, &ch->xfers_list);
Alexey Klimovc511fa3f2017-03-29 19:16:27 +0100912 }
913
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100914 return 0;
915}
916
Sudeep Holla8358c6b2016-10-19 14:51:10 +0200917static const struct of_device_id legacy_scpi_of_match[] = {
918 {.compatible = "arm,scpi-pre-1.0"},
919 {},
920};
921
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100922static int scpi_probe(struct platform_device *pdev)
923{
924 int count, idx, ret;
925 struct resource res;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100926 struct device *dev = &pdev->dev;
927 struct device_node *np = dev->of_node;
928
929 scpi_info = devm_kzalloc(dev, sizeof(*scpi_info), GFP_KERNEL);
930 if (!scpi_info)
931 return -ENOMEM;
932
Sudeep Holla8358c6b2016-10-19 14:51:10 +0200933 if (of_match_device(legacy_scpi_of_match, &pdev->dev))
934 scpi_info->is_legacy = true;
935
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100936 count = of_count_phandle_with_args(np, "mboxes", "#mbox-cells");
937 if (count < 0) {
Rob Herring9deee312017-07-18 16:43:01 -0500938 dev_err(dev, "no mboxes property in '%pOF'\n", np);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100939 return -ENODEV;
940 }
941
Heiner Kallweitc14f1db2017-12-05 23:16:42 +0100942 scpi_info->channels = devm_kcalloc(dev, count, sizeof(struct scpi_chan),
943 GFP_KERNEL);
944 if (!scpi_info->channels)
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100945 return -ENOMEM;
946
Heiner Kallweitc14f1db2017-12-05 23:16:42 +0100947 ret = devm_add_action(dev, scpi_free_channels, scpi_info);
948 if (ret)
949 return ret;
950
951 for (; scpi_info->num_chans < count; scpi_info->num_chans++) {
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100952 resource_size_t size;
Heiner Kallweitc14f1db2017-12-05 23:16:42 +0100953 int idx = scpi_info->num_chans;
954 struct scpi_chan *pchan = scpi_info->channels + idx;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100955 struct mbox_client *cl = &pchan->cl;
956 struct device_node *shmem = of_parse_phandle(np, "shmem", idx);
957
Peter Chenb079bd52016-07-04 14:55:57 +0800958 ret = of_address_to_resource(shmem, 0, &res);
959 of_node_put(shmem);
960 if (ret) {
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100961 dev_err(dev, "failed to get SCPI payload mem resource\n");
Heiner Kallweitc14f1db2017-12-05 23:16:42 +0100962 return ret;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100963 }
964
965 size = resource_size(&res);
966 pchan->rx_payload = devm_ioremap(dev, res.start, size);
967 if (!pchan->rx_payload) {
968 dev_err(dev, "failed to ioremap SCPI payload\n");
Heiner Kallweitc14f1db2017-12-05 23:16:42 +0100969 return -EADDRNOTAVAIL;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100970 }
971 pchan->tx_payload = pchan->rx_payload + (size >> 1);
972
973 cl->dev = dev;
974 cl->rx_callback = scpi_handle_remote_msg;
975 cl->tx_prepare = scpi_tx_prepare;
976 cl->tx_block = true;
Sudeep Holla3bdd8842016-01-15 11:57:38 +0000977 cl->tx_tout = 20;
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100978 cl->knows_txdone = false; /* controller can't ack */
979
980 INIT_LIST_HEAD(&pchan->rx_pending);
981 INIT_LIST_HEAD(&pchan->xfers_list);
982 spin_lock_init(&pchan->rx_lock);
983 mutex_init(&pchan->xfers_lock);
984
985 ret = scpi_alloc_xfer_list(dev, pchan);
986 if (!ret) {
987 pchan->chan = mbox_request_channel(cl, idx);
988 if (!IS_ERR(pchan->chan))
989 continue;
990 ret = PTR_ERR(pchan->chan);
991 if (ret != -EPROBE_DEFER)
992 dev_err(dev, "failed to get channel%d err %d\n",
993 idx, ret);
994 }
Sudeep Holla8cb7cf52015-03-30 10:59:52 +0100995 return ret;
996 }
997
Sudeep Holla761d0ef2016-10-05 09:33:27 +0200998 scpi_info->commands = scpi_std_commands;
Olof Johansson81faa552017-12-03 19:28:33 -0800999
1000 platform_set_drvdata(pdev, scpi_info);
Sudeep Holla761d0ef2016-10-05 09:33:27 +02001001
Neil Armstrong4dfe32d2016-10-19 14:51:08 +02001002 if (scpi_info->is_legacy) {
1003 /* Replace with legacy variants */
1004 scpi_ops.clk_set_val = legacy_scpi_clk_set_val;
1005 scpi_info->commands = scpi_legacy_commands;
1006
1007 /* Fill priority bitmap */
1008 for (idx = 0; idx < ARRAY_SIZE(legacy_hpriority_cmds); idx++)
1009 set_bit(legacy_hpriority_cmds[idx],
1010 scpi_info->cmd_priority);
1011 }
1012
Sudeep Holla8cb7cf52015-03-30 10:59:52 +01001013 ret = scpi_init_versions(scpi_info);
1014 if (ret) {
1015 dev_err(dev, "incorrect or no SCP firmware found\n");
Sudeep Holla8cb7cf52015-03-30 10:59:52 +01001016 return ret;
1017 }
1018
Heiner Kallweit7cd49a22017-12-05 23:16:55 +01001019 dev_info(dev, "SCP Protocol %lu.%lu Firmware %lu.%lu.%lu version\n",
1020 FIELD_GET(PROTO_REV_MAJOR_MASK, scpi_info->protocol_version),
1021 FIELD_GET(PROTO_REV_MINOR_MASK, scpi_info->protocol_version),
1022 FIELD_GET(FW_REV_MAJOR_MASK, scpi_info->firmware_version),
1023 FIELD_GET(FW_REV_MINOR_MASK, scpi_info->firmware_version),
1024 FIELD_GET(FW_REV_PATCH_MASK, scpi_info->firmware_version));
Olof Johansson81faa552017-12-03 19:28:33 -08001025 scpi_info->scpi_ops = &scpi_ops;
Heiner Kallweit931cf0c2017-09-29 23:44:05 +02001026
Heiner Kallweit5abc7932017-12-05 23:16:48 +01001027 ret = devm_device_add_groups(dev, versions_groups);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +01001028 if (ret)
1029 dev_err(dev, "unable to create sysfs version group\n");
1030
Heiner Kallweit5abc7932017-12-05 23:16:48 +01001031 return devm_of_platform_populate(dev);
Sudeep Holla8cb7cf52015-03-30 10:59:52 +01001032}
1033
1034static const struct of_device_id scpi_of_match[] = {
1035 {.compatible = "arm,scpi"},
Sudeep Holla8358c6b2016-10-19 14:51:10 +02001036 {.compatible = "arm,scpi-pre-1.0"},
Sudeep Holla8cb7cf52015-03-30 10:59:52 +01001037 {},
1038};
1039
1040MODULE_DEVICE_TABLE(of, scpi_of_match);
1041
1042static struct platform_driver scpi_driver = {
1043 .driver = {
1044 .name = "scpi_protocol",
1045 .of_match_table = scpi_of_match,
1046 },
1047 .probe = scpi_probe,
Olof Johansson81faa552017-12-03 19:28:33 -08001048 .remove = scpi_remove,
Sudeep Holla8cb7cf52015-03-30 10:59:52 +01001049};
1050module_platform_driver(scpi_driver);
1051
1052MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
1053MODULE_DESCRIPTION("ARM SCPI mailbox protocol driver");
1054MODULE_LICENSE("GPL v2");