Commit | Line | Data |
---|---|---|
39ae93e3 KH |
1 | /* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. |
2 | * | |
3 | * This program is free software; you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License version 2 and | |
5 | * only version 2 as published by the Free Software Foundation. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
11 | */ | |
12 | #include <linux/delay.h> | |
13 | #include <linux/err.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/io.h> | |
16 | #include <linux/kernel.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/of.h> | |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/slab.h> | |
21 | #include <linux/spmi.h> | |
22 | ||
23 | /* PMIC Arbiter configuration registers */ | |
24 | #define PMIC_ARB_VERSION 0x0000 | |
25 | #define PMIC_ARB_INT_EN 0x0004 | |
26 | ||
27 | /* PMIC Arbiter channel registers */ | |
28 | #define PMIC_ARB_CMD(N) (0x0800 + (0x80 * (N))) | |
29 | #define PMIC_ARB_CONFIG(N) (0x0804 + (0x80 * (N))) | |
30 | #define PMIC_ARB_STATUS(N) (0x0808 + (0x80 * (N))) | |
31 | #define PMIC_ARB_WDATA0(N) (0x0810 + (0x80 * (N))) | |
32 | #define PMIC_ARB_WDATA1(N) (0x0814 + (0x80 * (N))) | |
33 | #define PMIC_ARB_RDATA0(N) (0x0818 + (0x80 * (N))) | |
34 | #define PMIC_ARB_RDATA1(N) (0x081C + (0x80 * (N))) | |
35 | ||
36 | /* Interrupt Controller */ | |
37 | #define SPMI_PIC_OWNER_ACC_STATUS(M, N) (0x0000 + ((32 * (M)) + (4 * (N)))) | |
38 | #define SPMI_PIC_ACC_ENABLE(N) (0x0200 + (4 * (N))) | |
39 | #define SPMI_PIC_IRQ_STATUS(N) (0x0600 + (4 * (N))) | |
40 | #define SPMI_PIC_IRQ_CLEAR(N) (0x0A00 + (4 * (N))) | |
41 | ||
42 | /* Mapping Table */ | |
43 | #define SPMI_MAPPING_TABLE_REG(N) (0x0B00 + (4 * (N))) | |
44 | #define SPMI_MAPPING_BIT_INDEX(X) (((X) >> 18) & 0xF) | |
45 | #define SPMI_MAPPING_BIT_IS_0_FLAG(X) (((X) >> 17) & 0x1) | |
46 | #define SPMI_MAPPING_BIT_IS_0_RESULT(X) (((X) >> 9) & 0xFF) | |
47 | #define SPMI_MAPPING_BIT_IS_1_FLAG(X) (((X) >> 8) & 0x1) | |
48 | #define SPMI_MAPPING_BIT_IS_1_RESULT(X) (((X) >> 0) & 0xFF) | |
49 | ||
50 | #define SPMI_MAPPING_TABLE_LEN 255 | |
51 | #define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */ | |
52 | ||
53 | /* Ownership Table */ | |
54 | #define SPMI_OWNERSHIP_TABLE_REG(N) (0x0700 + (4 * (N))) | |
55 | #define SPMI_OWNERSHIP_PERIPH2OWNER(X) ((X) & 0x7) | |
56 | ||
57 | /* Channel Status fields */ | |
58 | enum pmic_arb_chnl_status { | |
59 | PMIC_ARB_STATUS_DONE = (1 << 0), | |
60 | PMIC_ARB_STATUS_FAILURE = (1 << 1), | |
61 | PMIC_ARB_STATUS_DENIED = (1 << 2), | |
62 | PMIC_ARB_STATUS_DROPPED = (1 << 3), | |
63 | }; | |
64 | ||
65 | /* Command register fields */ | |
66 | #define PMIC_ARB_CMD_MAX_BYTE_COUNT 8 | |
67 | ||
68 | /* Command Opcodes */ | |
69 | enum pmic_arb_cmd_op_code { | |
70 | PMIC_ARB_OP_EXT_WRITEL = 0, | |
71 | PMIC_ARB_OP_EXT_READL = 1, | |
72 | PMIC_ARB_OP_EXT_WRITE = 2, | |
73 | PMIC_ARB_OP_RESET = 3, | |
74 | PMIC_ARB_OP_SLEEP = 4, | |
75 | PMIC_ARB_OP_SHUTDOWN = 5, | |
76 | PMIC_ARB_OP_WAKEUP = 6, | |
77 | PMIC_ARB_OP_AUTHENTICATE = 7, | |
78 | PMIC_ARB_OP_MSTR_READ = 8, | |
79 | PMIC_ARB_OP_MSTR_WRITE = 9, | |
80 | PMIC_ARB_OP_EXT_READ = 13, | |
81 | PMIC_ARB_OP_WRITE = 14, | |
82 | PMIC_ARB_OP_READ = 15, | |
83 | PMIC_ARB_OP_ZERO_WRITE = 16, | |
84 | }; | |
85 | ||
86 | /* Maximum number of support PMIC peripherals */ | |
87 | #define PMIC_ARB_MAX_PERIPHS 256 | |
88 | #define PMIC_ARB_PERIPH_ID_VALID (1 << 15) | |
89 | #define PMIC_ARB_TIMEOUT_US 100 | |
90 | #define PMIC_ARB_MAX_TRANS_BYTES (8) | |
91 | ||
92 | #define PMIC_ARB_APID_MASK 0xFF | |
93 | #define PMIC_ARB_PPID_MASK 0xFFF | |
94 | ||
95 | /* interrupt enable bit */ | |
96 | #define SPMI_PIC_ACC_ENABLE_BIT BIT(0) | |
97 | ||
98 | /** | |
99 | * spmi_pmic_arb_dev - SPMI PMIC Arbiter object | |
100 | * | |
101 | * @base: address of the PMIC Arbiter core registers. | |
102 | * @intr: address of the SPMI interrupt control registers. | |
103 | * @cnfg: address of the PMIC Arbiter configuration registers. | |
104 | * @lock: lock to synchronize accesses. | |
105 | * @channel: which channel to use for accesses. | |
106 | */ | |
107 | struct spmi_pmic_arb_dev { | |
108 | void __iomem *base; | |
109 | void __iomem *intr; | |
110 | void __iomem *cnfg; | |
111 | raw_spinlock_t lock; | |
112 | u8 channel; | |
113 | }; | |
114 | ||
115 | static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset) | |
116 | { | |
117 | return readl_relaxed(dev->base + offset); | |
118 | } | |
119 | ||
120 | static inline void pmic_arb_base_write(struct spmi_pmic_arb_dev *dev, | |
121 | u32 offset, u32 val) | |
122 | { | |
123 | writel_relaxed(val, dev->base + offset); | |
124 | } | |
125 | ||
126 | /** | |
127 | * pa_read_data: reads pmic-arb's register and copy 1..4 bytes to buf | |
128 | * @bc: byte count -1. range: 0..3 | |
129 | * @reg: register's address | |
130 | * @buf: output parameter, length must be bc + 1 | |
131 | */ | |
132 | static void pa_read_data(struct spmi_pmic_arb_dev *dev, u8 *buf, u32 reg, u8 bc) | |
133 | { | |
134 | u32 data = pmic_arb_base_read(dev, reg); | |
135 | memcpy(buf, &data, (bc & 3) + 1); | |
136 | } | |
137 | ||
138 | /** | |
139 | * pa_write_data: write 1..4 bytes from buf to pmic-arb's register | |
140 | * @bc: byte-count -1. range: 0..3. | |
141 | * @reg: register's address. | |
142 | * @buf: buffer to write. length must be bc + 1. | |
143 | */ | |
144 | static void | |
145 | pa_write_data(struct spmi_pmic_arb_dev *dev, const u8 *buf, u32 reg, u8 bc) | |
146 | { | |
147 | u32 data = 0; | |
148 | memcpy(&data, buf, (bc & 3) + 1); | |
149 | pmic_arb_base_write(dev, reg, data); | |
150 | } | |
151 | ||
152 | static int pmic_arb_wait_for_done(struct spmi_controller *ctrl) | |
153 | { | |
154 | struct spmi_pmic_arb_dev *dev = spmi_controller_get_drvdata(ctrl); | |
155 | u32 status = 0; | |
156 | u32 timeout = PMIC_ARB_TIMEOUT_US; | |
157 | u32 offset = PMIC_ARB_STATUS(dev->channel); | |
158 | ||
159 | while (timeout--) { | |
160 | status = pmic_arb_base_read(dev, offset); | |
161 | ||
162 | if (status & PMIC_ARB_STATUS_DONE) { | |
163 | if (status & PMIC_ARB_STATUS_DENIED) { | |
164 | dev_err(&ctrl->dev, | |
165 | "%s: transaction denied (0x%x)\n", | |
166 | __func__, status); | |
167 | return -EPERM; | |
168 | } | |
169 | ||
170 | if (status & PMIC_ARB_STATUS_FAILURE) { | |
171 | dev_err(&ctrl->dev, | |
172 | "%s: transaction failed (0x%x)\n", | |
173 | __func__, status); | |
174 | return -EIO; | |
175 | } | |
176 | ||
177 | if (status & PMIC_ARB_STATUS_DROPPED) { | |
178 | dev_err(&ctrl->dev, | |
179 | "%s: transaction dropped (0x%x)\n", | |
180 | __func__, status); | |
181 | return -EIO; | |
182 | } | |
183 | ||
184 | return 0; | |
185 | } | |
186 | udelay(1); | |
187 | } | |
188 | ||
189 | dev_err(&ctrl->dev, | |
190 | "%s: timeout, status 0x%x\n", | |
191 | __func__, status); | |
192 | return -ETIMEDOUT; | |
193 | } | |
194 | ||
195 | /* Non-data command */ | |
196 | static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) | |
197 | { | |
198 | struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl); | |
199 | unsigned long flags; | |
200 | u32 cmd; | |
201 | int rc; | |
202 | ||
203 | /* Check for valid non-data command */ | |
204 | if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP) | |
205 | return -EINVAL; | |
206 | ||
207 | cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20); | |
208 | ||
209 | raw_spin_lock_irqsave(&pmic_arb->lock, flags); | |
210 | pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); | |
211 | rc = pmic_arb_wait_for_done(ctrl); | |
212 | raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); | |
213 | ||
214 | return rc; | |
215 | } | |
216 | ||
217 | static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, | |
218 | u16 addr, u8 *buf, size_t len) | |
219 | { | |
220 | struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl); | |
221 | unsigned long flags; | |
222 | u8 bc = len - 1; | |
223 | u32 cmd; | |
224 | int rc; | |
225 | ||
226 | if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { | |
227 | dev_err(&ctrl->dev, | |
228 | "pmic-arb supports 1..%d bytes per trans, but %d requested", | |
229 | PMIC_ARB_MAX_TRANS_BYTES, len); | |
230 | return -EINVAL; | |
231 | } | |
232 | ||
233 | /* Check the opcode */ | |
234 | if (opc >= 0x60 && opc <= 0x7F) | |
235 | opc = PMIC_ARB_OP_READ; | |
236 | else if (opc >= 0x20 && opc <= 0x2F) | |
237 | opc = PMIC_ARB_OP_EXT_READ; | |
238 | else if (opc >= 0x38 && opc <= 0x3F) | |
239 | opc = PMIC_ARB_OP_EXT_READL; | |
240 | else | |
241 | return -EINVAL; | |
242 | ||
243 | cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7); | |
244 | ||
245 | raw_spin_lock_irqsave(&pmic_arb->lock, flags); | |
246 | pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); | |
247 | rc = pmic_arb_wait_for_done(ctrl); | |
248 | if (rc) | |
249 | goto done; | |
250 | ||
251 | pa_read_data(pmic_arb, buf, PMIC_ARB_RDATA0(pmic_arb->channel), | |
252 | min_t(u8, bc, 3)); | |
253 | ||
254 | if (bc > 3) | |
255 | pa_read_data(pmic_arb, buf + 4, | |
256 | PMIC_ARB_RDATA1(pmic_arb->channel), bc - 4); | |
257 | ||
258 | done: | |
259 | raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); | |
260 | return rc; | |
261 | } | |
262 | ||
263 | static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, | |
264 | u16 addr, const u8 *buf, size_t len) | |
265 | { | |
266 | struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl); | |
267 | unsigned long flags; | |
268 | u8 bc = len - 1; | |
269 | u32 cmd; | |
270 | int rc; | |
271 | ||
272 | if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { | |
273 | dev_err(&ctrl->dev, | |
274 | "pmic-arb supports 1..%d bytes per trans, but:%d requested", | |
275 | PMIC_ARB_MAX_TRANS_BYTES, len); | |
276 | return -EINVAL; | |
277 | } | |
278 | ||
279 | /* Check the opcode */ | |
280 | if (opc >= 0x40 && opc <= 0x5F) | |
281 | opc = PMIC_ARB_OP_WRITE; | |
282 | else if (opc >= 0x00 && opc <= 0x0F) | |
283 | opc = PMIC_ARB_OP_EXT_WRITE; | |
284 | else if (opc >= 0x30 && opc <= 0x37) | |
285 | opc = PMIC_ARB_OP_EXT_WRITEL; | |
286 | else if (opc >= 0x80 && opc <= 0xFF) | |
287 | opc = PMIC_ARB_OP_ZERO_WRITE; | |
288 | else | |
289 | return -EINVAL; | |
290 | ||
291 | cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7); | |
292 | ||
293 | /* Write data to FIFOs */ | |
294 | raw_spin_lock_irqsave(&pmic_arb->lock, flags); | |
295 | pa_write_data(pmic_arb, buf, PMIC_ARB_WDATA0(pmic_arb->channel) | |
296 | , min_t(u8, bc, 3)); | |
297 | if (bc > 3) | |
298 | pa_write_data(pmic_arb, buf + 4, | |
299 | PMIC_ARB_WDATA1(pmic_arb->channel), bc - 4); | |
300 | ||
301 | /* Start the transaction */ | |
302 | pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); | |
303 | rc = pmic_arb_wait_for_done(ctrl); | |
304 | raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); | |
305 | ||
306 | return rc; | |
307 | } | |
308 | ||
309 | static int spmi_pmic_arb_probe(struct platform_device *pdev) | |
310 | { | |
311 | struct spmi_pmic_arb_dev *pa; | |
312 | struct spmi_controller *ctrl; | |
313 | struct resource *res; | |
314 | u32 channel; | |
315 | int err, i; | |
316 | ||
317 | ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa)); | |
318 | if (!ctrl) | |
319 | return -ENOMEM; | |
320 | ||
321 | pa = spmi_controller_get_drvdata(ctrl); | |
322 | ||
323 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); | |
324 | pa->base = devm_ioremap_resource(&ctrl->dev, res); | |
325 | if (IS_ERR(pa->base)) { | |
326 | err = PTR_ERR(pa->base); | |
327 | goto err_put_ctrl; | |
328 | } | |
329 | ||
330 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr"); | |
331 | pa->intr = devm_ioremap_resource(&ctrl->dev, res); | |
332 | if (IS_ERR(pa->intr)) { | |
333 | err = PTR_ERR(pa->intr); | |
334 | goto err_put_ctrl; | |
335 | } | |
336 | ||
337 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg"); | |
338 | pa->cnfg = devm_ioremap_resource(&ctrl->dev, res); | |
339 | if (IS_ERR(pa->cnfg)) { | |
340 | err = PTR_ERR(pa->cnfg); | |
341 | goto err_put_ctrl; | |
342 | } | |
343 | ||
344 | err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel); | |
345 | if (err) { | |
346 | dev_err(&pdev->dev, "channel unspecified.\n"); | |
347 | goto err_put_ctrl; | |
348 | } | |
349 | ||
350 | if (channel > 5) { | |
351 | dev_err(&pdev->dev, "invalid channel (%u) specified.\n", | |
352 | channel); | |
353 | goto err_put_ctrl; | |
354 | } | |
355 | ||
356 | pa->channel = channel; | |
357 | ||
358 | platform_set_drvdata(pdev, ctrl); | |
359 | raw_spin_lock_init(&pa->lock); | |
360 | ||
361 | ctrl->cmd = pmic_arb_cmd; | |
362 | ctrl->read_cmd = pmic_arb_read_cmd; | |
363 | ctrl->write_cmd = pmic_arb_write_cmd; | |
364 | ||
365 | err = spmi_controller_add(ctrl); | |
366 | if (err) | |
367 | goto err_put_ctrl; | |
368 | ||
369 | dev_dbg(&ctrl->dev, "PMIC Arb Version 0x%x\n", | |
370 | pmic_arb_base_read(pa, PMIC_ARB_VERSION)); | |
371 | ||
372 | return 0; | |
373 | ||
374 | err_put_ctrl: | |
375 | spmi_controller_put(ctrl); | |
376 | return err; | |
377 | } | |
378 | ||
379 | static int spmi_pmic_arb_remove(struct platform_device *pdev) | |
380 | { | |
381 | struct spmi_controller *ctrl = platform_get_drvdata(pdev); | |
382 | spmi_controller_remove(ctrl); | |
383 | spmi_controller_put(ctrl); | |
384 | return 0; | |
385 | } | |
386 | ||
387 | static const struct of_device_id spmi_pmic_arb_match_table[] = { | |
388 | { .compatible = "qcom,spmi-pmic-arb", }, | |
389 | {}, | |
390 | }; | |
391 | MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table); | |
392 | ||
393 | static struct platform_driver spmi_pmic_arb_driver = { | |
394 | .probe = spmi_pmic_arb_probe, | |
395 | .remove = spmi_pmic_arb_remove, | |
396 | .driver = { | |
397 | .name = "spmi_pmic_arb", | |
398 | .owner = THIS_MODULE, | |
399 | .of_match_table = spmi_pmic_arb_match_table, | |
400 | }, | |
401 | }; | |
402 | module_platform_driver(spmi_pmic_arb_driver); | |
403 | ||
404 | MODULE_LICENSE("GPL v2"); | |
405 | MODULE_ALIAS("platform:spmi_pmic_arb"); |