1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2018 Arm Ltd.
4 * Author: Liviu Dudau <liviu.dudau@foss.arm.com>
5 *
6 */
7
8#include <common.h>
9#include <dm.h>
10#include <errno.h>
11#include <i2c.h>
12#include <asm/io.h>
13#include <clk.h>
14#include <linux/bitops.h>
15#include <linux/delay.h>
16#include <linux/io.h>
17
18#define I2C_CONTROL_REG		0x00
19#define I2C_SET_REG		0x00
20#define I2C_CLEAR_REG		0x04
21
22#define SCL	BIT(0)
23#define SDA	BIT(1)
24
25struct versatile_i2c_priv {
26	phys_addr_t base;
27	u32 delay;
28};
29
30static inline void versatile_sda_set(struct versatile_i2c_priv *priv, u8 state)
31{
32	writel(SDA, priv->base + (state ? I2C_SET_REG : I2C_CLEAR_REG));
33	udelay(priv->delay);
34}
35
36static inline int versatile_sda_get(struct versatile_i2c_priv *priv)
37{
38	int v = !!(readl(priv->base + I2C_CONTROL_REG) & SDA);
39
40	udelay(priv->delay);
41	return v;
42}
43
44static inline void versatile_scl_set(struct versatile_i2c_priv *priv, u8 state)
45{
46	writel(SCL, priv->base + (state ? I2C_SET_REG : I2C_CLEAR_REG));
47	udelay(priv->delay);
48}
49
50static inline int versatile_scl_get(struct versatile_i2c_priv *priv)
51{
52	int v = !!(readl(priv->base + I2C_CONTROL_REG) & SCL);
53
54	udelay(priv->delay);
55	return v;
56}
57
58/* start: SDA goes from high to low while SCL is high */
59static void versatile_i2c_start(struct versatile_i2c_priv *priv)
60{
61	udelay(priv->delay);
62	versatile_sda_set(priv, 1);
63	versatile_scl_set(priv, 1);
64	versatile_sda_set(priv, 0);
65}
66
67/* stop: SDA goes from low to high while SCL is high */
68static void versatile_i2c_stop(struct versatile_i2c_priv *priv)
69{
70	versatile_scl_set(priv, 0);
71	versatile_sda_set(priv, 0);
72	versatile_scl_set(priv, 1);
73	versatile_sda_set(priv, 1);
74}
75
76/* read a bit from the SDA line (data or ACK/NACK) */
77static u8 versatile_i2c_read_bit(struct versatile_i2c_priv *priv)
78{
79	versatile_scl_set(priv, 0);
80	versatile_sda_set(priv, 1);
81	versatile_scl_set(priv, 1);
82	udelay(priv->delay);
83	return (u8)versatile_sda_get(priv);
84}
85
86/* write a bit on the SDA line */
87static void versatile_i2c_write_bit(struct versatile_i2c_priv *priv, u8 bit)
88{
89	versatile_scl_set(priv, 0);
90	versatile_sda_set(priv, bit);
91	versatile_scl_set(priv, 1);
92	udelay(priv->delay);
93}
94
95/* send a reset sequence of 9 clocks with SDA high */
96static void versatile_i2c_reset_bus(struct versatile_i2c_priv *priv)
97{
98	int i;
99
100	for (i = 0; i < 9; i++)
101		versatile_i2c_write_bit(priv, 1);
102
103	versatile_i2c_stop(priv);
104}
105
106/* write byte without start/stop sequence */
107static int versatile_i2c_write_byte(struct versatile_i2c_priv *priv, u8 byte)
108{
109	u8 nak, i;
110
111	for (i = 0; i < 8; i++) {
112		versatile_i2c_write_bit(priv, byte & 0x80);
113		byte <<= 1;
114	}
115
116	/* read ACK */
117	nak = versatile_i2c_read_bit(priv);
118	versatile_scl_set(priv, 0);
119
120	return nak;	/* not a nack is an ack */
121}
122
123static int versatile_i2c_read_byte(struct versatile_i2c_priv *priv,
124				   u8 *byte, u8 ack)
125{
126	u8 i;
127
128	*byte = 0;
129	for (i = 0; i < 8; i++) {
130		*byte <<= 1;
131		*byte |= versatile_i2c_read_bit(priv);
132	}
133	/* write the nack */
134	versatile_i2c_write_bit(priv, ack);
135
136	return 0;
137}
138
139static int versatile_i2c_send_slave_addr(struct versatile_i2c_priv *priv,
140					 struct i2c_msg *msg)
141{
142	u8 addr;
143	int ret;
144
145	if (msg->flags & I2C_M_TEN) {
146		/* 10-bit address, send extended address code first */
147		addr = 0xf0 | ((msg->addr >> 7) & 0x06);
148		ret = versatile_i2c_write_byte(priv, addr);
149		if (ret) {
150			versatile_i2c_stop(priv);
151			return -EIO;
152		}
153
154		/* remaining bits */
155		ret = versatile_i2c_write_byte(priv, msg->addr & 0xff);
156		if (ret) {
157			versatile_i2c_stop(priv);
158			return -EIO;
159		}
160		/* reads need to resend the addr */
161		if (msg->flags & I2C_M_RD) {
162			versatile_i2c_start(priv);
163			addr |= 1;
164			ret = versatile_i2c_write_byte(priv, addr);
165			if (ret) {
166				versatile_i2c_stop(priv);
167				return -EIO;
168			}
169		}
170	} else {
171		/* normal 7-bit address */
172		addr = msg->addr << 1;
173		if (msg->flags & I2C_M_RD)
174			addr |= 1;
175		ret = versatile_i2c_write_byte(priv, addr);
176		if (ret) {
177			versatile_i2c_stop(priv);
178			return -EIO;
179		}
180	}
181
182	return 0;
183}
184
185static int versatile_i2c_message_xfer(struct versatile_i2c_priv *priv,
186				      struct i2c_msg *msg)
187{
188	int i, ret;
189	u8 ack;
190
191	versatile_i2c_start(priv);
192	if (versatile_i2c_send_slave_addr(priv, msg))
193		return -EIO;
194
195	for (i = 0; i < msg->len; i++) {
196		if (msg->flags & I2C_M_RD) {
197			ack = (msg->len - i - 1) == 0 ? 1 : 0;
198			ret = versatile_i2c_read_byte(priv, &msg->buf[i], ack);
199		} else {
200			ret = versatile_i2c_write_byte(priv, msg->buf[i]);
201		}
202
203		if (ret)
204			break;
205	}
206
207	versatile_i2c_stop(priv);
208
209	return ret;
210}
211
212static int versatile_i2c_xfer(struct udevice *bus,
213			      struct i2c_msg *msg, int nmsgs)
214{
215	struct versatile_i2c_priv *priv = dev_get_priv(bus);
216	int ret;
217
218	for ( ; nmsgs > 0; nmsgs--, msg++) {
219		ret = versatile_i2c_message_xfer(priv, msg);
220		if (ret)
221			return -EREMOTEIO;
222	}
223
224	return 0;
225}
226
227static int versatile_i2c_chip_probe(struct udevice *bus,
228				    uint chip, uint chip_flags)
229{
230	/* probe the presence of a slave by writing a 0-size message */
231	struct i2c_msg msg = { .addr = chip, .flags = chip_flags,
232			       .len = 0, .buf = NULL };
233	struct versatile_i2c_priv *priv = dev_get_priv(bus);
234
235	return versatile_i2c_message_xfer(priv, &msg);
236}
237
238static int versatile_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
239{
240	struct versatile_i2c_priv *priv = dev_get_priv(bus);
241
242	priv->delay = 1000000 / (speed << 2);
243
244	versatile_i2c_reset_bus(priv);
245
246	return 0;
247}
248
249static int versatile_i2c_probe(struct udevice *dev)
250{
251	struct versatile_i2c_priv *priv = dev_get_priv(dev);
252
253	priv->base = (phys_addr_t)dev_read_addr(dev);
254	priv->delay = 25;	/* 25us * 4 = 100kHz */
255
256	return 0;
257}
258
259static const struct dm_i2c_ops versatile_i2c_ops = {
260	.xfer = versatile_i2c_xfer,
261	.probe_chip = versatile_i2c_chip_probe,
262	.set_bus_speed = versatile_i2c_set_bus_speed,
263};
264
265static const struct udevice_id versatile_i2c_of_match[] = {
266	{ .compatible = "arm,versatile-i2c" },
267	{ }
268};
269
270U_BOOT_DRIVER(versatile_i2c) = {
271	.name = "i2c-bus-versatile",
272	.id = UCLASS_I2C,
273	.of_match = versatile_i2c_of_match,
274	.probe = versatile_i2c_probe,
275	.priv_auto	= sizeof(struct versatile_i2c_priv),
276	.ops = &versatile_i2c_ops,
277};
278