ssbi.c revision 3f7a73b57c2f57aa25342ebdb7312f78c68502eb
1e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
2e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * Copyright (c) 2010, Google Inc.
3e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke *
4e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * Original authors: Code Aurora Forum
5e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke *
6e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * Author: Dima Zavin <dima@android.com>
7e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke *  - Largely rewritten from original to not be an i2c driver.
8e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke *
9e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * This program is free software; you can redistribute it and/or modify
10e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * it under the terms of the GNU General Public License version 2 and
11e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * only version 2 as published by the Free Software Foundation.
12e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke *
13e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * This program is distributed in the hope that it will be useful,
14e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * but WITHOUT ANY WARRANTY; without even the implied warranty of
15e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * GNU General Public License for more details.
17e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke */
18e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
19e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define pr_fmt(fmt) "%s: " fmt, __func__
20e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
21e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/delay.h>
22e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/err.h>
23e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/io.h>
24e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/kernel.h>
25e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/platform_device.h>
26e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/slab.h>
27e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/msm_ssbi.h>
28e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/module.h>
2997f00f7120fe3396302693cdc4b1d11bbacad963David Brown#include <linux/of.h>
3097f00f7120fe3396302693cdc4b1d11bbacad963David Brown#include <linux/of_device.h>
31e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
32e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* SSBI 2.0 controller registers */
33e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI2_CMD			0x0008
34e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI2_RD			0x0010
35e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI2_STATUS			0x0014
36e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI2_MODE2			0x001C
37e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
38e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* SSBI_CMD fields */
39e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_CMD_RDWRN			(1 << 24)
40e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
41e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* SSBI_STATUS fields */
42e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_STATUS_RD_READY		(1 << 2)
43e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_STATUS_READY		(1 << 1)
44e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_STATUS_MCHN_BUSY		(1 << 0)
45e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
46e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* SSBI_MODE2 fields */
47e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_MODE2_REG_ADDR_15_8_SHFT	0x04
48e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_MODE2_REG_ADDR_15_8_MASK	(0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT)
49e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
50e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \
51e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	(((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \
52e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	SSBI_MODE2_REG_ADDR_15_8_MASK))
53e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
54e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* SSBI PMIC Arbiter command registers */
55e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_PA_CMD			0x0000
56e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_PA_RD_STATUS		0x0004
57e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
58e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* SSBI_PA_CMD fields */
59e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_PA_CMD_RDWRN		(1 << 24)
60e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_PA_CMD_ADDR_MASK		0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/
61e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
62e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* SSBI_PA_RD_STATUS fields */
63e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_PA_RD_STATUS_TRANS_DONE	(1 << 27)
64e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_PA_RD_STATUS_TRANS_DENIED	(1 << 26)
65e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
66e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_TIMEOUT_US			100
67e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
68e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestruct msm_ssbi {
69e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	struct device		*dev;
70e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	struct device		*slave;
71e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	void __iomem		*base;
72e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spinlock_t		lock;
73e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	enum msm_ssbi_controller_type controller_type;
74e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int (*read)(struct msm_ssbi *, u16 addr, u8 *buf, int len);
75e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int (*write)(struct msm_ssbi *, u16 addr, u8 *buf, int len);
76e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke};
77e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
78e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define to_msm_ssbi(dev)	platform_get_drvdata(to_platform_device(dev))
79e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
80e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic inline u32 ssbi_readl(struct msm_ssbi *ssbi, u32 reg)
81e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
82e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return readl(ssbi->base + reg);
83e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
84e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
85e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic inline void ssbi_writel(struct msm_ssbi *ssbi, u32 val, u32 reg)
86e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
87e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	writel(val, ssbi->base + reg);
88e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
89e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
903f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown/*
913f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * Via private exchange with one of the original authors, the hardware
923f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * should generally finish a transaction in about 5us.  The worst
933f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * case, is when using the arbiter and both other CPUs have just
943f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * started trying to use the SSBI bus will result in a time of about
953f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * 20us.  It should never take longer than this.
963f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown *
973f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * As such, this wait merely spins, with a udelay.
983f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown */
99e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int ssbi_wait_mask(struct msm_ssbi *ssbi, u32 set_mask, u32 clr_mask)
100e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
101e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 timeout = SSBI_TIMEOUT_US;
102e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 val;
103e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
104e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (timeout--) {
105e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		val = ssbi_readl(ssbi, SSBI2_STATUS);
106e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0))
107e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			return 0;
108e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		udelay(1);
109e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
110e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
111e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	dev_err(ssbi->dev, "%s: timeout (status %x set_mask %x clr_mask %x)\n",
112e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		__func__, ssbi_readl(ssbi, SSBI2_STATUS), set_mask, clr_mask);
113e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return -ETIMEDOUT;
114e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
115e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
116e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int
117e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkemsm_ssbi_read_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
118e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
119e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16);
120e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret = 0;
121e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
122e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
123e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
124e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
125e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi_writel(ssbi, mode2, SSBI2_MODE2);
126e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
127e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
128e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (len) {
129e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
130e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
131e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
132e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
133e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi_writel(ssbi, cmd, SSBI2_CMD);
134e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0);
135e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
136e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
137e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		*buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff;
138e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		len--;
139e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
140e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
141e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr:
142e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
143e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
144e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
145e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int
146e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkemsm_ssbi_write_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
147e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
148e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret = 0;
149e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
150e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
151e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
152e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
153e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi_writel(ssbi, mode2, SSBI2_MODE2);
154e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
155e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
156e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (len) {
157e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
158e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
159e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
160e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
161e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD);
162e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY);
163e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
164e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
165e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		buf++;
166e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		len--;
167e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
168e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
169e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr:
170e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
171e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
172e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
1733f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown/*
1743f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * See ssbi_wait_mask for an explanation of the time and the
1753f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * busywait.
1763f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown */
177e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic inline int
178e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkemsm_ssbi_pa_transfer(struct msm_ssbi *ssbi, u32 cmd, u8 *data)
179e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
180e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 timeout = SSBI_TIMEOUT_US;
181e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 rd_status = 0;
182e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
183e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	ssbi_writel(ssbi, cmd, SSBI_PA_CMD);
184e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
185e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (timeout--) {
186e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS);
187e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
188e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED) {
189e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			dev_err(ssbi->dev, "%s: transaction denied (0x%x)\n",
190e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke					__func__, rd_status);
191e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			return -EPERM;
192e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		}
193e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
194e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) {
195e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			if (data)
196e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke				*data = rd_status & 0xff;
197e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			return 0;
198e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		}
199e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		udelay(1);
200e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
201e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
202e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	dev_err(ssbi->dev, "%s: timeout, status 0x%x\n", __func__, rd_status);
203e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return -ETIMEDOUT;
204e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
205e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
206e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int
207e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkemsm_ssbi_pa_read_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
208e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
209e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 cmd;
210e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret = 0;
211e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
212e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8;
213e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
214e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (len) {
215e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = msm_ssbi_pa_transfer(ssbi, cmd, buf);
216e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
217e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
218e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		buf++;
219e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		len--;
220e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
221e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
222e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr:
223e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
224e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
225e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
226e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int
227e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkemsm_ssbi_pa_write_bytes(struct msm_ssbi *ssbi, u16 addr, u8 *buf, int len)
228e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
229e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 cmd;
230e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret = 0;
231e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
232e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (len) {
233e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf;
234e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = msm_ssbi_pa_transfer(ssbi, cmd, NULL);
235e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
236e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
237e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		buf++;
238e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		len--;
239e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
240e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
241e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr:
242e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
243e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
244e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
245e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeint msm_ssbi_read(struct device *dev, u16 addr, u8 *buf, int len)
246e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
247e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	struct msm_ssbi *ssbi = to_msm_ssbi(dev);
248e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	unsigned long flags;
249e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret;
250e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
251e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (ssbi->dev != dev)
252e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		return -ENXIO;
253e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
254e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_lock_irqsave(&ssbi->lock, flags);
255e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	ret = ssbi->read(ssbi, addr, buf, len);
256e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_unlock_irqrestore(&ssbi->lock, flags);
257e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
258e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
259e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
260a1a906c5721f05252e0dbd4b11ef25539d7bd9d0David BrownEXPORT_SYMBOL_GPL(msm_ssbi_read);
261e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
262e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeint msm_ssbi_write(struct device *dev, u16 addr, u8 *buf, int len)
263e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
264e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	struct msm_ssbi *ssbi = to_msm_ssbi(dev);
265e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	unsigned long flags;
266e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret;
267e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
268e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (ssbi->dev != dev)
269e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		return -ENXIO;
270e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
271e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_lock_irqsave(&ssbi->lock, flags);
272e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	ret = ssbi->write(ssbi, addr, buf, len);
273e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_unlock_irqrestore(&ssbi->lock, flags);
274e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
275e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
276e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
277a1a906c5721f05252e0dbd4b11ef25539d7bd9d0David BrownEXPORT_SYMBOL_GPL(msm_ssbi_write);
278e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
279e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int msm_ssbi_probe(struct platform_device *pdev)
280e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
28197f00f7120fe3396302693cdc4b1d11bbacad963David Brown	struct device_node *np = pdev->dev.of_node;
282e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	struct resource *mem_res;
283e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	struct msm_ssbi *ssbi;
284e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret = 0;
28597f00f7120fe3396302693cdc4b1d11bbacad963David Brown	const char *type;
286e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
287e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	ssbi = kzalloc(sizeof(struct msm_ssbi), GFP_KERNEL);
288e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (!ssbi) {
289e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		pr_err("can not allocate ssbi_data\n");
290e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		return -ENOMEM;
291e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
292e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
293e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
294e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (!mem_res) {
295e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		pr_err("missing mem resource\n");
296e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = -EINVAL;
297e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		goto err_get_mem_res;
298e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
299e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
300e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	ssbi->base = ioremap(mem_res->start, resource_size(mem_res));
301e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (!ssbi->base) {
302e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		pr_err("ioremap of 0x%p failed\n", (void *)mem_res->start);
303e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = -EINVAL;
304e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		goto err_ioremap;
305e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
306e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	ssbi->dev = &pdev->dev;
307e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	platform_set_drvdata(pdev, ssbi);
308e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
30997f00f7120fe3396302693cdc4b1d11bbacad963David Brown	type = of_get_property(np, "qcom,controller-type", NULL);
31097f00f7120fe3396302693cdc4b1d11bbacad963David Brown	if (type == NULL) {
31197f00f7120fe3396302693cdc4b1d11bbacad963David Brown		pr_err("Missing qcom,controller-type property\n");
31297f00f7120fe3396302693cdc4b1d11bbacad963David Brown		ret = -EINVAL;
31397f00f7120fe3396302693cdc4b1d11bbacad963David Brown		goto err_ssbi_controller;
31497f00f7120fe3396302693cdc4b1d11bbacad963David Brown	}
31597f00f7120fe3396302693cdc4b1d11bbacad963David Brown	dev_info(&pdev->dev, "SSBI controller type: '%s'\n", type);
31697f00f7120fe3396302693cdc4b1d11bbacad963David Brown	if (strcmp(type, "ssbi") == 0)
31797f00f7120fe3396302693cdc4b1d11bbacad963David Brown		ssbi->controller_type = MSM_SBI_CTRL_SSBI;
31897f00f7120fe3396302693cdc4b1d11bbacad963David Brown	else if (strcmp(type, "ssbi2") == 0)
31997f00f7120fe3396302693cdc4b1d11bbacad963David Brown		ssbi->controller_type = MSM_SBI_CTRL_SSBI2;
32097f00f7120fe3396302693cdc4b1d11bbacad963David Brown	else if (strcmp(type, "pmic-arbiter") == 0)
32197f00f7120fe3396302693cdc4b1d11bbacad963David Brown		ssbi->controller_type = MSM_SBI_CTRL_PMIC_ARBITER;
32297f00f7120fe3396302693cdc4b1d11bbacad963David Brown	else {
32397f00f7120fe3396302693cdc4b1d11bbacad963David Brown		pr_err("Unknown qcom,controller-type\n");
32497f00f7120fe3396302693cdc4b1d11bbacad963David Brown		ret = -EINVAL;
32597f00f7120fe3396302693cdc4b1d11bbacad963David Brown		goto err_ssbi_controller;
32697f00f7120fe3396302693cdc4b1d11bbacad963David Brown	}
32797f00f7120fe3396302693cdc4b1d11bbacad963David Brown
328e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) {
329e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi->read = msm_ssbi_pa_read_bytes;
330e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi->write = msm_ssbi_pa_write_bytes;
331e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	} else {
332e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi->read = msm_ssbi_read_bytes;
333e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi->write = msm_ssbi_write_bytes;
334e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
335e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
336e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_lock_init(&ssbi->lock);
337e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
33897f00f7120fe3396302693cdc4b1d11bbacad963David Brown	ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
339e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (ret)
34097f00f7120fe3396302693cdc4b1d11bbacad963David Brown		goto err_ssbi_controller;
341e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
342e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return 0;
343e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
34497f00f7120fe3396302693cdc4b1d11bbacad963David Brownerr_ssbi_controller:
345e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	platform_set_drvdata(pdev, NULL);
346e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	iounmap(ssbi->base);
347e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr_ioremap:
348e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr_get_mem_res:
349e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	kfree(ssbi);
350e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
351e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
352e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
353e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int msm_ssbi_remove(struct platform_device *pdev)
354e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
355e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	struct msm_ssbi *ssbi = platform_get_drvdata(pdev);
356e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
357e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	platform_set_drvdata(pdev, NULL);
358e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	iounmap(ssbi->base);
359e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	kfree(ssbi);
360e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return 0;
361e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
362e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
36397f00f7120fe3396302693cdc4b1d11bbacad963David Brownstatic struct of_device_id ssbi_match_table[] = {
36497f00f7120fe3396302693cdc4b1d11bbacad963David Brown	{ .compatible = "qcom,ssbi" },
36597f00f7120fe3396302693cdc4b1d11bbacad963David Brown	{}
36697f00f7120fe3396302693cdc4b1d11bbacad963David Brown};
36797f00f7120fe3396302693cdc4b1d11bbacad963David Brown
368e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic struct platform_driver msm_ssbi_driver = {
369e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	.probe		= msm_ssbi_probe,
3707b67d5610879c8f1851a47fd5adfe7e924f3fe53David Brown	.remove		= msm_ssbi_remove,
371e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	.driver		= {
372e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		.name	= "msm_ssbi",
373e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		.owner	= THIS_MODULE,
37497f00f7120fe3396302693cdc4b1d11bbacad963David Brown		.of_match_table = ssbi_match_table,
375e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	},
376e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke};
377e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
378e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int __init msm_ssbi_init(void)
379e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
380e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return platform_driver_register(&msm_ssbi_driver);
381e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
382e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkepostcore_initcall(msm_ssbi_init);
383e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
384e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic void __exit msm_ssbi_exit(void)
385e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
386e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	platform_driver_unregister(&msm_ssbi_driver);
387e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
388e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkemodule_exit(msm_ssbi_exit)
389e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
390e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth HeitkeMODULE_LICENSE("GPL v2");
391e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth HeitkeMODULE_VERSION("1.0");
392e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth HeitkeMODULE_ALIAS("platform:msm_ssbi");
393e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth HeitkeMODULE_AUTHOR("Dima Zavin <dima@android.com>");
394