blob: 244de69b3590ce4edea611c0391e474f54b972c0 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: BSD-3-Clause
Mateusz Kulikowski5b472712016-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 Kulikowski5b472712016-03-31 23:12:29 +02008 */
9
10#include <common.h>
11#include <dm.h>
12#include <errno.h>
13#include <fdtdec.h>
Simon Glass401d1c42020-10-30 21:38:53 -060014#include <asm/global_data.h>
Mateusz Kulikowski5b472712016-03-31 23:12:29 +020015#include <asm/io.h>
Simon Glass336d4612020-02-03 07:36:16 -070016#include <dm/device_compat.h>
Mateusz Kulikowski5b472712016-03-31 23:12:29 +020017#include <spmi/spmi.h>
18
19DECLARE_GLOBAL_DATA_PTR;
20
Jorge Ramirez-Ortiz210d9592018-01-10 11:33:28 +010021/* PMIC Arbiter configuration registers */
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030022#define PMIC_ARB_VERSION 0x0000
23#define PMIC_ARB_VERSION_V2_MIN 0x20010000
24#define PMIC_ARB_VERSION_V3_MIN 0x30000000
25#define PMIC_ARB_VERSION_V5_MIN 0x50000000
Neil Armstrongee1d8aa2024-04-05 10:21:56 +020026#define PMIC_ARB_VERSION_V7_MIN 0x70000000
Jorge Ramirez-Ortiz210d9592018-01-10 11:33:28 +010027
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030028#define APID_MAP_OFFSET_V1_V2_V3 (0x800)
29#define APID_MAP_OFFSET_V5 (0x900)
Neil Armstrongee1d8aa2024-04-05 10:21:56 +020030#define APID_MAP_OFFSET_V7 (0x2000)
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030031#define ARB_CHANNEL_OFFSET(n) (0x4 * (n))
32#define SPMI_CH_OFFSET(chnl) ((chnl) * 0x8000)
33#define SPMI_V5_OBS_CH_OFFSET(chnl) ((chnl) * 0x80)
Neil Armstrongee1d8aa2024-04-05 10:21:56 +020034#define SPMI_V7_OBS_CH_OFFSET(chnl) ((chnl) * 0x20)
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030035#define SPMI_V5_RW_CH_OFFSET(chnl) ((chnl) * 0x10000)
Neil Armstrongee1d8aa2024-04-05 10:21:56 +020036#define SPMI_V7_RW_CH_OFFSET(chnl) ((chnl) * 0x1000)
Mateusz Kulikowski5b472712016-03-31 23:12:29 +020037
Neil Armstrong59e04822024-04-05 10:21:55 +020038#define SPMI_OWNERSHIP_PERIPH2OWNER(x) ((x) & 0x7)
39
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030040#define SPMI_REG_CMD0 0x0
41#define SPMI_REG_CONFIG 0x4
42#define SPMI_REG_STATUS 0x8
43#define SPMI_REG_WDATA 0x10
44#define SPMI_REG_RDATA 0x18
Mateusz Kulikowski5b472712016-03-31 23:12:29 +020045
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030046#define SPMI_CMD_OPCODE_SHIFT 27
47#define SPMI_CMD_SLAVE_ID_SHIFT 20
48#define SPMI_CMD_ADDR_SHIFT 12
49#define SPMI_CMD_ADDR_OFFSET_SHIFT 4
50#define SPMI_CMD_BYTE_CNT_SHIFT 0
Mateusz Kulikowski5b472712016-03-31 23:12:29 +020051
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030052#define SPMI_CMD_EXT_REG_WRITE_LONG 0x00
53#define SPMI_CMD_EXT_REG_READ_LONG 0x01
Mateusz Kulikowski5b472712016-03-31 23:12:29 +020054
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030055#define SPMI_STATUS_DONE 0x1
Mateusz Kulikowski5b472712016-03-31 23:12:29 +020056
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030057#define SPMI_MAX_CHANNELS 128
Neil Armstrong59e04822024-04-05 10:21:55 +020058#define SPMI_MAX_CHANNELS_V5 512
Neil Armstrongee1d8aa2024-04-05 10:21:56 +020059#define SPMI_MAX_CHANNELS_V7 1024
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030060#define SPMI_MAX_SLAVES 16
61#define SPMI_MAX_PERIPH 256
62
Neil Armstrong59e04822024-04-05 10:21:55 +020063#define SPMI_CHANNEL_READ_ONLY BIT(31)
64#define SPMI_CHANNEL_MASK 0xffff
65
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030066enum arb_ver {
67 V1 = 1,
68 V2,
69 V3,
Neil Armstrongee1d8aa2024-04-05 10:21:56 +020070 V5 = 5,
71 V7 = 7
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030072};
73
74/*
75 * PMIC arbiter version 5 uses different register offsets for read/write vs
76 * observer channels.
77 */
78enum pmic_arb_channel {
79 PMIC_ARB_CHANNEL_RW,
80 PMIC_ARB_CHANNEL_OBS,
81};
Mateusz Kulikowski5b472712016-03-31 23:12:29 +020082
83struct msm_spmi_priv {
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030084 phys_addr_t arb_chnl; /* ARB channel mapping base */
Caleb Connolly92fe0892023-12-05 13:46:53 +000085 phys_addr_t spmi_chnls; /* SPMI channels */
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030086 phys_addr_t spmi_obs; /* SPMI observer */
Neil Armstrong59e04822024-04-05 10:21:55 +020087 phys_addr_t spmi_cnfg; /* SPMI config */
88 u32 owner; /* Current owner */
89 unsigned int max_channels; /* Max channels */
Mateusz Kulikowski5b472712016-03-31 23:12:29 +020090 /* SPMI channel map */
Neil Armstrong59e04822024-04-05 10:21:55 +020091 uint32_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH];
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +030092 /* SPMI bus arbiter version */
93 u32 arb_ver;
Mateusz Kulikowski5b472712016-03-31 23:12:29 +020094};
95
Neil Armstrongf0b604d92024-04-05 10:21:54 +020096static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u8 pid, u8 off)
97{
98 return (opc << 27) | (sid << 20) | (pid << 12) | (off << 4) | 1;
99}
100
101static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 off)
102{
103 return (opc << 27) | (off << 4) | 1;
104}
105
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200106static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
107 uint8_t val)
108{
109 struct msm_spmi_priv *priv = dev_get_priv(dev);
110 unsigned channel;
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +0300111 unsigned int ch_offset;
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200112 uint32_t reg = 0;
113
114 if (usid >= SPMI_MAX_SLAVES)
115 return -EIO;
116 if (pid >= SPMI_MAX_PERIPH)
117 return -EIO;
Neil Armstrong59e04822024-04-05 10:21:55 +0200118 if (priv->channel_map[usid][pid] & SPMI_CHANNEL_READ_ONLY)
119 return -EPERM;
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200120
Neil Armstrong59e04822024-04-05 10:21:55 +0200121 channel = priv->channel_map[usid][pid] & SPMI_CHANNEL_MASK;
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200122
Neil Armstrongf0b604d92024-04-05 10:21:54 +0200123 dev_dbg(dev, "[%d:%d] %s: channel %d\n", usid, pid, __func__, channel);
124
125 switch (priv->arb_ver) {
126 case V1:
Neil Armstrongc2de6202024-04-05 10:21:53 +0200127 ch_offset = SPMI_CH_OFFSET(channel);
128
Neil Armstrongf0b604d92024-04-05 10:21:54 +0200129 reg = pmic_arb_fmt_cmd_v1(SPMI_CMD_EXT_REG_WRITE_LONG,
130 usid, pid, off);
131 break;
132
133 case V2:
134 ch_offset = SPMI_CH_OFFSET(channel);
135
136 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_WRITE_LONG, off);
137 break;
138
139 case V5:
140 ch_offset = SPMI_V5_RW_CH_OFFSET(channel);
141
142 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_WRITE_LONG, off);
143 break;
Neil Armstrongee1d8aa2024-04-05 10:21:56 +0200144
145 case V7:
146 ch_offset = SPMI_V7_RW_CH_OFFSET(channel);
147
148 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_WRITE_LONG, off);
149 break;
Neil Armstrongf0b604d92024-04-05 10:21:54 +0200150 }
151
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200152 /* Disable IRQ mode for the current channel*/
Neil Armstrongc2de6202024-04-05 10:21:53 +0200153 writel(0x0, priv->spmi_chnls + ch_offset + SPMI_REG_CONFIG);
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200154
155 /* Write single byte */
Neil Armstrongc2de6202024-04-05 10:21:53 +0200156 writel(val, priv->spmi_chnls + ch_offset + SPMI_REG_WDATA);
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200157
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200158 /* Send write command */
Neil Armstrongc2de6202024-04-05 10:21:53 +0200159 writel(reg, priv->spmi_chnls + ch_offset + SPMI_REG_CMD0);
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200160
161 /* Wait till CMD DONE status */
162 reg = 0;
163 while (!reg) {
Neil Armstrongc2de6202024-04-05 10:21:53 +0200164 reg = readl(priv->spmi_chnls + ch_offset +
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200165 SPMI_REG_STATUS);
166 }
167
168 if (reg ^ SPMI_STATUS_DONE) {
169 printf("SPMI write failure.\n");
170 return -EIO;
171 }
172
173 return 0;
174}
175
176static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off)
177{
178 struct msm_spmi_priv *priv = dev_get_priv(dev);
179 unsigned channel;
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +0300180 unsigned int ch_offset;
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200181 uint32_t reg = 0;
182
183 if (usid >= SPMI_MAX_SLAVES)
184 return -EIO;
185 if (pid >= SPMI_MAX_PERIPH)
186 return -EIO;
187
Neil Armstrong59e04822024-04-05 10:21:55 +0200188 channel = priv->channel_map[usid][pid] & SPMI_CHANNEL_MASK;
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200189
Neil Armstrongf0b604d92024-04-05 10:21:54 +0200190 dev_dbg(dev, "[%d:%d] %s: channel %d\n", usid, pid, __func__, channel);
191
192 switch (priv->arb_ver) {
193 case V1:
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +0300194 ch_offset = SPMI_CH_OFFSET(channel);
195
Neil Armstrongf0b604d92024-04-05 10:21:54 +0200196 /* Prepare read command */
197 reg = pmic_arb_fmt_cmd_v1(SPMI_CMD_EXT_REG_READ_LONG,
198 usid, pid, off);
199 break;
200
201 case V2:
202 ch_offset = SPMI_CH_OFFSET(channel);
203
204 /* Prepare read command */
205 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_READ_LONG, off);
206 break;
207
208 case V5:
209 ch_offset = SPMI_V5_OBS_CH_OFFSET(channel);
210
211 /* Prepare read command */
212 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_READ_LONG, off);
213 break;
Neil Armstrongee1d8aa2024-04-05 10:21:56 +0200214
215 case V7:
216 ch_offset = SPMI_V7_OBS_CH_OFFSET(channel);
217
218 /* Prepare read command */
219 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_READ_LONG, off);
220 break;
Neil Armstrongf0b604d92024-04-05 10:21:54 +0200221 }
222
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200223 /* Disable IRQ mode for the current channel*/
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +0300224 writel(0x0, priv->spmi_obs + ch_offset + SPMI_REG_CONFIG);
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200225
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200226 /* Request read */
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +0300227 writel(reg, priv->spmi_obs + ch_offset + SPMI_REG_CMD0);
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200228
229 /* Wait till CMD DONE status */
230 reg = 0;
231 while (!reg) {
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +0300232 reg = readl(priv->spmi_obs + ch_offset + SPMI_REG_STATUS);
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200233 }
234
235 if (reg ^ SPMI_STATUS_DONE) {
236 printf("SPMI read failure.\n");
237 return -EIO;
238 }
239
240 /* Read the data */
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +0300241 return readl(priv->spmi_obs + ch_offset +
242 SPMI_REG_RDATA) & 0xFF;
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200243}
244
245static struct dm_spmi_ops msm_spmi_ops = {
246 .read = msm_spmi_read,
247 .write = msm_spmi_write,
248};
249
250static int msm_spmi_probe(struct udevice *dev)
251{
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200252 struct msm_spmi_priv *priv = dev_get_priv(dev);
Caleb Connolly92fe0892023-12-05 13:46:53 +0000253 phys_addr_t core_addr;
Jorge Ramirez-Ortiz210d9592018-01-10 11:33:28 +0100254 u32 hw_ver;
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200255 int i;
256
Caleb Connolly92fe0892023-12-05 13:46:53 +0000257 core_addr = dev_read_addr_name(dev, "core");
258 priv->spmi_chnls = dev_read_addr_name(dev, "chnls");
259 priv->spmi_obs = dev_read_addr_name(dev, "obsrvr");
Neil Armstrong59e04822024-04-05 10:21:55 +0200260 dev_read_u32(dev, "qcom,ee", &priv->owner);
Jorge Ramirez-Ortiz210d9592018-01-10 11:33:28 +0100261
Caleb Connolly92fe0892023-12-05 13:46:53 +0000262 hw_ver = readl(core_addr + PMIC_ARB_VERSION);
Jorge Ramirez-Ortiz210d9592018-01-10 11:33:28 +0100263
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +0300264 if (hw_ver < PMIC_ARB_VERSION_V3_MIN) {
265 priv->arb_ver = V2;
Caleb Connolly92fe0892023-12-05 13:46:53 +0000266 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V1_V2_V3;
Neil Armstrong59e04822024-04-05 10:21:55 +0200267 priv->max_channels = SPMI_MAX_CHANNELS;
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +0300268 } else if (hw_ver < PMIC_ARB_VERSION_V5_MIN) {
269 priv->arb_ver = V3;
Caleb Connolly92fe0892023-12-05 13:46:53 +0000270 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V1_V2_V3;
Neil Armstrong59e04822024-04-05 10:21:55 +0200271 priv->max_channels = SPMI_MAX_CHANNELS;
Neil Armstrongee1d8aa2024-04-05 10:21:56 +0200272 } else if (hw_ver < PMIC_ARB_VERSION_V7_MIN) {
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +0300273 priv->arb_ver = V5;
Caleb Connolly92fe0892023-12-05 13:46:53 +0000274 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V5;
Neil Armstrongee1d8aa2024-04-05 10:21:56 +0200275 priv->max_channels = SPMI_MAX_CHANNELS;
276 priv->spmi_cnfg = dev_read_addr_name(dev, "cnfg");
277 } else {
278 /* TOFIX: handle second bus */
279 priv->arb_ver = V7;
280 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V7;
281 priv->max_channels = SPMI_MAX_CHANNELS_V7;
Neil Armstrong59e04822024-04-05 10:21:55 +0200282 priv->spmi_cnfg = dev_read_addr_name(dev, "cnfg");
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +0300283 }
284
Caleb Connolly92fe0892023-12-05 13:46:53 +0000285 dev_dbg(dev, "PMIC Arb Version-%d (%#x)\n", hw_ver >> 28, hw_ver);
Jorge Ramirez-Ortiz210d9592018-01-10 11:33:28 +0100286
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200287 if (priv->arb_chnl == FDT_ADDR_T_NONE ||
Caleb Connolly92fe0892023-12-05 13:46:53 +0000288 priv->spmi_chnls == FDT_ADDR_T_NONE ||
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200289 priv->spmi_obs == FDT_ADDR_T_NONE)
290 return -EINVAL;
291
Caleb Connolly92fe0892023-12-05 13:46:53 +0000292 dev_dbg(dev, "priv->arb_chnl address (%#08llx)\n", priv->arb_chnl);
293 dev_dbg(dev, "priv->spmi_chnls address (%#08llx)\n", priv->spmi_chnls);
294 dev_dbg(dev, "priv->spmi_obs address (%#08llx)\n", priv->spmi_obs);
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200295 /* Scan peripherals connected to each SPMI channel */
Neil Armstrong59e04822024-04-05 10:21:55 +0200296 for (i = 0; i < priv->max_channels; i++) {
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200297 uint32_t periph = readl(priv->arb_chnl + ARB_CHANNEL_OFFSET(i));
298 uint8_t slave_id = (periph & 0xf0000) >> 16;
299 uint8_t pid = (periph & 0xff00) >> 8;
300
301 priv->channel_map[slave_id][pid] = i;
Neil Armstrong59e04822024-04-05 10:21:55 +0200302
303 /* Mark channels read-only when from different owner */
Neil Armstrongee1d8aa2024-04-05 10:21:56 +0200304 if (priv->arb_ver == V5 || priv->arb_ver == V7) {
Neil Armstrong59e04822024-04-05 10:21:55 +0200305 uint32_t cnfg = readl(priv->spmi_cnfg + ARB_CHANNEL_OFFSET(i));
306 uint8_t owner = SPMI_OWNERSHIP_PERIPH2OWNER(cnfg);
307
308 if (owner != priv->owner)
309 priv->channel_map[slave_id][pid] |= SPMI_CHANNEL_READ_ONLY;
310 }
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200311 }
312 return 0;
313}
314
315static const struct udevice_id msm_spmi_ids[] = {
316 { .compatible = "qcom,spmi-pmic-arb" },
317 { }
318};
319
320U_BOOT_DRIVER(msm_spmi) = {
321 .name = "msm_spmi",
322 .id = UCLASS_SPMI,
323 .of_match = msm_spmi_ids,
324 .ops = &msm_spmi_ops,
325 .probe = msm_spmi_probe,
Dzmitry Sankouskif5a2d6b2021-10-17 13:44:28 +0300326 .priv_auto = sizeof(struct msm_spmi_priv),
Mateusz Kulikowski5b472712016-03-31 23:12:29 +0200327};