ssbi.c revision 6378c1e511d97218f33936f47888095023c9ffaf
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
68ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstruct ssbi {
69e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	struct device		*slave;
70e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	void __iomem		*base;
71e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spinlock_t		lock;
72ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	enum ssbi_controller_type controller_type;
73ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	int (*read)(struct ssbi *, u16 addr, u8 *buf, int len);
74ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	int (*write)(struct ssbi *, u16 addr, u8 *buf, int len);
75e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke};
76e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
77ce44bf5b5544cbe6358abb01f039361a99b80901David Brown#define to_ssbi(dev)	platform_get_drvdata(to_platform_device(dev))
78e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
79ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic inline u32 ssbi_readl(struct ssbi *ssbi, u32 reg)
80e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
81e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return readl(ssbi->base + reg);
82e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
83e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
84ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic inline void ssbi_writel(struct ssbi *ssbi, u32 val, u32 reg)
85e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
86e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	writel(val, ssbi->base + reg);
87e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
88e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
893f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown/*
903f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * Via private exchange with one of the original authors, the hardware
913f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * should generally finish a transaction in about 5us.  The worst
923f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * case, is when using the arbiter and both other CPUs have just
933f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * started trying to use the SSBI bus will result in a time of about
943f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * 20us.  It should never take longer than this.
953f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown *
963f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * As such, this wait merely spins, with a udelay.
973f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown */
98ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic int ssbi_wait_mask(struct ssbi *ssbi, u32 set_mask, u32 clr_mask)
99e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
100e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 timeout = SSBI_TIMEOUT_US;
101e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 val;
102e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
103e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (timeout--) {
104e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		val = ssbi_readl(ssbi, SSBI2_STATUS);
105e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0))
106e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			return 0;
107e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		udelay(1);
108e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
109e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
110e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return -ETIMEDOUT;
111e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
112e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
113e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int
114ce44bf5b5544cbe6358abb01f039361a99b80901David Brownssbi_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len)
115e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
116e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16);
117e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret = 0;
118e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
119e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
120e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
121e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
122e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi_writel(ssbi, mode2, SSBI2_MODE2);
123e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
124e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
125e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (len) {
126e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
127e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
128e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
129e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
130e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi_writel(ssbi, cmd, SSBI2_CMD);
131e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0);
132e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
133e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
134e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		*buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff;
135e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		len--;
136e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
137e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
138e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr:
139e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
140e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
141e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
142e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int
143ce44bf5b5544cbe6358abb01f039361a99b80901David Brownssbi_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len)
144e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
145e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret = 0;
146e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
147e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
148e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
149e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
150e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi_writel(ssbi, mode2, SSBI2_MODE2);
151e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
152e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
153e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (len) {
154e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
155e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
156e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
157e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
158e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD);
159e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY);
160e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
161e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
162e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		buf++;
163e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		len--;
164e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
165e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
166e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr:
167e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
168e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
169e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
1703f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown/*
1713f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * See ssbi_wait_mask for an explanation of the time and the
1723f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * busywait.
1733f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown */
174e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic inline int
175ce44bf5b5544cbe6358abb01f039361a99b80901David Brownssbi_pa_transfer(struct ssbi *ssbi, u32 cmd, u8 *data)
176e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
177e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 timeout = SSBI_TIMEOUT_US;
178e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 rd_status = 0;
179e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
180e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	ssbi_writel(ssbi, cmd, SSBI_PA_CMD);
181e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
182e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (timeout--) {
183e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS);
184e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
18537799ef4fa95ceec09b5c214fb281c6e6acddf5bDavid Brown		if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED)
186e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			return -EPERM;
187e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
188e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) {
189e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			if (data)
190e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke				*data = rd_status & 0xff;
191e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			return 0;
192e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		}
193e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		udelay(1);
194e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
195e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
196e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return -ETIMEDOUT;
197e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
198e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
199e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int
200ce44bf5b5544cbe6358abb01f039361a99b80901David Brownssbi_pa_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len)
201e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
202e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 cmd;
203e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret = 0;
204e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
205e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8;
206e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
207e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (len) {
208ce44bf5b5544cbe6358abb01f039361a99b80901David Brown		ret = ssbi_pa_transfer(ssbi, cmd, buf);
209e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
210e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
211e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		buf++;
212e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		len--;
213e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
214e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
215e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr:
216e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
217e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
218e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
219e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int
220ce44bf5b5544cbe6358abb01f039361a99b80901David Brownssbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len)
221e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
222e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	u32 cmd;
223e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret = 0;
224e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
225e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	while (len) {
226e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf;
227ce44bf5b5544cbe6358abb01f039361a99b80901David Brown		ret = ssbi_pa_transfer(ssbi, cmd, NULL);
228e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		if (ret)
229e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke			goto err;
230e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		buf++;
231e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		len--;
232e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
233e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
234e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr:
235e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
236e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
237e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
238ce44bf5b5544cbe6358abb01f039361a99b80901David Brownint ssbi_read(struct device *dev, u16 addr, u8 *buf, int len)
239e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
240ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	struct ssbi *ssbi = to_ssbi(dev);
241e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	unsigned long flags;
242e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret;
243e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
244e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_lock_irqsave(&ssbi->lock, flags);
245e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	ret = ssbi->read(ssbi, addr, buf, len);
246e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_unlock_irqrestore(&ssbi->lock, flags);
247e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
248e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
249e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
250ce44bf5b5544cbe6358abb01f039361a99b80901David BrownEXPORT_SYMBOL_GPL(ssbi_read);
251e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
252ce44bf5b5544cbe6358abb01f039361a99b80901David Brownint ssbi_write(struct device *dev, u16 addr, u8 *buf, int len)
253e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
254ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	struct ssbi *ssbi = to_ssbi(dev);
255e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	unsigned long flags;
256e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret;
257e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
258e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_lock_irqsave(&ssbi->lock, flags);
259e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	ret = ssbi->write(ssbi, addr, buf, len);
260e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_unlock_irqrestore(&ssbi->lock, flags);
261e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
262e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
263e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
264ce44bf5b5544cbe6358abb01f039361a99b80901David BrownEXPORT_SYMBOL_GPL(ssbi_write);
265e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
266ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic int ssbi_probe(struct platform_device *pdev)
267e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
26897f00f7120fe3396302693cdc4b1d11bbacad963David Brown	struct device_node *np = pdev->dev.of_node;
269e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	struct resource *mem_res;
270ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	struct ssbi *ssbi;
271e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	int ret = 0;
27297f00f7120fe3396302693cdc4b1d11bbacad963David Brown	const char *type;
273e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
274ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	ssbi = kzalloc(sizeof(struct ssbi), GFP_KERNEL);
275e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (!ssbi) {
276e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		pr_err("can not allocate ssbi_data\n");
277e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		return -ENOMEM;
278e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
279e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
280e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
281e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (!mem_res) {
282e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		pr_err("missing mem resource\n");
283e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = -EINVAL;
284e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		goto err_get_mem_res;
285e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
286e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
287e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	ssbi->base = ioremap(mem_res->start, resource_size(mem_res));
288e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (!ssbi->base) {
289e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		pr_err("ioremap of 0x%p failed\n", (void *)mem_res->start);
290e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		ret = -EINVAL;
291e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		goto err_ioremap;
292e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
293e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	platform_set_drvdata(pdev, ssbi);
294e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
29597f00f7120fe3396302693cdc4b1d11bbacad963David Brown	type = of_get_property(np, "qcom,controller-type", NULL);
29697f00f7120fe3396302693cdc4b1d11bbacad963David Brown	if (type == NULL) {
29797f00f7120fe3396302693cdc4b1d11bbacad963David Brown		pr_err("Missing qcom,controller-type property\n");
29897f00f7120fe3396302693cdc4b1d11bbacad963David Brown		ret = -EINVAL;
29997f00f7120fe3396302693cdc4b1d11bbacad963David Brown		goto err_ssbi_controller;
30097f00f7120fe3396302693cdc4b1d11bbacad963David Brown	}
30197f00f7120fe3396302693cdc4b1d11bbacad963David Brown	dev_info(&pdev->dev, "SSBI controller type: '%s'\n", type);
30297f00f7120fe3396302693cdc4b1d11bbacad963David Brown	if (strcmp(type, "ssbi") == 0)
30397f00f7120fe3396302693cdc4b1d11bbacad963David Brown		ssbi->controller_type = MSM_SBI_CTRL_SSBI;
30497f00f7120fe3396302693cdc4b1d11bbacad963David Brown	else if (strcmp(type, "ssbi2") == 0)
30597f00f7120fe3396302693cdc4b1d11bbacad963David Brown		ssbi->controller_type = MSM_SBI_CTRL_SSBI2;
30697f00f7120fe3396302693cdc4b1d11bbacad963David Brown	else if (strcmp(type, "pmic-arbiter") == 0)
30797f00f7120fe3396302693cdc4b1d11bbacad963David Brown		ssbi->controller_type = MSM_SBI_CTRL_PMIC_ARBITER;
30897f00f7120fe3396302693cdc4b1d11bbacad963David Brown	else {
30997f00f7120fe3396302693cdc4b1d11bbacad963David Brown		pr_err("Unknown qcom,controller-type\n");
31097f00f7120fe3396302693cdc4b1d11bbacad963David Brown		ret = -EINVAL;
31197f00f7120fe3396302693cdc4b1d11bbacad963David Brown		goto err_ssbi_controller;
31297f00f7120fe3396302693cdc4b1d11bbacad963David Brown	}
31397f00f7120fe3396302693cdc4b1d11bbacad963David Brown
314e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) {
315ce44bf5b5544cbe6358abb01f039361a99b80901David Brown		ssbi->read = ssbi_pa_read_bytes;
316ce44bf5b5544cbe6358abb01f039361a99b80901David Brown		ssbi->write = ssbi_pa_write_bytes;
317e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	} else {
318ce44bf5b5544cbe6358abb01f039361a99b80901David Brown		ssbi->read = ssbi_read_bytes;
319ce44bf5b5544cbe6358abb01f039361a99b80901David Brown		ssbi->write = ssbi_write_bytes;
320e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	}
321e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
322e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	spin_lock_init(&ssbi->lock);
323e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
32497f00f7120fe3396302693cdc4b1d11bbacad963David Brown	ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
325e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	if (ret)
32697f00f7120fe3396302693cdc4b1d11bbacad963David Brown		goto err_ssbi_controller;
327e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
328e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return 0;
329e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
33097f00f7120fe3396302693cdc4b1d11bbacad963David Brownerr_ssbi_controller:
331e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	platform_set_drvdata(pdev, NULL);
332e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	iounmap(ssbi->base);
333e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr_ioremap:
334e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr_get_mem_res:
335e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	kfree(ssbi);
336e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return ret;
337e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
338e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
339ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic int ssbi_remove(struct platform_device *pdev)
340e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
341ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	struct ssbi *ssbi = platform_get_drvdata(pdev);
342e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
343e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	platform_set_drvdata(pdev, NULL);
344e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	iounmap(ssbi->base);
345e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	kfree(ssbi);
346e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	return 0;
347e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
348e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
34997f00f7120fe3396302693cdc4b1d11bbacad963David Brownstatic struct of_device_id ssbi_match_table[] = {
35097f00f7120fe3396302693cdc4b1d11bbacad963David Brown	{ .compatible = "qcom,ssbi" },
35197f00f7120fe3396302693cdc4b1d11bbacad963David Brown	{}
35297f00f7120fe3396302693cdc4b1d11bbacad963David Brown};
3536378c1e511d97218f33936f47888095023c9ffafStephen BoydMODULE_DEVICE_TABLE(of, ssbi_match_table);
35497f00f7120fe3396302693cdc4b1d11bbacad963David Brown
355ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic struct platform_driver ssbi_driver = {
356ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	.probe		= ssbi_probe,
357ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	.remove		= ssbi_remove,
358e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	.driver		= {
359ce44bf5b5544cbe6358abb01f039361a99b80901David Brown		.name	= "ssbi",
360e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke		.owner	= THIS_MODULE,
36197f00f7120fe3396302693cdc4b1d11bbacad963David Brown		.of_match_table = ssbi_match_table,
362e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke	},
363e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke};
364e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
365ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic int __init ssbi_init(void)
366e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
367ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	return platform_driver_register(&ssbi_driver);
368e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
369ce44bf5b5544cbe6358abb01f039361a99b80901David Brownmodule_init(ssbi_init);
370e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
371ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic void __exit ssbi_exit(void)
372e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{
373ce44bf5b5544cbe6358abb01f039361a99b80901David Brown	platform_driver_unregister(&ssbi_driver);
374e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}
375ce44bf5b5544cbe6358abb01f039361a99b80901David Brownmodule_exit(ssbi_exit)
376e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke
377e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth HeitkeMODULE_LICENSE("GPL v2");
378e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth HeitkeMODULE_VERSION("1.0");
379ce44bf5b5544cbe6358abb01f039361a99b80901David BrownMODULE_ALIAS("platform:ssbi");
380e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth HeitkeMODULE_AUTHOR("Dima Zavin <dima@android.com>");
381