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