aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ssbi
diff options
context:
space:
mode:
authorKenneth Heitke <kheitke@codeaurora.org>2013-03-12 14:41:46 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-03-25 13:33:56 -0400
commite44b0ceee4cc2a926225e73ac1e20b9a5bb22c2d (patch)
tree3802f03fdeb9ab4a7c7b130120e8e19bd3113c20 /drivers/ssbi
parent501ad1c49b3d8c7b7495ef044c698c22710fc27d (diff)
add single-wire serial bus interface (SSBI) driver
SSBI is the Qualcomm single-wire serial bus interface used to connect the MSM devices to the PMIC and other devices. Since SSBI only supports a single slave, the driver gets the name of the slave device passed in from the board file through the master device's platform data. SSBI registers pretty early (postcore), so that the PMIC can come up before the board init. This is useful if the board init requires the use of gpios that are connected through the PMIC. Based on a patch by Dima Zavin <dima@android.com> that can be found at: http://android.git.kernel.org/?p=kernel/msm.git;a=commitdiff;h=eb060bac4 This patch adds PMIC Arbiter support for the MSM8660. The PMIC Arbiter is a hardware wrapper around the SSBI 2.0 controller that is designed to overcome concurrency issues and security limitations. A controller_type field is added to the platform data to specify the type of the SSBI controller (1.0, 2.0, or PMIC Arbiter). [davidb@codeaurora.org: I've moved this driver into drivers/ssbi/ and added an include for linux/module.h so that it will compile] Signed-off-by: Kenneth Heitke <kheitke@codeaurora.org> Signed-off-by: David Brown <davidb@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/ssbi')
-rw-r--r--drivers/ssbi/Kconfig16
-rw-r--r--drivers/ssbi/Makefile1
-rw-r--r--drivers/ssbi/ssbi.c397
3 files changed, 414 insertions, 0 deletions
diff --git a/drivers/ssbi/Kconfig b/drivers/ssbi/Kconfig
new file mode 100644
index 000000000000..b57c41bd0119
--- /dev/null
+++ b/drivers/ssbi/Kconfig
@@ -0,0 +1,16 @@
1#
2# MSM SSBI bus support
3#
4
5menu "Qualcomm MSM SSBI bus support"
6
7config MSM_SSBI
8 bool "Qualcomm Single-wire Serial Bus Interface (SSBI)"
9 help
10 If you say yes to this option, support will be included for the
11 built-in SSBI interface on Qualcomm MSM family processors.
12
13 This is required for communicating with Qualcomm PMICs and
14 other devices that have the SSBI interface.
15
16endmenu
diff --git a/drivers/ssbi/Makefile b/drivers/ssbi/Makefile
new file mode 100644
index 000000000000..22e408f45d61
--- /dev/null
+++ b/drivers/ssbi/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_MSM_SSBI) += ssbi.o
diff --git a/drivers/ssbi/ssbi.c b/drivers/ssbi/ssbi.c
new file mode 100644
index 000000000000..8b0b10d6e1de
--- /dev/null
+++ b/drivers/ssbi/ssbi.c
@@ -0,0 +1,397 @@
1/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
2 * 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
21#include <linux/delay.h>
22#include <linux/err.h>
23#include <linux/io.h>
24#include <linux/kernel.h>
25#include <linux/platform_device.h>
26#include <linux/slab.h>
27#include <linux/msm_ssbi.h>
28#include <linux/module.h>
29
30/* SSBI 2.0 controller registers */
31#define SSBI2_CMD 0x0008
32#define SSBI2_RD 0x0010
33#define SSBI2_STATUS 0x0014
34#define SSBI2_MODE2 0x001C
35
36/* SSBI_CMD fields */
37#define SSBI_CMD_RDWRN (1 << 24)
38
39/* SSBI_STATUS fields */
40#define SSBI_STATUS_RD_READY (1 << 2)
41#define SSBI_STATUS_READY (1 << 1)
42#define SSBI_STATUS_MCHN_BUSY (1 << 0)
43
44/* SSBI_MODE2 fields */
45#define SSBI_MODE2_REG_ADDR_15_8_SHFT 0x04
46#define SSBI_MODE2_REG_ADDR_15_8_MASK (0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT)
47
48#define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \
49 (((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \
50 SSBI_MODE2_REG_ADDR_15_8_MASK))
51
52/* SSBI PMIC Arbiter command registers */
53#define SSBI_PA_CMD 0x0000
54#define SSBI_PA_RD_STATUS 0x0004
55
56/* SSBI_PA_CMD fields */
57#define SSBI_PA_CMD_RDWRN (1 << 24)
58#define SSBI_PA_CMD_ADDR_MASK 0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/
59
60/* SSBI_PA_RD_STATUS fields */
61#define SSBI_PA_RD_STATUS_TRANS_DONE (1 << 27)
62#define SSBI_PA_RD_STATUS_TRANS_DENIED (1 << 26)
63
64#define SSBI_TIMEOUT_US 100
65
66struct msm_ssbi {
67 struct device *dev;
68 struct device *slave;
69 void __iomem *base;
70 spinlock_t lock;
71 enum msm_ssbi_controller_type controller_type;
72 int (*read)(struct msm_ssbi *, u16 addr, u8 *buf, int len);
73 int (*write)(struct msm_ssbi *, u16 addr, u8 *buf, int len);
74};
75
76#define to_msm_ssbi(dev) platform_get_drvdata(to_platform_device(dev))
77
78static inline u32 ssbi_readl(struct msm_ssbi *ssbi, u32 reg)
79{
80 return readl(ssbi->base + reg);
81}
82
83static inline void ssbi_writel(struct msm_ssbi *ssbi, u32 val, u32 reg)
84{
85 writel(val, ssbi->base + reg);
86}
87
88static int ssbi_wait_mask(struct msm_ssbi *ssbi, u32 set_mask, u32 clr_mask)
89{
90 u32 timeout = SSBI_TIMEOUT_US;
91 u32 val;
92
93 while (timeout--) {
94 val = ssbi_readl(ssbi, SSBI2_STATUS);
95 if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0))
96 return 0;
97 udelay(1);
98 }
99
100 dev_err(ssbi->dev, "%s: timeout (status %x set_mask %x clr_mask %x)\n",
101 __func__, ssbi_readl(ssbi, SSBI2_STATUS), set_mask, clr_mask);
102 return -ETIMEDOUT;
103}
104
105static int
106msm_ssbi_read_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
107{
108 u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16);
109 int ret = 0;
110
111 if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
112 u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
113 mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
114 ssbi_writel(ssbi, mode2, SSBI2_MODE2);
115 }
116
117 while (len) {
118 ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
119 if (ret)
120 goto err;
121
122 ssbi_writel(ssbi, cmd, SSBI2_CMD);
123 ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0);
124 if (ret)
125 goto err;
126 *buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff;
127 len--;
128 }
129
130err:
131 return ret;
132}
133
134static int
135msm_ssbi_write_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
136{
137 int ret = 0;
138
139 if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
140 u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
141 mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
142 ssbi_writel(ssbi, mode2, SSBI2_MODE2);
143 }
144
145 while (len) {
146 ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
147 if (ret)
148 goto err;
149
150 ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD);
151 ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY);
152 if (ret)
153 goto err;
154 buf++;
155 len--;
156 }
157
158err:
159 return ret;
160}
161
162static inline int
163msm_ssbi_pa_transfer(struct msm_ssbi *ssbi, u32 cmd, u8 *data)
164{
165 u32 timeout = SSBI_TIMEOUT_US;
166 u32 rd_status = 0;
167
168 ssbi_writel(ssbi, cmd, SSBI_PA_CMD);
169
170 while (timeout--) {
171 rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS);
172
173 if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED) {
174 dev_err(ssbi->dev, "%s: transaction denied (0x%x)\n",
175 __func__, rd_status);
176 return -EPERM;
177 }
178
179 if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) {
180 if (data)
181 *data = rd_status & 0xff;
182 return 0;
183 }
184 udelay(1);
185 }
186
187 dev_err(ssbi->dev, "%s: timeout, status 0x%x\n", __func__, rd_status);
188 return -ETIMEDOUT;
189}
190
191static int
192msm_ssbi_pa_read_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
193{
194 u32 cmd;
195 int ret = 0;
196
197 cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8;
198
199 while (len) {
200 ret = msm_ssbi_pa_transfer(ssbi, cmd, buf);
201 if (ret)
202 goto err;
203 buf++;
204 len--;
205 }
206
207err:
208 return ret;
209}
210
211static int
212msm_ssbi_pa_write_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
213{
214 u32 cmd;
215 int ret = 0;
216
217 while (len) {
218 cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf;
219 ret = msm_ssbi_pa_transfer(ssbi, cmd, NULL);
220 if (ret)
221 goto err;
222 buf++;
223 len--;
224 }
225
226err:
227 return ret;
228}
229
230int msm_ssbi_read(struct device *dev, u16 addr, u8 *buf, int len)
231{
232 struct msm_ssbi *ssbi = to_msm_ssbi(dev);
233 unsigned long flags;
234 int ret;
235
236 if (ssbi->dev != dev)
237 return -ENXIO;
238
239 spin_lock_irqsave(&ssbi->lock, flags);
240 ret = ssbi->read(ssbi, addr, buf, len);
241 spin_unlock_irqrestore(&ssbi->lock, flags);
242
243 return ret;
244}
245EXPORT_SYMBOL(msm_ssbi_read);
246
247int msm_ssbi_write(struct device *dev, u16 addr, u8 *buf, int len)
248{
249 struct msm_ssbi *ssbi = to_msm_ssbi(dev);
250 unsigned long flags;
251 int ret;
252
253 if (ssbi->dev != dev)
254 return -ENXIO;
255
256 spin_lock_irqsave(&ssbi->lock, flags);
257 ret = ssbi->write(ssbi, addr, buf, len);
258 spin_unlock_irqrestore(&ssbi->lock, flags);
259
260 return ret;
261}
262EXPORT_SYMBOL(msm_ssbi_write);
263
264static int msm_ssbi_add_slave(struct msm_ssbi *ssbi,
265 const struct msm_ssbi_slave_info *slave)
266{
267 struct platform_device *slave_pdev;
268 int ret;
269
270 if (ssbi->slave) {
271 pr_err("slave already attached??\n");
272 return -EBUSY;
273 }
274
275 slave_pdev = platform_device_alloc(slave->name, -1);
276 if (!slave_pdev) {
277 pr_err("cannot allocate pdev for slave '%s'", slave->name);
278 ret = -ENOMEM;
279 goto err;
280 }
281
282 slave_pdev->dev.parent = ssbi->dev;
283 slave_pdev->dev.platform_data = slave->platform_data;
284
285 ret = platform_device_add(slave_pdev);
286 if (ret) {
287 pr_err("cannot add slave platform device for '%s'\n",
288 slave->name);
289 goto err;
290 }
291
292 ssbi->slave = &slave_pdev->dev;
293 return 0;
294
295err:
296 if (slave_pdev)
297 platform_device_put(slave_pdev);
298 return ret;
299}
300
301static int msm_ssbi_probe(struct platform_device *pdev)
302{
303 const struct msm_ssbi_platform_data *pdata = pdev->dev.platform_data;
304 struct resource *mem_res;
305 struct msm_ssbi *ssbi;
306 int ret = 0;
307
308 if (!pdata) {
309 pr_err("missing platform data\n");
310 return -EINVAL;
311 }
312
313 pr_debug("%s\n", pdata->slave.name);
314
315 ssbi = kzalloc(sizeof(struct msm_ssbi), GFP_KERNEL);
316 if (!ssbi) {
317 pr_err("can not allocate ssbi_data\n");
318 return -ENOMEM;
319 }
320
321 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
322 if (!mem_res) {
323 pr_err("missing mem resource\n");
324 ret = -EINVAL;
325 goto err_get_mem_res;
326 }
327
328 ssbi->base = ioremap(mem_res->start, resource_size(mem_res));
329 if (!ssbi->base) {
330 pr_err("ioremap of 0x%p failed\n", (void *)mem_res->start);
331 ret = -EINVAL;
332 goto err_ioremap;
333 }
334 ssbi->dev = &pdev->dev;
335 platform_set_drvdata(pdev, ssbi);
336
337 ssbi->controller_type = pdata->controller_type;
338 if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) {
339 ssbi->read = msm_ssbi_pa_read_bytes;
340 ssbi->write = msm_ssbi_pa_write_bytes;
341 } else {
342 ssbi->read = msm_ssbi_read_bytes;
343 ssbi->write = msm_ssbi_write_bytes;
344 }
345
346 spin_lock_init(&ssbi->lock);
347
348 ret = msm_ssbi_add_slave(ssbi, &pdata->slave);
349 if (ret)
350 goto err_ssbi_add_slave;
351
352 return 0;
353
354err_ssbi_add_slave:
355 platform_set_drvdata(pdev, NULL);
356 iounmap(ssbi->base);
357err_ioremap:
358err_get_mem_res:
359 kfree(ssbi);
360 return ret;
361}
362
363static int msm_ssbi_remove(struct platform_device *pdev)
364{
365 struct msm_ssbi *ssbi = platform_get_drvdata(pdev);
366
367 platform_set_drvdata(pdev, NULL);
368 iounmap(ssbi->base);
369 kfree(ssbi);
370 return 0;
371}
372
373static struct platform_driver msm_ssbi_driver = {
374 .probe = msm_ssbi_probe,
375 .remove = __exit_p(msm_ssbi_remove),
376 .driver = {
377 .name = "msm_ssbi",
378 .owner = THIS_MODULE,
379 },
380};
381
382static int __init msm_ssbi_init(void)
383{
384 return platform_driver_register(&msm_ssbi_driver);
385}
386postcore_initcall(msm_ssbi_init);
387
388static void __exit msm_ssbi_exit(void)
389{
390 platform_driver_unregister(&msm_ssbi_driver);
391}
392module_exit(msm_ssbi_exit)
393
394MODULE_LICENSE("GPL v2");
395MODULE_VERSION("1.0");
396MODULE_ALIAS("platform:msm_ssbi");
397MODULE_AUTHOR("Dima Zavin <dima@android.com>");