blob: a35cb0ba01e403c57c65caf2e4644ff6a6c2dcda [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
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030027#define APID_MAP_OFFSET_V1_V2_V3 (0x800)
28#define APID_MAP_OFFSET_V5 (0x900)
Neil Armstronga243fb22024-04-05 10:21:56 +020029#define APID_MAP_OFFSET_V7 (0x2000)
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030030#define ARB_CHANNEL_OFFSET(n) (0x4 * (n))
31#define SPMI_CH_OFFSET(chnl) ((chnl) * 0x8000)
32#define SPMI_V5_OBS_CH_OFFSET(chnl) ((chnl) * 0x80)
Neil Armstronga243fb22024-04-05 10:21:56 +020033#define SPMI_V7_OBS_CH_OFFSET(chnl) ((chnl) * 0x20)
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030034#define SPMI_V5_RW_CH_OFFSET(chnl) ((chnl) * 0x10000)
Neil Armstronga243fb22024-04-05 10:21:56 +020035#define SPMI_V7_RW_CH_OFFSET(chnl) ((chnl) * 0x1000)
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020036
Neil Armstrong3fe83672024-04-05 10:21:55 +020037#define SPMI_OWNERSHIP_PERIPH2OWNER(x) ((x) & 0x7)
38
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030039#define SPMI_REG_CMD0 0x0
40#define SPMI_REG_CONFIG 0x4
41#define SPMI_REG_STATUS 0x8
42#define SPMI_REG_WDATA 0x10
43#define SPMI_REG_RDATA 0x18
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020044
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030045#define SPMI_CMD_OPCODE_SHIFT 27
46#define SPMI_CMD_SLAVE_ID_SHIFT 20
47#define SPMI_CMD_ADDR_SHIFT 12
48#define SPMI_CMD_ADDR_OFFSET_SHIFT 4
49#define SPMI_CMD_BYTE_CNT_SHIFT 0
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020050
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030051#define SPMI_CMD_EXT_REG_WRITE_LONG 0x00
52#define SPMI_CMD_EXT_REG_READ_LONG 0x01
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020053
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030054#define SPMI_STATUS_DONE 0x1
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020055
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030056#define SPMI_MAX_CHANNELS 128
Neil Armstrong3fe83672024-04-05 10:21:55 +020057#define SPMI_MAX_CHANNELS_V5 512
Neil Armstronga243fb22024-04-05 10:21:56 +020058#define SPMI_MAX_CHANNELS_V7 1024
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030059#define SPMI_MAX_SLAVES 16
60#define SPMI_MAX_PERIPH 256
61
Neil Armstrong3fe83672024-04-05 10:21:55 +020062#define SPMI_CHANNEL_READ_ONLY BIT(31)
63#define SPMI_CHANNEL_MASK 0xffff
64
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030065enum arb_ver {
66 V1 = 1,
67 V2,
68 V3,
Neil Armstronga243fb22024-04-05 10:21:56 +020069 V5 = 5,
70 V7 = 7
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030071};
72
73/*
74 * PMIC arbiter version 5 uses different register offsets for read/write vs
75 * observer channels.
76 */
77enum pmic_arb_channel {
78 PMIC_ARB_CHANNEL_RW,
79 PMIC_ARB_CHANNEL_OBS,
80};
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020081
82struct msm_spmi_priv {
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030083 phys_addr_t arb_chnl; /* ARB channel mapping base */
Caleb Connolly99f591c2023-12-05 13:46:53 +000084 phys_addr_t spmi_chnls; /* SPMI channels */
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030085 phys_addr_t spmi_obs; /* SPMI observer */
Neil Armstrong3fe83672024-04-05 10:21:55 +020086 phys_addr_t spmi_cnfg; /* SPMI config */
87 u32 owner; /* Current owner */
88 unsigned int max_channels; /* Max channels */
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020089 /* SPMI channel map */
Neil Armstrong3fe83672024-04-05 10:21:55 +020090 uint32_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH];
Dzmitry Sankouski682351a2021-10-17 13:44:28 +030091 /* SPMI bus arbiter version */
92 u32 arb_ver;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +020093};
94
Neil Armstrongdde6d552024-04-05 10:21:54 +020095static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u8 pid, u8 off)
96{
97 return (opc << 27) | (sid << 20) | (pid << 12) | (off << 4) | 1;
98}
99
100static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 off)
101{
102 return (opc << 27) | (off << 4) | 1;
103}
104
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200105static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
106 uint8_t val)
107{
108 struct msm_spmi_priv *priv = dev_get_priv(dev);
109 unsigned channel;
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300110 unsigned int ch_offset;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200111 uint32_t reg = 0;
112
113 if (usid >= SPMI_MAX_SLAVES)
114 return -EIO;
115 if (pid >= SPMI_MAX_PERIPH)
116 return -EIO;
Neil Armstrong3fe83672024-04-05 10:21:55 +0200117 if (priv->channel_map[usid][pid] & SPMI_CHANNEL_READ_ONLY)
118 return -EPERM;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200119
Neil Armstrong3fe83672024-04-05 10:21:55 +0200120 channel = priv->channel_map[usid][pid] & SPMI_CHANNEL_MASK;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200121
Caleb Connolly2b7001d2024-06-24 14:46:27 +0200122 debug("%s: [%d:%d] %s: channel %d\n", dev->name, usid, pid, __func__, channel);
Neil Armstrongdde6d552024-04-05 10:21:54 +0200123
124 switch (priv->arb_ver) {
125 case V1:
126 ch_offset = SPMI_CH_OFFSET(channel);
127
128 reg = pmic_arb_fmt_cmd_v1(SPMI_CMD_EXT_REG_WRITE_LONG,
129 usid, pid, off);
130 break;
131
132 case V2:
Neil Armstrong62f34392024-04-05 10:21:53 +0200133 ch_offset = SPMI_CH_OFFSET(channel);
134
Neil Armstrongdde6d552024-04-05 10:21:54 +0200135 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_WRITE_LONG, off);
136 break;
137
138 case V5:
139 ch_offset = SPMI_V5_RW_CH_OFFSET(channel);
140
141 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_WRITE_LONG, off);
142 break;
Neil Armstronga243fb22024-04-05 10:21:56 +0200143
144 case V7:
145 ch_offset = SPMI_V7_RW_CH_OFFSET(channel);
146
147 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_WRITE_LONG, off);
148 break;
Neil Armstrongdde6d552024-04-05 10:21:54 +0200149 }
150
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200151 /* Disable IRQ mode for the current channel*/
Neil Armstrong62f34392024-04-05 10:21:53 +0200152 writel(0x0, priv->spmi_chnls + ch_offset + SPMI_REG_CONFIG);
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200153
154 /* Write single byte */
Neil Armstrong62f34392024-04-05 10:21:53 +0200155 writel(val, priv->spmi_chnls + ch_offset + SPMI_REG_WDATA);
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200156
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200157 /* Send write command */
Neil Armstrong62f34392024-04-05 10:21:53 +0200158 writel(reg, priv->spmi_chnls + ch_offset + SPMI_REG_CMD0);
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200159
160 /* Wait till CMD DONE status */
161 reg = 0;
162 while (!reg) {
Neil Armstrong62f34392024-04-05 10:21:53 +0200163 reg = readl(priv->spmi_chnls + ch_offset +
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200164 SPMI_REG_STATUS);
165 }
166
167 if (reg ^ SPMI_STATUS_DONE) {
168 printf("SPMI write failure.\n");
169 return -EIO;
170 }
171
172 return 0;
173}
174
175static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off)
176{
177 struct msm_spmi_priv *priv = dev_get_priv(dev);
178 unsigned channel;
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300179 unsigned int ch_offset;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200180 uint32_t reg = 0;
181
182 if (usid >= SPMI_MAX_SLAVES)
183 return -EIO;
184 if (pid >= SPMI_MAX_PERIPH)
185 return -EIO;
186
Neil Armstrong3fe83672024-04-05 10:21:55 +0200187 channel = priv->channel_map[usid][pid] & SPMI_CHANNEL_MASK;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200188
Caleb Connolly2b7001d2024-06-24 14:46:27 +0200189 debug("%s: [%d:%d] %s: channel %d\n", dev->name, usid, pid, __func__, channel);
Neil Armstrongdde6d552024-04-05 10:21:54 +0200190
191 switch (priv->arb_ver) {
192 case V1:
193 ch_offset = SPMI_CH_OFFSET(channel);
194
195 /* Prepare read command */
196 reg = pmic_arb_fmt_cmd_v1(SPMI_CMD_EXT_REG_READ_LONG,
197 usid, pid, off);
198 break;
199
200 case V2:
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300201 ch_offset = SPMI_CH_OFFSET(channel);
202
Neil Armstrongdde6d552024-04-05 10:21:54 +0200203 /* Prepare read command */
204 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_READ_LONG, off);
205 break;
206
207 case V5:
208 ch_offset = SPMI_V5_OBS_CH_OFFSET(channel);
209
210 /* Prepare read command */
211 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_READ_LONG, off);
212 break;
Neil Armstronga243fb22024-04-05 10:21:56 +0200213
214 case V7:
215 ch_offset = SPMI_V7_OBS_CH_OFFSET(channel);
216
217 /* Prepare read command */
218 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_READ_LONG, off);
219 break;
Neil Armstrongdde6d552024-04-05 10:21:54 +0200220 }
221
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200222 /* Disable IRQ mode for the current channel*/
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300223 writel(0x0, priv->spmi_obs + ch_offset + SPMI_REG_CONFIG);
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200224
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200225 /* Request read */
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300226 writel(reg, priv->spmi_obs + ch_offset + SPMI_REG_CMD0);
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200227
228 /* Wait till CMD DONE status */
229 reg = 0;
230 while (!reg) {
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300231 reg = readl(priv->spmi_obs + ch_offset + SPMI_REG_STATUS);
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200232 }
233
234 if (reg ^ SPMI_STATUS_DONE) {
235 printf("SPMI read failure.\n");
236 return -EIO;
237 }
238
239 /* Read the data */
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300240 return readl(priv->spmi_obs + ch_offset +
241 SPMI_REG_RDATA) & 0xFF;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200242}
243
244static struct dm_spmi_ops msm_spmi_ops = {
245 .read = msm_spmi_read,
246 .write = msm_spmi_write,
247};
248
249static int msm_spmi_probe(struct udevice *dev)
250{
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200251 struct msm_spmi_priv *priv = dev_get_priv(dev);
Caleb Connolly99f591c2023-12-05 13:46:53 +0000252 phys_addr_t core_addr;
Jorge Ramirez-Ortiz4bcef682018-01-10 11:33:28 +0100253 u32 hw_ver;
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200254 int i;
255
Caleb Connolly99f591c2023-12-05 13:46:53 +0000256 core_addr = dev_read_addr_name(dev, "core");
257 priv->spmi_chnls = dev_read_addr_name(dev, "chnls");
258 priv->spmi_obs = dev_read_addr_name(dev, "obsrvr");
Neil Armstrong3fe83672024-04-05 10:21:55 +0200259 dev_read_u32(dev, "qcom,ee", &priv->owner);
Jorge Ramirez-Ortiz4bcef682018-01-10 11:33:28 +0100260
Caleb Connolly99f591c2023-12-05 13:46:53 +0000261 hw_ver = readl(core_addr + PMIC_ARB_VERSION);
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300262
263 if (hw_ver < PMIC_ARB_VERSION_V3_MIN) {
264 priv->arb_ver = V2;
Caleb Connolly99f591c2023-12-05 13:46:53 +0000265 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V1_V2_V3;
Neil Armstrong3fe83672024-04-05 10:21:55 +0200266 priv->max_channels = SPMI_MAX_CHANNELS;
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300267 } else if (hw_ver < PMIC_ARB_VERSION_V5_MIN) {
268 priv->arb_ver = V3;
Caleb Connolly99f591c2023-12-05 13:46:53 +0000269 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V1_V2_V3;
Neil Armstrong3fe83672024-04-05 10:21:55 +0200270 priv->max_channels = SPMI_MAX_CHANNELS;
Neil Armstronga243fb22024-04-05 10:21:56 +0200271 } else if (hw_ver < PMIC_ARB_VERSION_V7_MIN) {
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300272 priv->arb_ver = V5;
Caleb Connolly99f591c2023-12-05 13:46:53 +0000273 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V5;
Neil Armstronga243fb22024-04-05 10:21:56 +0200274 priv->max_channels = SPMI_MAX_CHANNELS;
275 priv->spmi_cnfg = dev_read_addr_name(dev, "cnfg");
276 } else {
277 /* TOFIX: handle second bus */
278 priv->arb_ver = V7;
279 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V7;
280 priv->max_channels = SPMI_MAX_CHANNELS_V7;
Neil Armstrong3fe83672024-04-05 10:21:55 +0200281 priv->spmi_cnfg = dev_read_addr_name(dev, "cnfg");
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300282 }
283
Caleb Connolly99f591c2023-12-05 13:46:53 +0000284 dev_dbg(dev, "PMIC Arb Version-%d (%#x)\n", hw_ver >> 28, hw_ver);
Jorge Ramirez-Ortiz4bcef682018-01-10 11:33:28 +0100285
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200286 if (priv->arb_chnl == FDT_ADDR_T_NONE ||
Caleb Connolly99f591c2023-12-05 13:46:53 +0000287 priv->spmi_chnls == FDT_ADDR_T_NONE ||
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200288 priv->spmi_obs == FDT_ADDR_T_NONE)
289 return -EINVAL;
290
Caleb Connolly99f591c2023-12-05 13:46:53 +0000291 dev_dbg(dev, "priv->arb_chnl address (%#08llx)\n", priv->arb_chnl);
292 dev_dbg(dev, "priv->spmi_chnls address (%#08llx)\n", priv->spmi_chnls);
293 dev_dbg(dev, "priv->spmi_obs address (%#08llx)\n", priv->spmi_obs);
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200294 /* Scan peripherals connected to each SPMI channel */
Neil Armstrong3fe83672024-04-05 10:21:55 +0200295 for (i = 0; i < priv->max_channels; i++) {
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200296 uint32_t periph = readl(priv->arb_chnl + ARB_CHANNEL_OFFSET(i));
297 uint8_t slave_id = (periph & 0xf0000) >> 16;
298 uint8_t pid = (periph & 0xff00) >> 8;
299
300 priv->channel_map[slave_id][pid] = i;
Neil Armstrong3fe83672024-04-05 10:21:55 +0200301
302 /* Mark channels read-only when from different owner */
Neil Armstronga243fb22024-04-05 10:21:56 +0200303 if (priv->arb_ver == V5 || priv->arb_ver == V7) {
Neil Armstrong3fe83672024-04-05 10:21:55 +0200304 uint32_t cnfg = readl(priv->spmi_cnfg + ARB_CHANNEL_OFFSET(i));
305 uint8_t owner = SPMI_OWNERSHIP_PERIPH2OWNER(cnfg);
306
307 if (owner != priv->owner)
308 priv->channel_map[slave_id][pid] |= SPMI_CHANNEL_READ_ONLY;
309 }
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200310 }
311 return 0;
312}
313
314static const struct udevice_id msm_spmi_ids[] = {
315 { .compatible = "qcom,spmi-pmic-arb" },
316 { }
317};
318
319U_BOOT_DRIVER(msm_spmi) = {
320 .name = "msm_spmi",
321 .id = UCLASS_SPMI,
322 .of_match = msm_spmi_ids,
323 .ops = &msm_spmi_ops,
324 .probe = msm_spmi_probe,
Dzmitry Sankouski682351a2021-10-17 13:44:28 +0300325 .priv_auto = sizeof(struct msm_spmi_priv),
Mateusz Kulikowski73bec9c2016-03-31 23:12:29 +0200326};