blob: 0a1a85dfc288fc3dbd53567f0e42f57cddf4ea83 [file] [log] [blame]
Liviu Dudau86c83e82018-09-28 13:46:48 +01001// 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>
Simon Glasscd93d622020-05-10 11:40:13 -060014#include <linux/bitops.h>
Simon Glassc05ed002020-05-10 11:40:11 -060015#include <linux/delay.h>
Liviu Dudau86c83e82018-09-28 13:46:48 +010016#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 */
Liviu Dudau86c83e82018-09-28 13:46:48 +0100255
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,
Simon Glass41575d82020-12-03 16:55:17 -0700275 .priv_auto = sizeof(struct versatile_i2c_priv),
Liviu Dudau86c83e82018-09-28 13:46:48 +0100276 .ops = &versatile_i2c_ops,
277};