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