blob: 265d56f32c41a40acb10593de5afe80765c92b40 [file] [log] [blame]
Kenneth Heitke84245ed2012-01-12 13:58:58 -07001/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 * Copyright (c) 2010, Google Inc.
3 *
4 * Original authors: Code Aurora Forum
5 *
6 * Author: Dima Zavin <dima@android.com>
7 * - Largely rewritten from original to not be an i2c driver.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 and
11 * only version 2 as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19#define pr_fmt(fmt) "%s: " fmt, __func__
20
Steve Mucklef132c6c2012-06-06 18:30:57 -070021#include <linux/module.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070022#include <linux/delay.h>
23#include <linux/err.h>
24#include <linux/io.h>
25#include <linux/kernel.h>
26#include <linux/platform_device.h>
27#include <linux/slab.h>
28#include <linux/msm_ssbi.h>
Kenneth Heitke84245ed2012-01-12 13:58:58 -070029#include <linux/remote_spinlock.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030
31/* SSBI 2.0 controller registers */
32#define SSBI2_CMD 0x0008
33#define SSBI2_RD 0x0010
34#define SSBI2_STATUS 0x0014
35#define SSBI2_MODE2 0x001C
36
37/* SSBI_CMD fields */
38#define SSBI_CMD_RDWRN (1 << 24)
39
40/* SSBI_STATUS fields */
41#define SSBI_STATUS_RD_READY (1 << 2)
42#define SSBI_STATUS_READY (1 << 1)
43#define SSBI_STATUS_MCHN_BUSY (1 << 0)
44
45/* SSBI_MODE2 fields */
46#define SSBI_MODE2_REG_ADDR_15_8_SHFT 0x04
47#define SSBI_MODE2_REG_ADDR_15_8_MASK (0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT)
48
49#define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \
50 (((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \
51 SSBI_MODE2_REG_ADDR_15_8_MASK))
52
53/* SSBI PMIC Arbiter command registers */
54#define SSBI_PA_CMD 0x0000
55#define SSBI_PA_RD_STATUS 0x0004
56
57/* SSBI_PA_CMD fields */
58#define SSBI_PA_CMD_RDWRN (1 << 24)
59#define SSBI_PA_CMD_ADDR_MASK 0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/
60
61/* SSBI_PA_RD_STATUS fields */
62#define SSBI_PA_RD_STATUS_TRANS_DONE (1 << 27)
63#define SSBI_PA_RD_STATUS_TRANS_DENIED (1 << 26)
64
65#define SSBI_TIMEOUT_US 100
66
Anirudh Ghayalb65f5322011-10-09 23:12:06 -040067/* SSBI_FSM Read and Write commands for the FSM9xxx SSBI implementation */
68#define SSBI_FSM_CMD_REG_ADDR_SHFT (0x08)
69
70#define SSBI_FSM_CMD_READ(AD) \
71 (SSBI_CMD_RDWRN | (((AD) & 0xFFFF) << SSBI_FSM_CMD_REG_ADDR_SHFT))
72
73#define SSBI_FSM_CMD_WRITE(AD, DT) \
74 ((((AD) & 0xFFFF) << SSBI_FSM_CMD_REG_ADDR_SHFT) | ((DT) & 0xFF))
75
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070076struct msm_ssbi {
77 struct device *dev;
78 struct device *slave;
79 void __iomem *base;
Kenneth Heitke84245ed2012-01-12 13:58:58 -070080 bool use_rlock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070081 spinlock_t lock;
Kenneth Heitke84245ed2012-01-12 13:58:58 -070082 remote_spinlock_t rspin_lock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070083 enum msm_ssbi_controller_type controller_type;
84 int (*read)(struct msm_ssbi *, u16 addr, u8 *buf, int len);
85 int (*write)(struct msm_ssbi *, u16 addr, u8 *buf, int len);
86};
87
88#define to_msm_ssbi(dev) platform_get_drvdata(to_platform_device(dev))
89
90static inline u32 ssbi_readl(struct msm_ssbi *ssbi, u32 reg)
91{
92 return readl(ssbi->base + reg);
93}
94
95static inline void ssbi_writel(struct msm_ssbi *ssbi, u32 val, u32 reg)
96{
97 writel(val, ssbi->base + reg);
98}
99
100static int ssbi_wait_mask(struct msm_ssbi *ssbi, u32 set_mask, u32 clr_mask)
101{
102 u32 timeout = SSBI_TIMEOUT_US;
103 u32 val;
104
105 while (timeout--) {
106 val = ssbi_readl(ssbi, SSBI2_STATUS);
107 if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0))
108 return 0;
109 udelay(1);
110 }
111
112 dev_err(ssbi->dev, "%s: timeout (status %x set_mask %x clr_mask %x)\n",
113 __func__, ssbi_readl(ssbi, SSBI2_STATUS), set_mask, clr_mask);
114 return -ETIMEDOUT;
115}
116
117static int
118msm_ssbi_read_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
119{
120 u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16);
121 int ret = 0;
122
123 if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
124 u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
125 mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
126 ssbi_writel(ssbi, mode2, SSBI2_MODE2);
127 }
128
Anirudh Ghayalb65f5322011-10-09 23:12:06 -0400129 if (ssbi->controller_type == FSM_SBI_CTRL_SSBI)
130 cmd = SSBI_FSM_CMD_READ(addr);
131 else
132 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16);
133
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134 while (len) {
135 ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
136 if (ret)
137 goto err;
138
139 ssbi_writel(ssbi, cmd, SSBI2_CMD);
140 ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0);
141 if (ret)
142 goto err;
143 *buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff;
144 len--;
145 }
146
147err:
148 return ret;
149}
150
151static int
152msm_ssbi_write_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
153{
154 int ret = 0;
155
156 if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
157 u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
158 mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
159 ssbi_writel(ssbi, mode2, SSBI2_MODE2);
160 }
161
162 while (len) {
163 ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
164 if (ret)
165 goto err;
166
Anirudh Ghayalb65f5322011-10-09 23:12:06 -0400167 if (ssbi->controller_type == FSM_SBI_CTRL_SSBI)
168 ssbi_writel(ssbi, SSBI_FSM_CMD_WRITE(addr, *buf),
169 SSBI2_CMD);
170 else
171 ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf,
172 SSBI2_CMD);
173
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700174 ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY);
175 if (ret)
176 goto err;
177 buf++;
178 len--;
179 }
180
181err:
182 return ret;
183}
184
185static inline int
186msm_ssbi_pa_transfer(struct msm_ssbi *ssbi, u32 cmd, u8 *data)
187{
188 u32 timeout = SSBI_TIMEOUT_US;
189 u32 rd_status = 0;
190
191 ssbi_writel(ssbi, cmd, SSBI_PA_CMD);
192
193 while (timeout--) {
194 rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS);
195
196 if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED) {
197 dev_err(ssbi->dev, "%s: transaction denied (0x%x)\n",
198 __func__, rd_status);
199 return -EPERM;
200 }
201
202 if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) {
203 if (data)
204 *data = rd_status & 0xff;
205 return 0;
206 }
207 udelay(1);
208 }
209
210 dev_err(ssbi->dev, "%s: timeout, status 0x%x\n", __func__, rd_status);
211 return -ETIMEDOUT;
212}
213
214static int
215msm_ssbi_pa_read_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
216{
217 u32 cmd;
218 int ret = 0;
219
220 cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8;
221
222 while (len) {
223 ret = msm_ssbi_pa_transfer(ssbi, cmd, buf);
224 if (ret)
225 goto err;
226 buf++;
227 len--;
228 }
229
230err:
231 return ret;
232}
233
234static int
235msm_ssbi_pa_write_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
236{
237 u32 cmd;
238 int ret = 0;
239
240 while (len) {
241 cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf;
242 ret = msm_ssbi_pa_transfer(ssbi, cmd, NULL);
243 if (ret)
244 goto err;
245 buf++;
246 len--;
247 }
248
249err:
250 return ret;
251}
252
253int msm_ssbi_read(struct device *dev, u16 addr, u8 *buf, int len)
254{
255 struct msm_ssbi *ssbi = to_msm_ssbi(dev);
256 unsigned long flags;
257 int ret;
258
259 if (ssbi->dev != dev)
260 return -ENXIO;
261
Kenneth Heitke84245ed2012-01-12 13:58:58 -0700262 if (ssbi->use_rlock) {
263 remote_spin_lock_irqsave(&ssbi->rspin_lock, flags);
264 ret = ssbi->read(ssbi, addr, buf, len);
265 remote_spin_unlock_irqrestore(&ssbi->rspin_lock, flags);
266 } else {
267 spin_lock_irqsave(&ssbi->lock, flags);
268 ret = ssbi->read(ssbi, addr, buf, len);
269 spin_unlock_irqrestore(&ssbi->lock, flags);
270 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700271
272 return ret;
273}
274EXPORT_SYMBOL(msm_ssbi_read);
275
276int msm_ssbi_write(struct device *dev, u16 addr, u8 *buf, int len)
277{
278 struct msm_ssbi *ssbi = to_msm_ssbi(dev);
279 unsigned long flags;
280 int ret;
281
282 if (ssbi->dev != dev)
283 return -ENXIO;
284
Kenneth Heitke84245ed2012-01-12 13:58:58 -0700285 if (ssbi->use_rlock) {
286 remote_spin_lock_irqsave(&ssbi->rspin_lock, flags);
287 ret = ssbi->write(ssbi, addr, buf, len);
288 remote_spin_unlock_irqrestore(&ssbi->rspin_lock, flags);
289 } else {
290 spin_lock_irqsave(&ssbi->lock, flags);
291 ret = ssbi->write(ssbi, addr, buf, len);
292 spin_unlock_irqrestore(&ssbi->lock, flags);
293 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294
295 return ret;
296}
297EXPORT_SYMBOL(msm_ssbi_write);
298
299static int __devinit msm_ssbi_add_slave(struct msm_ssbi *ssbi,
300 const struct msm_ssbi_slave_info *slave)
301{
302 struct platform_device *slave_pdev;
303 int ret;
304
305 if (ssbi->slave) {
306 pr_err("slave already attached??\n");
307 return -EBUSY;
308 }
309
310 slave_pdev = platform_device_alloc(slave->name, -1);
311 if (!slave_pdev) {
312 pr_err("cannot allocate pdev for slave '%s'", slave->name);
313 ret = -ENOMEM;
314 goto err;
315 }
316
317 slave_pdev->dev.parent = ssbi->dev;
318 slave_pdev->dev.platform_data = slave->platform_data;
319
320 ret = platform_device_add(slave_pdev);
321 if (ret) {
322 pr_err("cannot add slave platform device for '%s'\n",
323 slave->name);
324 goto err;
325 }
326
327 ssbi->slave = &slave_pdev->dev;
328 return 0;
329
330err:
331 if (slave_pdev)
332 platform_device_put(slave_pdev);
333 return ret;
334}
335
336static int __devinit msm_ssbi_probe(struct platform_device *pdev)
337{
338 const struct msm_ssbi_platform_data *pdata = pdev->dev.platform_data;
339 struct resource *mem_res;
340 struct msm_ssbi *ssbi;
341 int ret = 0;
342
343 if (!pdata) {
344 pr_err("missing platform data\n");
345 return -EINVAL;
346 }
347
348 pr_debug("%s\n", pdata->slave.name);
349
350 ssbi = kzalloc(sizeof(struct msm_ssbi), GFP_KERNEL);
351 if (!ssbi) {
352 pr_err("can not allocate ssbi_data\n");
353 return -ENOMEM;
354 }
355
356 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
357 if (!mem_res) {
358 pr_err("missing mem resource\n");
359 ret = -EINVAL;
360 goto err_get_mem_res;
361 }
362
363 ssbi->base = ioremap(mem_res->start, resource_size(mem_res));
364 if (!ssbi->base) {
365 pr_err("ioremap of 0x%p failed\n", (void *)mem_res->start);
366 ret = -EINVAL;
367 goto err_ioremap;
368 }
369 ssbi->dev = &pdev->dev;
370 platform_set_drvdata(pdev, ssbi);
371
372 ssbi->controller_type = pdata->controller_type;
373 if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) {
374 ssbi->read = msm_ssbi_pa_read_bytes;
375 ssbi->write = msm_ssbi_pa_write_bytes;
376 } else {
377 ssbi->read = msm_ssbi_read_bytes;
378 ssbi->write = msm_ssbi_write_bytes;
379 }
380
Kenneth Heitke84245ed2012-01-12 13:58:58 -0700381 if (pdata->rsl_id) {
382 ret = remote_spin_lock_init(&ssbi->rspin_lock, pdata->rsl_id);
383 if (ret) {
384 dev_err(&pdev->dev, "remote spinlock init failed\n");
385 goto err_ssbi_add_slave;
386 }
387 ssbi->use_rlock = 1;
388 }
389
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700390 spin_lock_init(&ssbi->lock);
391
392 ret = msm_ssbi_add_slave(ssbi, &pdata->slave);
393 if (ret)
394 goto err_ssbi_add_slave;
395
396 return 0;
397
398err_ssbi_add_slave:
399 platform_set_drvdata(pdev, NULL);
400 iounmap(ssbi->base);
401err_ioremap:
402err_get_mem_res:
403 kfree(ssbi);
404 return ret;
405}
406
407static int __devexit msm_ssbi_remove(struct platform_device *pdev)
408{
409 struct msm_ssbi *ssbi = platform_get_drvdata(pdev);
410
411 platform_set_drvdata(pdev, NULL);
412 iounmap(ssbi->base);
413 kfree(ssbi);
414 return 0;
415}
416
417static struct platform_driver msm_ssbi_driver = {
418 .probe = msm_ssbi_probe,
419 .remove = __exit_p(msm_ssbi_remove),
420 .driver = {
421 .name = "msm_ssbi",
422 .owner = THIS_MODULE,
423 },
424};
425
426static int __init msm_ssbi_init(void)
427{
428 return platform_driver_register(&msm_ssbi_driver);
429}
430postcore_initcall(msm_ssbi_init);
431
432static void __exit msm_ssbi_exit(void)
433{
434 platform_driver_unregister(&msm_ssbi_driver);
435}
436module_exit(msm_ssbi_exit)
437
438MODULE_LICENSE("GPL v2");
439MODULE_VERSION("1.0");
440MODULE_ALIAS("platform:msm_ssbi");
441MODULE_AUTHOR("Dima Zavin <dima@android.com>");