1ce44bf5b5544cbe6358abb01f039361a99b80901David Brown/* Copyright (c) 2009-2013, The Linux Foundation. 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>
27ce44bf5b5544cbe6358abb01f039361a99b80901David Brown#include <linux/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
68bae911a055a3d88c8754e9c1879f29da6ba16663Stephen Boydenum ssbi_controller_type {
69bae911a055a3d88c8754e9c1879f29da6ba16663Stephen Boyd	MSM_SBI_CTRL_SSBI = 0,
70bae911a055a3d88c8754e9c1879f29da6ba16663Stephen Boyd	MSM_SBI_CTRL_SSBI2,
71bae911a055a3d88c8754e9c1879f29da6ba16663Stephen Boyd	MSM_SBI_CTRL_PMIC_ARBITER,
72bae911a055a3d88c8754e9c1879f29da6ba16663Stephen Boyd};
73bae911a055a3d88c8754e9c1879f29da6ba16663Stephen Boyd
74ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstruct ssbi {
75e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	struct device		*slave;
76e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	void __iomem		*base;
77e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spinlock_t		lock;
78ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	enum ssbi_controller_type controller_type;
79ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	int (*read)(struct ssbi *, u16 addr, u8 *buf, int len);
805eec14ccf90942fecd89e147e0b88ab12dd83e70Stephen Boyd	int (*write)(struct ssbi *, u16 addr, const u8 *buf, int len);
81e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke};
82e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
83ce44bf5b5544cbe6358abb01f039361a99b80901David Brown#define to_ssbi(dev)	platform_get_drvdata(to_platform_device(dev))
84e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
85ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic inline u32 ssbi_readl(struct ssbi *ssbi, u32 reg)
86e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
87e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return readl(ssbi->base + reg);
88e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
89e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
90ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic inline void ssbi_writel(struct ssbi *ssbi, u32 val, u32 reg)
91e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
92e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	writel(val, ssbi->base + reg);
93e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
94e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
953f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown/*
963f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * Via private exchange with one of the original authors, the hardware
973f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * should generally finish a transaction in about 5us.  The worst
983f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * case, is when using the arbiter and both other CPUs have just
993f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * started trying to use the SSBI bus will result in a time of about
1003f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * 20us.  It should never take longer than this.
1013f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown *
1023f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * As such, this wait merely spins, with a udelay.
1033f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown */
104ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic int ssbi_wait_mask(struct ssbi *ssbi, u32 set_mask, u32 clr_mask)
105e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
106e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 timeout = SSBI_TIMEOUT_US;
107e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 val;
108e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
109e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (timeout--) {
110e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		val = ssbi_readl(ssbi, SSBI2_STATUS);
111e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0))
112e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			return 0;
113e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		udelay(1);
114e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
115e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
116e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return -ETIMEDOUT;
117e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
118e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
119e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int
120ce44bf5b5544cbe6358abb01f039361a99b80901David Brownssbi_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len)
121e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
122e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16);
123e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret = 0;
124e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
125e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
126e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
127e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
128e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi_writel(ssbi, mode2, SSBI2_MODE2);
129e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
130e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
131e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (len) {
132e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
133e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
134e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
135e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
136e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi_writel(ssbi, cmd, SSBI2_CMD);
137e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0);
138e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
139e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
140e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		*buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff;
141e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		len--;
142e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
143e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
144e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr:
145e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
146e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
147e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
148e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int
1495eec14ccf90942fecd89e147e0b88ab12dd83e70Stephen Boydssbi_write_bytes(struct ssbi *ssbi, u16 addr, const u8 *buf, int len)
150e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
151e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret = 0;
152e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
153e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
154e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
155e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
156e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi_writel(ssbi, mode2, SSBI2_MODE2);
157e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
158e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
159e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (len) {
160e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
161e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
162e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
163e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
164e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD);
165e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY);
166e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
167e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
168e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		buf++;
169e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		len--;
170e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
171e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
172e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr:
173e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
174e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
175e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
1763f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown/*
1773f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * See ssbi_wait_mask for an explanation of the time and the
1783f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * busywait.
1793f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown */
180e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic inline int
181ce44bf5b5544cbe6358abb01f039361a99b80901David Brownssbi_pa_transfer(struct ssbi *ssbi, u32 cmd, u8 *data)
182e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
183e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 timeout = SSBI_TIMEOUT_US;
184e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 rd_status = 0;
185e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
186e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	ssbi_writel(ssbi, cmd, SSBI_PA_CMD);
187e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
188e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (timeout--) {
189e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS);
190e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
19137799ef4fa95ceec09b5c214fb281c6e6acddf5bDavid Brown		if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED)
192e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			return -EPERM;
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	return -ETIMEDOUT;
203e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
204e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
205e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int
206ce44bf5b5544cbe6358abb01f039361a99b80901David Brownssbi_pa_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len)
207e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
208e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 cmd;
209e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret = 0;
210e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
211e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8;
212e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
213e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (len) {
214ce44bf5b5544cbe6358abb01f039361a99b80901David Brown		ret = ssbi_pa_transfer(ssbi, cmd, buf);
215e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
216e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
217e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		buf++;
218e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		len--;
219e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
220e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
221e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr:
222e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
223e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
224e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
225e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int
2265eec14ccf90942fecd89e147e0b88ab12dd83e70Stephen Boydssbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, const u8 *buf, int len)
227e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
228e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 cmd;
229e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret = 0;
230e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
231e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (len) {
232e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf;
233ce44bf5b5544cbe6358abb01f039361a99b80901David Brown		ret = ssbi_pa_transfer(ssbi, cmd, NULL);
234e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
235e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
236e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		buf++;
237e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		len--;
238e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
239e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
240e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr:
241e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
242e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
243e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
244ce44bf5b5544cbe6358abb01f039361a99b80901David Brownint ssbi_read(struct device *dev, u16 addr, u8 *buf, int len)
245e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
246ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	struct ssbi *ssbi = to_ssbi(dev);
247e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	unsigned long flags;
248e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret;
249e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
250e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_lock_irqsave(&ssbi->lock, flags);
251e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	ret = ssbi->read(ssbi, addr, buf, len);
252e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_unlock_irqrestore(&ssbi->lock, flags);
253e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
254e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
255e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
256ce44bf5b5544cbe6358abb01f039361a99b80901David BrownEXPORT_SYMBOL_GPL(ssbi_read);
257e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
2585eec14ccf90942fecd89e147e0b88ab12dd83e70Stephen Boydint ssbi_write(struct device *dev, u16 addr, const u8 *buf, int len)
259e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
260ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	struct ssbi *ssbi = to_ssbi(dev);
261e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	unsigned long flags;
262e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret;
263e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
264e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_lock_irqsave(&ssbi->lock, flags);
265e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	ret = ssbi->write(ssbi, addr, buf, len);
266e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_unlock_irqrestore(&ssbi->lock, flags);
267e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
268e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
269e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
270ce44bf5b5544cbe6358abb01f039361a99b80901David BrownEXPORT_SYMBOL_GPL(ssbi_write);
271e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
272ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic int ssbi_probe(struct platform_device *pdev)
273e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
27497f00f7120fe3396302693cdc4b1d11bbacad963David Brown	struct device_node *np = pdev->dev.of_node;
275e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	struct resource *mem_res;
276ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	struct ssbi *ssbi;
27797f00f7120fe3396302693cdc4b1d11bbacad963David Brown	const char *type;
278e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
279e578438820cdca91cb5eab477ec062236433ce5fStephen Boyd	ssbi = devm_kzalloc(&pdev->dev, sizeof(*ssbi), GFP_KERNEL);
280e578438820cdca91cb5eab477ec062236433ce5fStephen Boyd	if (!ssbi)
281e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		return -ENOMEM;
282e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
283e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
284e578438820cdca91cb5eab477ec062236433ce5fStephen Boyd	ssbi->base = devm_ioremap_resource(&pdev->dev, mem_res);
285e578438820cdca91cb5eab477ec062236433ce5fStephen Boyd	if (IS_ERR(ssbi->base))
286e578438820cdca91cb5eab477ec062236433ce5fStephen Boyd		return PTR_ERR(ssbi->base);
287e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
288e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	platform_set_drvdata(pdev, ssbi);
289e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
29097f00f7120fe3396302693cdc4b1d11bbacad963David Brown	type = of_get_property(np, "qcom,controller-type", NULL);
29197f00f7120fe3396302693cdc4b1d11bbacad963David Brown	if (type == NULL) {
292e578438820cdca91cb5eab477ec062236433ce5fStephen Boyd		dev_err(&pdev->dev, "Missing qcom,controller-type property\n");
293e578438820cdca91cb5eab477ec062236433ce5fStephen Boyd		return -EINVAL;
29497f00f7120fe3396302693cdc4b1d11bbacad963David Brown	}
29597f00f7120fe3396302693cdc4b1d11bbacad963David Brown	dev_info(&pdev->dev, "SSBI controller type: '%s'\n", type);
29697f00f7120fe3396302693cdc4b1d11bbacad963David Brown	if (strcmp(type, "ssbi") == 0)
29797f00f7120fe3396302693cdc4b1d11bbacad963David Brown		ssbi->controller_type = MSM_SBI_CTRL_SSBI;
29897f00f7120fe3396302693cdc4b1d11bbacad963David Brown	else if (strcmp(type, "ssbi2") == 0)
29997f00f7120fe3396302693cdc4b1d11bbacad963David Brown		ssbi->controller_type = MSM_SBI_CTRL_SSBI2;
30097f00f7120fe3396302693cdc4b1d11bbacad963David Brown	else if (strcmp(type, "pmic-arbiter") == 0)
30197f00f7120fe3396302693cdc4b1d11bbacad963David Brown		ssbi->controller_type = MSM_SBI_CTRL_PMIC_ARBITER;
30297f00f7120fe3396302693cdc4b1d11bbacad963David Brown	else {
303e578438820cdca91cb5eab477ec062236433ce5fStephen Boyd		dev_err(&pdev->dev, "Unknown qcom,controller-type\n");
304e578438820cdca91cb5eab477ec062236433ce5fStephen Boyd		return -EINVAL;
30597f00f7120fe3396302693cdc4b1d11bbacad963David Brown	}
30697f00f7120fe3396302693cdc4b1d11bbacad963David Brown
307e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) {
308ce44bf5b5544cbe6358abb01f039361a99b80901David Brown		ssbi->read = ssbi_pa_read_bytes;
309ce44bf5b5544cbe6358abb01f039361a99b80901David Brown		ssbi->write = ssbi_pa_write_bytes;
310e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	} else {
311ce44bf5b5544cbe6358abb01f039361a99b80901David Brown		ssbi->read = ssbi_read_bytes;
312ce44bf5b5544cbe6358abb01f039361a99b80901David Brown		ssbi->write = ssbi_write_bytes;
313e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
314e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
315e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_lock_init(&ssbi->lock);
316e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
317e578438820cdca91cb5eab477ec062236433ce5fStephen Boyd	return of_platform_populate(np, NULL, NULL, &pdev->dev);
318e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
319e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
32012eda2a2e644d319dfc49bede1620c2f48ccbc8aStephen Boydstatic const struct of_device_id ssbi_match_table[] = {
32197f00f7120fe3396302693cdc4b1d11bbacad963David Brown	{ .compatible = "qcom,ssbi" },
32297f00f7120fe3396302693cdc4b1d11bbacad963David Brown	{}
32397f00f7120fe3396302693cdc4b1d11bbacad963David Brown};
3246378c1e511d97218f33936f47888095023c9ffafStephen BoydMODULE_DEVICE_TABLE(of, ssbi_match_table);
32597f00f7120fe3396302693cdc4b1d11bbacad963David Brown
326ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic struct platform_driver ssbi_driver = {
327ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	.probe		= ssbi_probe,
328e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	.driver		= {
329ce44bf5b5544cbe6358abb01f039361a99b80901David Brown		.name	= "ssbi",
330e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		.owner	= THIS_MODULE,
33197f00f7120fe3396302693cdc4b1d11bbacad963David Brown		.of_match_table = ssbi_match_table,
332e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	},
333e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke};
334e578438820cdca91cb5eab477ec062236433ce5fStephen Boydmodule_platform_driver(ssbi_driver);
335e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
336e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth HeitkeMODULE_LICENSE("GPL v2");
337e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth HeitkeMODULE_VERSION("1.0");
338ce44bf5b5544cbe6358abb01f039361a99b80901David BrownMODULE_ALIAS("platform:ssbi");
339e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth HeitkeMODULE_AUTHOR("Dima Zavin <dima@android.com>");
340