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