blob: 32d7809dba6500908ab4c2a060e24c6d7040eae6 [file] [log] [blame]
Peng Fand684adb2017-02-24 09:54:18 +08001/*
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
18DECLARE_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 */
23int __weak init_i2c_power(unsigned i2c_num)
24{
25 return 0;
26}
27
Simon Glassba1dea42017-05-17 17:18:05 -060028static int imx_lpci2c_check_busy_bus(const struct imx_lpi2c_reg *regs)
Peng Fand684adb2017-02-24 09:54:18 +080029{
Peng Fand684adb2017-02-24 09:54:18 +080030 lpi2c_status_t result = LPI2C_SUCESS;
31 u32 status;
32
33 status = readl(&regs->msr);
34
35 if ((status & LPI2C_MSR_BBF_MASK) && !(status & LPI2C_MSR_MBF_MASK))
36 result = LPI2C_BUSY;
37
38 return result;
39}
40
Simon Glassba1dea42017-05-17 17:18:05 -060041static int imx_lpci2c_check_clear_error(struct imx_lpi2c_reg *regs)
Peng Fand684adb2017-02-24 09:54:18 +080042{
Peng Fand684adb2017-02-24 09:54:18 +080043 lpi2c_status_t result = LPI2C_SUCESS;
44 u32 val, status;
45
46 status = readl(&regs->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, &regs->msr);
63 /* reset fifos */
64 val = readl(&regs->mcr);
65 val |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK;
66 writel(val, &regs->mcr);
67 }
68
69 return result;
70}
71
Simon Glassba1dea42017-05-17 17:18:05 -060072static int bus_i2c_wait_for_tx_ready(struct imx_lpi2c_reg *regs)
Peng Fand684adb2017-02-24 09:54:18 +080073{
Peng Fand684adb2017-02-24 09:54:18 +080074 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(&regs->mfsr));
80 txcount = LPI2C_FIFO_SIZE - txcount;
Simon Glassba1dea42017-05-17 17:18:05 -060081 result = imx_lpci2c_check_clear_error(regs);
Peng Fand684adb2017-02-24 09:54:18 +080082 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
Simon Glassba1dea42017-05-17 17:18:05 -060095static int bus_i2c_send(struct imx_lpi2c_reg *regs, u8 *txbuf, int len)
Peng Fand684adb2017-02-24 09:54:18 +080096{
Peng Fand684adb2017-02-24 09:54:18 +080097 lpi2c_status_t result = LPI2C_SUCESS;
98
99 /* empty tx */
100 if (!len)
101 return result;
102
103 while (len--) {
Simon Glassba1dea42017-05-17 17:18:05 -0600104 result = bus_i2c_wait_for_tx_ready(regs);
Peng Fand684adb2017-02-24 09:54:18 +0800105 if (result) {
106 debug("i2c: send wait fot tx ready: %d\n", result);
107 return result;
108 }
109 writel(*txbuf++, &regs->mtdr);
110 }
111
112 return result;
113}
114
Simon Glassba1dea42017-05-17 17:18:05 -0600115static int bus_i2c_receive(struct imx_lpi2c_reg *regs, u8 *rxbuf, int len)
Peng Fand684adb2017-02-24 09:54:18 +0800116{
Peng Fand684adb2017-02-24 09:54:18 +0800117 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
Simon Glassba1dea42017-05-17 17:18:05 -0600125 result = bus_i2c_wait_for_tx_ready(regs);
Peng Fand684adb2017-02-24 09:54:18 +0800126 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, &regs->msr);
133 /* send receive command */
134 val = LPI2C_MTDR_CMD(0x1) | LPI2C_MTDR_DATA(len - 1);
135 writel(val, &regs->mtdr);
136
137 while (len--) {
138 do {
Simon Glassba1dea42017-05-17 17:18:05 -0600139 result = imx_lpci2c_check_clear_error(regs);
Peng Fand684adb2017-02-24 09:54:18 +0800140 if (result) {
Simon Glassba1dea42017-05-17 17:18:05 -0600141 debug("i2c: receive check clear error: %d\n",
142 result);
Peng Fand684adb2017-02-24 09:54:18 +0800143 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(&regs->mrdr);
150 } while (val & LPI2C_MRDR_RXEMPTY_MASK);
151 *rxbuf++ = LPI2C_MRDR_DATA(val);
152 }
153
154 return result;
155}
156
Simon Glassba1dea42017-05-17 17:18:05 -0600157static int bus_i2c_start(struct imx_lpi2c_reg *regs, u8 addr, u8 dir)
Peng Fand684adb2017-02-24 09:54:18 +0800158{
Heinrich Schuchardt1a5d4852018-03-18 11:14:56 +0100159 lpi2c_status_t result;
Peng Fand684adb2017-02-24 09:54:18 +0800160 u32 val;
161
Simon Glassba1dea42017-05-17 17:18:05 -0600162 result = imx_lpci2c_check_busy_bus(regs);
Peng Fand684adb2017-02-24 09:54:18 +0800163 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, &regs->msr);
169 /* turn off auto-stop condition */
170 val = readl(&regs->mcfgr1) & ~LPI2C_MCFGR1_AUTOSTOP_MASK;
171 writel(val, &regs->mcfgr1);
172 /* wait tx fifo ready */
Simon Glassba1dea42017-05-17 17:18:05 -0600173 result = bus_i2c_wait_for_tx_ready(regs);
Peng Fand684adb2017-02-24 09:54:18 +0800174 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, &regs->mtdr);
181
182 return result;
183}
Simon Glassba1dea42017-05-17 17:18:05 -0600184
185static int bus_i2c_stop(struct imx_lpi2c_reg *regs)
Peng Fand684adb2017-02-24 09:54:18 +0800186{
Heinrich Schuchardt1a5d4852018-03-18 11:14:56 +0100187 lpi2c_status_t result;
Peng Fand684adb2017-02-24 09:54:18 +0800188 u32 status;
189
Simon Glassba1dea42017-05-17 17:18:05 -0600190 result = bus_i2c_wait_for_tx_ready(regs);
Peng Fand684adb2017-02-24 09:54:18 +0800191 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), &regs->mtdr);
198
199 while (result == LPI2C_SUCESS) {
200 status = readl(&regs->msr);
Simon Glassba1dea42017-05-17 17:18:05 -0600201 result = imx_lpci2c_check_clear_error(regs);
Peng Fand684adb2017-02-24 09:54:18 +0800202 /* stop detect flag */
203 if (status & LPI2C_MSR_SDF_MASK) {
204 /* clear stop flag */
205 status &= LPI2C_MSR_SDF_MASK;
206 writel(status, &regs->msr);
207 break;
208 }
209 }
210
211 return result;
212}
213
Simon Glassba1dea42017-05-17 17:18:05 -0600214static int bus_i2c_read(struct imx_lpi2c_reg *regs, u32 chip, u8 *buf, int len)
Peng Fand684adb2017-02-24 09:54:18 +0800215{
Heinrich Schuchardt1a5d4852018-03-18 11:14:56 +0100216 lpi2c_status_t result;
Peng Fand684adb2017-02-24 09:54:18 +0800217
Simon Glassba1dea42017-05-17 17:18:05 -0600218 result = bus_i2c_start(regs, chip, 1);
Peng Fand684adb2017-02-24 09:54:18 +0800219 if (result)
220 return result;
Simon Glassba1dea42017-05-17 17:18:05 -0600221 result = bus_i2c_receive(regs, buf, len);
Peng Fand684adb2017-02-24 09:54:18 +0800222 if (result)
223 return result;
Simon Glassba1dea42017-05-17 17:18:05 -0600224 result = bus_i2c_stop(regs);
Peng Fand684adb2017-02-24 09:54:18 +0800225 if (result)
226 return result;
227
228 return result;
229}
230
Simon Glassba1dea42017-05-17 17:18:05 -0600231static int bus_i2c_write(struct imx_lpi2c_reg *regs, u32 chip, u8 *buf, int len)
Peng Fand684adb2017-02-24 09:54:18 +0800232{
Heinrich Schuchardt1a5d4852018-03-18 11:14:56 +0100233 lpi2c_status_t result;
Peng Fand684adb2017-02-24 09:54:18 +0800234
Simon Glassba1dea42017-05-17 17:18:05 -0600235 result = bus_i2c_start(regs, chip, 0);
Peng Fand684adb2017-02-24 09:54:18 +0800236 if (result)
237 return result;
Simon Glassba1dea42017-05-17 17:18:05 -0600238 result = bus_i2c_send(regs, buf, len);
Peng Fand684adb2017-02-24 09:54:18 +0800239 if (result)
240 return result;
Simon Glassba1dea42017-05-17 17:18:05 -0600241 result = bus_i2c_stop(regs);
Peng Fand684adb2017-02-24 09:54:18 +0800242 if (result)
243 return result;
244
245 return result;
246}
247
248
249static int bus_i2c_set_bus_speed(struct udevice *bus, int speed)
250{
Simon Glassba1dea42017-05-17 17:18:05 -0600251 struct imx_lpi2c_reg *regs;
Peng Fand684adb2017-02-24 09:54:18 +0800252 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
Simon Glassba1dea42017-05-17 17:18:05 -0600260 regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
Peng Fan32306dd2018-01-02 15:41:52 +0800261 clock_rate = imx_get_i2cclk(bus->seq);
Peng Fand684adb2017-02-24 09:54:18 +0800262 if (!clock_rate)
263 return -EPERM;
264
265 mode = (readl(&regs->mcr) & LPI2C_MCR_MEN_MASK) >> LPI2C_MCR_MEN_SHIFT;
266 /* disable master mode */
267 val = readl(&regs->mcr) & ~LPI2C_MCR_MEN_MASK;
268 writel(val | LPI2C_MCR_MEN(0), &regs->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, &regs->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(&regs->mcfgr1) & ~LPI2C_MCFGR1_PRESCALE_MASK;
307 writel(val | LPI2C_MCFGR1_PRESCALE(best_pre), &regs->mcfgr1);
308
309 if (mode) {
310 val = readl(&regs->mcr) & ~LPI2C_MCR_MEN_MASK;
311 writel(val | LPI2C_MCR_MEN(1), &regs->mcr);
312 }
313
314 return 0;
315}
316
317static int bus_i2c_init(struct udevice *bus, int speed)
318{
Simon Glassba1dea42017-05-17 17:18:05 -0600319 struct imx_lpi2c_reg *regs;
Peng Fand684adb2017-02-24 09:54:18 +0800320 u32 val;
321 int ret;
322
Simon Glassba1dea42017-05-17 17:18:05 -0600323 regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
Peng Fand684adb2017-02-24 09:54:18 +0800324 /* reset peripheral */
325 writel(LPI2C_MCR_RST_MASK, &regs->mcr);
326 writel(0x0, &regs->mcr);
327 /* Disable Dozen mode */
328 writel(LPI2C_MCR_DBGEN(0) | LPI2C_MCR_DOZEN(1), &regs->mcr);
329 /* host request disable, active high, external pin */
330 val = readl(&regs->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, &regs->mcfgr0);
335 /* pincfg and ignore ack */
336 val = readl(&regs->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, &regs->mcfgr1);
341
342 ret = bus_i2c_set_bus_speed(bus, speed);
343
344 /* enable lpi2c in master mode */
345 val = readl(&regs->mcr) & ~LPI2C_MCR_MEN_MASK;
346 writel(val | LPI2C_MCR_MEN(1), &regs->mcr);
347
348 debug("i2c : controller bus %d, speed %d:\n", bus->seq, speed);
349
350 return ret;
351}
352
353static int imx_lpi2c_probe_chip(struct udevice *bus, u32 chip,
354 u32 chip_flags)
355{
Simon Glassba1dea42017-05-17 17:18:05 -0600356 struct imx_lpi2c_reg *regs;
Heinrich Schuchardt1a5d4852018-03-18 11:14:56 +0100357 lpi2c_status_t result;
Peng Fand684adb2017-02-24 09:54:18 +0800358
Simon Glassba1dea42017-05-17 17:18:05 -0600359 regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
360 result = bus_i2c_start(regs, chip, 0);
Peng Fand684adb2017-02-24 09:54:18 +0800361 if (result) {
Simon Glassba1dea42017-05-17 17:18:05 -0600362 bus_i2c_stop(regs);
Peng Fand684adb2017-02-24 09:54:18 +0800363 bus_i2c_init(bus, 100000);
364 return result;
365 }
366
Simon Glassba1dea42017-05-17 17:18:05 -0600367 result = bus_i2c_stop(regs);
Peng Fand684adb2017-02-24 09:54:18 +0800368 if (result) {
369 bus_i2c_init(bus, 100000);
370 return -result;
371 }
372
373 return result;
374}
375
376static int imx_lpi2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
377{
Simon Glassba1dea42017-05-17 17:18:05 -0600378 struct imx_lpi2c_reg *regs;
Peng Fand684adb2017-02-24 09:54:18 +0800379 int ret = 0;
380
Simon Glassba1dea42017-05-17 17:18:05 -0600381 regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
Peng Fand684adb2017-02-24 09:54:18 +0800382 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)
Simon Glassba1dea42017-05-17 17:18:05 -0600385 ret = bus_i2c_read(regs, msg->addr, msg->buf, msg->len);
Peng Fand684adb2017-02-24 09:54:18 +0800386 else {
Simon Glassba1dea42017-05-17 17:18:05 -0600387 ret = bus_i2c_write(regs, msg->addr, msg->buf,
Peng Fand684adb2017-02-24 09:54:18 +0800388 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
400static int imx_lpi2c_set_bus_speed(struct udevice *bus, unsigned int speed)
401{
402 return bus_i2c_set_bus_speed(bus, speed);
403}
404
405static 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
Simon Glassba1dea42017-05-17 17:18:05 -0600413 addr = devfdt_get_addr(bus);
Peng Fand684adb2017-02-24 09:54:18 +0800414 if (addr == FDT_ADDR_T_NONE)
Simon Glassf44b4bf2017-09-17 16:54:53 -0600415 return -EINVAL;
Peng Fand684adb2017-02-24 09:54:18 +0800416
417 i2c_bus->base = addr;
418 i2c_bus->index = bus->seq;
419 i2c_bus->bus = bus;
420
421 /* power up i2c resource */
Peng Fan32306dd2018-01-02 15:41:52 +0800422 ret = init_i2c_power(bus->seq);
Peng Fand684adb2017-02-24 09:54:18 +0800423 if (ret) {
424 debug("init_i2c_power err = %d\n", ret);
425 return ret;
426 }
427
Peng Fan32306dd2018-01-02 15:41:52 +0800428 /* To i.MX7ULP, only i2c4-7 can be handled by A7 core */
429 ret = enable_i2c_clk(1, bus->seq);
Peng Fand684adb2017-02-24 09:54:18 +0800430 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
444static 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
450static const struct udevice_id imx_lpi2c_ids[] = {
451 { .compatible = "fsl,imx7ulp-lpi2c", },
452 {}
453};
454
455U_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};