]>
Commit | Line | Data |
---|---|---|
d0c6ae41 GA |
1 | /* |
2 | * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. | |
39ae93e3 KH |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 and | |
6 | * only version 2 as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | */ | |
987a9f12 | 13 | #include <linux/bitmap.h> |
39ae93e3 KH |
14 | #include <linux/delay.h> |
15 | #include <linux/err.h> | |
16 | #include <linux/interrupt.h> | |
17 | #include <linux/io.h> | |
67b563f1 JC |
18 | #include <linux/irqchip/chained_irq.h> |
19 | #include <linux/irqdomain.h> | |
20 | #include <linux/irq.h> | |
39ae93e3 KH |
21 | #include <linux/kernel.h> |
22 | #include <linux/module.h> | |
23 | #include <linux/of.h> | |
24 | #include <linux/platform_device.h> | |
25 | #include <linux/slab.h> | |
26 | #include <linux/spmi.h> | |
27 | ||
28 | /* PMIC Arbiter configuration registers */ | |
29 | #define PMIC_ARB_VERSION 0x0000 | |
d0c6ae41 | 30 | #define PMIC_ARB_VERSION_V2_MIN 0x20010000 |
319f6884 | 31 | #define PMIC_ARB_VERSION_V3_MIN 0x30000000 |
39ae93e3 KH |
32 | #define PMIC_ARB_INT_EN 0x0004 |
33 | ||
d0c6ae41 GA |
34 | /* PMIC Arbiter channel registers offsets */ |
35 | #define PMIC_ARB_CMD 0x00 | |
36 | #define PMIC_ARB_CONFIG 0x04 | |
37 | #define PMIC_ARB_STATUS 0x08 | |
38 | #define PMIC_ARB_WDATA0 0x10 | |
39 | #define PMIC_ARB_WDATA1 0x14 | |
40 | #define PMIC_ARB_RDATA0 0x18 | |
41 | #define PMIC_ARB_RDATA1 0x1C | |
42 | #define PMIC_ARB_REG_CHNL(N) (0x800 + 0x4 * (N)) | |
39ae93e3 KH |
43 | |
44 | /* Mapping Table */ | |
45 | #define SPMI_MAPPING_TABLE_REG(N) (0x0B00 + (4 * (N))) | |
46 | #define SPMI_MAPPING_BIT_INDEX(X) (((X) >> 18) & 0xF) | |
47 | #define SPMI_MAPPING_BIT_IS_0_FLAG(X) (((X) >> 17) & 0x1) | |
48 | #define SPMI_MAPPING_BIT_IS_0_RESULT(X) (((X) >> 9) & 0xFF) | |
49 | #define SPMI_MAPPING_BIT_IS_1_FLAG(X) (((X) >> 8) & 0x1) | |
50 | #define SPMI_MAPPING_BIT_IS_1_RESULT(X) (((X) >> 0) & 0xFF) | |
51 | ||
39ae93e3 | 52 | #define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */ |
987a9f12 SB |
53 | #define PMIC_ARB_MAX_PPID BIT(12) /* PPID is 12bit */ |
54 | #define PMIC_ARB_CHAN_VALID BIT(15) | |
39ae93e3 KH |
55 | |
56 | /* Ownership Table */ | |
57 | #define SPMI_OWNERSHIP_TABLE_REG(N) (0x0700 + (4 * (N))) | |
58 | #define SPMI_OWNERSHIP_PERIPH2OWNER(X) ((X) & 0x7) | |
59 | ||
60 | /* Channel Status fields */ | |
61 | enum pmic_arb_chnl_status { | |
111a10bf AD |
62 | PMIC_ARB_STATUS_DONE = BIT(0), |
63 | PMIC_ARB_STATUS_FAILURE = BIT(1), | |
64 | PMIC_ARB_STATUS_DENIED = BIT(2), | |
65 | PMIC_ARB_STATUS_DROPPED = BIT(3), | |
39ae93e3 KH |
66 | }; |
67 | ||
68 | /* Command register fields */ | |
69 | #define PMIC_ARB_CMD_MAX_BYTE_COUNT 8 | |
70 | ||
71 | /* Command Opcodes */ | |
72 | enum pmic_arb_cmd_op_code { | |
73 | PMIC_ARB_OP_EXT_WRITEL = 0, | |
74 | PMIC_ARB_OP_EXT_READL = 1, | |
75 | PMIC_ARB_OP_EXT_WRITE = 2, | |
76 | PMIC_ARB_OP_RESET = 3, | |
77 | PMIC_ARB_OP_SLEEP = 4, | |
78 | PMIC_ARB_OP_SHUTDOWN = 5, | |
79 | PMIC_ARB_OP_WAKEUP = 6, | |
80 | PMIC_ARB_OP_AUTHENTICATE = 7, | |
81 | PMIC_ARB_OP_MSTR_READ = 8, | |
82 | PMIC_ARB_OP_MSTR_WRITE = 9, | |
83 | PMIC_ARB_OP_EXT_READ = 13, | |
84 | PMIC_ARB_OP_WRITE = 14, | |
85 | PMIC_ARB_OP_READ = 15, | |
86 | PMIC_ARB_OP_ZERO_WRITE = 16, | |
87 | }; | |
88 | ||
89 | /* Maximum number of support PMIC peripherals */ | |
987a9f12 | 90 | #define PMIC_ARB_MAX_PERIPHS 512 |
39ae93e3 KH |
91 | #define PMIC_ARB_TIMEOUT_US 100 |
92 | #define PMIC_ARB_MAX_TRANS_BYTES (8) | |
93 | ||
94 | #define PMIC_ARB_APID_MASK 0xFF | |
95 | #define PMIC_ARB_PPID_MASK 0xFFF | |
96 | ||
97 | /* interrupt enable bit */ | |
98 | #define SPMI_PIC_ACC_ENABLE_BIT BIT(0) | |
99 | ||
319f6884 AD |
100 | #define HWIRQ(slave_id, periph_id, irq_id, apid) \ |
101 | ((((slave_id) & 0xF) << 28) | \ | |
102 | (((periph_id) & 0xFF) << 20) | \ | |
103 | (((irq_id) & 0x7) << 16) | \ | |
104 | (((apid) & 0x1FF) << 0)) | |
105 | ||
106 | #define HWIRQ_SID(hwirq) (((hwirq) >> 28) & 0xF) | |
107 | #define HWIRQ_PER(hwirq) (((hwirq) >> 20) & 0xFF) | |
108 | #define HWIRQ_IRQ(hwirq) (((hwirq) >> 16) & 0x7) | |
109 | #define HWIRQ_APID(hwirq) (((hwirq) >> 0) & 0x1FF) | |
110 | ||
d0c6ae41 GA |
111 | struct pmic_arb_ver_ops; |
112 | ||
6bc546e7 AD |
113 | struct apid_data { |
114 | u16 ppid; | |
115 | u8 owner; | |
116 | }; | |
117 | ||
39ae93e3 | 118 | /** |
111a10bf | 119 | * spmi_pmic_arb - SPMI PMIC Arbiter object |
39ae93e3 | 120 | * |
d0c6ae41 GA |
121 | * @rd_base: on v1 "core", on v2 "observer" register base off DT. |
122 | * @wr_base: on v1 "core", on v2 "chnls" register base off DT. | |
39ae93e3 KH |
123 | * @intr: address of the SPMI interrupt control registers. |
124 | * @cnfg: address of the PMIC Arbiter configuration registers. | |
125 | * @lock: lock to synchronize accesses. | |
d0c6ae41 | 126 | * @channel: execution environment channel to use for accesses. |
67b563f1 JC |
127 | * @irq: PMIC ARB interrupt. |
128 | * @ee: the current Execution Environment | |
129 | * @min_apid: minimum APID (used for bounding IRQ search) | |
130 | * @max_apid: maximum APID | |
57102ad7 | 131 | * @max_periph: maximum number of PMIC peripherals supported by HW. |
67b563f1 JC |
132 | * @mapping_table: in-memory copy of PPID -> APID mapping table. |
133 | * @domain: irq domain object for PMIC IRQ domain | |
134 | * @spmic: SPMI controller object | |
d0c6ae41 | 135 | * @ver_ops: version dependent operations. |
1ef1ce4e | 136 | * @ppid_to_apid in-memory copy of PPID -> channel (APID) mapping table. |
39ae93e3 | 137 | */ |
111a10bf | 138 | struct spmi_pmic_arb { |
d0c6ae41 GA |
139 | void __iomem *rd_base; |
140 | void __iomem *wr_base; | |
39ae93e3 KH |
141 | void __iomem *intr; |
142 | void __iomem *cnfg; | |
987a9f12 SB |
143 | void __iomem *core; |
144 | resource_size_t core_size; | |
39ae93e3 KH |
145 | raw_spinlock_t lock; |
146 | u8 channel; | |
67b563f1 JC |
147 | int irq; |
148 | u8 ee; | |
987a9f12 SB |
149 | u16 min_apid; |
150 | u16 max_apid; | |
57102ad7 | 151 | u16 max_periph; |
987a9f12 SB |
152 | u32 *mapping_table; |
153 | DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS); | |
67b563f1 JC |
154 | struct irq_domain *domain; |
155 | struct spmi_controller *spmic; | |
d0c6ae41 | 156 | const struct pmic_arb_ver_ops *ver_ops; |
1ef1ce4e AD |
157 | u16 *ppid_to_apid; |
158 | u16 last_apid; | |
6bc546e7 | 159 | struct apid_data apid_data[PMIC_ARB_MAX_PERIPHS]; |
d0c6ae41 GA |
160 | }; |
161 | ||
162 | /** | |
163 | * pmic_arb_ver: version dependent functionality. | |
164 | * | |
319f6884 AD |
165 | * @ver_str: version string. |
166 | * @ppid_to_apid: finds the apid for a given ppid. | |
167 | * @mode: access rights to specified pmic peripheral. | |
d0c6ae41 GA |
168 | * @non_data_cmd: on v1 issues an spmi non-data command. |
169 | * on v2 no HW support, returns -EOPNOTSUPP. | |
170 | * @offset: on v1 offset of per-ee channel. | |
171 | * on v2 offset of per-ee and per-ppid channel. | |
172 | * @fmt_cmd: formats a GENI/SPMI command. | |
173 | * @owner_acc_status: on v1 offset of PMIC_ARB_SPMI_PIC_OWNERm_ACC_STATUSn | |
174 | * on v2 offset of SPMI_PIC_OWNERm_ACC_STATUSn. | |
175 | * @acc_enable: on v1 offset of PMIC_ARB_SPMI_PIC_ACC_ENABLEn | |
176 | * on v2 offset of SPMI_PIC_ACC_ENABLEn. | |
177 | * @irq_status: on v1 offset of PMIC_ARB_SPMI_PIC_IRQ_STATUSn | |
178 | * on v2 offset of SPMI_PIC_IRQ_STATUSn. | |
179 | * @irq_clear: on v1 offset of PMIC_ARB_SPMI_PIC_IRQ_CLEARn | |
180 | * on v2 offset of SPMI_PIC_IRQ_CLEARn. | |
181 | */ | |
182 | struct pmic_arb_ver_ops { | |
319f6884 | 183 | const char *ver_str; |
7f1d4e58 | 184 | int (*ppid_to_apid)(struct spmi_pmic_arb *pa, u8 sid, u16 addr, |
319f6884 | 185 | u16 *apid); |
111a10bf | 186 | int (*mode)(struct spmi_pmic_arb *dev, u8 sid, u16 addr, |
57102ad7 | 187 | mode_t *mode); |
d0c6ae41 | 188 | /* spmi commands (read_cmd, write_cmd, cmd) functionality */ |
111a10bf | 189 | int (*offset)(struct spmi_pmic_arb *dev, u8 sid, u16 addr, |
987a9f12 | 190 | u32 *offset); |
d0c6ae41 GA |
191 | u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc); |
192 | int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid); | |
193 | /* Interrupts controller functionality (offset of PIC registers) */ | |
319f6884 AD |
194 | u32 (*owner_acc_status)(u8 m, u16 n); |
195 | u32 (*acc_enable)(u16 n); | |
196 | u32 (*irq_status)(u16 n); | |
197 | u32 (*irq_clear)(u16 n); | |
39ae93e3 KH |
198 | }; |
199 | ||
111a10bf | 200 | static inline void pmic_arb_base_write(struct spmi_pmic_arb *pa, |
39ae93e3 KH |
201 | u32 offset, u32 val) |
202 | { | |
111a10bf | 203 | writel_relaxed(val, pa->wr_base + offset); |
d0c6ae41 GA |
204 | } |
205 | ||
111a10bf | 206 | static inline void pmic_arb_set_rd_cmd(struct spmi_pmic_arb *pa, |
d0c6ae41 GA |
207 | u32 offset, u32 val) |
208 | { | |
111a10bf | 209 | writel_relaxed(val, pa->rd_base + offset); |
39ae93e3 KH |
210 | } |
211 | ||
212 | /** | |
213 | * pa_read_data: reads pmic-arb's register and copy 1..4 bytes to buf | |
214 | * @bc: byte count -1. range: 0..3 | |
215 | * @reg: register's address | |
216 | * @buf: output parameter, length must be bc + 1 | |
217 | */ | |
111a10bf | 218 | static void pa_read_data(struct spmi_pmic_arb *pa, u8 *buf, u32 reg, u8 bc) |
39ae93e3 | 219 | { |
111a10bf AD |
220 | u32 data = __raw_readl(pa->rd_base + reg); |
221 | ||
39ae93e3 KH |
222 | memcpy(buf, &data, (bc & 3) + 1); |
223 | } | |
224 | ||
225 | /** | |
226 | * pa_write_data: write 1..4 bytes from buf to pmic-arb's register | |
227 | * @bc: byte-count -1. range: 0..3. | |
228 | * @reg: register's address. | |
229 | * @buf: buffer to write. length must be bc + 1. | |
230 | */ | |
231 | static void | |
111a10bf | 232 | pa_write_data(struct spmi_pmic_arb *pa, const u8 *buf, u32 reg, u8 bc) |
39ae93e3 KH |
233 | { |
234 | u32 data = 0; | |
111a10bf | 235 | |
39ae93e3 | 236 | memcpy(&data, buf, (bc & 3) + 1); |
111a10bf | 237 | pmic_arb_base_write(pa, reg, data); |
39ae93e3 KH |
238 | } |
239 | ||
d0c6ae41 GA |
240 | static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, |
241 | void __iomem *base, u8 sid, u16 addr) | |
39ae93e3 | 242 | { |
111a10bf | 243 | struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); |
39ae93e3 KH |
244 | u32 status = 0; |
245 | u32 timeout = PMIC_ARB_TIMEOUT_US; | |
987a9f12 SB |
246 | u32 offset; |
247 | int rc; | |
248 | ||
111a10bf | 249 | rc = pa->ver_ops->offset(pa, sid, addr, &offset); |
987a9f12 SB |
250 | if (rc) |
251 | return rc; | |
252 | ||
253 | offset += PMIC_ARB_STATUS; | |
39ae93e3 KH |
254 | |
255 | while (timeout--) { | |
d0c6ae41 | 256 | status = readl_relaxed(base + offset); |
39ae93e3 KH |
257 | |
258 | if (status & PMIC_ARB_STATUS_DONE) { | |
259 | if (status & PMIC_ARB_STATUS_DENIED) { | |
260 | dev_err(&ctrl->dev, | |
261 | "%s: transaction denied (0x%x)\n", | |
262 | __func__, status); | |
263 | return -EPERM; | |
264 | } | |
265 | ||
266 | if (status & PMIC_ARB_STATUS_FAILURE) { | |
267 | dev_err(&ctrl->dev, | |
268 | "%s: transaction failed (0x%x)\n", | |
269 | __func__, status); | |
270 | return -EIO; | |
271 | } | |
272 | ||
273 | if (status & PMIC_ARB_STATUS_DROPPED) { | |
274 | dev_err(&ctrl->dev, | |
275 | "%s: transaction dropped (0x%x)\n", | |
276 | __func__, status); | |
277 | return -EIO; | |
278 | } | |
279 | ||
280 | return 0; | |
281 | } | |
282 | udelay(1); | |
283 | } | |
284 | ||
285 | dev_err(&ctrl->dev, | |
286 | "%s: timeout, status 0x%x\n", | |
287 | __func__, status); | |
288 | return -ETIMEDOUT; | |
289 | } | |
290 | ||
d0c6ae41 GA |
291 | static int |
292 | pmic_arb_non_data_cmd_v1(struct spmi_controller *ctrl, u8 opc, u8 sid) | |
39ae93e3 | 293 | { |
111a10bf | 294 | struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); |
39ae93e3 KH |
295 | unsigned long flags; |
296 | u32 cmd; | |
297 | int rc; | |
987a9f12 SB |
298 | u32 offset; |
299 | ||
111a10bf | 300 | rc = pa->ver_ops->offset(pa, sid, 0, &offset); |
987a9f12 SB |
301 | if (rc) |
302 | return rc; | |
39ae93e3 KH |
303 | |
304 | cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20); | |
305 | ||
111a10bf AD |
306 | raw_spin_lock_irqsave(&pa->lock, flags); |
307 | pmic_arb_base_write(pa, offset + PMIC_ARB_CMD, cmd); | |
308 | rc = pmic_arb_wait_for_done(ctrl, pa->wr_base, sid, 0); | |
309 | raw_spin_unlock_irqrestore(&pa->lock, flags); | |
39ae93e3 KH |
310 | |
311 | return rc; | |
312 | } | |
313 | ||
d0c6ae41 GA |
314 | static int |
315 | pmic_arb_non_data_cmd_v2(struct spmi_controller *ctrl, u8 opc, u8 sid) | |
316 | { | |
317 | return -EOPNOTSUPP; | |
318 | } | |
319 | ||
320 | /* Non-data command */ | |
321 | static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) | |
322 | { | |
111a10bf | 323 | struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); |
d0c6ae41 GA |
324 | |
325 | dev_dbg(&ctrl->dev, "cmd op:0x%x sid:%d\n", opc, sid); | |
326 | ||
327 | /* Check for valid non-data command */ | |
328 | if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP) | |
329 | return -EINVAL; | |
330 | ||
111a10bf | 331 | return pa->ver_ops->non_data_cmd(ctrl, opc, sid); |
d0c6ae41 GA |
332 | } |
333 | ||
39ae93e3 KH |
334 | static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, |
335 | u16 addr, u8 *buf, size_t len) | |
336 | { | |
111a10bf | 337 | struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); |
39ae93e3 KH |
338 | unsigned long flags; |
339 | u8 bc = len - 1; | |
340 | u32 cmd; | |
341 | int rc; | |
987a9f12 | 342 | u32 offset; |
57102ad7 | 343 | mode_t mode; |
987a9f12 | 344 | |
111a10bf | 345 | rc = pa->ver_ops->offset(pa, sid, addr, &offset); |
987a9f12 SB |
346 | if (rc) |
347 | return rc; | |
39ae93e3 | 348 | |
111a10bf | 349 | rc = pa->ver_ops->mode(pa, sid, addr, &mode); |
57102ad7 AD |
350 | if (rc) |
351 | return rc; | |
352 | ||
353 | if (!(mode & S_IRUSR)) { | |
111a10bf | 354 | dev_err(&pa->spmic->dev, |
57102ad7 AD |
355 | "error: impermissible read from peripheral sid:%d addr:0x%x\n", |
356 | sid, addr); | |
357 | return -EPERM; | |
358 | } | |
359 | ||
39ae93e3 KH |
360 | if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { |
361 | dev_err(&ctrl->dev, | |
d0c6ae41 | 362 | "pmic-arb supports 1..%d bytes per trans, but:%zu requested", |
39ae93e3 KH |
363 | PMIC_ARB_MAX_TRANS_BYTES, len); |
364 | return -EINVAL; | |
365 | } | |
366 | ||
367 | /* Check the opcode */ | |
368 | if (opc >= 0x60 && opc <= 0x7F) | |
369 | opc = PMIC_ARB_OP_READ; | |
370 | else if (opc >= 0x20 && opc <= 0x2F) | |
371 | opc = PMIC_ARB_OP_EXT_READ; | |
372 | else if (opc >= 0x38 && opc <= 0x3F) | |
373 | opc = PMIC_ARB_OP_EXT_READL; | |
374 | else | |
375 | return -EINVAL; | |
376 | ||
111a10bf | 377 | cmd = pa->ver_ops->fmt_cmd(opc, sid, addr, bc); |
39ae93e3 | 378 | |
111a10bf AD |
379 | raw_spin_lock_irqsave(&pa->lock, flags); |
380 | pmic_arb_set_rd_cmd(pa, offset + PMIC_ARB_CMD, cmd); | |
381 | rc = pmic_arb_wait_for_done(ctrl, pa->rd_base, sid, addr); | |
39ae93e3 KH |
382 | if (rc) |
383 | goto done; | |
384 | ||
111a10bf | 385 | pa_read_data(pa, buf, offset + PMIC_ARB_RDATA0, |
39ae93e3 KH |
386 | min_t(u8, bc, 3)); |
387 | ||
388 | if (bc > 3) | |
111a10bf | 389 | pa_read_data(pa, buf + 4, offset + PMIC_ARB_RDATA1, bc - 4); |
39ae93e3 KH |
390 | |
391 | done: | |
111a10bf | 392 | raw_spin_unlock_irqrestore(&pa->lock, flags); |
39ae93e3 KH |
393 | return rc; |
394 | } | |
395 | ||
396 | static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, | |
397 | u16 addr, const u8 *buf, size_t len) | |
398 | { | |
111a10bf | 399 | struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); |
39ae93e3 KH |
400 | unsigned long flags; |
401 | u8 bc = len - 1; | |
402 | u32 cmd; | |
403 | int rc; | |
987a9f12 | 404 | u32 offset; |
57102ad7 | 405 | mode_t mode; |
987a9f12 | 406 | |
111a10bf | 407 | rc = pa->ver_ops->offset(pa, sid, addr, &offset); |
987a9f12 SB |
408 | if (rc) |
409 | return rc; | |
39ae93e3 | 410 | |
111a10bf | 411 | rc = pa->ver_ops->mode(pa, sid, addr, &mode); |
57102ad7 AD |
412 | if (rc) |
413 | return rc; | |
414 | ||
415 | if (!(mode & S_IWUSR)) { | |
111a10bf | 416 | dev_err(&pa->spmic->dev, |
57102ad7 AD |
417 | "error: impermissible write to peripheral sid:%d addr:0x%x\n", |
418 | sid, addr); | |
419 | return -EPERM; | |
420 | } | |
421 | ||
39ae93e3 KH |
422 | if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { |
423 | dev_err(&ctrl->dev, | |
d0c6ae41 | 424 | "pmic-arb supports 1..%d bytes per trans, but:%zu requested", |
39ae93e3 KH |
425 | PMIC_ARB_MAX_TRANS_BYTES, len); |
426 | return -EINVAL; | |
427 | } | |
428 | ||
429 | /* Check the opcode */ | |
430 | if (opc >= 0x40 && opc <= 0x5F) | |
431 | opc = PMIC_ARB_OP_WRITE; | |
432 | else if (opc >= 0x00 && opc <= 0x0F) | |
433 | opc = PMIC_ARB_OP_EXT_WRITE; | |
434 | else if (opc >= 0x30 && opc <= 0x37) | |
435 | opc = PMIC_ARB_OP_EXT_WRITEL; | |
9b76968d | 436 | else if (opc >= 0x80) |
39ae93e3 KH |
437 | opc = PMIC_ARB_OP_ZERO_WRITE; |
438 | else | |
439 | return -EINVAL; | |
440 | ||
111a10bf | 441 | cmd = pa->ver_ops->fmt_cmd(opc, sid, addr, bc); |
39ae93e3 KH |
442 | |
443 | /* Write data to FIFOs */ | |
111a10bf AD |
444 | raw_spin_lock_irqsave(&pa->lock, flags); |
445 | pa_write_data(pa, buf, offset + PMIC_ARB_WDATA0, min_t(u8, bc, 3)); | |
39ae93e3 | 446 | if (bc > 3) |
111a10bf | 447 | pa_write_data(pa, buf + 4, offset + PMIC_ARB_WDATA1, bc - 4); |
39ae93e3 KH |
448 | |
449 | /* Start the transaction */ | |
111a10bf AD |
450 | pmic_arb_base_write(pa, offset + PMIC_ARB_CMD, cmd); |
451 | rc = pmic_arb_wait_for_done(ctrl, pa->wr_base, sid, addr); | |
452 | raw_spin_unlock_irqrestore(&pa->lock, flags); | |
39ae93e3 KH |
453 | |
454 | return rc; | |
455 | } | |
456 | ||
67b563f1 JC |
457 | enum qpnpint_regs { |
458 | QPNPINT_REG_RT_STS = 0x10, | |
459 | QPNPINT_REG_SET_TYPE = 0x11, | |
460 | QPNPINT_REG_POLARITY_HIGH = 0x12, | |
461 | QPNPINT_REG_POLARITY_LOW = 0x13, | |
462 | QPNPINT_REG_LATCHED_CLR = 0x14, | |
463 | QPNPINT_REG_EN_SET = 0x15, | |
464 | QPNPINT_REG_EN_CLR = 0x16, | |
465 | QPNPINT_REG_LATCHED_STS = 0x18, | |
466 | }; | |
467 | ||
468 | struct spmi_pmic_arb_qpnpint_type { | |
469 | u8 type; /* 1 -> edge */ | |
470 | u8 polarity_high; | |
471 | u8 polarity_low; | |
472 | } __packed; | |
473 | ||
474 | /* Simplified accessor functions for irqchip callbacks */ | |
475 | static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf, | |
476 | size_t len) | |
477 | { | |
111a10bf | 478 | struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); |
319f6884 AD |
479 | u8 sid = HWIRQ_SID(d->hwirq); |
480 | u8 per = HWIRQ_PER(d->hwirq); | |
67b563f1 JC |
481 | |
482 | if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, | |
483 | (per << 8) + reg, buf, len)) | |
484 | dev_err_ratelimited(&pa->spmic->dev, | |
485 | "failed irqchip transaction on %x\n", | |
486 | d->irq); | |
487 | } | |
488 | ||
489 | static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len) | |
490 | { | |
111a10bf | 491 | struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); |
319f6884 AD |
492 | u8 sid = HWIRQ_SID(d->hwirq); |
493 | u8 per = HWIRQ_PER(d->hwirq); | |
67b563f1 JC |
494 | |
495 | if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid, | |
496 | (per << 8) + reg, buf, len)) | |
497 | dev_err_ratelimited(&pa->spmic->dev, | |
498 | "failed irqchip transaction on %x\n", | |
499 | d->irq); | |
500 | } | |
501 | ||
319f6884 | 502 | static void cleanup_irq(struct spmi_pmic_arb *pa, u16 apid, int id) |
6bc546e7 AD |
503 | { |
504 | u16 ppid = pa->apid_data[apid].ppid; | |
505 | u8 sid = ppid >> 8; | |
506 | u8 per = ppid & 0xFF; | |
507 | u8 irq_mask = BIT(id); | |
508 | ||
509 | writel_relaxed(irq_mask, pa->intr + pa->ver_ops->irq_clear(apid)); | |
510 | ||
511 | if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, | |
512 | (per << 8) + QPNPINT_REG_LATCHED_CLR, &irq_mask, 1)) | |
513 | dev_err_ratelimited(&pa->spmic->dev, | |
514 | "failed to ack irq_mask = 0x%x for ppid = %x\n", | |
515 | irq_mask, ppid); | |
516 | ||
517 | if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, | |
518 | (per << 8) + QPNPINT_REG_EN_CLR, &irq_mask, 1)) | |
519 | dev_err_ratelimited(&pa->spmic->dev, | |
520 | "failed to ack irq_mask = 0x%x for ppid = %x\n", | |
521 | irq_mask, ppid); | |
522 | } | |
523 | ||
319f6884 | 524 | static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid) |
67b563f1 JC |
525 | { |
526 | unsigned int irq; | |
527 | u32 status; | |
528 | int id; | |
319f6884 AD |
529 | u8 sid = (pa->apid_data[apid].ppid >> 8) & 0xF; |
530 | u8 per = pa->apid_data[apid].ppid & 0xFF; | |
67b563f1 | 531 | |
d0c6ae41 | 532 | status = readl_relaxed(pa->intr + pa->ver_ops->irq_status(apid)); |
67b563f1 JC |
533 | while (status) { |
534 | id = ffs(status) - 1; | |
111a10bf | 535 | status &= ~BIT(id); |
319f6884 | 536 | irq = irq_find_mapping(pa->domain, HWIRQ(sid, per, id, apid)); |
6bc546e7 AD |
537 | if (irq == 0) { |
538 | cleanup_irq(pa, apid, id); | |
539 | continue; | |
540 | } | |
67b563f1 JC |
541 | generic_handle_irq(irq); |
542 | } | |
543 | } | |
544 | ||
bd0b9ac4 | 545 | static void pmic_arb_chained_irq(struct irq_desc *desc) |
67b563f1 | 546 | { |
111a10bf | 547 | struct spmi_pmic_arb *pa = irq_desc_get_handler_data(desc); |
7fe88f3c | 548 | struct irq_chip *chip = irq_desc_get_chip(desc); |
67b563f1 JC |
549 | void __iomem *intr = pa->intr; |
550 | int first = pa->min_apid >> 5; | |
551 | int last = pa->max_apid >> 5; | |
472eaf8b AD |
552 | u32 status, enable; |
553 | int i, id, apid; | |
67b563f1 JC |
554 | |
555 | chained_irq_enter(chip, desc); | |
556 | ||
557 | for (i = first; i <= last; ++i) { | |
558 | status = readl_relaxed(intr + | |
d0c6ae41 | 559 | pa->ver_ops->owner_acc_status(pa->ee, i)); |
67b563f1 JC |
560 | while (status) { |
561 | id = ffs(status) - 1; | |
111a10bf | 562 | status &= ~BIT(id); |
472eaf8b AD |
563 | apid = id + i * 32; |
564 | enable = readl_relaxed(intr + | |
565 | pa->ver_ops->acc_enable(apid)); | |
566 | if (enable & SPMI_PIC_ACC_ENABLE_BIT) | |
567 | periph_interrupt(pa, apid); | |
67b563f1 JC |
568 | } |
569 | } | |
570 | ||
571 | chained_irq_exit(chip, desc); | |
572 | } | |
573 | ||
574 | static void qpnpint_irq_ack(struct irq_data *d) | |
575 | { | |
111a10bf | 576 | struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); |
319f6884 AD |
577 | u8 irq = HWIRQ_IRQ(d->hwirq); |
578 | u16 apid = HWIRQ_APID(d->hwirq); | |
67b563f1 JC |
579 | u8 data; |
580 | ||
111a10bf | 581 | writel_relaxed(BIT(irq), pa->intr + pa->ver_ops->irq_clear(apid)); |
67b563f1 | 582 | |
111a10bf | 583 | data = BIT(irq); |
67b563f1 JC |
584 | qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1); |
585 | } | |
586 | ||
587 | static void qpnpint_irq_mask(struct irq_data *d) | |
588 | { | |
319f6884 | 589 | u8 irq = HWIRQ_IRQ(d->hwirq); |
6bc546e7 | 590 | u8 data = BIT(irq); |
67b563f1 | 591 | |
67b563f1 JC |
592 | qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1); |
593 | } | |
594 | ||
595 | static void qpnpint_irq_unmask(struct irq_data *d) | |
596 | { | |
111a10bf | 597 | struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); |
319f6884 AD |
598 | u8 irq = HWIRQ_IRQ(d->hwirq); |
599 | u16 apid = HWIRQ_APID(d->hwirq); | |
cee0fad7 | 600 | u8 buf[2]; |
67b563f1 | 601 | |
6bc546e7 AD |
602 | writel_relaxed(SPMI_PIC_ACC_ENABLE_BIT, |
603 | pa->intr + pa->ver_ops->acc_enable(apid)); | |
67b563f1 | 604 | |
cee0fad7 AD |
605 | qpnpint_spmi_read(d, QPNPINT_REG_EN_SET, &buf[0], 1); |
606 | if (!(buf[0] & BIT(irq))) { | |
607 | /* | |
608 | * Since the interrupt is currently disabled, write to both the | |
609 | * LATCHED_CLR and EN_SET registers so that a spurious interrupt | |
610 | * cannot be triggered when the interrupt is enabled | |
611 | */ | |
612 | buf[0] = BIT(irq); | |
613 | buf[1] = BIT(irq); | |
614 | qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &buf, 2); | |
615 | } | |
67b563f1 JC |
616 | } |
617 | ||
67b563f1 JC |
618 | static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type) |
619 | { | |
620 | struct spmi_pmic_arb_qpnpint_type type; | |
319f6884 | 621 | u8 irq = HWIRQ_IRQ(d->hwirq); |
111a10bf | 622 | u8 bit_mask_irq = BIT(irq); |
67b563f1 JC |
623 | |
624 | qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); | |
625 | ||
626 | if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { | |
111a10bf | 627 | type.type |= bit_mask_irq; |
67b563f1 | 628 | if (flow_type & IRQF_TRIGGER_RISING) |
111a10bf | 629 | type.polarity_high |= bit_mask_irq; |
67b563f1 | 630 | if (flow_type & IRQF_TRIGGER_FALLING) |
111a10bf | 631 | type.polarity_low |= bit_mask_irq; |
67b563f1 JC |
632 | } else { |
633 | if ((flow_type & (IRQF_TRIGGER_HIGH)) && | |
634 | (flow_type & (IRQF_TRIGGER_LOW))) | |
635 | return -EINVAL; | |
636 | ||
111a10bf | 637 | type.type &= ~bit_mask_irq; /* level trig */ |
67b563f1 | 638 | if (flow_type & IRQF_TRIGGER_HIGH) |
111a10bf | 639 | type.polarity_high |= bit_mask_irq; |
67b563f1 | 640 | else |
111a10bf | 641 | type.polarity_low |= bit_mask_irq; |
67b563f1 JC |
642 | } |
643 | ||
644 | qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); | |
5f9b2ea3 AD |
645 | |
646 | if (flow_type & IRQ_TYPE_EDGE_BOTH) | |
647 | irq_set_handler_locked(d, handle_edge_irq); | |
648 | else | |
649 | irq_set_handler_locked(d, handle_level_irq); | |
650 | ||
67b563f1 JC |
651 | return 0; |
652 | } | |
653 | ||
60be4230 CC |
654 | static int qpnpint_get_irqchip_state(struct irq_data *d, |
655 | enum irqchip_irq_state which, | |
656 | bool *state) | |
657 | { | |
319f6884 | 658 | u8 irq = HWIRQ_IRQ(d->hwirq); |
60be4230 CC |
659 | u8 status = 0; |
660 | ||
661 | if (which != IRQCHIP_STATE_LINE_LEVEL) | |
662 | return -EINVAL; | |
663 | ||
664 | qpnpint_spmi_read(d, QPNPINT_REG_RT_STS, &status, 1); | |
665 | *state = !!(status & BIT(irq)); | |
666 | ||
667 | return 0; | |
668 | } | |
669 | ||
67b563f1 JC |
670 | static struct irq_chip pmic_arb_irqchip = { |
671 | .name = "pmic_arb", | |
67b563f1 JC |
672 | .irq_ack = qpnpint_irq_ack, |
673 | .irq_mask = qpnpint_irq_mask, | |
674 | .irq_unmask = qpnpint_irq_unmask, | |
675 | .irq_set_type = qpnpint_irq_set_type, | |
60be4230 | 676 | .irq_get_irqchip_state = qpnpint_get_irqchip_state, |
67b563f1 JC |
677 | .flags = IRQCHIP_MASK_ON_SUSPEND |
678 | | IRQCHIP_SKIP_SET_WAKE, | |
679 | }; | |
680 | ||
67b563f1 JC |
681 | static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, |
682 | struct device_node *controller, | |
683 | const u32 *intspec, | |
684 | unsigned int intsize, | |
685 | unsigned long *out_hwirq, | |
686 | unsigned int *out_type) | |
687 | { | |
111a10bf | 688 | struct spmi_pmic_arb *pa = d->host_data; |
7f1d4e58 | 689 | int rc; |
319f6884 | 690 | u16 apid; |
67b563f1 JC |
691 | |
692 | dev_dbg(&pa->spmic->dev, | |
693 | "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", | |
694 | intspec[0], intspec[1], intspec[2]); | |
695 | ||
5d4c9bc7 | 696 | if (irq_domain_get_of_node(d) != controller) |
67b563f1 JC |
697 | return -EINVAL; |
698 | if (intsize != 4) | |
699 | return -EINVAL; | |
700 | if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7) | |
701 | return -EINVAL; | |
702 | ||
7f1d4e58 AD |
703 | rc = pa->ver_ops->ppid_to_apid(pa, intspec[0], |
704 | (intspec[1] << 8), &apid); | |
705 | if (rc < 0) { | |
706 | dev_err(&pa->spmic->dev, | |
707 | "failed to xlate sid = 0x%x, periph = 0x%x, irq = %x rc = %d\n", | |
708 | intspec[0], intspec[1], intspec[2], rc); | |
709 | return rc; | |
710 | } | |
67b563f1 JC |
711 | |
712 | /* Keep track of {max,min}_apid for bounding search during interrupt */ | |
713 | if (apid > pa->max_apid) | |
714 | pa->max_apid = apid; | |
715 | if (apid < pa->min_apid) | |
716 | pa->min_apid = apid; | |
717 | ||
319f6884 | 718 | *out_hwirq = HWIRQ(intspec[0], intspec[1], intspec[2], apid); |
67b563f1 JC |
719 | *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; |
720 | ||
721 | dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq); | |
722 | ||
723 | return 0; | |
724 | } | |
725 | ||
726 | static int qpnpint_irq_domain_map(struct irq_domain *d, | |
727 | unsigned int virq, | |
728 | irq_hw_number_t hwirq) | |
729 | { | |
111a10bf | 730 | struct spmi_pmic_arb *pa = d->host_data; |
67b563f1 JC |
731 | |
732 | dev_dbg(&pa->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq); | |
733 | ||
734 | irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq); | |
735 | irq_set_chip_data(virq, d->host_data); | |
736 | irq_set_noprobe(virq); | |
737 | return 0; | |
738 | } | |
739 | ||
7f1d4e58 | 740 | static int |
319f6884 | 741 | pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid) |
7f1d4e58 AD |
742 | { |
743 | u16 ppid = sid << 8 | ((addr >> 8) & 0xFF); | |
744 | u32 *mapping_table = pa->mapping_table; | |
745 | int index = 0, i; | |
746 | u16 apid_valid; | |
747 | u32 data; | |
748 | ||
749 | apid_valid = pa->ppid_to_apid[ppid]; | |
750 | if (apid_valid & PMIC_ARB_CHAN_VALID) { | |
751 | *apid = (apid_valid & ~PMIC_ARB_CHAN_VALID); | |
752 | return 0; | |
753 | } | |
754 | ||
755 | for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) { | |
756 | if (!test_and_set_bit(index, pa->mapping_table_valid)) | |
757 | mapping_table[index] = readl_relaxed(pa->cnfg + | |
758 | SPMI_MAPPING_TABLE_REG(index)); | |
759 | ||
760 | data = mapping_table[index]; | |
761 | ||
762 | if (ppid & BIT(SPMI_MAPPING_BIT_INDEX(data))) { | |
763 | if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) { | |
764 | index = SPMI_MAPPING_BIT_IS_1_RESULT(data); | |
765 | } else { | |
766 | *apid = SPMI_MAPPING_BIT_IS_1_RESULT(data); | |
767 | pa->ppid_to_apid[ppid] | |
768 | = *apid | PMIC_ARB_CHAN_VALID; | |
6bc546e7 | 769 | pa->apid_data[*apid].ppid = ppid; |
7f1d4e58 AD |
770 | return 0; |
771 | } | |
772 | } else { | |
773 | if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) { | |
774 | index = SPMI_MAPPING_BIT_IS_0_RESULT(data); | |
775 | } else { | |
776 | *apid = SPMI_MAPPING_BIT_IS_0_RESULT(data); | |
777 | pa->ppid_to_apid[ppid] | |
778 | = *apid | PMIC_ARB_CHAN_VALID; | |
6bc546e7 | 779 | pa->apid_data[*apid].ppid = ppid; |
7f1d4e58 AD |
780 | return 0; |
781 | } | |
782 | } | |
783 | } | |
784 | ||
785 | return -ENODEV; | |
786 | } | |
787 | ||
57102ad7 | 788 | static int |
319f6884 | 789 | pmic_arb_mode_v1_v3(struct spmi_pmic_arb *pa, u8 sid, u16 addr, mode_t *mode) |
57102ad7 AD |
790 | { |
791 | *mode = S_IRUSR | S_IWUSR; | |
792 | return 0; | |
793 | } | |
794 | ||
d0c6ae41 | 795 | /* v1 offset per ee */ |
987a9f12 | 796 | static int |
111a10bf | 797 | pmic_arb_offset_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u32 *offset) |
d0c6ae41 | 798 | { |
987a9f12 SB |
799 | *offset = 0x800 + 0x80 * pa->channel; |
800 | return 0; | |
801 | } | |
802 | ||
1ef1ce4e | 803 | static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pa, u16 ppid) |
987a9f12 SB |
804 | { |
805 | u32 regval, offset; | |
1ef1ce4e | 806 | u16 apid; |
987a9f12 SB |
807 | u16 id; |
808 | ||
809 | /* | |
810 | * PMIC_ARB_REG_CHNL is a table in HW mapping channel to ppid. | |
1ef1ce4e | 811 | * ppid_to_apid is an in-memory invert of that table. |
987a9f12 | 812 | */ |
1ef1ce4e | 813 | for (apid = pa->last_apid; apid < pa->max_periph; apid++) { |
57102ad7 | 814 | regval = readl_relaxed(pa->cnfg + |
1ef1ce4e | 815 | SPMI_OWNERSHIP_TABLE_REG(apid)); |
6bc546e7 | 816 | pa->apid_data[apid].owner = SPMI_OWNERSHIP_PERIPH2OWNER(regval); |
57102ad7 | 817 | |
1ef1ce4e | 818 | offset = PMIC_ARB_REG_CHNL(apid); |
987a9f12 SB |
819 | if (offset >= pa->core_size) |
820 | break; | |
821 | ||
822 | regval = readl_relaxed(pa->core + offset); | |
823 | if (!regval) | |
824 | continue; | |
825 | ||
826 | id = (regval >> 8) & PMIC_ARB_PPID_MASK; | |
1ef1ce4e | 827 | pa->ppid_to_apid[id] = apid | PMIC_ARB_CHAN_VALID; |
6bc546e7 | 828 | pa->apid_data[apid].ppid = id; |
987a9f12 | 829 | if (id == ppid) { |
1ef1ce4e | 830 | apid |= PMIC_ARB_CHAN_VALID; |
987a9f12 SB |
831 | break; |
832 | } | |
833 | } | |
1ef1ce4e | 834 | pa->last_apid = apid & ~PMIC_ARB_CHAN_VALID; |
987a9f12 | 835 | |
1ef1ce4e | 836 | return apid; |
d0c6ae41 GA |
837 | } |
838 | ||
987a9f12 | 839 | |
57102ad7 | 840 | static int |
319f6884 | 841 | pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid) |
57102ad7 AD |
842 | { |
843 | u16 ppid = (sid << 8) | (addr >> 8); | |
7f1d4e58 | 844 | u16 apid_valid; |
57102ad7 | 845 | |
7f1d4e58 AD |
846 | apid_valid = pa->ppid_to_apid[ppid]; |
847 | if (!(apid_valid & PMIC_ARB_CHAN_VALID)) | |
848 | apid_valid = pmic_arb_find_apid(pa, ppid); | |
849 | if (!(apid_valid & PMIC_ARB_CHAN_VALID)) | |
57102ad7 AD |
850 | return -ENODEV; |
851 | ||
7f1d4e58 AD |
852 | *apid = (apid_valid & ~PMIC_ARB_CHAN_VALID); |
853 | return 0; | |
854 | } | |
855 | ||
856 | static int | |
857 | pmic_arb_mode_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, mode_t *mode) | |
858 | { | |
319f6884 | 859 | u16 apid; |
7f1d4e58 AD |
860 | int rc; |
861 | ||
862 | rc = pmic_arb_ppid_to_apid_v2(pa, sid, addr, &apid); | |
863 | if (rc < 0) | |
864 | return rc; | |
865 | ||
57102ad7 AD |
866 | *mode = 0; |
867 | *mode |= S_IRUSR; | |
868 | ||
6bc546e7 | 869 | if (pa->ee == pa->apid_data[apid].owner) |
57102ad7 AD |
870 | *mode |= S_IWUSR; |
871 | return 0; | |
872 | } | |
873 | ||
1ef1ce4e | 874 | /* v2 offset per ppid and per ee */ |
987a9f12 | 875 | static int |
111a10bf | 876 | pmic_arb_offset_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u32 *offset) |
d0c6ae41 | 877 | { |
319f6884 | 878 | u16 apid; |
7f1d4e58 | 879 | int rc; |
987a9f12 | 880 | |
7f1d4e58 AD |
881 | rc = pmic_arb_ppid_to_apid_v2(pa, sid, addr, &apid); |
882 | if (rc < 0) | |
883 | return rc; | |
d0c6ae41 | 884 | |
1ef1ce4e | 885 | *offset = 0x1000 * pa->ee + 0x8000 * apid; |
987a9f12 | 886 | return 0; |
d0c6ae41 GA |
887 | } |
888 | ||
889 | static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc) | |
890 | { | |
891 | return (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7); | |
892 | } | |
893 | ||
894 | static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc) | |
895 | { | |
896 | return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7); | |
897 | } | |
898 | ||
319f6884 | 899 | static u32 pmic_arb_owner_acc_status_v1(u8 m, u16 n) |
d0c6ae41 GA |
900 | { |
901 | return 0x20 * m + 0x4 * n; | |
902 | } | |
903 | ||
319f6884 | 904 | static u32 pmic_arb_owner_acc_status_v2(u8 m, u16 n) |
d0c6ae41 GA |
905 | { |
906 | return 0x100000 + 0x1000 * m + 0x4 * n; | |
907 | } | |
908 | ||
319f6884 AD |
909 | static u32 pmic_arb_owner_acc_status_v3(u8 m, u16 n) |
910 | { | |
911 | return 0x200000 + 0x1000 * m + 0x4 * n; | |
912 | } | |
913 | ||
914 | static u32 pmic_arb_acc_enable_v1(u16 n) | |
d0c6ae41 GA |
915 | { |
916 | return 0x200 + 0x4 * n; | |
917 | } | |
918 | ||
319f6884 | 919 | static u32 pmic_arb_acc_enable_v2(u16 n) |
d0c6ae41 GA |
920 | { |
921 | return 0x1000 * n; | |
922 | } | |
923 | ||
319f6884 | 924 | static u32 pmic_arb_irq_status_v1(u16 n) |
d0c6ae41 GA |
925 | { |
926 | return 0x600 + 0x4 * n; | |
927 | } | |
928 | ||
319f6884 | 929 | static u32 pmic_arb_irq_status_v2(u16 n) |
d0c6ae41 GA |
930 | { |
931 | return 0x4 + 0x1000 * n; | |
932 | } | |
933 | ||
319f6884 | 934 | static u32 pmic_arb_irq_clear_v1(u16 n) |
d0c6ae41 GA |
935 | { |
936 | return 0xA00 + 0x4 * n; | |
937 | } | |
938 | ||
319f6884 | 939 | static u32 pmic_arb_irq_clear_v2(u16 n) |
d0c6ae41 GA |
940 | { |
941 | return 0x8 + 0x1000 * n; | |
942 | } | |
943 | ||
944 | static const struct pmic_arb_ver_ops pmic_arb_v1 = { | |
319f6884 | 945 | .ver_str = "v1", |
7f1d4e58 | 946 | .ppid_to_apid = pmic_arb_ppid_to_apid_v1, |
319f6884 | 947 | .mode = pmic_arb_mode_v1_v3, |
d0c6ae41 GA |
948 | .non_data_cmd = pmic_arb_non_data_cmd_v1, |
949 | .offset = pmic_arb_offset_v1, | |
950 | .fmt_cmd = pmic_arb_fmt_cmd_v1, | |
951 | .owner_acc_status = pmic_arb_owner_acc_status_v1, | |
952 | .acc_enable = pmic_arb_acc_enable_v1, | |
953 | .irq_status = pmic_arb_irq_status_v1, | |
954 | .irq_clear = pmic_arb_irq_clear_v1, | |
955 | }; | |
956 | ||
957 | static const struct pmic_arb_ver_ops pmic_arb_v2 = { | |
319f6884 | 958 | .ver_str = "v2", |
7f1d4e58 | 959 | .ppid_to_apid = pmic_arb_ppid_to_apid_v2, |
57102ad7 | 960 | .mode = pmic_arb_mode_v2, |
d0c6ae41 GA |
961 | .non_data_cmd = pmic_arb_non_data_cmd_v2, |
962 | .offset = pmic_arb_offset_v2, | |
963 | .fmt_cmd = pmic_arb_fmt_cmd_v2, | |
964 | .owner_acc_status = pmic_arb_owner_acc_status_v2, | |
965 | .acc_enable = pmic_arb_acc_enable_v2, | |
966 | .irq_status = pmic_arb_irq_status_v2, | |
967 | .irq_clear = pmic_arb_irq_clear_v2, | |
968 | }; | |
969 | ||
319f6884 AD |
970 | static const struct pmic_arb_ver_ops pmic_arb_v3 = { |
971 | .ver_str = "v3", | |
972 | .ppid_to_apid = pmic_arb_ppid_to_apid_v2, | |
973 | .mode = pmic_arb_mode_v1_v3, | |
974 | .non_data_cmd = pmic_arb_non_data_cmd_v2, | |
975 | .offset = pmic_arb_offset_v2, | |
976 | .fmt_cmd = pmic_arb_fmt_cmd_v2, | |
977 | .owner_acc_status = pmic_arb_owner_acc_status_v3, | |
978 | .acc_enable = pmic_arb_acc_enable_v2, | |
979 | .irq_status = pmic_arb_irq_status_v2, | |
980 | .irq_clear = pmic_arb_irq_clear_v2, | |
981 | }; | |
982 | ||
67b563f1 JC |
983 | static const struct irq_domain_ops pmic_arb_irq_domain_ops = { |
984 | .map = qpnpint_irq_domain_map, | |
985 | .xlate = qpnpint_irq_domain_dt_translate, | |
986 | }; | |
987 | ||
39ae93e3 KH |
988 | static int spmi_pmic_arb_probe(struct platform_device *pdev) |
989 | { | |
111a10bf | 990 | struct spmi_pmic_arb *pa; |
39ae93e3 KH |
991 | struct spmi_controller *ctrl; |
992 | struct resource *res; | |
d0c6ae41 GA |
993 | void __iomem *core; |
994 | u32 channel, ee, hw_ver; | |
987a9f12 | 995 | int err; |
39ae93e3 KH |
996 | |
997 | ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa)); | |
998 | if (!ctrl) | |
999 | return -ENOMEM; | |
1000 | ||
1001 | pa = spmi_controller_get_drvdata(ctrl); | |
67b563f1 | 1002 | pa->spmic = ctrl; |
39ae93e3 KH |
1003 | |
1004 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); | |
987a9f12 | 1005 | pa->core_size = resource_size(res); |
57102ad7 AD |
1006 | if (pa->core_size <= 0x800) { |
1007 | dev_err(&pdev->dev, "core_size is smaller than 0x800. Failing Probe\n"); | |
1008 | err = -EINVAL; | |
1009 | goto err_put_ctrl; | |
1010 | } | |
1011 | ||
d0c6ae41 GA |
1012 | core = devm_ioremap_resource(&ctrl->dev, res); |
1013 | if (IS_ERR(core)) { | |
1014 | err = PTR_ERR(core); | |
39ae93e3 KH |
1015 | goto err_put_ctrl; |
1016 | } | |
1017 | ||
eba9718e SB |
1018 | pa->ppid_to_apid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PPID, |
1019 | sizeof(*pa->ppid_to_apid), GFP_KERNEL); | |
1020 | if (!pa->ppid_to_apid) { | |
1021 | err = -ENOMEM; | |
1022 | goto err_put_ctrl; | |
1023 | } | |
1024 | ||
d0c6ae41 | 1025 | hw_ver = readl_relaxed(core + PMIC_ARB_VERSION); |
d0c6ae41 | 1026 | |
319f6884 | 1027 | if (hw_ver < PMIC_ARB_VERSION_V2_MIN) { |
d0c6ae41 GA |
1028 | pa->ver_ops = &pmic_arb_v1; |
1029 | pa->wr_base = core; | |
1030 | pa->rd_base = core; | |
1031 | } else { | |
987a9f12 | 1032 | pa->core = core; |
319f6884 AD |
1033 | |
1034 | if (hw_ver < PMIC_ARB_VERSION_V3_MIN) | |
1035 | pa->ver_ops = &pmic_arb_v2; | |
1036 | else | |
1037 | pa->ver_ops = &pmic_arb_v3; | |
d0c6ae41 | 1038 | |
57102ad7 | 1039 | /* the apid to ppid table starts at PMIC_ARB_REG_CHNL(0) */ |
319f6884 | 1040 | pa->max_periph = (pa->core_size - PMIC_ARB_REG_CHNL(0)) / 4; |
57102ad7 | 1041 | |
d0c6ae41 GA |
1042 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, |
1043 | "obsrvr"); | |
1044 | pa->rd_base = devm_ioremap_resource(&ctrl->dev, res); | |
1045 | if (IS_ERR(pa->rd_base)) { | |
1046 | err = PTR_ERR(pa->rd_base); | |
1047 | goto err_put_ctrl; | |
1048 | } | |
1049 | ||
1050 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
1051 | "chnls"); | |
1052 | pa->wr_base = devm_ioremap_resource(&ctrl->dev, res); | |
1053 | if (IS_ERR(pa->wr_base)) { | |
1054 | err = PTR_ERR(pa->wr_base); | |
1055 | goto err_put_ctrl; | |
1056 | } | |
d0c6ae41 GA |
1057 | } |
1058 | ||
319f6884 AD |
1059 | dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n", |
1060 | pa->ver_ops->ver_str, hw_ver); | |
1061 | ||
39ae93e3 KH |
1062 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr"); |
1063 | pa->intr = devm_ioremap_resource(&ctrl->dev, res); | |
1064 | if (IS_ERR(pa->intr)) { | |
1065 | err = PTR_ERR(pa->intr); | |
1066 | goto err_put_ctrl; | |
1067 | } | |
1068 | ||
1069 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg"); | |
1070 | pa->cnfg = devm_ioremap_resource(&ctrl->dev, res); | |
1071 | if (IS_ERR(pa->cnfg)) { | |
1072 | err = PTR_ERR(pa->cnfg); | |
1073 | goto err_put_ctrl; | |
1074 | } | |
1075 | ||
67b563f1 JC |
1076 | pa->irq = platform_get_irq_byname(pdev, "periph_irq"); |
1077 | if (pa->irq < 0) { | |
1078 | err = pa->irq; | |
1079 | goto err_put_ctrl; | |
1080 | } | |
1081 | ||
39ae93e3 KH |
1082 | err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel); |
1083 | if (err) { | |
1084 | dev_err(&pdev->dev, "channel unspecified.\n"); | |
1085 | goto err_put_ctrl; | |
1086 | } | |
1087 | ||
1088 | if (channel > 5) { | |
1089 | dev_err(&pdev->dev, "invalid channel (%u) specified.\n", | |
1090 | channel); | |
e98cc182 | 1091 | err = -EINVAL; |
39ae93e3 KH |
1092 | goto err_put_ctrl; |
1093 | } | |
1094 | ||
1095 | pa->channel = channel; | |
1096 | ||
67b563f1 JC |
1097 | err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee); |
1098 | if (err) { | |
1099 | dev_err(&pdev->dev, "EE unspecified.\n"); | |
1100 | goto err_put_ctrl; | |
1101 | } | |
1102 | ||
1103 | if (ee > 5) { | |
1104 | dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee); | |
1105 | err = -EINVAL; | |
1106 | goto err_put_ctrl; | |
1107 | } | |
1108 | ||
1109 | pa->ee = ee; | |
1110 | ||
987a9f12 SB |
1111 | pa->mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS - 1, |
1112 | sizeof(*pa->mapping_table), GFP_KERNEL); | |
1113 | if (!pa->mapping_table) { | |
1114 | err = -ENOMEM; | |
1115 | goto err_put_ctrl; | |
1116 | } | |
67b563f1 JC |
1117 | |
1118 | /* Initialize max_apid/min_apid to the opposite bounds, during | |
1119 | * the irq domain translation, we are sure to update these */ | |
1120 | pa->max_apid = 0; | |
1121 | pa->min_apid = PMIC_ARB_MAX_PERIPHS - 1; | |
1122 | ||
39ae93e3 KH |
1123 | platform_set_drvdata(pdev, ctrl); |
1124 | raw_spin_lock_init(&pa->lock); | |
1125 | ||
1126 | ctrl->cmd = pmic_arb_cmd; | |
1127 | ctrl->read_cmd = pmic_arb_read_cmd; | |
1128 | ctrl->write_cmd = pmic_arb_write_cmd; | |
1129 | ||
67b563f1 JC |
1130 | dev_dbg(&pdev->dev, "adding irq domain\n"); |
1131 | pa->domain = irq_domain_add_tree(pdev->dev.of_node, | |
1132 | &pmic_arb_irq_domain_ops, pa); | |
1133 | if (!pa->domain) { | |
1134 | dev_err(&pdev->dev, "unable to create irq_domain\n"); | |
1135 | err = -ENOMEM; | |
1136 | goto err_put_ctrl; | |
1137 | } | |
1138 | ||
fb68ba6d | 1139 | irq_set_chained_handler_and_data(pa->irq, pmic_arb_chained_irq, pa); |
76b069b1 | 1140 | enable_irq_wake(pa->irq); |
67b563f1 | 1141 | |
39ae93e3 KH |
1142 | err = spmi_controller_add(ctrl); |
1143 | if (err) | |
67b563f1 | 1144 | goto err_domain_remove; |
39ae93e3 | 1145 | |
39ae93e3 KH |
1146 | return 0; |
1147 | ||
67b563f1 | 1148 | err_domain_remove: |
fb68ba6d | 1149 | irq_set_chained_handler_and_data(pa->irq, NULL, NULL); |
67b563f1 | 1150 | irq_domain_remove(pa->domain); |
39ae93e3 KH |
1151 | err_put_ctrl: |
1152 | spmi_controller_put(ctrl); | |
1153 | return err; | |
1154 | } | |
1155 | ||
1156 | static int spmi_pmic_arb_remove(struct platform_device *pdev) | |
1157 | { | |
1158 | struct spmi_controller *ctrl = platform_get_drvdata(pdev); | |
111a10bf | 1159 | struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); |
39ae93e3 | 1160 | spmi_controller_remove(ctrl); |
fb68ba6d | 1161 | irq_set_chained_handler_and_data(pa->irq, NULL, NULL); |
67b563f1 | 1162 | irq_domain_remove(pa->domain); |
39ae93e3 KH |
1163 | spmi_controller_put(ctrl); |
1164 | return 0; | |
1165 | } | |
1166 | ||
1167 | static const struct of_device_id spmi_pmic_arb_match_table[] = { | |
1168 | { .compatible = "qcom,spmi-pmic-arb", }, | |
1169 | {}, | |
1170 | }; | |
1171 | MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table); | |
1172 | ||
1173 | static struct platform_driver spmi_pmic_arb_driver = { | |
1174 | .probe = spmi_pmic_arb_probe, | |
1175 | .remove = spmi_pmic_arb_remove, | |
1176 | .driver = { | |
1177 | .name = "spmi_pmic_arb", | |
39ae93e3 KH |
1178 | .of_match_table = spmi_pmic_arb_match_table, |
1179 | }, | |
1180 | }; | |
1181 | module_platform_driver(spmi_pmic_arb_driver); | |
1182 | ||
1183 | MODULE_LICENSE("GPL v2"); | |
1184 | MODULE_ALIAS("platform:spmi_pmic_arb"); |