]>
Commit | Line | Data |
---|---|---|
7ee3f149 PF |
1 | /* |
2 | * Copyright 2016 Freescale Semiconductors, Inc. | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0+ | |
5 | */ | |
6 | ||
7 | #include <common.h> | |
8 | #include <errno.h> | |
9 | #include <asm/io.h> | |
10 | #include <asm/arch/clock.h> | |
11 | #include <asm/arch/imx-regs.h> | |
12 | #include <asm/arch/imx_lpi2c.h> | |
13 | #include <asm/arch/sys_proto.h> | |
14 | #include <dm.h> | |
15 | #include <fdtdec.h> | |
16 | #include <i2c.h> | |
17 | ||
18 | DECLARE_GLOBAL_DATA_PTR; | |
19 | #define LPI2C_FIFO_SIZE 4 | |
20 | #define LPI2C_TIMEOUT_MS 100 | |
21 | ||
22 | /* Weak linked function for overridden by some SoC power function */ | |
23 | int __weak init_i2c_power(unsigned i2c_num) | |
24 | { | |
25 | return 0; | |
26 | } | |
27 | ||
a821c4af | 28 | static int imx_lpci2c_check_busy_bus(const struct imx_lpi2c_reg *regs) |
7ee3f149 | 29 | { |
7ee3f149 PF |
30 | lpi2c_status_t result = LPI2C_SUCESS; |
31 | u32 status; | |
32 | ||
33 | status = readl(®s->msr); | |
34 | ||
35 | if ((status & LPI2C_MSR_BBF_MASK) && !(status & LPI2C_MSR_MBF_MASK)) | |
36 | result = LPI2C_BUSY; | |
37 | ||
38 | return result; | |
39 | } | |
40 | ||
a821c4af | 41 | static int imx_lpci2c_check_clear_error(struct imx_lpi2c_reg *regs) |
7ee3f149 | 42 | { |
7ee3f149 PF |
43 | lpi2c_status_t result = LPI2C_SUCESS; |
44 | u32 val, status; | |
45 | ||
46 | status = readl(®s->msr); | |
47 | /* errors to check for */ | |
48 | status &= LPI2C_MSR_NDF_MASK | LPI2C_MSR_ALF_MASK | | |
49 | LPI2C_MSR_FEF_MASK | LPI2C_MSR_PLTF_MASK; | |
50 | ||
51 | if (status) { | |
52 | if (status & LPI2C_MSR_PLTF_MASK) | |
53 | result = LPI2C_PIN_LOW_TIMEOUT_ERR; | |
54 | else if (status & LPI2C_MSR_ALF_MASK) | |
55 | result = LPI2C_ARB_LOST_ERR; | |
56 | else if (status & LPI2C_MSR_NDF_MASK) | |
57 | result = LPI2C_NAK_ERR; | |
58 | else if (status & LPI2C_MSR_FEF_MASK) | |
59 | result = LPI2C_FIFO_ERR; | |
60 | ||
61 | /* clear status flags */ | |
62 | writel(0x7f00, ®s->msr); | |
63 | /* reset fifos */ | |
64 | val = readl(®s->mcr); | |
65 | val |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK; | |
66 | writel(val, ®s->mcr); | |
67 | } | |
68 | ||
69 | return result; | |
70 | } | |
71 | ||
a821c4af | 72 | static int bus_i2c_wait_for_tx_ready(struct imx_lpi2c_reg *regs) |
7ee3f149 | 73 | { |
7ee3f149 PF |
74 | lpi2c_status_t result = LPI2C_SUCESS; |
75 | u32 txcount = 0; | |
76 | ulong start_time = get_timer(0); | |
77 | ||
78 | do { | |
79 | txcount = LPI2C_MFSR_TXCOUNT(readl(®s->mfsr)); | |
80 | txcount = LPI2C_FIFO_SIZE - txcount; | |
a821c4af | 81 | result = imx_lpci2c_check_clear_error(regs); |
7ee3f149 PF |
82 | if (result) { |
83 | debug("i2c: wait for tx ready: result 0x%x\n", result); | |
84 | return result; | |
85 | } | |
86 | if (get_timer(start_time) > LPI2C_TIMEOUT_MS) { | |
87 | debug("i2c: wait for tx ready: timeout\n"); | |
88 | return -1; | |
89 | } | |
90 | } while (!txcount); | |
91 | ||
92 | return result; | |
93 | } | |
94 | ||
a821c4af | 95 | static int bus_i2c_send(struct imx_lpi2c_reg *regs, u8 *txbuf, int len) |
7ee3f149 | 96 | { |
7ee3f149 PF |
97 | lpi2c_status_t result = LPI2C_SUCESS; |
98 | ||
99 | /* empty tx */ | |
100 | if (!len) | |
101 | return result; | |
102 | ||
103 | while (len--) { | |
a821c4af | 104 | result = bus_i2c_wait_for_tx_ready(regs); |
7ee3f149 PF |
105 | if (result) { |
106 | debug("i2c: send wait fot tx ready: %d\n", result); | |
107 | return result; | |
108 | } | |
109 | writel(*txbuf++, ®s->mtdr); | |
110 | } | |
111 | ||
112 | return result; | |
113 | } | |
114 | ||
a821c4af | 115 | static int bus_i2c_receive(struct imx_lpi2c_reg *regs, u8 *rxbuf, int len) |
7ee3f149 | 116 | { |
7ee3f149 PF |
117 | lpi2c_status_t result = LPI2C_SUCESS; |
118 | u32 val; | |
119 | ulong start_time = get_timer(0); | |
120 | ||
121 | /* empty read */ | |
122 | if (!len) | |
123 | return result; | |
124 | ||
a821c4af | 125 | result = bus_i2c_wait_for_tx_ready(regs); |
7ee3f149 PF |
126 | if (result) { |
127 | debug("i2c: receive wait fot tx ready: %d\n", result); | |
128 | return result; | |
129 | } | |
130 | ||
131 | /* clear all status flags */ | |
132 | writel(0x7f00, ®s->msr); | |
133 | /* send receive command */ | |
134 | val = LPI2C_MTDR_CMD(0x1) | LPI2C_MTDR_DATA(len - 1); | |
135 | writel(val, ®s->mtdr); | |
136 | ||
137 | while (len--) { | |
138 | do { | |
a821c4af | 139 | result = imx_lpci2c_check_clear_error(regs); |
7ee3f149 | 140 | if (result) { |
a821c4af SG |
141 | debug("i2c: receive check clear error: %d\n", |
142 | result); | |
7ee3f149 PF |
143 | return result; |
144 | } | |
145 | if (get_timer(start_time) > LPI2C_TIMEOUT_MS) { | |
146 | debug("i2c: receive mrdr: timeout\n"); | |
147 | return -1; | |
148 | } | |
149 | val = readl(®s->mrdr); | |
150 | } while (val & LPI2C_MRDR_RXEMPTY_MASK); | |
151 | *rxbuf++ = LPI2C_MRDR_DATA(val); | |
152 | } | |
153 | ||
154 | return result; | |
155 | } | |
156 | ||
a821c4af | 157 | static int bus_i2c_start(struct imx_lpi2c_reg *regs, u8 addr, u8 dir) |
7ee3f149 | 158 | { |
7ee3f149 PF |
159 | lpi2c_status_t result = LPI2C_SUCESS; |
160 | u32 val; | |
161 | ||
a821c4af | 162 | result = imx_lpci2c_check_busy_bus(regs); |
7ee3f149 PF |
163 | if (result) { |
164 | debug("i2c: start check busy bus: 0x%x\n", result); | |
165 | return result; | |
166 | } | |
167 | /* clear all status flags */ | |
168 | writel(0x7f00, ®s->msr); | |
169 | /* turn off auto-stop condition */ | |
170 | val = readl(®s->mcfgr1) & ~LPI2C_MCFGR1_AUTOSTOP_MASK; | |
171 | writel(val, ®s->mcfgr1); | |
172 | /* wait tx fifo ready */ | |
a821c4af | 173 | result = bus_i2c_wait_for_tx_ready(regs); |
7ee3f149 PF |
174 | if (result) { |
175 | debug("i2c: start wait for tx ready: 0x%x\n", result); | |
176 | return result; | |
177 | } | |
178 | /* issue start command */ | |
179 | val = LPI2C_MTDR_CMD(0x4) | (addr << 0x1) | dir; | |
180 | writel(val, ®s->mtdr); | |
181 | ||
182 | return result; | |
183 | } | |
a821c4af SG |
184 | |
185 | static int bus_i2c_stop(struct imx_lpi2c_reg *regs) | |
7ee3f149 | 186 | { |
7ee3f149 PF |
187 | lpi2c_status_t result = LPI2C_SUCESS; |
188 | u32 status; | |
189 | ||
a821c4af | 190 | result = bus_i2c_wait_for_tx_ready(regs); |
7ee3f149 PF |
191 | if (result) { |
192 | debug("i2c: stop wait for tx ready: 0x%x\n", result); | |
193 | return result; | |
194 | } | |
195 | ||
196 | /* send stop command */ | |
197 | writel(LPI2C_MTDR_CMD(0x2), ®s->mtdr); | |
198 | ||
199 | while (result == LPI2C_SUCESS) { | |
200 | status = readl(®s->msr); | |
a821c4af | 201 | result = imx_lpci2c_check_clear_error(regs); |
7ee3f149 PF |
202 | /* stop detect flag */ |
203 | if (status & LPI2C_MSR_SDF_MASK) { | |
204 | /* clear stop flag */ | |
205 | status &= LPI2C_MSR_SDF_MASK; | |
206 | writel(status, ®s->msr); | |
207 | break; | |
208 | } | |
209 | } | |
210 | ||
211 | return result; | |
212 | } | |
213 | ||
a821c4af | 214 | static int bus_i2c_read(struct imx_lpi2c_reg *regs, u32 chip, u8 *buf, int len) |
7ee3f149 PF |
215 | { |
216 | lpi2c_status_t result = LPI2C_SUCESS; | |
217 | ||
a821c4af | 218 | result = bus_i2c_start(regs, chip, 1); |
7ee3f149 PF |
219 | if (result) |
220 | return result; | |
a821c4af | 221 | result = bus_i2c_receive(regs, buf, len); |
7ee3f149 PF |
222 | if (result) |
223 | return result; | |
a821c4af | 224 | result = bus_i2c_stop(regs); |
7ee3f149 PF |
225 | if (result) |
226 | return result; | |
227 | ||
228 | return result; | |
229 | } | |
230 | ||
a821c4af | 231 | static int bus_i2c_write(struct imx_lpi2c_reg *regs, u32 chip, u8 *buf, int len) |
7ee3f149 PF |
232 | { |
233 | lpi2c_status_t result = LPI2C_SUCESS; | |
234 | ||
a821c4af | 235 | result = bus_i2c_start(regs, chip, 0); |
7ee3f149 PF |
236 | if (result) |
237 | return result; | |
a821c4af | 238 | result = bus_i2c_send(regs, buf, len); |
7ee3f149 PF |
239 | if (result) |
240 | return result; | |
a821c4af | 241 | result = bus_i2c_stop(regs); |
7ee3f149 PF |
242 | if (result) |
243 | return result; | |
244 | ||
245 | return result; | |
246 | } | |
247 | ||
248 | ||
249 | static int bus_i2c_set_bus_speed(struct udevice *bus, int speed) | |
250 | { | |
a821c4af | 251 | struct imx_lpi2c_reg *regs; |
7ee3f149 PF |
252 | u32 val; |
253 | u32 preescale = 0, best_pre = 0, clkhi = 0; | |
254 | u32 best_clkhi = 0, abs_error = 0, rate; | |
255 | u32 error = 0xffffffff; | |
256 | u32 clock_rate; | |
257 | bool mode; | |
258 | int i; | |
259 | ||
a821c4af | 260 | regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus); |
7ee3f149 PF |
261 | clock_rate = imx_get_i2cclk(bus->seq + 4); |
262 | if (!clock_rate) | |
263 | return -EPERM; | |
264 | ||
265 | mode = (readl(®s->mcr) & LPI2C_MCR_MEN_MASK) >> LPI2C_MCR_MEN_SHIFT; | |
266 | /* disable master mode */ | |
267 | val = readl(®s->mcr) & ~LPI2C_MCR_MEN_MASK; | |
268 | writel(val | LPI2C_MCR_MEN(0), ®s->mcr); | |
269 | ||
270 | for (preescale = 1; (preescale <= 128) && | |
271 | (error != 0); preescale = 2 * preescale) { | |
272 | for (clkhi = 1; clkhi < 32; clkhi++) { | |
273 | if (clkhi == 1) | |
274 | rate = (clock_rate / preescale) / (1 + 3 + 2 + 2 / preescale); | |
275 | else | |
276 | rate = (clock_rate / preescale / (3 * clkhi + 2 + 2 / preescale)); | |
277 | ||
278 | abs_error = speed > rate ? speed - rate : rate - speed; | |
279 | ||
280 | if (abs_error < error) { | |
281 | best_pre = preescale; | |
282 | best_clkhi = clkhi; | |
283 | error = abs_error; | |
284 | if (abs_error == 0) | |
285 | break; | |
286 | } | |
287 | } | |
288 | } | |
289 | ||
290 | /* Standard, fast, fast mode plus and ultra-fast transfers. */ | |
291 | val = LPI2C_MCCR0_CLKHI(best_clkhi); | |
292 | if (best_clkhi < 2) | |
293 | val |= LPI2C_MCCR0_CLKLO(3) | LPI2C_MCCR0_SETHOLD(2) | LPI2C_MCCR0_DATAVD(1); | |
294 | else | |
295 | val |= LPI2C_MCCR0_CLKLO(2 * best_clkhi) | LPI2C_MCCR0_SETHOLD(best_clkhi) | | |
296 | LPI2C_MCCR0_DATAVD(best_clkhi / 2); | |
297 | writel(val, ®s->mccr0); | |
298 | ||
299 | for (i = 0; i < 8; i++) { | |
300 | if (best_pre == (1 << i)) { | |
301 | best_pre = i; | |
302 | break; | |
303 | } | |
304 | } | |
305 | ||
306 | val = readl(®s->mcfgr1) & ~LPI2C_MCFGR1_PRESCALE_MASK; | |
307 | writel(val | LPI2C_MCFGR1_PRESCALE(best_pre), ®s->mcfgr1); | |
308 | ||
309 | if (mode) { | |
310 | val = readl(®s->mcr) & ~LPI2C_MCR_MEN_MASK; | |
311 | writel(val | LPI2C_MCR_MEN(1), ®s->mcr); | |
312 | } | |
313 | ||
314 | return 0; | |
315 | } | |
316 | ||
317 | static int bus_i2c_init(struct udevice *bus, int speed) | |
318 | { | |
a821c4af | 319 | struct imx_lpi2c_reg *regs; |
7ee3f149 PF |
320 | u32 val; |
321 | int ret; | |
322 | ||
a821c4af | 323 | regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus); |
7ee3f149 PF |
324 | /* reset peripheral */ |
325 | writel(LPI2C_MCR_RST_MASK, ®s->mcr); | |
326 | writel(0x0, ®s->mcr); | |
327 | /* Disable Dozen mode */ | |
328 | writel(LPI2C_MCR_DBGEN(0) | LPI2C_MCR_DOZEN(1), ®s->mcr); | |
329 | /* host request disable, active high, external pin */ | |
330 | val = readl(®s->mcfgr0); | |
331 | val &= (~(LPI2C_MCFGR0_HREN_MASK | LPI2C_MCFGR0_HRPOL_MASK | | |
332 | LPI2C_MCFGR0_HRSEL_MASK)); | |
333 | val |= LPI2C_MCFGR0_HRPOL(0x1); | |
334 | writel(val, ®s->mcfgr0); | |
335 | /* pincfg and ignore ack */ | |
336 | val = readl(®s->mcfgr1); | |
337 | val &= ~(LPI2C_MCFGR1_PINCFG_MASK | LPI2C_MCFGR1_IGNACK_MASK); | |
338 | val |= LPI2C_MCFGR1_PINCFG(0x0); /* 2 pin open drain */ | |
339 | val |= LPI2C_MCFGR1_IGNACK(0x0); /* ignore nack */ | |
340 | writel(val, ®s->mcfgr1); | |
341 | ||
342 | ret = bus_i2c_set_bus_speed(bus, speed); | |
343 | ||
344 | /* enable lpi2c in master mode */ | |
345 | val = readl(®s->mcr) & ~LPI2C_MCR_MEN_MASK; | |
346 | writel(val | LPI2C_MCR_MEN(1), ®s->mcr); | |
347 | ||
348 | debug("i2c : controller bus %d, speed %d:\n", bus->seq, speed); | |
349 | ||
350 | return ret; | |
351 | } | |
352 | ||
353 | static int imx_lpi2c_probe_chip(struct udevice *bus, u32 chip, | |
354 | u32 chip_flags) | |
355 | { | |
a821c4af | 356 | struct imx_lpi2c_reg *regs; |
7ee3f149 PF |
357 | lpi2c_status_t result = LPI2C_SUCESS; |
358 | ||
a821c4af SG |
359 | regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus); |
360 | result = bus_i2c_start(regs, chip, 0); | |
7ee3f149 | 361 | if (result) { |
a821c4af | 362 | bus_i2c_stop(regs); |
7ee3f149 PF |
363 | bus_i2c_init(bus, 100000); |
364 | return result; | |
365 | } | |
366 | ||
a821c4af | 367 | result = bus_i2c_stop(regs); |
7ee3f149 PF |
368 | if (result) { |
369 | bus_i2c_init(bus, 100000); | |
370 | return -result; | |
371 | } | |
372 | ||
373 | return result; | |
374 | } | |
375 | ||
376 | static int imx_lpi2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) | |
377 | { | |
a821c4af | 378 | struct imx_lpi2c_reg *regs; |
7ee3f149 PF |
379 | int ret = 0; |
380 | ||
a821c4af | 381 | regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus); |
7ee3f149 PF |
382 | for (; nmsgs > 0; nmsgs--, msg++) { |
383 | debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); | |
384 | if (msg->flags & I2C_M_RD) | |
a821c4af | 385 | ret = bus_i2c_read(regs, msg->addr, msg->buf, msg->len); |
7ee3f149 | 386 | else { |
a821c4af | 387 | ret = bus_i2c_write(regs, msg->addr, msg->buf, |
7ee3f149 PF |
388 | msg->len); |
389 | if (ret) | |
390 | break; | |
391 | } | |
392 | } | |
393 | ||
394 | if (ret) | |
395 | debug("i2c_write: error sending\n"); | |
396 | ||
397 | return ret; | |
398 | } | |
399 | ||
400 | static int imx_lpi2c_set_bus_speed(struct udevice *bus, unsigned int speed) | |
401 | { | |
402 | return bus_i2c_set_bus_speed(bus, speed); | |
403 | } | |
404 | ||
405 | static int imx_lpi2c_probe(struct udevice *bus) | |
406 | { | |
407 | struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus); | |
408 | fdt_addr_t addr; | |
409 | int ret; | |
410 | ||
411 | i2c_bus->driver_data = dev_get_driver_data(bus); | |
412 | ||
a821c4af | 413 | addr = devfdt_get_addr(bus); |
7ee3f149 PF |
414 | if (addr == FDT_ADDR_T_NONE) |
415 | return -ENODEV; | |
416 | ||
417 | i2c_bus->base = addr; | |
418 | i2c_bus->index = bus->seq; | |
419 | i2c_bus->bus = bus; | |
420 | ||
421 | /* power up i2c resource */ | |
422 | ret = init_i2c_power(bus->seq + 4); | |
423 | if (ret) { | |
424 | debug("init_i2c_power err = %d\n", ret); | |
425 | return ret; | |
426 | } | |
427 | ||
428 | /* Enable clk, only i2c4-7 can be handled by A7 core */ | |
429 | ret = enable_i2c_clk(1, bus->seq + 4); | |
430 | if (ret < 0) | |
431 | return ret; | |
432 | ||
433 | ret = bus_i2c_init(bus, 100000); | |
434 | if (ret < 0) | |
435 | return ret; | |
436 | ||
437 | debug("i2c : controller bus %d at %lu , speed %d: ", | |
438 | bus->seq, i2c_bus->base, | |
439 | i2c_bus->speed); | |
440 | ||
441 | return 0; | |
442 | } | |
443 | ||
444 | static const struct dm_i2c_ops imx_lpi2c_ops = { | |
445 | .xfer = imx_lpi2c_xfer, | |
446 | .probe_chip = imx_lpi2c_probe_chip, | |
447 | .set_bus_speed = imx_lpi2c_set_bus_speed, | |
448 | }; | |
449 | ||
450 | static const struct udevice_id imx_lpi2c_ids[] = { | |
451 | { .compatible = "fsl,imx7ulp-lpi2c", }, | |
452 | {} | |
453 | }; | |
454 | ||
455 | U_BOOT_DRIVER(imx_lpi2c) = { | |
456 | .name = "imx_lpi2c", | |
457 | .id = UCLASS_I2C, | |
458 | .of_match = imx_lpi2c_ids, | |
459 | .probe = imx_lpi2c_probe, | |
460 | .priv_auto_alloc_size = sizeof(struct imx_lpi2c_bus), | |
461 | .ops = &imx_lpi2c_ops, | |
462 | }; |