2 * System Control and Power Interface (SCPI) Message Protocol driver
4 * Copyright (C) 2014 ARM Ltd.
5 * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms and conditions of the GNU General Public License,
9 * version 2, as published by the Free Software Foundation.
11 * This program is distributed in the hope it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
19 #include <linux/of_device.h>
20 #include <linux/platform_device.h>
21 #include <linux/err.h>
22 #include <linux/export.h>
23 #include <linux/kernel.h>
24 #include <linux/module.h>
25 #include <linux/printk.h>
26 #include <linux/mailbox_client.h>
27 #include <linux/scpi_protocol.h>
28 #include <linux/slab.h>
29 #include <linux/rockchip-mailbox.h>
30 #include <linux/rockchip/common.h>
34 #define CMD_ID_SHIFT 0
35 #define CMD_ID_MASK 0xff
36 #define CMD_SENDER_ID_SHIFT 8
37 #define CMD_SENDER_ID_MASK 0xff
38 #define CMD_DATA_SIZE_SHIFT 20
39 #define CMD_DATA_SIZE_MASK 0x1ff
40 #define PACK_SCPI_CMD(cmd, sender, txsz) \
41 ((((cmd) & CMD_ID_MASK) << CMD_ID_SHIFT) | \
42 (((sender) & CMD_SENDER_ID_MASK) << CMD_SENDER_ID_SHIFT) | \
43 (((txsz) & CMD_DATA_SIZE_MASK) << CMD_DATA_SIZE_SHIFT))
44 #define SCPI_CMD_DEFAULT_TIMEOUT_MS 1000
46 #define MAX_DVFS_DOMAINS 3
47 #define MAX_DVFS_OPPS 8
48 #define DVFS_LATENCY(hdr) ((hdr) >> 16)
49 #define DVFS_OPP_COUNT(hdr) (((hdr) >> 8) & 0xff)
51 struct scpi_data_buf {
53 struct rockchip_mbox_msg *data;
54 struct completion complete;
58 static int high_priority_cmds[] = {
59 SCPI_CMD_GET_CSS_PWR_STATE,
60 SCPI_CMD_CFG_PWR_STATE_STAT,
61 SCPI_CMD_GET_PWR_STATE_STAT,
66 SCPI_CMD_SET_CLOCK_INDEX,
67 SCPI_CMD_SET_CLOCK_VALUE,
68 SCPI_CMD_GET_CLOCK_VALUE,
71 SCPI_CMD_SENSOR_CFG_PERIODIC,
72 SCPI_CMD_SENSOR_CFG_BOUNDS,
75 static struct scpi_opp *scpi_opps[MAX_DVFS_DOMAINS];
77 static struct device *the_scpi_device;
79 static int scpi_linux_errmap[SCPI_ERR_MAX] = {
80 0, -EINVAL, -ENOEXEC, -EMSGSIZE,
81 -EINVAL, -EACCES, -ERANGE, -ETIMEDOUT,
82 -ENOMEM, -EINVAL, -EOPNOTSUPP, -EIO,
85 static inline int scpi_to_linux_errno(int errno)
87 if (errno >= SCPI_SUCCESS && errno < SCPI_ERR_MAX)
88 return scpi_linux_errmap[errno];
92 static bool high_priority_chan_supported(int cmd)
96 for (idx = 0; idx < ARRAY_SIZE(high_priority_cmds); idx++)
97 if (cmd == high_priority_cmds[idx])
102 static void scpi_rx_callback(struct mbox_client *cl, void *msg)
104 struct rockchip_mbox_msg *data = (struct rockchip_mbox_msg *)msg;
105 struct scpi_data_buf *scpi_buf = data->cl_data;
107 complete(&scpi_buf->complete);
110 static int send_scpi_cmd(struct scpi_data_buf *scpi_buf, bool high_priority)
112 struct mbox_chan *chan;
113 struct mbox_client cl;
114 struct rockchip_mbox_msg *data = scpi_buf->data;
117 int timeout = msecs_to_jiffies(scpi_buf->timeout_ms);
119 if (!the_scpi_device) {
120 pr_err("Scpi initializes unsuccessfully\n");
124 cl.dev = the_scpi_device;
125 cl.rx_callback = scpi_rx_callback;
128 cl.knows_txdone = false;
130 chan = mbox_request_channel(&cl, high_priority);
132 return PTR_ERR(chan);
134 init_completion(&scpi_buf->complete);
135 if (mbox_send_message(chan, (void *)data) < 0) {
136 status = SCPI_ERR_TIMEOUT;
140 ret = wait_for_completion_timeout(&scpi_buf->complete, timeout);
142 status = SCPI_ERR_TIMEOUT;
145 status = *(u32 *)(data->rx_buf); /* read first word */
148 mbox_free_channel(chan);
150 return scpi_to_linux_errno(status);
153 #define SCPI_SETUP_DBUF(scpi_buf, mbox_buf, _client_id,\
154 _cmd, _tx_buf, _rx_buf) \
156 struct rockchip_mbox_msg *pdata = &mbox_buf; \
158 pdata->tx_buf = &_tx_buf; \
159 pdata->tx_size = sizeof(_tx_buf); \
160 pdata->rx_buf = &_rx_buf; \
161 pdata->rx_size = sizeof(_rx_buf); \
162 scpi_buf.client_id = _client_id; \
163 scpi_buf.data = pdata; \
164 scpi_buf.timeout_ms = SCPI_CMD_DEFAULT_TIMEOUT_MS; \
167 static int scpi_execute_cmd(struct scpi_data_buf *scpi_buf)
169 struct rockchip_mbox_msg *data;
172 if (!scpi_buf || !scpi_buf->data)
175 data = scpi_buf->data;
176 high_priority = high_priority_chan_supported(data->cmd);
177 data->cmd = PACK_SCPI_CMD(data->cmd, scpi_buf->client_id,
179 data->cl_data = scpi_buf;
181 return send_scpi_cmd(scpi_buf, high_priority);
184 unsigned long scpi_clk_get_val(u16 clk_id)
186 struct scpi_data_buf sdata;
187 struct rockchip_mbox_msg mdata;
193 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_CLOCKS,
194 SCPI_CMD_GET_CLOCK_VALUE, clk_id, buf);
195 if (scpi_execute_cmd(&sdata))
200 EXPORT_SYMBOL_GPL(scpi_clk_get_val);
202 int scpi_clk_set_val(u16 clk_id, unsigned long rate)
204 struct scpi_data_buf sdata;
205 struct rockchip_mbox_msg mdata;
212 buf.clk_rate = (u32)rate;
215 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_CLOCKS,
216 SCPI_CMD_SET_CLOCK_VALUE, buf, stat);
217 return scpi_execute_cmd(&sdata);
219 EXPORT_SYMBOL_GPL(scpi_clk_set_val);
221 struct scpi_opp *scpi_dvfs_get_opps(u8 domain)
223 struct scpi_data_buf sdata;
224 struct rockchip_mbox_msg mdata;
228 struct scpi_opp_entry opp[MAX_DVFS_OPPS];
230 struct scpi_opp *opps;
234 if (domain >= MAX_DVFS_DOMAINS)
235 return ERR_PTR(-EINVAL);
237 if (scpi_opps[domain]) /* data already populated */
238 return scpi_opps[domain];
240 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DVFS,
241 SCPI_CMD_GET_DVFS_INFO, domain, buf);
242 ret = scpi_execute_cmd(&sdata);
246 opps = kmalloc(sizeof(*opps), GFP_KERNEL);
248 return ERR_PTR(-ENOMEM);
250 count = DVFS_OPP_COUNT(buf.header);
251 opps_sz = count * sizeof(*(opps->opp));
254 opps->latency = DVFS_LATENCY(buf.header);
255 opps->opp = kmalloc(opps_sz, GFP_KERNEL);
258 return ERR_PTR(-ENOMEM);
261 memcpy(opps->opp, &buf.opp[0], opps_sz);
262 scpi_opps[domain] = opps;
266 EXPORT_SYMBOL_GPL(scpi_dvfs_get_opps);
268 int scpi_dvfs_get_idx(u8 domain)
270 struct scpi_data_buf sdata;
271 struct rockchip_mbox_msg mdata;
278 if (domain >= MAX_DVFS_DOMAINS)
281 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DVFS,
282 SCPI_CMD_GET_DVFS, domain, buf);
283 ret = scpi_execute_cmd(&sdata);
289 EXPORT_SYMBOL_GPL(scpi_dvfs_get_idx);
291 int scpi_dvfs_set_idx(u8 domain, u8 idx)
293 struct scpi_data_buf sdata;
294 struct rockchip_mbox_msg mdata;
302 buf.dvfs_domain = domain;
304 if (domain >= MAX_DVFS_DOMAINS)
307 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DVFS,
308 SCPI_CMD_SET_DVFS, buf, stat);
309 return scpi_execute_cmd(&sdata);
311 EXPORT_SYMBOL_GPL(scpi_dvfs_set_idx);
313 int scpi_get_sensor(char *name)
315 struct scpi_data_buf sdata;
316 struct rockchip_mbox_msg mdata;
331 /* This should be handled by a generic macro */
333 struct rockchip_mbox_msg *pdata = &mdata;
335 pdata->cmd = SCPI_CMD_SENSOR_CAPABILITIES;
337 pdata->rx_buf = &cap_buf;
338 pdata->rx_size = sizeof(cap_buf);
339 sdata.client_id = SCPI_CL_THERMAL;
343 ret = scpi_execute_cmd(&sdata);
348 for (sensor_id = 0; sensor_id < cap_buf.sensors; sensor_id++) {
349 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_THERMAL,
350 SCPI_CMD_SENSOR_INFO, sensor_id, info_buf);
351 ret = scpi_execute_cmd(&sdata);
355 if (!strcmp(name, info_buf.name)) {
363 EXPORT_SYMBOL_GPL(scpi_get_sensor);
365 int scpi_get_sensor_value(u16 sensor, u32 *val)
367 struct scpi_data_buf sdata;
368 struct rockchip_mbox_msg mdata;
375 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_THERMAL, SCPI_CMD_SENSOR_VALUE,
378 ret = scpi_execute_cmd(&sdata);
384 EXPORT_SYMBOL_GPL(scpi_get_sensor_value);
386 static int scpi_get_version(u32 old, u32 *ver)
388 struct scpi_data_buf sdata;
389 struct rockchip_mbox_msg mdata;
396 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_SYS, SCPI_SYS_GET_VERSION,
399 ret = scpi_execute_cmd(&sdata);
406 int scpi_sys_set_mcu_state_suspend(void)
408 struct scpi_data_buf sdata;
409 struct rockchip_mbox_msg mdata;
418 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_SYS,
419 SCPI_SYS_SET_MCU_STATE_SUSPEND, tx_buf, rx_buf);
420 return scpi_execute_cmd(&sdata);
422 EXPORT_SYMBOL_GPL(scpi_sys_set_mcu_state_suspend);
424 int scpi_sys_set_mcu_state_resume(void)
426 struct scpi_data_buf sdata;
427 struct rockchip_mbox_msg mdata;
437 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_SYS,
438 SCPI_SYS_SET_MCU_STATE_RESUME, tx_buf, rx_buf);
439 return scpi_execute_cmd(&sdata);
441 EXPORT_SYMBOL_GPL(scpi_sys_set_mcu_state_resume);
443 int scpi_ddr_init(u32 dram_speed_bin, u32 freq, u32 lcdc_type)
445 struct scpi_data_buf sdata;
446 struct rockchip_mbox_msg mdata;
456 tx_buf.dram_speed_bin = (u32)dram_speed_bin;
457 tx_buf.freq = (u32)freq;
458 tx_buf.lcdc_type = (u32)lcdc_type;
460 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DDR,
461 SCPI_DDR_INIT, tx_buf, rx_buf);
462 return scpi_execute_cmd(&sdata);
464 EXPORT_SYMBOL_GPL(scpi_ddr_init);
466 int scpi_ddr_set_clk_rate(u32 rate, u32 lcdc_type)
468 struct scpi_data_buf sdata;
469 struct rockchip_mbox_msg mdata;
478 tx_buf.clk_rate = (u32)rate;
479 tx_buf.lcdc_type = (u32)lcdc_type;
480 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DDR,
481 SCPI_DDR_SET_FREQ, tx_buf, rx_buf);
482 return scpi_execute_cmd(&sdata);
484 EXPORT_SYMBOL_GPL(scpi_ddr_set_clk_rate);
486 int scpi_ddr_round_rate(u32 m_hz)
488 struct scpi_data_buf sdata;
489 struct rockchip_mbox_msg mdata;
498 tx_buf.clk_rate = (u32)m_hz;
500 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DDR,
501 SCPI_DDR_ROUND_RATE, tx_buf, rx_buf);
502 if (scpi_execute_cmd(&sdata))
505 return rx_buf.round_rate;
507 EXPORT_SYMBOL_GPL(scpi_ddr_round_rate);
509 int scpi_ddr_set_auto_self_refresh(u32 en)
511 struct scpi_data_buf sdata;
512 struct rockchip_mbox_msg mdata;
520 tx_buf.enable = (u32)en;
522 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DDR,
523 SCPI_DDR_AUTO_SELF_REFRESH, tx_buf, rx_buf);
524 return scpi_execute_cmd(&sdata);
526 EXPORT_SYMBOL_GPL(scpi_ddr_set_auto_self_refresh);
528 int scpi_ddr_bandwidth_get(struct ddr_bw_info *ddr_bw_ch0,
529 struct ddr_bw_info *ddr_bw_ch1)
531 struct scpi_data_buf sdata;
532 struct rockchip_mbox_msg mdata;
538 struct ddr_bw_info ddr_bw_ch0;
539 struct ddr_bw_info ddr_bw_ch1;
544 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DDR,
545 SCPI_DDR_BANDWIDTH_GET, tx_buf, rx_buf);
546 if (scpi_execute_cmd(&sdata))
549 memcpy(ddr_bw_ch0, &(rx_buf.ddr_bw_ch0), sizeof(rx_buf.ddr_bw_ch0));
550 memcpy(ddr_bw_ch1, &(rx_buf.ddr_bw_ch1), sizeof(rx_buf.ddr_bw_ch1));
554 EXPORT_SYMBOL_GPL(scpi_ddr_bandwidth_get);
556 int scpi_ddr_get_clk_rate(void)
558 struct scpi_data_buf sdata;
559 struct rockchip_mbox_msg mdata;
569 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DDR,
570 SCPI_DDR_GET_FREQ, tx_buf, rx_buf);
571 if (scpi_execute_cmd(&sdata))
574 return rx_buf.clk_rate;
576 EXPORT_SYMBOL_GPL(scpi_ddr_get_clk_rate);
578 int scpi_thermal_get_temperature(void)
580 struct scpi_data_buf sdata;
581 struct rockchip_mbox_msg mdata;
592 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_THERMAL,
593 SCPI_THERMAL_GET_TSADC_DATA, tx_buf, rx_buf);
594 if (scpi_execute_cmd(&sdata))
597 return rx_buf.tsadc_data;
599 EXPORT_SYMBOL_GPL(scpi_thermal_get_temperature);
601 int scpi_thermal_set_clk_cycle(u32 cycle)
603 struct scpi_data_buf sdata;
604 struct rockchip_mbox_msg mdata;
613 tx_buf.clk_cycle = cycle;
614 SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_THERMAL,
615 SCPI_THERMAL_SET_TSADC_CYCLE, tx_buf, rx_buf);
617 return scpi_execute_cmd(&sdata);
619 EXPORT_SYMBOL_GPL(scpi_thermal_set_clk_cycle);
621 static struct of_device_id mobx_scpi_of_match[] = {
622 { .compatible = "rockchip,mbox-scpi"},
625 MODULE_DEVICE_TABLE(of, mobx_scpi_of_match);
627 static int mobx_scpi_probe(struct platform_device *pdev)
632 int check_version = 0; /*0: not check version, 1: check version*/
634 the_scpi_device = &pdev->dev;
636 while ((retry--) && (check_version != 0)) {
637 ret = scpi_get_version(SCPI_VERSION, &ver);
638 if ((ret == 0) && (ver == SCPI_VERSION))
642 if ((retry <= 0) && (check_version != 0)) {
643 dev_err(&pdev->dev, "Failed to get scpi version\n");
649 "Scpi initialize, version: 0x%x\n", ver);
652 the_scpi_device = NULL;
656 static struct platform_driver mbox_scpi_driver = {
657 .probe = mobx_scpi_probe,
660 .of_match_table = of_match_ptr(mobx_scpi_of_match),
664 static int __init rockchip_mbox_scpi_init(void)
666 return platform_driver_register(&mbox_scpi_driver);
668 subsys_initcall(rockchip_mbox_scpi_init);