]>
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 | ||
28 | static int imx_lpci2c_check_busy_bus(struct udevice *bus) | |
29 | { | |
30 | struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); | |
31 | lpi2c_status_t result = LPI2C_SUCESS; | |
32 | u32 status; | |
33 | ||
34 | status = readl(®s->msr); | |
35 | ||
36 | if ((status & LPI2C_MSR_BBF_MASK) && !(status & LPI2C_MSR_MBF_MASK)) | |
37 | result = LPI2C_BUSY; | |
38 | ||
39 | return result; | |
40 | } | |
41 | ||
42 | static int imx_lpci2c_check_clear_error(struct udevice *bus) | |
43 | { | |
44 | struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); | |
45 | lpi2c_status_t result = LPI2C_SUCESS; | |
46 | u32 val, status; | |
47 | ||
48 | status = readl(®s->msr); | |
49 | /* errors to check for */ | |
50 | status &= LPI2C_MSR_NDF_MASK | LPI2C_MSR_ALF_MASK | | |
51 | LPI2C_MSR_FEF_MASK | LPI2C_MSR_PLTF_MASK; | |
52 | ||
53 | if (status) { | |
54 | if (status & LPI2C_MSR_PLTF_MASK) | |
55 | result = LPI2C_PIN_LOW_TIMEOUT_ERR; | |
56 | else if (status & LPI2C_MSR_ALF_MASK) | |
57 | result = LPI2C_ARB_LOST_ERR; | |
58 | else if (status & LPI2C_MSR_NDF_MASK) | |
59 | result = LPI2C_NAK_ERR; | |
60 | else if (status & LPI2C_MSR_FEF_MASK) | |
61 | result = LPI2C_FIFO_ERR; | |
62 | ||
63 | /* clear status flags */ | |
64 | writel(0x7f00, ®s->msr); | |
65 | /* reset fifos */ | |
66 | val = readl(®s->mcr); | |
67 | val |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK; | |
68 | writel(val, ®s->mcr); | |
69 | } | |
70 | ||
71 | return result; | |
72 | } | |
73 | ||
74 | static int bus_i2c_wait_for_tx_ready(struct udevice *bus) | |
75 | { | |
76 | struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); | |
77 | lpi2c_status_t result = LPI2C_SUCESS; | |
78 | u32 txcount = 0; | |
79 | ulong start_time = get_timer(0); | |
80 | ||
81 | do { | |
82 | txcount = LPI2C_MFSR_TXCOUNT(readl(®s->mfsr)); | |
83 | txcount = LPI2C_FIFO_SIZE - txcount; | |
84 | result = imx_lpci2c_check_clear_error(bus); | |
85 | if (result) { | |
86 | debug("i2c: wait for tx ready: result 0x%x\n", result); | |
87 | return result; | |
88 | } | |
89 | if (get_timer(start_time) > LPI2C_TIMEOUT_MS) { | |
90 | debug("i2c: wait for tx ready: timeout\n"); | |
91 | return -1; | |
92 | } | |
93 | } while (!txcount); | |
94 | ||
95 | return result; | |
96 | } | |
97 | ||
98 | static int bus_i2c_send(struct udevice *bus, u8 *txbuf, int len) | |
99 | { | |
100 | struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); | |
101 | lpi2c_status_t result = LPI2C_SUCESS; | |
102 | ||
103 | /* empty tx */ | |
104 | if (!len) | |
105 | return result; | |
106 | ||
107 | while (len--) { | |
108 | result = bus_i2c_wait_for_tx_ready(bus); | |
109 | if (result) { | |
110 | debug("i2c: send wait fot tx ready: %d\n", result); | |
111 | return result; | |
112 | } | |
113 | writel(*txbuf++, ®s->mtdr); | |
114 | } | |
115 | ||
116 | return result; | |
117 | } | |
118 | ||
119 | static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len) | |
120 | { | |
121 | struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); | |
122 | lpi2c_status_t result = LPI2C_SUCESS; | |
123 | u32 val; | |
124 | ulong start_time = get_timer(0); | |
125 | ||
126 | /* empty read */ | |
127 | if (!len) | |
128 | return result; | |
129 | ||
130 | result = bus_i2c_wait_for_tx_ready(bus); | |
131 | if (result) { | |
132 | debug("i2c: receive wait fot tx ready: %d\n", result); | |
133 | return result; | |
134 | } | |
135 | ||
136 | /* clear all status flags */ | |
137 | writel(0x7f00, ®s->msr); | |
138 | /* send receive command */ | |
139 | val = LPI2C_MTDR_CMD(0x1) | LPI2C_MTDR_DATA(len - 1); | |
140 | writel(val, ®s->mtdr); | |
141 | ||
142 | while (len--) { | |
143 | do { | |
144 | result = imx_lpci2c_check_clear_error(bus); | |
145 | if (result) { | |
146 | debug("i2c: receive check clear error: %d\n", result); | |
147 | return result; | |
148 | } | |
149 | if (get_timer(start_time) > LPI2C_TIMEOUT_MS) { | |
150 | debug("i2c: receive mrdr: timeout\n"); | |
151 | return -1; | |
152 | } | |
153 | val = readl(®s->mrdr); | |
154 | } while (val & LPI2C_MRDR_RXEMPTY_MASK); | |
155 | *rxbuf++ = LPI2C_MRDR_DATA(val); | |
156 | } | |
157 | ||
158 | return result; | |
159 | } | |
160 | ||
161 | static int bus_i2c_start(struct udevice *bus, u8 addr, u8 dir) | |
162 | { | |
163 | struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); | |
164 | lpi2c_status_t result = LPI2C_SUCESS; | |
165 | u32 val; | |
166 | ||
167 | result = imx_lpci2c_check_busy_bus(bus); | |
168 | if (result) { | |
169 | debug("i2c: start check busy bus: 0x%x\n", result); | |
170 | return result; | |
171 | } | |
172 | /* clear all status flags */ | |
173 | writel(0x7f00, ®s->msr); | |
174 | /* turn off auto-stop condition */ | |
175 | val = readl(®s->mcfgr1) & ~LPI2C_MCFGR1_AUTOSTOP_MASK; | |
176 | writel(val, ®s->mcfgr1); | |
177 | /* wait tx fifo ready */ | |
178 | result = bus_i2c_wait_for_tx_ready(bus); | |
179 | if (result) { | |
180 | debug("i2c: start wait for tx ready: 0x%x\n", result); | |
181 | return result; | |
182 | } | |
183 | /* issue start command */ | |
184 | val = LPI2C_MTDR_CMD(0x4) | (addr << 0x1) | dir; | |
185 | writel(val, ®s->mtdr); | |
186 | ||
187 | return result; | |
188 | } | |
189 | static int bus_i2c_stop(struct udevice *bus) | |
190 | { | |
191 | struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); | |
192 | lpi2c_status_t result = LPI2C_SUCESS; | |
193 | u32 status; | |
194 | ||
195 | result = bus_i2c_wait_for_tx_ready(bus); | |
196 | if (result) { | |
197 | debug("i2c: stop wait for tx ready: 0x%x\n", result); | |
198 | return result; | |
199 | } | |
200 | ||
201 | /* send stop command */ | |
202 | writel(LPI2C_MTDR_CMD(0x2), ®s->mtdr); | |
203 | ||
204 | while (result == LPI2C_SUCESS) { | |
205 | status = readl(®s->msr); | |
206 | result = imx_lpci2c_check_clear_error(bus); | |
207 | /* stop detect flag */ | |
208 | if (status & LPI2C_MSR_SDF_MASK) { | |
209 | /* clear stop flag */ | |
210 | status &= LPI2C_MSR_SDF_MASK; | |
211 | writel(status, ®s->msr); | |
212 | break; | |
213 | } | |
214 | } | |
215 | ||
216 | return result; | |
217 | } | |
218 | ||
219 | static int bus_i2c_read(struct udevice *bus, u32 chip, u8 *buf, int len) | |
220 | { | |
221 | lpi2c_status_t result = LPI2C_SUCESS; | |
222 | ||
223 | result = bus_i2c_start(bus, chip, 1); | |
224 | if (result) | |
225 | return result; | |
226 | result = bus_i2c_receive(bus, buf, len); | |
227 | if (result) | |
228 | return result; | |
229 | result = bus_i2c_stop(bus); | |
230 | if (result) | |
231 | return result; | |
232 | ||
233 | return result; | |
234 | } | |
235 | ||
236 | static int bus_i2c_write(struct udevice *bus, u32 chip, u8 *buf, int len) | |
237 | { | |
238 | lpi2c_status_t result = LPI2C_SUCESS; | |
239 | ||
240 | result = bus_i2c_start(bus, chip, 0); | |
241 | if (result) | |
242 | return result; | |
243 | result = bus_i2c_send(bus, buf, len); | |
244 | if (result) | |
245 | return result; | |
246 | result = bus_i2c_stop(bus); | |
247 | if (result) | |
248 | return result; | |
249 | ||
250 | return result; | |
251 | } | |
252 | ||
253 | ||
254 | static int bus_i2c_set_bus_speed(struct udevice *bus, int speed) | |
255 | { | |
256 | struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); | |
257 | u32 val; | |
258 | u32 preescale = 0, best_pre = 0, clkhi = 0; | |
259 | u32 best_clkhi = 0, abs_error = 0, rate; | |
260 | u32 error = 0xffffffff; | |
261 | u32 clock_rate; | |
262 | bool mode; | |
263 | int i; | |
264 | ||
265 | clock_rate = imx_get_i2cclk(bus->seq + 4); | |
266 | if (!clock_rate) | |
267 | return -EPERM; | |
268 | ||
269 | mode = (readl(®s->mcr) & LPI2C_MCR_MEN_MASK) >> LPI2C_MCR_MEN_SHIFT; | |
270 | /* disable master mode */ | |
271 | val = readl(®s->mcr) & ~LPI2C_MCR_MEN_MASK; | |
272 | writel(val | LPI2C_MCR_MEN(0), ®s->mcr); | |
273 | ||
274 | for (preescale = 1; (preescale <= 128) && | |
275 | (error != 0); preescale = 2 * preescale) { | |
276 | for (clkhi = 1; clkhi < 32; clkhi++) { | |
277 | if (clkhi == 1) | |
278 | rate = (clock_rate / preescale) / (1 + 3 + 2 + 2 / preescale); | |
279 | else | |
280 | rate = (clock_rate / preescale / (3 * clkhi + 2 + 2 / preescale)); | |
281 | ||
282 | abs_error = speed > rate ? speed - rate : rate - speed; | |
283 | ||
284 | if (abs_error < error) { | |
285 | best_pre = preescale; | |
286 | best_clkhi = clkhi; | |
287 | error = abs_error; | |
288 | if (abs_error == 0) | |
289 | break; | |
290 | } | |
291 | } | |
292 | } | |
293 | ||
294 | /* Standard, fast, fast mode plus and ultra-fast transfers. */ | |
295 | val = LPI2C_MCCR0_CLKHI(best_clkhi); | |
296 | if (best_clkhi < 2) | |
297 | val |= LPI2C_MCCR0_CLKLO(3) | LPI2C_MCCR0_SETHOLD(2) | LPI2C_MCCR0_DATAVD(1); | |
298 | else | |
299 | val |= LPI2C_MCCR0_CLKLO(2 * best_clkhi) | LPI2C_MCCR0_SETHOLD(best_clkhi) | | |
300 | LPI2C_MCCR0_DATAVD(best_clkhi / 2); | |
301 | writel(val, ®s->mccr0); | |
302 | ||
303 | for (i = 0; i < 8; i++) { | |
304 | if (best_pre == (1 << i)) { | |
305 | best_pre = i; | |
306 | break; | |
307 | } | |
308 | } | |
309 | ||
310 | val = readl(®s->mcfgr1) & ~LPI2C_MCFGR1_PRESCALE_MASK; | |
311 | writel(val | LPI2C_MCFGR1_PRESCALE(best_pre), ®s->mcfgr1); | |
312 | ||
313 | if (mode) { | |
314 | val = readl(®s->mcr) & ~LPI2C_MCR_MEN_MASK; | |
315 | writel(val | LPI2C_MCR_MEN(1), ®s->mcr); | |
316 | } | |
317 | ||
318 | return 0; | |
319 | } | |
320 | ||
321 | static int bus_i2c_init(struct udevice *bus, int speed) | |
322 | { | |
323 | struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); | |
324 | u32 val; | |
325 | int ret; | |
326 | ||
327 | /* reset peripheral */ | |
328 | writel(LPI2C_MCR_RST_MASK, ®s->mcr); | |
329 | writel(0x0, ®s->mcr); | |
330 | /* Disable Dozen mode */ | |
331 | writel(LPI2C_MCR_DBGEN(0) | LPI2C_MCR_DOZEN(1), ®s->mcr); | |
332 | /* host request disable, active high, external pin */ | |
333 | val = readl(®s->mcfgr0); | |
334 | val &= (~(LPI2C_MCFGR0_HREN_MASK | LPI2C_MCFGR0_HRPOL_MASK | | |
335 | LPI2C_MCFGR0_HRSEL_MASK)); | |
336 | val |= LPI2C_MCFGR0_HRPOL(0x1); | |
337 | writel(val, ®s->mcfgr0); | |
338 | /* pincfg and ignore ack */ | |
339 | val = readl(®s->mcfgr1); | |
340 | val &= ~(LPI2C_MCFGR1_PINCFG_MASK | LPI2C_MCFGR1_IGNACK_MASK); | |
341 | val |= LPI2C_MCFGR1_PINCFG(0x0); /* 2 pin open drain */ | |
342 | val |= LPI2C_MCFGR1_IGNACK(0x0); /* ignore nack */ | |
343 | writel(val, ®s->mcfgr1); | |
344 | ||
345 | ret = bus_i2c_set_bus_speed(bus, speed); | |
346 | ||
347 | /* enable lpi2c in master mode */ | |
348 | val = readl(®s->mcr) & ~LPI2C_MCR_MEN_MASK; | |
349 | writel(val | LPI2C_MCR_MEN(1), ®s->mcr); | |
350 | ||
351 | debug("i2c : controller bus %d, speed %d:\n", bus->seq, speed); | |
352 | ||
353 | return ret; | |
354 | } | |
355 | ||
356 | static int imx_lpi2c_probe_chip(struct udevice *bus, u32 chip, | |
357 | u32 chip_flags) | |
358 | { | |
359 | lpi2c_status_t result = LPI2C_SUCESS; | |
360 | ||
361 | result = bus_i2c_start(bus, chip, 0); | |
362 | if (result) { | |
363 | bus_i2c_stop(bus); | |
364 | bus_i2c_init(bus, 100000); | |
365 | return result; | |
366 | } | |
367 | ||
368 | result = bus_i2c_stop(bus); | |
369 | if (result) { | |
370 | bus_i2c_init(bus, 100000); | |
371 | return -result; | |
372 | } | |
373 | ||
374 | return result; | |
375 | } | |
376 | ||
377 | static int imx_lpi2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) | |
378 | { | |
379 | int ret = 0; | |
380 | ||
381 | for (; nmsgs > 0; nmsgs--, msg++) { | |
382 | debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); | |
383 | if (msg->flags & I2C_M_RD) | |
384 | ret = bus_i2c_read(bus, msg->addr, msg->buf, | |
385 | msg->len); | |
386 | else { | |
387 | ret = bus_i2c_write(bus, msg->addr, msg->buf, | |
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 | ||
413 | addr = dev_get_addr(bus); | |
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 | }; |