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