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 | ||
12a9eeae | 734 | |
25655c75 BM |
735 | static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, |
736 | struct irq_domain *domain, unsigned int virq, | |
737 | irq_hw_number_t hwirq, unsigned int type) | |
67b563f1 | 738 | { |
12a9eeae BM |
739 | irq_flow_handler_t handler; |
740 | ||
741 | dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n", | |
742 | virq, hwirq, type); | |
743 | ||
744 | if (type & IRQ_TYPE_EDGE_BOTH) | |
745 | handler = handle_edge_irq; | |
135ef21a | 746 | else |
25655c75 | 747 | handler = handle_level_irq; |
12a9eeae BM |
748 | |
749 | irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb, | |
750 | handler, NULL, NULL); | |
751 | } | |
752 | ||
753 | static int qpnpint_irq_domain_alloc(struct irq_domain *domain, | |
754 | unsigned int virq, unsigned int nr_irqs, | |
755 | void *data) | |
756 | { | |
757 | struct spmi_pmic_arb *pmic_arb = domain->host_data; | |
758 | struct irq_fwspec *fwspec = data; | |
759 | irq_hw_number_t hwirq; | |
760 | unsigned int type; | |
761 | int ret, i; | |
762 | ||
763 | ret = qpnpint_irq_domain_translate(domain, fwspec, &hwirq, &type); | |
764 | if (ret) | |
765 | return ret; | |
67b563f1 | 766 | |
25655c75 BM |
767 | for (i = 0; i < nr_irqs; i++) |
768 | qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i, | |
769 | type); | |
67b563f1 | 770 | |
67b563f1 JC |
771 | return 0; |
772 | } | |
773 | ||
ff615ed9 | 774 | static int pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pmic_arb, u16 ppid) |
7f1d4e58 | 775 | { |
02abec36 | 776 | u32 *mapping_table = pmic_arb->mapping_table; |
7f1d4e58 AD |
777 | int index = 0, i; |
778 | u16 apid_valid; | |
ff615ed9 | 779 | u16 apid; |
7f1d4e58 AD |
780 | u32 data; |
781 | ||
02abec36 KG |
782 | apid_valid = pmic_arb->ppid_to_apid[ppid]; |
783 | if (apid_valid & PMIC_ARB_APID_VALID) { | |
ff615ed9 KG |
784 | apid = apid_valid & ~PMIC_ARB_APID_VALID; |
785 | return apid; | |
7f1d4e58 AD |
786 | } |
787 | ||
788 | for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) { | |
02abec36 KG |
789 | if (!test_and_set_bit(index, pmic_arb->mapping_table_valid)) |
790 | mapping_table[index] = readl_relaxed(pmic_arb->cnfg + | |
7f1d4e58 AD |
791 | SPMI_MAPPING_TABLE_REG(index)); |
792 | ||
793 | data = mapping_table[index]; | |
794 | ||
795 | if (ppid & BIT(SPMI_MAPPING_BIT_INDEX(data))) { | |
796 | if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) { | |
797 | index = SPMI_MAPPING_BIT_IS_1_RESULT(data); | |
798 | } else { | |
ff615ed9 | 799 | apid = SPMI_MAPPING_BIT_IS_1_RESULT(data); |
02abec36 | 800 | pmic_arb->ppid_to_apid[ppid] |
ff615ed9 KG |
801 | = apid | PMIC_ARB_APID_VALID; |
802 | pmic_arb->apid_data[apid].ppid = ppid; | |
803 | return apid; | |
7f1d4e58 AD |
804 | } |
805 | } else { | |
806 | if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) { | |
807 | index = SPMI_MAPPING_BIT_IS_0_RESULT(data); | |
808 | } else { | |
ff615ed9 | 809 | apid = SPMI_MAPPING_BIT_IS_0_RESULT(data); |
02abec36 | 810 | pmic_arb->ppid_to_apid[ppid] |
ff615ed9 KG |
811 | = apid | PMIC_ARB_APID_VALID; |
812 | pmic_arb->apid_data[apid].ppid = ppid; | |
813 | return apid; | |
7f1d4e58 AD |
814 | } |
815 | } | |
816 | } | |
817 | ||
818 | return -ENODEV; | |
819 | } | |
820 | ||
d0c6ae41 | 821 | /* v1 offset per ee */ |
40f318f0 DC |
822 | static int pmic_arb_offset_v1(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, |
823 | enum pmic_arb_channel ch_type) | |
d0c6ae41 | 824 | { |
ff615ed9 | 825 | return 0x800 + 0x80 * pmic_arb->channel; |
987a9f12 SB |
826 | } |
827 | ||
02abec36 | 828 | static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pmic_arb, u16 ppid) |
987a9f12 | 829 | { |
f2f31564 | 830 | struct apid_data *apidd = &pmic_arb->apid_data[pmic_arb->last_apid]; |
987a9f12 | 831 | u32 regval, offset; |
f2f31564 | 832 | u16 id, apid; |
987a9f12 | 833 | |
f2f31564 | 834 | for (apid = pmic_arb->last_apid; ; apid++, apidd++) { |
40f318f0 | 835 | offset = pmic_arb->ver_ops->apid_map_offset(apid); |
02abec36 | 836 | if (offset >= pmic_arb->core_size) |
987a9f12 SB |
837 | break; |
838 | ||
02abec36 | 839 | regval = readl_relaxed(pmic_arb->cnfg + |
b319b592 | 840 | SPMI_OWNERSHIP_TABLE_REG(apid)); |
40f318f0 DC |
841 | apidd->irq_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval); |
842 | apidd->write_ee = apidd->irq_ee; | |
b319b592 | 843 | |
02abec36 | 844 | regval = readl_relaxed(pmic_arb->core + offset); |
987a9f12 SB |
845 | if (!regval) |
846 | continue; | |
847 | ||
848 | id = (regval >> 8) & PMIC_ARB_PPID_MASK; | |
02abec36 | 849 | pmic_arb->ppid_to_apid[id] = apid | PMIC_ARB_APID_VALID; |
f2f31564 | 850 | apidd->ppid = id; |
987a9f12 | 851 | if (id == ppid) { |
02abec36 | 852 | apid |= PMIC_ARB_APID_VALID; |
987a9f12 SB |
853 | break; |
854 | } | |
855 | } | |
02abec36 | 856 | pmic_arb->last_apid = apid & ~PMIC_ARB_APID_VALID; |
987a9f12 | 857 | |
1ef1ce4e | 858 | return apid; |
d0c6ae41 GA |
859 | } |
860 | ||
ff615ed9 | 861 | static int pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pmic_arb, u16 ppid) |
57102ad7 | 862 | { |
7f1d4e58 | 863 | u16 apid_valid; |
57102ad7 | 864 | |
02abec36 KG |
865 | apid_valid = pmic_arb->ppid_to_apid[ppid]; |
866 | if (!(apid_valid & PMIC_ARB_APID_VALID)) | |
867 | apid_valid = pmic_arb_find_apid(pmic_arb, ppid); | |
868 | if (!(apid_valid & PMIC_ARB_APID_VALID)) | |
57102ad7 AD |
869 | return -ENODEV; |
870 | ||
ff615ed9 | 871 | return apid_valid & ~PMIC_ARB_APID_VALID; |
7f1d4e58 AD |
872 | } |
873 | ||
40f318f0 DC |
874 | static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pmic_arb) |
875 | { | |
876 | struct apid_data *apidd = pmic_arb->apid_data; | |
877 | struct apid_data *prev_apidd; | |
878 | u16 i, apid, ppid; | |
879 | bool valid, is_irq_ee; | |
880 | u32 regval, offset; | |
881 | ||
882 | /* | |
883 | * In order to allow multiple EEs to write to a single PPID in arbiter | |
884 | * version 5, there is more than one APID mapped to each PPID. | |
885 | * The owner field for each of these mappings specifies the EE which is | |
886 | * allowed to write to the APID. The owner of the last (highest) APID | |
887 | * for a given PPID will receive interrupts from the PPID. | |
888 | */ | |
889 | for (i = 0; ; i++, apidd++) { | |
890 | offset = pmic_arb->ver_ops->apid_map_offset(i); | |
891 | if (offset >= pmic_arb->core_size) | |
892 | break; | |
893 | ||
894 | regval = readl_relaxed(pmic_arb->core + offset); | |
895 | if (!regval) | |
896 | continue; | |
897 | ppid = (regval >> 8) & PMIC_ARB_PPID_MASK; | |
898 | is_irq_ee = PMIC_ARB_CHAN_IS_IRQ_OWNER(regval); | |
899 | ||
900 | regval = readl_relaxed(pmic_arb->cnfg + | |
901 | SPMI_OWNERSHIP_TABLE_REG(i)); | |
902 | apidd->write_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval); | |
903 | ||
904 | apidd->irq_ee = is_irq_ee ? apidd->write_ee : INVALID_EE; | |
905 | ||
906 | valid = pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID; | |
907 | apid = pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID; | |
908 | prev_apidd = &pmic_arb->apid_data[apid]; | |
909 | ||
910 | if (valid && is_irq_ee && | |
911 | prev_apidd->write_ee == pmic_arb->ee) { | |
912 | /* | |
913 | * Duplicate PPID mapping after the one for this EE; | |
914 | * override the irq owner | |
915 | */ | |
916 | prev_apidd->irq_ee = apidd->irq_ee; | |
917 | } else if (!valid || is_irq_ee) { | |
918 | /* First PPID mapping or duplicate for another EE */ | |
919 | pmic_arb->ppid_to_apid[ppid] = i | PMIC_ARB_APID_VALID; | |
920 | } | |
921 | ||
922 | apidd->ppid = ppid; | |
923 | pmic_arb->last_apid = i; | |
924 | } | |
925 | ||
926 | /* Dump the mapping table for debug purposes. */ | |
927 | dev_dbg(&pmic_arb->spmic->dev, "PPID APID Write-EE IRQ-EE\n"); | |
928 | for (ppid = 0; ppid < PMIC_ARB_MAX_PPID; ppid++) { | |
929 | apid = pmic_arb->ppid_to_apid[ppid]; | |
930 | if (apid & PMIC_ARB_APID_VALID) { | |
931 | apid &= ~PMIC_ARB_APID_VALID; | |
932 | apidd = &pmic_arb->apid_data[apid]; | |
933 | dev_dbg(&pmic_arb->spmic->dev, "%#03X %3u %2u %2u\n", | |
934 | ppid, apid, apidd->write_ee, apidd->irq_ee); | |
935 | } | |
936 | } | |
937 | ||
938 | return 0; | |
939 | } | |
940 | ||
941 | static int pmic_arb_ppid_to_apid_v5(struct spmi_pmic_arb *pmic_arb, u16 ppid) | |
942 | { | |
943 | if (!(pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID)) | |
944 | return -ENODEV; | |
945 | ||
946 | return pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID; | |
947 | } | |
948 | ||
1ef1ce4e | 949 | /* v2 offset per ppid and per ee */ |
40f318f0 DC |
950 | static int pmic_arb_offset_v2(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, |
951 | enum pmic_arb_channel ch_type) | |
d0c6ae41 | 952 | { |
319f6884 | 953 | u16 apid; |
ff615ed9 | 954 | u16 ppid; |
7f1d4e58 | 955 | int rc; |
987a9f12 | 956 | |
ff615ed9 KG |
957 | ppid = sid << 8 | ((addr >> 8) & 0xFF); |
958 | rc = pmic_arb_ppid_to_apid_v2(pmic_arb, ppid); | |
7f1d4e58 AD |
959 | if (rc < 0) |
960 | return rc; | |
d0c6ae41 | 961 | |
ff615ed9 KG |
962 | apid = rc; |
963 | return 0x1000 * pmic_arb->ee + 0x8000 * apid; | |
d0c6ae41 GA |
964 | } |
965 | ||
40f318f0 DC |
966 | /* |
967 | * v5 offset per ee and per apid for observer channels and per apid for | |
968 | * read/write channels. | |
969 | */ | |
970 | static int pmic_arb_offset_v5(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, | |
971 | enum pmic_arb_channel ch_type) | |
972 | { | |
973 | u16 apid; | |
974 | int rc; | |
975 | u32 offset = 0; | |
976 | u16 ppid = (sid << 8) | (addr >> 8); | |
977 | ||
978 | rc = pmic_arb_ppid_to_apid_v5(pmic_arb, ppid); | |
979 | if (rc < 0) | |
980 | return rc; | |
981 | ||
982 | apid = rc; | |
983 | switch (ch_type) { | |
984 | case PMIC_ARB_CHANNEL_OBS: | |
985 | offset = 0x10000 * pmic_arb->ee + 0x80 * apid; | |
986 | break; | |
987 | case PMIC_ARB_CHANNEL_RW: | |
988 | offset = 0x10000 * apid; | |
989 | break; | |
990 | } | |
991 | ||
992 | return offset; | |
993 | } | |
994 | ||
d0c6ae41 GA |
995 | static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc) |
996 | { | |
997 | return (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7); | |
998 | } | |
999 | ||
1000 | static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc) | |
1001 | { | |
1002 | return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7); | |
1003 | } | |
1004 | ||
e95d073c KG |
1005 | static void __iomem * |
1006 | pmic_arb_owner_acc_status_v1(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) | |
d0c6ae41 | 1007 | { |
e95d073c | 1008 | return pmic_arb->intr + 0x20 * m + 0x4 * n; |
d0c6ae41 GA |
1009 | } |
1010 | ||
e95d073c KG |
1011 | static void __iomem * |
1012 | pmic_arb_owner_acc_status_v2(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) | |
d0c6ae41 | 1013 | { |
e95d073c | 1014 | return pmic_arb->intr + 0x100000 + 0x1000 * m + 0x4 * n; |
d0c6ae41 GA |
1015 | } |
1016 | ||
e95d073c KG |
1017 | static void __iomem * |
1018 | pmic_arb_owner_acc_status_v3(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) | |
319f6884 | 1019 | { |
e95d073c | 1020 | return pmic_arb->intr + 0x200000 + 0x1000 * m + 0x4 * n; |
319f6884 AD |
1021 | } |
1022 | ||
40f318f0 DC |
1023 | static void __iomem * |
1024 | pmic_arb_owner_acc_status_v5(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) | |
1025 | { | |
1026 | return pmic_arb->intr + 0x10000 * m + 0x4 * n; | |
1027 | } | |
1028 | ||
e95d073c KG |
1029 | static void __iomem * |
1030 | pmic_arb_acc_enable_v1(struct spmi_pmic_arb *pmic_arb, u16 n) | |
d0c6ae41 | 1031 | { |
e95d073c | 1032 | return pmic_arb->intr + 0x200 + 0x4 * n; |
d0c6ae41 GA |
1033 | } |
1034 | ||
e95d073c KG |
1035 | static void __iomem * |
1036 | pmic_arb_acc_enable_v2(struct spmi_pmic_arb *pmic_arb, u16 n) | |
d0c6ae41 | 1037 | { |
e95d073c | 1038 | return pmic_arb->intr + 0x1000 * n; |
d0c6ae41 GA |
1039 | } |
1040 | ||
40f318f0 DC |
1041 | static void __iomem * |
1042 | pmic_arb_acc_enable_v5(struct spmi_pmic_arb *pmic_arb, u16 n) | |
1043 | { | |
1044 | return pmic_arb->wr_base + 0x100 + 0x10000 * n; | |
1045 | } | |
1046 | ||
e95d073c KG |
1047 | static void __iomem * |
1048 | pmic_arb_irq_status_v1(struct spmi_pmic_arb *pmic_arb, u16 n) | |
d0c6ae41 | 1049 | { |
e95d073c | 1050 | return pmic_arb->intr + 0x600 + 0x4 * n; |
d0c6ae41 GA |
1051 | } |
1052 | ||
e95d073c KG |
1053 | static void __iomem * |
1054 | pmic_arb_irq_status_v2(struct spmi_pmic_arb *pmic_arb, u16 n) | |
d0c6ae41 | 1055 | { |
e95d073c | 1056 | return pmic_arb->intr + 0x4 + 0x1000 * n; |
d0c6ae41 GA |
1057 | } |
1058 | ||
40f318f0 DC |
1059 | static void __iomem * |
1060 | pmic_arb_irq_status_v5(struct spmi_pmic_arb *pmic_arb, u16 n) | |
1061 | { | |
1062 | return pmic_arb->wr_base + 0x104 + 0x10000 * n; | |
1063 | } | |
1064 | ||
e95d073c KG |
1065 | static void __iomem * |
1066 | pmic_arb_irq_clear_v1(struct spmi_pmic_arb *pmic_arb, u16 n) | |
d0c6ae41 | 1067 | { |
e95d073c | 1068 | return pmic_arb->intr + 0xA00 + 0x4 * n; |
d0c6ae41 GA |
1069 | } |
1070 | ||
e95d073c KG |
1071 | static void __iomem * |
1072 | pmic_arb_irq_clear_v2(struct spmi_pmic_arb *pmic_arb, u16 n) | |
d0c6ae41 | 1073 | { |
e95d073c | 1074 | return pmic_arb->intr + 0x8 + 0x1000 * n; |
d0c6ae41 GA |
1075 | } |
1076 | ||
40f318f0 DC |
1077 | static void __iomem * |
1078 | pmic_arb_irq_clear_v5(struct spmi_pmic_arb *pmic_arb, u16 n) | |
1079 | { | |
1080 | return pmic_arb->wr_base + 0x108 + 0x10000 * n; | |
1081 | } | |
1082 | ||
1083 | static u32 pmic_arb_apid_map_offset_v2(u16 n) | |
1084 | { | |
1085 | return 0x800 + 0x4 * n; | |
1086 | } | |
1087 | ||
1088 | static u32 pmic_arb_apid_map_offset_v5(u16 n) | |
1089 | { | |
1090 | return 0x900 + 0x4 * n; | |
1091 | } | |
1092 | ||
d0c6ae41 | 1093 | static const struct pmic_arb_ver_ops pmic_arb_v1 = { |
319f6884 | 1094 | .ver_str = "v1", |
7f1d4e58 | 1095 | .ppid_to_apid = pmic_arb_ppid_to_apid_v1, |
d0c6ae41 GA |
1096 | .non_data_cmd = pmic_arb_non_data_cmd_v1, |
1097 | .offset = pmic_arb_offset_v1, | |
1098 | .fmt_cmd = pmic_arb_fmt_cmd_v1, | |
1099 | .owner_acc_status = pmic_arb_owner_acc_status_v1, | |
1100 | .acc_enable = pmic_arb_acc_enable_v1, | |
1101 | .irq_status = pmic_arb_irq_status_v1, | |
1102 | .irq_clear = pmic_arb_irq_clear_v1, | |
40f318f0 | 1103 | .apid_map_offset = pmic_arb_apid_map_offset_v2, |
d0c6ae41 GA |
1104 | }; |
1105 | ||
1106 | static const struct pmic_arb_ver_ops pmic_arb_v2 = { | |
319f6884 | 1107 | .ver_str = "v2", |
7f1d4e58 | 1108 | .ppid_to_apid = pmic_arb_ppid_to_apid_v2, |
d0c6ae41 GA |
1109 | .non_data_cmd = pmic_arb_non_data_cmd_v2, |
1110 | .offset = pmic_arb_offset_v2, | |
1111 | .fmt_cmd = pmic_arb_fmt_cmd_v2, | |
1112 | .owner_acc_status = pmic_arb_owner_acc_status_v2, | |
1113 | .acc_enable = pmic_arb_acc_enable_v2, | |
1114 | .irq_status = pmic_arb_irq_status_v2, | |
1115 | .irq_clear = pmic_arb_irq_clear_v2, | |
40f318f0 | 1116 | .apid_map_offset = pmic_arb_apid_map_offset_v2, |
d0c6ae41 GA |
1117 | }; |
1118 | ||
319f6884 AD |
1119 | static const struct pmic_arb_ver_ops pmic_arb_v3 = { |
1120 | .ver_str = "v3", | |
1121 | .ppid_to_apid = pmic_arb_ppid_to_apid_v2, | |
319f6884 AD |
1122 | .non_data_cmd = pmic_arb_non_data_cmd_v2, |
1123 | .offset = pmic_arb_offset_v2, | |
1124 | .fmt_cmd = pmic_arb_fmt_cmd_v2, | |
1125 | .owner_acc_status = pmic_arb_owner_acc_status_v3, | |
1126 | .acc_enable = pmic_arb_acc_enable_v2, | |
1127 | .irq_status = pmic_arb_irq_status_v2, | |
1128 | .irq_clear = pmic_arb_irq_clear_v2, | |
40f318f0 DC |
1129 | .apid_map_offset = pmic_arb_apid_map_offset_v2, |
1130 | }; | |
1131 | ||
1132 | static const struct pmic_arb_ver_ops pmic_arb_v5 = { | |
1133 | .ver_str = "v5", | |
1134 | .ppid_to_apid = pmic_arb_ppid_to_apid_v5, | |
1135 | .non_data_cmd = pmic_arb_non_data_cmd_v2, | |
1136 | .offset = pmic_arb_offset_v5, | |
1137 | .fmt_cmd = pmic_arb_fmt_cmd_v2, | |
1138 | .owner_acc_status = pmic_arb_owner_acc_status_v5, | |
1139 | .acc_enable = pmic_arb_acc_enable_v5, | |
1140 | .irq_status = pmic_arb_irq_status_v5, | |
1141 | .irq_clear = pmic_arb_irq_clear_v5, | |
1142 | .apid_map_offset = pmic_arb_apid_map_offset_v5, | |
319f6884 AD |
1143 | }; |
1144 | ||
67b563f1 | 1145 | static const struct irq_domain_ops pmic_arb_irq_domain_ops = { |
12a9eeae BM |
1146 | .activate = qpnpint_irq_domain_activate, |
1147 | .alloc = qpnpint_irq_domain_alloc, | |
1148 | .free = irq_domain_free_irqs_common, | |
1149 | .translate = qpnpint_irq_domain_translate, | |
67b563f1 JC |
1150 | }; |
1151 | ||
39ae93e3 KH |
1152 | static int spmi_pmic_arb_probe(struct platform_device *pdev) |
1153 | { | |
02abec36 | 1154 | struct spmi_pmic_arb *pmic_arb; |
39ae93e3 KH |
1155 | struct spmi_controller *ctrl; |
1156 | struct resource *res; | |
d0c6ae41 | 1157 | void __iomem *core; |
4788e613 | 1158 | u32 *mapping_table; |
d0c6ae41 | 1159 | u32 channel, ee, hw_ver; |
987a9f12 | 1160 | int err; |
39ae93e3 | 1161 | |
02abec36 | 1162 | ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pmic_arb)); |
39ae93e3 KH |
1163 | if (!ctrl) |
1164 | return -ENOMEM; | |
1165 | ||
02abec36 KG |
1166 | pmic_arb = spmi_controller_get_drvdata(ctrl); |
1167 | pmic_arb->spmic = ctrl; | |
39ae93e3 KH |
1168 | |
1169 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); | |
d0c6ae41 GA |
1170 | core = devm_ioremap_resource(&ctrl->dev, res); |
1171 | if (IS_ERR(core)) { | |
1172 | err = PTR_ERR(core); | |
39ae93e3 KH |
1173 | goto err_put_ctrl; |
1174 | } | |
1175 | ||
000e1a43 KG |
1176 | pmic_arb->core_size = resource_size(res); |
1177 | ||
02abec36 KG |
1178 | pmic_arb->ppid_to_apid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PPID, |
1179 | sizeof(*pmic_arb->ppid_to_apid), | |
1180 | GFP_KERNEL); | |
1181 | if (!pmic_arb->ppid_to_apid) { | |
eba9718e SB |
1182 | err = -ENOMEM; |
1183 | goto err_put_ctrl; | |
1184 | } | |
1185 | ||
d0c6ae41 | 1186 | hw_ver = readl_relaxed(core + PMIC_ARB_VERSION); |
d0c6ae41 | 1187 | |
319f6884 | 1188 | if (hw_ver < PMIC_ARB_VERSION_V2_MIN) { |
02abec36 KG |
1189 | pmic_arb->ver_ops = &pmic_arb_v1; |
1190 | pmic_arb->wr_base = core; | |
1191 | pmic_arb->rd_base = core; | |
d0c6ae41 | 1192 | } else { |
02abec36 | 1193 | pmic_arb->core = core; |
319f6884 AD |
1194 | |
1195 | if (hw_ver < PMIC_ARB_VERSION_V3_MIN) | |
02abec36 | 1196 | pmic_arb->ver_ops = &pmic_arb_v2; |
40f318f0 | 1197 | else if (hw_ver < PMIC_ARB_VERSION_V5_MIN) |
02abec36 | 1198 | pmic_arb->ver_ops = &pmic_arb_v3; |
40f318f0 DC |
1199 | else |
1200 | pmic_arb->ver_ops = &pmic_arb_v5; | |
d0c6ae41 GA |
1201 | |
1202 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
1203 | "obsrvr"); | |
02abec36 KG |
1204 | pmic_arb->rd_base = devm_ioremap_resource(&ctrl->dev, res); |
1205 | if (IS_ERR(pmic_arb->rd_base)) { | |
1206 | err = PTR_ERR(pmic_arb->rd_base); | |
d0c6ae41 GA |
1207 | goto err_put_ctrl; |
1208 | } | |
1209 | ||
1210 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
1211 | "chnls"); | |
02abec36 KG |
1212 | pmic_arb->wr_base = devm_ioremap_resource(&ctrl->dev, res); |
1213 | if (IS_ERR(pmic_arb->wr_base)) { | |
1214 | err = PTR_ERR(pmic_arb->wr_base); | |
d0c6ae41 GA |
1215 | goto err_put_ctrl; |
1216 | } | |
d0c6ae41 GA |
1217 | } |
1218 | ||
319f6884 | 1219 | dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n", |
02abec36 | 1220 | pmic_arb->ver_ops->ver_str, hw_ver); |
319f6884 | 1221 | |
39ae93e3 | 1222 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr"); |
02abec36 KG |
1223 | pmic_arb->intr = devm_ioremap_resource(&ctrl->dev, res); |
1224 | if (IS_ERR(pmic_arb->intr)) { | |
1225 | err = PTR_ERR(pmic_arb->intr); | |
39ae93e3 KH |
1226 | goto err_put_ctrl; |
1227 | } | |
1228 | ||
1229 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg"); | |
02abec36 KG |
1230 | pmic_arb->cnfg = devm_ioremap_resource(&ctrl->dev, res); |
1231 | if (IS_ERR(pmic_arb->cnfg)) { | |
1232 | err = PTR_ERR(pmic_arb->cnfg); | |
39ae93e3 KH |
1233 | goto err_put_ctrl; |
1234 | } | |
1235 | ||
02abec36 KG |
1236 | pmic_arb->irq = platform_get_irq_byname(pdev, "periph_irq"); |
1237 | if (pmic_arb->irq < 0) { | |
1238 | err = pmic_arb->irq; | |
67b563f1 JC |
1239 | goto err_put_ctrl; |
1240 | } | |
1241 | ||
39ae93e3 KH |
1242 | err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel); |
1243 | if (err) { | |
1244 | dev_err(&pdev->dev, "channel unspecified.\n"); | |
1245 | goto err_put_ctrl; | |
1246 | } | |
1247 | ||
1248 | if (channel > 5) { | |
1249 | dev_err(&pdev->dev, "invalid channel (%u) specified.\n", | |
1250 | channel); | |
e98cc182 | 1251 | err = -EINVAL; |
39ae93e3 KH |
1252 | goto err_put_ctrl; |
1253 | } | |
1254 | ||
02abec36 | 1255 | pmic_arb->channel = channel; |
39ae93e3 | 1256 | |
67b563f1 JC |
1257 | err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee); |
1258 | if (err) { | |
1259 | dev_err(&pdev->dev, "EE unspecified.\n"); | |
1260 | goto err_put_ctrl; | |
1261 | } | |
1262 | ||
1263 | if (ee > 5) { | |
1264 | dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee); | |
1265 | err = -EINVAL; | |
1266 | goto err_put_ctrl; | |
1267 | } | |
1268 | ||
02abec36 | 1269 | pmic_arb->ee = ee; |
4788e613 KG |
1270 | mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS, |
1271 | sizeof(*mapping_table), GFP_KERNEL); | |
1272 | if (!mapping_table) { | |
987a9f12 SB |
1273 | err = -ENOMEM; |
1274 | goto err_put_ctrl; | |
1275 | } | |
67b563f1 | 1276 | |
4788e613 | 1277 | pmic_arb->mapping_table = mapping_table; |
67b563f1 JC |
1278 | /* Initialize max_apid/min_apid to the opposite bounds, during |
1279 | * the irq domain translation, we are sure to update these */ | |
02abec36 KG |
1280 | pmic_arb->max_apid = 0; |
1281 | pmic_arb->min_apid = PMIC_ARB_MAX_PERIPHS - 1; | |
67b563f1 | 1282 | |
39ae93e3 | 1283 | platform_set_drvdata(pdev, ctrl); |
02abec36 | 1284 | raw_spin_lock_init(&pmic_arb->lock); |
39ae93e3 KH |
1285 | |
1286 | ctrl->cmd = pmic_arb_cmd; | |
1287 | ctrl->read_cmd = pmic_arb_read_cmd; | |
1288 | ctrl->write_cmd = pmic_arb_write_cmd; | |
1289 | ||
40f318f0 DC |
1290 | if (hw_ver >= PMIC_ARB_VERSION_V5_MIN) { |
1291 | err = pmic_arb_read_apid_map_v5(pmic_arb); | |
1292 | if (err) { | |
1293 | dev_err(&pdev->dev, "could not read APID->PPID mapping table, rc= %d\n", | |
1294 | err); | |
1295 | goto err_put_ctrl; | |
1296 | } | |
1297 | } | |
1298 | ||
67b563f1 | 1299 | dev_dbg(&pdev->dev, "adding irq domain\n"); |
02abec36 KG |
1300 | pmic_arb->domain = irq_domain_add_tree(pdev->dev.of_node, |
1301 | &pmic_arb_irq_domain_ops, pmic_arb); | |
1302 | if (!pmic_arb->domain) { | |
67b563f1 JC |
1303 | dev_err(&pdev->dev, "unable to create irq_domain\n"); |
1304 | err = -ENOMEM; | |
1305 | goto err_put_ctrl; | |
1306 | } | |
1307 | ||
02abec36 KG |
1308 | irq_set_chained_handler_and_data(pmic_arb->irq, pmic_arb_chained_irq, |
1309 | pmic_arb); | |
39ae93e3 KH |
1310 | err = spmi_controller_add(ctrl); |
1311 | if (err) | |
67b563f1 | 1312 | goto err_domain_remove; |
39ae93e3 | 1313 | |
39ae93e3 KH |
1314 | return 0; |
1315 | ||
67b563f1 | 1316 | err_domain_remove: |
02abec36 KG |
1317 | irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL); |
1318 | irq_domain_remove(pmic_arb->domain); | |
39ae93e3 KH |
1319 | err_put_ctrl: |
1320 | spmi_controller_put(ctrl); | |
1321 | return err; | |
1322 | } | |
1323 | ||
1324 | static int spmi_pmic_arb_remove(struct platform_device *pdev) | |
1325 | { | |
1326 | struct spmi_controller *ctrl = platform_get_drvdata(pdev); | |
02abec36 | 1327 | struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); |
39ae93e3 | 1328 | spmi_controller_remove(ctrl); |
02abec36 KG |
1329 | irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL); |
1330 | irq_domain_remove(pmic_arb->domain); | |
39ae93e3 KH |
1331 | spmi_controller_put(ctrl); |
1332 | return 0; | |
1333 | } | |
1334 | ||
1335 | static const struct of_device_id spmi_pmic_arb_match_table[] = { | |
1336 | { .compatible = "qcom,spmi-pmic-arb", }, | |
1337 | {}, | |
1338 | }; | |
1339 | MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table); | |
1340 | ||
1341 | static struct platform_driver spmi_pmic_arb_driver = { | |
1342 | .probe = spmi_pmic_arb_probe, | |
1343 | .remove = spmi_pmic_arb_remove, | |
1344 | .driver = { | |
1345 | .name = "spmi_pmic_arb", | |
39ae93e3 KH |
1346 | .of_match_table = spmi_pmic_arb_match_table, |
1347 | }, | |
1348 | }; | |
1349 | module_platform_driver(spmi_pmic_arb_driver); | |
1350 | ||
1351 | MODULE_LICENSE("GPL v2"); | |
1352 | MODULE_ALIAS("platform:spmi_pmic_arb"); |