]>
Commit | Line | Data |
---|---|---|
7f5ea250 AL |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * (C) Copyright 2020 | |
4 | * Arthur Li, Cortina Access, [email protected]. | |
5 | */ | |
6 | ||
7f5ea250 AL |
7 | #include <i2c.h> |
8 | #include <log.h> | |
9 | #include <asm/io.h> | |
10 | #include <dm.h> | |
11 | #include <mapmem.h> | |
03de305e | 12 | #include <time.h> |
7f5ea250 AL |
13 | #include "i2c-cortina.h" |
14 | ||
15 | static void set_speed(struct i2c_regs *regs, int i2c_spd) | |
16 | { | |
17 | union ca_biw_cfg i2c_cfg; | |
18 | ||
19 | i2c_cfg.wrd = readl(®s->i2c_cfg); | |
20 | i2c_cfg.bf.core_en = 0; | |
21 | writel(i2c_cfg.wrd, ®s->i2c_cfg); | |
22 | ||
23 | switch (i2c_spd) { | |
24 | case IC_SPEED_MODE_FAST_PLUS: | |
25 | i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / | |
26 | (5 * I2C_SPEED_FAST_PLUS_RATE) - 1; | |
27 | break; | |
28 | ||
29 | case IC_SPEED_MODE_STANDARD: | |
30 | i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / | |
31 | (5 * I2C_SPEED_STANDARD_RATE) - 1; | |
32 | break; | |
33 | ||
34 | case IC_SPEED_MODE_FAST: | |
35 | default: | |
36 | i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / | |
37 | (5 * I2C_SPEED_FAST_RATE) - 1; | |
38 | break; | |
39 | } | |
40 | ||
41 | i2c_cfg.bf.core_en = 1; | |
42 | writel(i2c_cfg.wrd, ®s->i2c_cfg); | |
43 | } | |
44 | ||
45 | static int ca_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) | |
46 | { | |
47 | struct ca_i2c *priv = dev_get_priv(bus); | |
48 | int i2c_spd; | |
49 | ||
50 | if (speed >= I2C_SPEED_FAST_PLUS_RATE) { | |
51 | i2c_spd = IC_SPEED_MODE_FAST_PLUS; | |
52 | priv->speed = I2C_SPEED_FAST_PLUS_RATE; | |
53 | } else if (speed >= I2C_SPEED_FAST_RATE) { | |
54 | i2c_spd = IC_SPEED_MODE_FAST; | |
55 | priv->speed = I2C_SPEED_FAST_RATE; | |
56 | } else { | |
57 | i2c_spd = IC_SPEED_MODE_STANDARD; | |
58 | priv->speed = I2C_SPEED_STANDARD_RATE; | |
59 | } | |
60 | ||
61 | set_speed(priv->regs, i2c_spd); | |
62 | ||
63 | return 0; | |
64 | } | |
65 | ||
66 | static int ca_i2c_get_bus_speed(struct udevice *bus) | |
67 | { | |
68 | struct ca_i2c *priv = dev_get_priv(bus); | |
69 | ||
70 | return priv->speed; | |
71 | } | |
72 | ||
73 | static void ca_i2c_init(struct i2c_regs *regs) | |
74 | { | |
75 | union ca_biw_cfg i2c_cfg; | |
76 | ||
77 | i2c_cfg.wrd = readl(®s->i2c_cfg); | |
78 | i2c_cfg.bf.core_en = 0; | |
79 | i2c_cfg.bf.biw_soft_reset = 1; | |
80 | writel(i2c_cfg.wrd, ®s->i2c_cfg); | |
81 | mdelay(10); | |
82 | i2c_cfg.bf.biw_soft_reset = 0; | |
83 | writel(i2c_cfg.wrd, ®s->i2c_cfg); | |
84 | ||
85 | set_speed(regs, IC_SPEED_MODE_STANDARD); | |
86 | ||
87 | i2c_cfg.wrd = readl(®s->i2c_cfg); | |
88 | i2c_cfg.bf.core_en = 1; | |
89 | writel(i2c_cfg.wrd, ®s->i2c_cfg); | |
90 | } | |
91 | ||
92 | static int i2c_wait_complete(struct i2c_regs *regs) | |
93 | { | |
94 | union ca_biw_ctrl i2c_ctrl; | |
95 | unsigned long start_time_bb = get_timer(0); | |
96 | ||
97 | i2c_ctrl.wrd = readl(®s->i2c_ctrl); | |
98 | ||
99 | while (i2c_ctrl.bf.biwdone == 0) { | |
100 | i2c_ctrl.wrd = readl(®s->i2c_ctrl); | |
101 | ||
102 | if (get_timer(start_time_bb) > | |
103 | (unsigned long)(I2C_BYTE_TO_BB)) { | |
104 | printf("%s not done!!!\n", __func__); | |
105 | return -ETIMEDOUT; | |
106 | } | |
107 | } | |
108 | ||
109 | /* Clear done bit */ | |
110 | writel(i2c_ctrl.wrd, ®s->i2c_ctrl); | |
111 | ||
112 | return 0; | |
113 | } | |
114 | ||
115 | static void i2c_setaddress(struct i2c_regs *regs, unsigned int i2c_addr, | |
116 | int write_read) | |
117 | { | |
118 | writel(i2c_addr | write_read, ®s->i2c_txr); | |
119 | ||
120 | writel(BIW_CTRL_START | BIW_CTRL_WRITE, | |
121 | ®s->i2c_ctrl); | |
122 | ||
123 | i2c_wait_complete(regs); | |
124 | } | |
125 | ||
126 | static int i2c_wait_for_bus_busy(struct i2c_regs *regs) | |
127 | { | |
128 | union ca_biw_ack i2c_ack; | |
129 | unsigned long start_time_bb = get_timer(0); | |
130 | ||
131 | i2c_ack.wrd = readl(®s->i2c_ack); | |
132 | ||
133 | while (i2c_ack.bf.biw_busy) { | |
134 | i2c_ack.wrd = readl(®s->i2c_ack); | |
135 | ||
136 | if (get_timer(start_time_bb) > | |
137 | (unsigned long)(I2C_BYTE_TO_BB)) { | |
138 | printf("%s: timeout!\n", __func__); | |
139 | return -ETIMEDOUT; | |
140 | } | |
141 | } | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
146 | static int i2c_xfer_init(struct i2c_regs *regs, uint8_t chip, uint addr, | |
147 | int alen, int write_read) | |
148 | { | |
149 | int addr_len = alen; | |
150 | ||
151 | if (i2c_wait_for_bus_busy(regs)) | |
152 | return 1; | |
153 | ||
154 | /* First cycle must write addr + offset */ | |
155 | chip = ((chip & 0x7F) << 1); | |
156 | if (alen == 0 && write_read == I2C_CMD_RD) | |
157 | i2c_setaddress(regs, chip, I2C_CMD_RD); | |
158 | else | |
159 | i2c_setaddress(regs, chip, I2C_CMD_WT); | |
160 | ||
161 | while (alen) { | |
162 | alen--; | |
163 | writel(addr, ®s->i2c_txr); | |
164 | if (write_read == I2C_CMD_RD) | |
165 | writel(BIW_CTRL_WRITE | BIW_CTRL_STOP, | |
166 | ®s->i2c_ctrl); | |
167 | else | |
168 | writel(BIW_CTRL_WRITE, ®s->i2c_ctrl); | |
169 | i2c_wait_complete(regs); | |
170 | } | |
171 | ||
172 | /* Send address again with Read flag if it's read command */ | |
173 | if (write_read == I2C_CMD_RD && addr_len > 0) | |
174 | i2c_setaddress(regs, chip, I2C_CMD_RD); | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | static int i2c_xfer_finish(struct i2c_regs *regs) | |
180 | { | |
181 | /* Dummy read makes bus free */ | |
182 | writel(BIW_CTRL_READ | BIW_CTRL_STOP, ®s->i2c_ctrl); | |
183 | i2c_wait_complete(regs); | |
184 | ||
185 | if (i2c_wait_for_bus_busy(regs)) { | |
186 | printf("Timed out waiting for bus\n"); | |
187 | return -ETIMEDOUT; | |
188 | } | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
193 | static int ca_i2c_read(struct i2c_regs *regs, uint8_t chip, uint addr, | |
194 | int alen, uint8_t *buffer, int len) | |
195 | { | |
196 | unsigned long start_time_rx; | |
197 | int rc = 0; | |
198 | ||
199 | rc = i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_RD); | |
200 | if (rc) | |
201 | return rc; | |
202 | ||
203 | start_time_rx = get_timer(0); | |
204 | while (len) { | |
205 | /* ACK_IN is ack value to send during read. | |
206 | * ack high only on the very last byte! | |
207 | */ | |
208 | if (len == 1) | |
209 | writel(BIW_CTRL_READ | BIW_CTRL_ACK_IN | BIW_CTRL_STOP, | |
210 | ®s->i2c_ctrl); | |
211 | else | |
212 | writel(BIW_CTRL_READ, ®s->i2c_ctrl); | |
213 | ||
214 | rc = i2c_wait_complete(regs); | |
215 | udelay(1); | |
216 | ||
217 | if (rc == 0) { | |
218 | *buffer++ = | |
219 | (uchar) readl(®s->i2c_rxr); | |
220 | len--; | |
221 | start_time_rx = get_timer(0); | |
222 | ||
223 | } else if (get_timer(start_time_rx) > I2C_BYTE_TO) { | |
224 | return -ETIMEDOUT; | |
225 | } | |
226 | } | |
227 | i2c_xfer_finish(regs); | |
228 | return rc; | |
229 | } | |
230 | ||
231 | static int ca_i2c_write(struct i2c_regs *regs, uint8_t chip, uint addr, | |
232 | int alen, uint8_t *buffer, int len) | |
233 | { | |
234 | int rc, nb = len; | |
235 | unsigned long start_time_tx; | |
236 | ||
237 | rc = i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_WT); | |
238 | if (rc) | |
239 | return rc; | |
240 | ||
241 | start_time_tx = get_timer(0); | |
242 | while (len) { | |
243 | writel(*buffer, ®s->i2c_txr); | |
244 | if (len == 1) | |
245 | writel(BIW_CTRL_WRITE | BIW_CTRL_STOP, | |
246 | ®s->i2c_ctrl); | |
247 | else | |
248 | writel(BIW_CTRL_WRITE, ®s->i2c_ctrl); | |
249 | ||
250 | rc = i2c_wait_complete(regs); | |
251 | ||
252 | if (rc == 0) { | |
253 | len--; | |
254 | buffer++; | |
255 | start_time_tx = get_timer(0); | |
256 | } else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) { | |
257 | return -ETIMEDOUT; | |
258 | } | |
259 | } | |
260 | ||
261 | return 0; | |
262 | } | |
263 | ||
264 | static int ca_i2c_probe_chip(struct udevice *bus, uint chip_addr, | |
265 | uint chip_flags) | |
266 | { | |
267 | struct ca_i2c *priv = dev_get_priv(bus); | |
268 | int ret; | |
269 | u32 tmp; | |
270 | ||
271 | /* Try to read the first location of the chip */ | |
272 | ret = ca_i2c_read(priv->regs, chip_addr, 0, 1, (uchar *)&tmp, 1); | |
273 | if (ret) | |
274 | ca_i2c_init(priv->regs); | |
275 | ||
276 | return ret; | |
277 | } | |
278 | ||
279 | static int ca_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) | |
280 | { | |
281 | struct ca_i2c *priv = dev_get_priv(bus); | |
282 | int ret; | |
283 | ||
284 | debug("i2c_xfer: %d messages\n", nmsgs); | |
285 | for (; nmsgs > 0; nmsgs--, msg++) { | |
286 | debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); | |
287 | if (msg->flags & I2C_M_RD) | |
288 | ret = ca_i2c_read(priv->regs, msg->addr, 0, 0, | |
289 | msg->buf, msg->len); | |
290 | else | |
291 | ret = ca_i2c_write(priv->regs, msg->addr, 0, 0, | |
292 | msg->buf, msg->len); | |
293 | ||
294 | if (ret) { | |
295 | printf("i2c_xfer: %s error\n", | |
296 | msg->flags & I2C_M_RD ? "read" : "write"); | |
297 | return ret; | |
298 | } | |
299 | } | |
300 | ||
301 | return 0; | |
302 | } | |
303 | ||
304 | static const struct dm_i2c_ops ca_i2c_ops = { | |
305 | .xfer = ca_i2c_xfer, | |
306 | .probe_chip = ca_i2c_probe_chip, | |
307 | .set_bus_speed = ca_i2c_set_bus_speed, | |
308 | .get_bus_speed = ca_i2c_get_bus_speed, | |
309 | }; | |
310 | ||
311 | static const struct udevice_id ca_i2c_ids[] = { | |
312 | { .compatible = "cortina,ca-i2c", }, | |
313 | { } | |
314 | }; | |
315 | ||
316 | static int ca_i2c_probe(struct udevice *bus) | |
317 | { | |
318 | struct ca_i2c *priv = dev_get_priv(bus); | |
319 | ||
320 | ca_i2c_init(priv->regs); | |
321 | ||
322 | return 0; | |
323 | } | |
324 | ||
d1998a9f | 325 | static int ca_i2c_of_to_plat(struct udevice *bus) |
7f5ea250 AL |
326 | { |
327 | struct ca_i2c *priv = dev_get_priv(bus); | |
328 | ||
329 | priv->regs = map_sysmem(dev_read_addr(bus), sizeof(struct i2c_regs)); | |
330 | if (!priv->regs) { | |
331 | printf("I2C: base address is invalid\n"); | |
332 | return -EINVAL; | |
333 | } | |
334 | ||
335 | return 0; | |
336 | } | |
337 | ||
338 | U_BOOT_DRIVER(i2c_cortina) = { | |
339 | .name = "i2c_cortina", | |
340 | .id = UCLASS_I2C, | |
341 | .of_match = ca_i2c_ids, | |
d1998a9f | 342 | .of_to_plat = ca_i2c_of_to_plat, |
7f5ea250 | 343 | .probe = ca_i2c_probe, |
41575d8e | 344 | .priv_auto = sizeof(struct ca_i2c), |
7f5ea250 AL |
345 | .ops = &ca_i2c_ops, |
346 | .flags = DM_FLAG_PRE_RELOC, | |
347 | }; |