blob: 9b2be1c1b9e381985b621297774500dd97844963 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: BSD-3-Clause
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +02002/*
3 * Qualcomm SPMI bus driver
4 *
5 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
6 *
7 * Loosely based on Little Kernel driver
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +02008 */
9
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020010#include <dm.h>
11#include <errno.h>
12#include <fdtdec.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060013#include <asm/global_data.h>
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020014#include <asm/io.h>
Simon Glass9bc15642020-02-03 07:36:16 -070015#include <dm/device_compat.h>
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020016#include <spmi/spmi.h>
17
18DECLARE_GLOBAL_DATA_PTR;
19
Jorge Ramirez-Ortiz4bcef682018-01-10 11:33:28 +010020/* PMIC Arbiter configuration registers */
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030021#define PMIC_ARB_VERSION 0x0000
22#define PMIC_ARB_VERSION_V2_MIN 0x20010000
23#define PMIC_ARB_VERSION_V3_MIN 0x30000000
24#define PMIC_ARB_VERSION_V5_MIN 0x50000000
Neil Armstronga243fb22024-04-05 10:21:56 +020025#define PMIC_ARB_VERSION_V7_MIN 0x70000000
Jorge Ramirez-Ortiz4bcef682018-01-10 11:33:28 +010026
Neil Armstrong93627ce2025-03-28 09:53:21 +010027#define PMIC_ARB_FEATURES 0x0004
28#define PMIC_ARB_FEATURES_PERIPH_MASK GENMASK(10, 0)
29
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030030#define APID_MAP_OFFSET_V1_V2_V3 (0x800)
31#define APID_MAP_OFFSET_V5 (0x900)
Neil Armstronga243fb22024-04-05 10:21:56 +020032#define APID_MAP_OFFSET_V7 (0x2000)
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030033#define ARB_CHANNEL_OFFSET(n) (0x4 * (n))
34#define SPMI_CH_OFFSET(chnl) ((chnl) * 0x8000)
35#define SPMI_V5_OBS_CH_OFFSET(chnl) ((chnl) * 0x80)
Neil Armstronga243fb22024-04-05 10:21:56 +020036#define SPMI_V7_OBS_CH_OFFSET(chnl) ((chnl) * 0x20)
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030037#define SPMI_V5_RW_CH_OFFSET(chnl) ((chnl) * 0x10000)
Neil Armstronga243fb22024-04-05 10:21:56 +020038#define SPMI_V7_RW_CH_OFFSET(chnl) ((chnl) * 0x1000)
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020039
Neil Armstrong3fe83672024-04-05 10:21:55 +020040#define SPMI_OWNERSHIP_PERIPH2OWNER(x) ((x) & 0x7)
41
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030042#define SPMI_REG_CMD0 0x0
43#define SPMI_REG_CONFIG 0x4
44#define SPMI_REG_STATUS 0x8
45#define SPMI_REG_WDATA 0x10
46#define SPMI_REG_RDATA 0x18
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020047
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030048#define SPMI_CMD_OPCODE_SHIFT 27
49#define SPMI_CMD_SLAVE_ID_SHIFT 20
50#define SPMI_CMD_ADDR_SHIFT 12
51#define SPMI_CMD_ADDR_OFFSET_SHIFT 4
52#define SPMI_CMD_BYTE_CNT_SHIFT 0
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020053
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030054#define SPMI_CMD_EXT_REG_WRITE_LONG 0x00
55#define SPMI_CMD_EXT_REG_READ_LONG 0x01
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020056
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030057#define SPMI_STATUS_DONE 0x1
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020058
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030059#define SPMI_MAX_CHANNELS 128
Neil Armstrong3fe83672024-04-05 10:21:55 +020060#define SPMI_MAX_CHANNELS_V5 512
Neil Armstronga243fb22024-04-05 10:21:56 +020061#define SPMI_MAX_CHANNELS_V7 1024
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030062#define SPMI_MAX_SLAVES 16
63#define SPMI_MAX_PERIPH 256
64
Neil Armstrong3fe83672024-04-05 10:21:55 +020065#define SPMI_CHANNEL_READ_ONLY BIT(31)
66#define SPMI_CHANNEL_MASK 0xffff
67
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030068enum arb_ver {
69 V1 = 1,
70 V2,
71 V3,
Neil Armstronga243fb22024-04-05 10:21:56 +020072 V5 = 5,
73 V7 = 7
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030074};
75
76/*
77 * PMIC arbiter version 5 uses different register offsets for read/write vs
78 * observer channels.
79 */
80enum pmic_arb_channel {
81 PMIC_ARB_CHANNEL_RW,
82 PMIC_ARB_CHANNEL_OBS,
83};
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020084
85struct msm_spmi_priv {
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030086 phys_addr_t arb_chnl; /* ARB channel mapping base */
Caleb Connolly99f591c2023-12-05 13:46:53 +000087 phys_addr_t spmi_chnls; /* SPMI channels */
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030088 phys_addr_t spmi_obs; /* SPMI observer */
Neil Armstrong3fe83672024-04-05 10:21:55 +020089 phys_addr_t spmi_cnfg; /* SPMI config */
90 u32 owner; /* Current owner */
91 unsigned int max_channels; /* Max channels */
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020092 /* SPMI channel map */
Neil Armstrong3fe83672024-04-05 10:21:55 +020093 uint32_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH];
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030094 /* SPMI bus arbiter version */
95 u32 arb_ver;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020096};
97
Neil Armstrongdde6d552024-04-05 10:21:54 +020098static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u8 pid, u8 off)
99{
100 return (opc << 27) | (sid << 20) | (pid << 12) | (off << 4) | 1;
101}
102
103static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 off)
104{
105 return (opc << 27) | (off << 4) | 1;
106}
107
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200108static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
109 uint8_t val)
110{
111 struct msm_spmi_priv *priv = dev_get_priv(dev);
112 unsigned channel;
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300113 unsigned int ch_offset;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200114 uint32_t reg = 0;
115
116 if (usid >= SPMI_MAX_SLAVES)
117 return -EIO;
118 if (pid >= SPMI_MAX_PERIPH)
119 return -EIO;
Neil Armstrong3fe83672024-04-05 10:21:55 +0200120 if (priv->channel_map[usid][pid] & SPMI_CHANNEL_READ_ONLY)
121 return -EPERM;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200122
Neil Armstrong3fe83672024-04-05 10:21:55 +0200123 channel = priv->channel_map[usid][pid] & SPMI_CHANNEL_MASK;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200124
Caleb Connolly2b7001d2024-06-24 14:46:27 +0200125 debug("%s: [%d:%d] %s: channel %d\n", dev->name, usid, pid, __func__, channel);
Neil Armstrongdde6d552024-04-05 10:21:54 +0200126
127 switch (priv->arb_ver) {
128 case V1:
129 ch_offset = SPMI_CH_OFFSET(channel);
130
131 reg = pmic_arb_fmt_cmd_v1(SPMI_CMD_EXT_REG_WRITE_LONG,
132 usid, pid, off);
133 break;
134
135 case V2:
Neil Armstrong62f34392024-04-05 10:21:53 +0200136 ch_offset = SPMI_CH_OFFSET(channel);
137
Neil Armstrongdde6d552024-04-05 10:21:54 +0200138 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_WRITE_LONG, off);
139 break;
140
141 case V5:
142 ch_offset = SPMI_V5_RW_CH_OFFSET(channel);
143
144 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_WRITE_LONG, off);
145 break;
Neil Armstronga243fb22024-04-05 10:21:56 +0200146
147 case V7:
148 ch_offset = SPMI_V7_RW_CH_OFFSET(channel);
149
150 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_WRITE_LONG, off);
151 break;
Neil Armstrongdde6d552024-04-05 10:21:54 +0200152 }
153
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200154 /* Disable IRQ mode for the current channel*/
Neil Armstrong62f34392024-04-05 10:21:53 +0200155 writel(0x0, priv->spmi_chnls + ch_offset + SPMI_REG_CONFIG);
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200156
157 /* Write single byte */
Neil Armstrong62f34392024-04-05 10:21:53 +0200158 writel(val, priv->spmi_chnls + ch_offset + SPMI_REG_WDATA);
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200159
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200160 /* Send write command */
Neil Armstrong62f34392024-04-05 10:21:53 +0200161 writel(reg, priv->spmi_chnls + ch_offset + SPMI_REG_CMD0);
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200162
163 /* Wait till CMD DONE status */
164 reg = 0;
165 while (!reg) {
Neil Armstrong62f34392024-04-05 10:21:53 +0200166 reg = readl(priv->spmi_chnls + ch_offset +
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200167 SPMI_REG_STATUS);
168 }
169
170 if (reg ^ SPMI_STATUS_DONE) {
171 printf("SPMI write failure.\n");
172 return -EIO;
173 }
174
175 return 0;
176}
177
178static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off)
179{
180 struct msm_spmi_priv *priv = dev_get_priv(dev);
181 unsigned channel;
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300182 unsigned int ch_offset;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200183 uint32_t reg = 0;
184
185 if (usid >= SPMI_MAX_SLAVES)
186 return -EIO;
187 if (pid >= SPMI_MAX_PERIPH)
188 return -EIO;
189
Neil Armstrong3fe83672024-04-05 10:21:55 +0200190 channel = priv->channel_map[usid][pid] & SPMI_CHANNEL_MASK;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200191
Caleb Connolly2b7001d2024-06-24 14:46:27 +0200192 debug("%s: [%d:%d] %s: channel %d\n", dev->name, usid, pid, __func__, channel);
Neil Armstrongdde6d552024-04-05 10:21:54 +0200193
194 switch (priv->arb_ver) {
195 case V1:
196 ch_offset = SPMI_CH_OFFSET(channel);
197
198 /* Prepare read command */
199 reg = pmic_arb_fmt_cmd_v1(SPMI_CMD_EXT_REG_READ_LONG,
200 usid, pid, off);
201 break;
202
203 case V2:
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300204 ch_offset = SPMI_CH_OFFSET(channel);
205
Neil Armstrongdde6d552024-04-05 10:21:54 +0200206 /* Prepare read command */
207 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_READ_LONG, off);
208 break;
209
210 case V5:
211 ch_offset = SPMI_V5_OBS_CH_OFFSET(channel);
212
213 /* Prepare read command */
214 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_READ_LONG, off);
215 break;
Neil Armstronga243fb22024-04-05 10:21:56 +0200216
217 case V7:
218 ch_offset = SPMI_V7_OBS_CH_OFFSET(channel);
219
220 /* Prepare read command */
221 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_READ_LONG, off);
222 break;
Neil Armstrongdde6d552024-04-05 10:21:54 +0200223 }
224
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200225 /* Disable IRQ mode for the current channel*/
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300226 writel(0x0, priv->spmi_obs + ch_offset + SPMI_REG_CONFIG);
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200227
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200228 /* Request read */
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300229 writel(reg, priv->spmi_obs + ch_offset + SPMI_REG_CMD0);
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200230
231 /* Wait till CMD DONE status */
232 reg = 0;
233 while (!reg) {
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300234 reg = readl(priv->spmi_obs + ch_offset + SPMI_REG_STATUS);
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200235 }
236
237 if (reg ^ SPMI_STATUS_DONE) {
238 printf("SPMI read failure.\n");
239 return -EIO;
240 }
241
242 /* Read the data */
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300243 return readl(priv->spmi_obs + ch_offset +
244 SPMI_REG_RDATA) & 0xFF;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200245}
246
247static struct dm_spmi_ops msm_spmi_ops = {
248 .read = msm_spmi_read,
249 .write = msm_spmi_write,
250};
251
Neil Armstrong7cc504e2025-03-28 09:53:22 +0100252static void msm_spmi_channel_map_v5(struct msm_spmi_priv *priv, unsigned int i,
253 uint8_t slave_id, uint8_t pid)
254{
255 /* Mark channels read-only when from different owner */
256 uint32_t cnfg = readl(priv->spmi_cnfg + ARB_CHANNEL_OFFSET(i));
257 uint8_t owner = SPMI_OWNERSHIP_PERIPH2OWNER(cnfg);
258
259 priv->channel_map[slave_id][pid] = i;
260 if (owner != priv->owner)
261 priv->channel_map[slave_id][pid] |= SPMI_CHANNEL_READ_ONLY;
262}
263
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200264static int msm_spmi_probe(struct udevice *dev)
265{
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200266 struct msm_spmi_priv *priv = dev_get_priv(dev);
Caleb Connolly99f591c2023-12-05 13:46:53 +0000267 phys_addr_t core_addr;
Jorge Ramirez-Ortiz4bcef682018-01-10 11:33:28 +0100268 u32 hw_ver;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200269 int i;
270
Caleb Connolly99f591c2023-12-05 13:46:53 +0000271 core_addr = dev_read_addr_name(dev, "core");
272 priv->spmi_chnls = dev_read_addr_name(dev, "chnls");
273 priv->spmi_obs = dev_read_addr_name(dev, "obsrvr");
Neil Armstrong3fe83672024-04-05 10:21:55 +0200274 dev_read_u32(dev, "qcom,ee", &priv->owner);
Jorge Ramirez-Ortiz4bcef682018-01-10 11:33:28 +0100275
Caleb Connolly99f591c2023-12-05 13:46:53 +0000276 hw_ver = readl(core_addr + PMIC_ARB_VERSION);
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300277
278 if (hw_ver < PMIC_ARB_VERSION_V3_MIN) {
279 priv->arb_ver = V2;
Caleb Connolly99f591c2023-12-05 13:46:53 +0000280 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V1_V2_V3;
Neil Armstrong3fe83672024-04-05 10:21:55 +0200281 priv->max_channels = SPMI_MAX_CHANNELS;
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300282 } else if (hw_ver < PMIC_ARB_VERSION_V5_MIN) {
283 priv->arb_ver = V3;
Caleb Connolly99f591c2023-12-05 13:46:53 +0000284 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V1_V2_V3;
Neil Armstrong3fe83672024-04-05 10:21:55 +0200285 priv->max_channels = SPMI_MAX_CHANNELS;
Neil Armstronga243fb22024-04-05 10:21:56 +0200286 } else if (hw_ver < PMIC_ARB_VERSION_V7_MIN) {
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300287 priv->arb_ver = V5;
Caleb Connolly99f591c2023-12-05 13:46:53 +0000288 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V5;
Neil Armstrong93627ce2025-03-28 09:53:21 +0100289 priv->max_channels = min_t(u32, readl(core_addr + PMIC_ARB_FEATURES) &
290 PMIC_ARB_FEATURES_PERIPH_MASK,
291 SPMI_MAX_CHANNELS_V5);
Neil Armstronga243fb22024-04-05 10:21:56 +0200292 priv->spmi_cnfg = dev_read_addr_name(dev, "cnfg");
293 } else {
294 /* TOFIX: handle second bus */
295 priv->arb_ver = V7;
296 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V7;
Neil Armstrong93627ce2025-03-28 09:53:21 +0100297 priv->max_channels = min_t(u32, readl(core_addr + PMIC_ARB_FEATURES) &
298 PMIC_ARB_FEATURES_PERIPH_MASK,
299 SPMI_MAX_CHANNELS_V7);
Neil Armstrong3fe83672024-04-05 10:21:55 +0200300 priv->spmi_cnfg = dev_read_addr_name(dev, "cnfg");
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300301 }
302
Caleb Connolly99f591c2023-12-05 13:46:53 +0000303 dev_dbg(dev, "PMIC Arb Version-%d (%#x)\n", hw_ver >> 28, hw_ver);
Jorge Ramirez-Ortiz4bcef682018-01-10 11:33:28 +0100304
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200305 if (priv->arb_chnl == FDT_ADDR_T_NONE ||
Caleb Connolly99f591c2023-12-05 13:46:53 +0000306 priv->spmi_chnls == FDT_ADDR_T_NONE ||
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200307 priv->spmi_obs == FDT_ADDR_T_NONE)
308 return -EINVAL;
309
Caleb Connolly99f591c2023-12-05 13:46:53 +0000310 dev_dbg(dev, "priv->arb_chnl address (%#08llx)\n", priv->arb_chnl);
311 dev_dbg(dev, "priv->spmi_chnls address (%#08llx)\n", priv->spmi_chnls);
312 dev_dbg(dev, "priv->spmi_obs address (%#08llx)\n", priv->spmi_obs);
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200313 /* Scan peripherals connected to each SPMI channel */
Neil Armstrong3fe83672024-04-05 10:21:55 +0200314 for (i = 0; i < priv->max_channels; i++) {
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200315 uint32_t periph = readl(priv->arb_chnl + ARB_CHANNEL_OFFSET(i));
316 uint8_t slave_id = (periph & 0xf0000) >> 16;
317 uint8_t pid = (periph & 0xff00) >> 8;
318
Neil Armstrong7cc504e2025-03-28 09:53:22 +0100319 switch (priv->arb_ver) {
320 case V2:
321 case V3:
322 priv->channel_map[slave_id][pid] = i;
323 break;
Neil Armstrong3fe83672024-04-05 10:21:55 +0200324
Neil Armstrong7cc504e2025-03-28 09:53:22 +0100325 case V5:
326 case V7:
327 msm_spmi_channel_map_v5(priv, i, slave_id, pid);
328 break;
Neil Armstrong3fe83672024-04-05 10:21:55 +0200329 }
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200330 }
331 return 0;
332}
333
334static const struct udevice_id msm_spmi_ids[] = {
335 { .compatible = "qcom,spmi-pmic-arb" },
336 { }
337};
338
339U_BOOT_DRIVER(msm_spmi) = {
340 .name = "msm_spmi",
341 .id = UCLASS_SPMI,
342 .of_match = msm_spmi_ids,
343 .ops = &msm_spmi_ops,
344 .probe = msm_spmi_probe,
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300345 .priv_auto = sizeof(struct msm_spmi_priv),
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200346};