]> git.ipfire.org Git - thirdparty/u-boot.git/blame - drivers/spmi/spmi-msm.c
Revert "Merge patch series "arm: dts: am62-beagleplay: Fix Beagleplay Ethernet""
[thirdparty/u-boot.git] / drivers / spmi / spmi-msm.c
CommitLineData
83d290c5 1// SPDX-License-Identifier: BSD-3-Clause
5b47271c
MK
2/*
3 * Qualcomm SPMI bus driver
4 *
5 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
6 *
7 * Loosely based on Little Kernel driver
5b47271c
MK
8 */
9
d678a59d 10#include <common.h>
5b47271c
MK
11#include <dm.h>
12#include <errno.h>
13#include <fdtdec.h>
401d1c4f 14#include <asm/global_data.h>
5b47271c 15#include <asm/io.h>
336d4615 16#include <dm/device_compat.h>
5b47271c
MK
17#include <spmi/spmi.h>
18
19DECLARE_GLOBAL_DATA_PTR;
20
210d9592 21/* PMIC Arbiter configuration registers */
f5a2d6b4
DS
22#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
ee1d8aa5 26#define PMIC_ARB_VERSION_V7_MIN 0x70000000
f5a2d6b4
DS
27
28#define APID_MAP_OFFSET_V1_V2_V3 (0x800)
29#define APID_MAP_OFFSET_V5 (0x900)
ee1d8aa5 30#define APID_MAP_OFFSET_V7 (0x2000)
f5a2d6b4
DS
31#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)
ee1d8aa5 34#define SPMI_V7_OBS_CH_OFFSET(chnl) ((chnl) * 0x20)
f5a2d6b4 35#define SPMI_V5_RW_CH_OFFSET(chnl) ((chnl) * 0x10000)
ee1d8aa5 36#define SPMI_V7_RW_CH_OFFSET(chnl) ((chnl) * 0x1000)
f5a2d6b4 37
59e0482b
NA
38#define SPMI_OWNERSHIP_PERIPH2OWNER(x) ((x) & 0x7)
39
f5a2d6b4
DS
40#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
45
46#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
51
52#define SPMI_CMD_EXT_REG_WRITE_LONG 0x00
53#define SPMI_CMD_EXT_REG_READ_LONG 0x01
54
55#define SPMI_STATUS_DONE 0x1
56
57#define SPMI_MAX_CHANNELS 128
59e0482b 58#define SPMI_MAX_CHANNELS_V5 512
ee1d8aa5 59#define SPMI_MAX_CHANNELS_V7 1024
f5a2d6b4
DS
60#define SPMI_MAX_SLAVES 16
61#define SPMI_MAX_PERIPH 256
62
59e0482b
NA
63#define SPMI_CHANNEL_READ_ONLY BIT(31)
64#define SPMI_CHANNEL_MASK 0xffff
65
f5a2d6b4
DS
66enum arb_ver {
67 V1 = 1,
68 V2,
69 V3,
ee1d8aa5
NA
70 V5 = 5,
71 V7 = 7
f5a2d6b4 72};
5b47271c 73
f5a2d6b4
DS
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};
5b47271c
MK
82
83struct msm_spmi_priv {
f5a2d6b4 84 phys_addr_t arb_chnl; /* ARB channel mapping base */
92fe0892 85 phys_addr_t spmi_chnls; /* SPMI channels */
f5a2d6b4 86 phys_addr_t spmi_obs; /* SPMI observer */
59e0482b
NA
87 phys_addr_t spmi_cnfg; /* SPMI config */
88 u32 owner; /* Current owner */
89 unsigned int max_channels; /* Max channels */
5b47271c 90 /* SPMI channel map */
59e0482b 91 uint32_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH];
f5a2d6b4
DS
92 /* SPMI bus arbiter version */
93 u32 arb_ver;
5b47271c
MK
94};
95
f0b604d9
NA
96static 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
5b47271c
MK
106static 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;
f5a2d6b4 111 unsigned int ch_offset;
5b47271c
MK
112 uint32_t reg = 0;
113
114 if (usid >= SPMI_MAX_SLAVES)
115 return -EIO;
116 if (pid >= SPMI_MAX_PERIPH)
117 return -EIO;
59e0482b
NA
118 if (priv->channel_map[usid][pid] & SPMI_CHANNEL_READ_ONLY)
119 return -EPERM;
5b47271c 120
59e0482b 121 channel = priv->channel_map[usid][pid] & SPMI_CHANNEL_MASK;
5b47271c 122
f0b604d9
NA
123 dev_dbg(dev, "[%d:%d] %s: channel %d\n", usid, pid, __func__, channel);
124
125 switch (priv->arb_ver) {
126 case V1:
127 ch_offset = SPMI_CH_OFFSET(channel);
128
129 reg = pmic_arb_fmt_cmd_v1(SPMI_CMD_EXT_REG_WRITE_LONG,
130 usid, pid, off);
131 break;
132
133 case V2:
c2de620d
NA
134 ch_offset = SPMI_CH_OFFSET(channel);
135
f0b604d9
NA
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
ee1d8aa5
NA
142 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_WRITE_LONG, off);
143 break;
144
145 case V7:
146 ch_offset = SPMI_V7_RW_CH_OFFSET(channel);
147
f0b604d9
NA
148 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_WRITE_LONG, off);
149 break;
150 }
151
5b47271c 152 /* Disable IRQ mode for the current channel*/
c2de620d 153 writel(0x0, priv->spmi_chnls + ch_offset + SPMI_REG_CONFIG);
5b47271c
MK
154
155 /* Write single byte */
c2de620d 156 writel(val, priv->spmi_chnls + ch_offset + SPMI_REG_WDATA);
5b47271c 157
5b47271c 158 /* Send write command */
c2de620d 159 writel(reg, priv->spmi_chnls + ch_offset + SPMI_REG_CMD0);
5b47271c
MK
160
161 /* Wait till CMD DONE status */
162 reg = 0;
163 while (!reg) {
c2de620d 164 reg = readl(priv->spmi_chnls + ch_offset +
5b47271c
MK
165 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;
f5a2d6b4 180 unsigned int ch_offset;
5b47271c
MK
181 uint32_t reg = 0;
182
183 if (usid >= SPMI_MAX_SLAVES)
184 return -EIO;
185 if (pid >= SPMI_MAX_PERIPH)
186 return -EIO;
187
59e0482b 188 channel = priv->channel_map[usid][pid] & SPMI_CHANNEL_MASK;
5b47271c 189
f0b604d9
NA
190 dev_dbg(dev, "[%d:%d] %s: channel %d\n", usid, pid, __func__, channel);
191
192 switch (priv->arb_ver) {
193 case V1:
194 ch_offset = SPMI_CH_OFFSET(channel);
195
196 /* 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:
f5a2d6b4
DS
202 ch_offset = SPMI_CH_OFFSET(channel);
203
f0b604d9
NA
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
ee1d8aa5
NA
211 /* Prepare read command */
212 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_READ_LONG, off);
213 break;
214
215 case V7:
216 ch_offset = SPMI_V7_OBS_CH_OFFSET(channel);
217
f0b604d9
NA
218 /* Prepare read command */
219 reg = pmic_arb_fmt_cmd_v2(SPMI_CMD_EXT_REG_READ_LONG, off);
220 break;
221 }
222
5b47271c 223 /* Disable IRQ mode for the current channel*/
f5a2d6b4 224 writel(0x0, priv->spmi_obs + ch_offset + SPMI_REG_CONFIG);
5b47271c 225
5b47271c 226 /* Request read */
f5a2d6b4 227 writel(reg, priv->spmi_obs + ch_offset + SPMI_REG_CMD0);
5b47271c
MK
228
229 /* Wait till CMD DONE status */
230 reg = 0;
231 while (!reg) {
f5a2d6b4 232 reg = readl(priv->spmi_obs + ch_offset + SPMI_REG_STATUS);
5b47271c
MK
233 }
234
235 if (reg ^ SPMI_STATUS_DONE) {
236 printf("SPMI read failure.\n");
237 return -EIO;
238 }
239
240 /* Read the data */
f5a2d6b4
DS
241 return readl(priv->spmi_obs + ch_offset +
242 SPMI_REG_RDATA) & 0xFF;
5b47271c
MK
243}
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{
5b47271c 252 struct msm_spmi_priv *priv = dev_get_priv(dev);
92fe0892 253 phys_addr_t core_addr;
210d9592 254 u32 hw_ver;
5b47271c 255 int i;
f5a2d6b4 256
92fe0892
CC
257 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");
59e0482b 260 dev_read_u32(dev, "qcom,ee", &priv->owner);
f5a2d6b4 261
92fe0892 262 hw_ver = readl(core_addr + PMIC_ARB_VERSION);
f5a2d6b4
DS
263
264 if (hw_ver < PMIC_ARB_VERSION_V3_MIN) {
265 priv->arb_ver = V2;
92fe0892 266 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V1_V2_V3;
59e0482b 267 priv->max_channels = SPMI_MAX_CHANNELS;
f5a2d6b4
DS
268 } else if (hw_ver < PMIC_ARB_VERSION_V5_MIN) {
269 priv->arb_ver = V3;
92fe0892 270 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V1_V2_V3;
59e0482b 271 priv->max_channels = SPMI_MAX_CHANNELS;
ee1d8aa5 272 } else if (hw_ver < PMIC_ARB_VERSION_V7_MIN) {
f5a2d6b4 273 priv->arb_ver = V5;
92fe0892 274 priv->arb_chnl = core_addr + APID_MAP_OFFSET_V5;
ee1d8aa5
NA
275 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;
59e0482b 282 priv->spmi_cnfg = dev_read_addr_name(dev, "cnfg");
f5a2d6b4 283 }
5b47271c 284
92fe0892 285 dev_dbg(dev, "PMIC Arb Version-%d (%#x)\n", hw_ver >> 28, hw_ver);
210d9592 286
5b47271c 287 if (priv->arb_chnl == FDT_ADDR_T_NONE ||
92fe0892 288 priv->spmi_chnls == FDT_ADDR_T_NONE ||
5b47271c
MK
289 priv->spmi_obs == FDT_ADDR_T_NONE)
290 return -EINVAL;
291
92fe0892
CC
292 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);
5b47271c 295 /* Scan peripherals connected to each SPMI channel */
59e0482b 296 for (i = 0; i < priv->max_channels; i++) {
5b47271c
MK
297 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;
59e0482b
NA
302
303 /* Mark channels read-only when from different owner */
ee1d8aa5 304 if (priv->arb_ver == V5 || priv->arb_ver == V7) {
59e0482b
NA
305 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 }
5b47271c
MK
311 }
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,
f5a2d6b4 326 .priv_auto = sizeof(struct msm_spmi_priv),
5b47271c 327};