Blame view
drivers/i2c/s3c24x0_i2c.c
8.01 KB
1cb8e980c * Patches by Davi... |
1 2 3 4 |
/* * (C) Copyright 2002 * David Mueller, ELSOFT AG, d.mueller@elsoft.ch * |
1a4596601 Add GPL-2.0+ SPDX... |
5 |
* SPDX-License-Identifier: GPL-2.0+ |
1cb8e980c * Patches by Davi... |
6 |
*/ |
1cb8e980c * Patches by Davi... |
7 |
#include <common.h> |
8dfcbaa68 dm: i2c: s3c24x0:... |
8 9 |
#include <errno.h> #include <dm.h> |
a9d2ae701 I2C: Driver chang... |
10 |
#include <fdtdec.h> |
c86d9ed38 drivers:i2c: Modi... |
11 |
#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) |
ab7e52bb2 I2C: Modify the I... |
12 13 |
#include <asm/arch/clk.h> #include <asm/arch/cpu.h> |
a9d2ae701 I2C: Driver chang... |
14 |
#include <asm/arch/pinmux.h> |
ab7e52bb2 I2C: Modify the I... |
15 |
#else |
ac67804fb Add a unified s3c... |
16 |
#include <asm/arch/s3c24x0_cpu.h> |
ab7e52bb2 I2C: Modify the I... |
17 |
#endif |
eb0ae7f54 Clean-up of s3c24... |
18 |
#include <asm/io.h> |
1cb8e980c * Patches by Davi... |
19 |
#include <i2c.h> |
ab7e52bb2 I2C: Modify the I... |
20 |
#include "s3c24x0_i2c.h" |
1cb8e980c * Patches by Davi... |
21 |
|
a298712e9 i2c: s3c24x0: fix... |
22 23 24 25 26 |
#ifndef CONFIG_SYS_I2C_S3C24X0_SLAVE #define SYS_I2C_S3C24X0_SLAVE_ADDR 0 #else #define SYS_I2C_S3C24X0_SLAVE_ADDR CONFIG_SYS_I2C_S3C24X0_SLAVE #endif |
8dfcbaa68 dm: i2c: s3c24x0:... |
27 |
DECLARE_GLOBAL_DATA_PTR; |
e4e240207 exynos: i2c: Fix ... |
28 29 30 31 32 33 34 35 |
/* * Wait til the byte transfer is completed. * * @param i2c- pointer to the appropriate i2c register bank. * @return I2C_OK, if transmission was ACKED * I2C_NACK, if transmission was NACKED * I2C_NOK_TIMEOUT, if transaction did not complete in I2C_TIMEOUT_MS */ |
ab7e52bb2 I2C: Modify the I... |
36 |
static int WaitForXfer(struct s3c24x0_i2c *i2c) |
1cb8e980c * Patches by Davi... |
37 |
{ |
e4e240207 exynos: i2c: Fix ... |
38 |
ulong start_time = get_timer(0); |
1cb8e980c * Patches by Davi... |
39 |
|
e4e240207 exynos: i2c: Fix ... |
40 41 42 43 44 |
do { if (readl(&i2c->iiccon) & I2CCON_IRPND) return (readl(&i2c->iicstat) & I2CSTAT_NACK) ? I2C_NACK : I2C_OK; } while (get_timer(start_time) < I2C_TIMEOUT_MS); |
1cb8e980c * Patches by Davi... |
45 |
|
e4e240207 exynos: i2c: Fix ... |
46 |
return I2C_NOK_TOUT; |
1cb8e980c * Patches by Davi... |
47 |
} |
26ea76850 exynos: i2c: Fix ... |
48 |
static void read_write_byte(struct s3c24x0_i2c *i2c) |
1cb8e980c * Patches by Davi... |
49 |
{ |
26ea76850 exynos: i2c: Fix ... |
50 |
clrbits_le32(&i2c->iiccon, I2CCON_IRPND); |
1cb8e980c * Patches by Davi... |
51 |
} |
ab7e52bb2 I2C: Modify the I... |
52 53 54 |
static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) { ulong freq, pres = 16, div; |
c86d9ed38 drivers:i2c: Modi... |
55 |
#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) |
ab7e52bb2 I2C: Modify the I... |
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
freq = get_i2c_clk(); #else freq = get_PCLK(); #endif /* calculate prescaler and divisor values */ if ((freq / pres / (16 + 1)) > speed) /* set prescaler to 512 */ pres = 512; div = 0; while ((freq / pres / (div + 1)) > speed) div++; /* set prescaler, divisor according to freq, also set ACKGEN, IRQ */ writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon); /* init to SLAVE REVEIVE and set slaveaddr */ writel(0, &i2c->iicstat); writel(slaveadd, &i2c->iicadd); /* program Master Transmit (and implicit STOP) */ writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); } |
8dfcbaa68 dm: i2c: s3c24x0:... |
78 |
static int s3c24x0_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) |
2d8f1e276 driver:i2c:s3c24x... |
79 |
{ |
9a1bff69c samsung: i2c: Dro... |
80 |
struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev); |
2d8f1e276 driver:i2c:s3c24x... |
81 |
|
2d8f1e276 driver:i2c:s3c24x... |
82 |
i2c_bus->clock_frequency = speed; |
37b8eb37f samsung: i2c: Spl... |
83 |
i2c_ch_init(i2c_bus->regs, i2c_bus->clock_frequency, |
a298712e9 i2c: s3c24x0: fix... |
84 |
SYS_I2C_S3C24X0_SLAVE_ADDR); |
2d8f1e276 driver:i2c:s3c24x... |
85 86 87 |
return 0; } |
296a461dc i2c: s3c24xx: add... |
88 |
/* |
fc3e2165e * Patch by Sangmo... |
89 90 91 92 93 94 |
* cmd_type is 0 for write, 1 for read. * * addr_len can take any value from 0-255, it is only limited * by the char, we could make it larger if needed. If it is * 0 we skip the address write cycle. */ |
ab7e52bb2 I2C: Modify the I... |
95 96 97 98 99 100 101 |
static int i2c_transfer(struct s3c24x0_i2c *i2c, unsigned char cmd_type, unsigned char chip, unsigned char addr[], unsigned char addr_len, unsigned char data[], unsigned short data_len) |
1cb8e980c * Patches by Davi... |
102 |
{ |
e4e240207 exynos: i2c: Fix ... |
103 104 |
int i = 0, result; ulong start_time = get_timer(0); |
1cb8e980c * Patches by Davi... |
105 |
|
fc3e2165e * Patch by Sangmo... |
106 107 |
if (data == 0 || data_len == 0) { /*Don't support data transfer of no length or to address 0 */ |
ab7e52bb2 I2C: Modify the I... |
108 109 |
debug("i2c_transfer: bad call "); |
fc3e2165e * Patch by Sangmo... |
110 111 |
return I2C_NOK; } |
1cb8e980c * Patches by Davi... |
112 |
|
e4e240207 exynos: i2c: Fix ... |
113 114 115 |
while (readl(&i2c->iicstat) & I2CSTAT_BSY) { if (get_timer(start_time) > I2C_TIMEOUT_MS) return I2C_NOK_TOUT; |
fc3e2165e * Patch by Sangmo... |
116 |
} |
1cb8e980c * Patches by Davi... |
117 |
|
ab7e52bb2 I2C: Modify the I... |
118 |
writel(readl(&i2c->iiccon) | I2CCON_ACKGEN, &i2c->iiccon); |
1cb8e980c * Patches by Davi... |
119 |
|
e4e240207 exynos: i2c: Fix ... |
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
/* Get the slave chip address going */ writel(chip, &i2c->iicds); if ((cmd_type == I2C_WRITE) || (addr && addr_len)) writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, &i2c->iicstat); else writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP, &i2c->iicstat); /* Wait for chip address to transmit. */ result = WaitForXfer(i2c); if (result != I2C_OK) goto bailout; /* If register address needs to be transmitted - do it now. */ if (addr && addr_len) { while ((i < addr_len) && (result == I2C_OK)) { writel(addr[i++], &i2c->iicds); |
26ea76850 exynos: i2c: Fix ... |
138 |
read_write_byte(i2c); |
e4e240207 exynos: i2c: Fix ... |
139 |
result = WaitForXfer(i2c); |
1cb8e980c * Patches by Davi... |
140 |
} |
e4e240207 exynos: i2c: Fix ... |
141 142 143 144 |
i = 0; if (result != I2C_OK) goto bailout; } |
1cb8e980c * Patches by Davi... |
145 |
|
e4e240207 exynos: i2c: Fix ... |
146 147 148 149 |
switch (cmd_type) { case I2C_WRITE: while ((i < data_len) && (result == I2C_OK)) { writel(data[i++], &i2c->iicds); |
26ea76850 exynos: i2c: Fix ... |
150 |
read_write_byte(i2c); |
ab7e52bb2 I2C: Modify the I... |
151 |
result = WaitForXfer(i2c); |
e4e240207 exynos: i2c: Fix ... |
152 |
} |
fc3e2165e * Patch by Sangmo... |
153 |
break; |
1cb8e980c * Patches by Davi... |
154 |
|
48b42616e * Patches by Davi... |
155 |
case I2C_READ: |
fc3e2165e * Patch by Sangmo... |
156 |
if (addr && addr_len) { |
e4e240207 exynos: i2c: Fix ... |
157 158 159 160 |
/* * Register address has been sent, now send slave chip * address again to start the actual read transaction. */ |
d9abba825 Add generic suppo... |
161 |
writel(chip, &i2c->iicds); |
e4e240207 exynos: i2c: Fix ... |
162 163 |
/* Generate a re-START. */ |
cb466c056 I2C: S3C24X0: Bug... |
164 165 |
writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP, &i2c->iicstat); |
26ea76850 exynos: i2c: Fix ... |
166 |
read_write_byte(i2c); |
ab7e52bb2 I2C: Modify the I... |
167 |
result = WaitForXfer(i2c); |
fc3e2165e * Patch by Sangmo... |
168 |
|
e4e240207 exynos: i2c: Fix ... |
169 170 |
if (result != I2C_OK) goto bailout; |
1cb8e980c * Patches by Davi... |
171 |
} |
1cb8e980c * Patches by Davi... |
172 |
|
e4e240207 exynos: i2c: Fix ... |
173 174 175 176 177 178 |
while ((i < data_len) && (result == I2C_OK)) { /* disable ACK for final READ */ if (i == data_len - 1) writel(readl(&i2c->iiccon) & ~I2CCON_ACKGEN, &i2c->iiccon); |
26ea76850 exynos: i2c: Fix ... |
179 |
read_write_byte(i2c); |
e4e240207 exynos: i2c: Fix ... |
180 181 182 183 184 |
result = WaitForXfer(i2c); data[i++] = readl(&i2c->iicds); } if (result == I2C_NACK) result = I2C_OK; /* Normal terminated read. */ |
fc3e2165e * Patch by Sangmo... |
185 |
break; |
1cb8e980c * Patches by Davi... |
186 187 |
default: |
ab7e52bb2 I2C: Modify the I... |
188 189 |
debug("i2c_transfer: bad call "); |
fc3e2165e * Patch by Sangmo... |
190 191 192 |
result = I2C_NOK; break; } |
1cb8e980c * Patches by Davi... |
193 |
|
e4e240207 exynos: i2c: Fix ... |
194 195 196 |
bailout: /* Send STOP. */ writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); |
26ea76850 exynos: i2c: Fix ... |
197 |
read_write_byte(i2c); |
e4e240207 exynos: i2c: Fix ... |
198 |
|
ab7e52bb2 I2C: Modify the I... |
199 |
return result; |
1cb8e980c * Patches by Davi... |
200 |
} |
8dfcbaa68 dm: i2c: s3c24x0:... |
201 |
static int s3c24x0_i2c_probe(struct udevice *dev, uint chip, uint chip_flags) |
1cb8e980c * Patches by Davi... |
202 |
{ |
9a1bff69c samsung: i2c: Dro... |
203 |
struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev); |
fc3e2165e * Patch by Sangmo... |
204 |
uchar buf[1]; |
296a461dc i2c: s3c24xx: add... |
205 |
int ret; |
1cb8e980c * Patches by Davi... |
206 |
|
fc3e2165e * Patch by Sangmo... |
207 |
buf[0] = 0; |
1cb8e980c * Patches by Davi... |
208 |
|
fc3e2165e * Patch by Sangmo... |
209 210 211 212 213 |
/* * What is needed is to send the chip address and verify that the * address was <ACK>ed (i.e. there was a chip at that address which * drove the data line low). */ |
37b8eb37f samsung: i2c: Spl... |
214 |
ret = i2c_transfer(i2c_bus->regs, I2C_READ, chip << 1, 0, 0, buf, 1); |
296a461dc i2c: s3c24xx: add... |
215 |
|
296a461dc i2c: s3c24xx: add... |
216 |
return ret != I2C_OK; |
1cb8e980c * Patches by Davi... |
217 |
} |
45d9ae87c exynos: i2c: Tidy... |
218 219 |
static int s3c24x0_do_msg(struct s3c24x0_i2c_bus *i2c_bus, struct i2c_msg *msg, int seq) |
8dfcbaa68 dm: i2c: s3c24x0:... |
220 |
{ |
45d9ae87c exynos: i2c: Tidy... |
221 222 223 224 225 |
struct s3c24x0_i2c *i2c = i2c_bus->regs; bool is_read = msg->flags & I2C_M_RD; uint status; uint addr; int ret, i; |
8dfcbaa68 dm: i2c: s3c24x0:... |
226 |
|
45d9ae87c exynos: i2c: Tidy... |
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
if (!seq) setbits_le32(&i2c->iiccon, I2CCON_ACKGEN); /* Get the slave chip address going */ addr = msg->addr << 1; writel(addr, &i2c->iicds); status = I2C_TXRX_ENA | I2C_START_STOP; if (is_read) status |= I2C_MODE_MR; else status |= I2C_MODE_MT; writel(status, &i2c->iicstat); if (seq) read_write_byte(i2c); /* Wait for chip address to transmit */ ret = WaitForXfer(i2c); if (ret) goto err; if (is_read) { for (i = 0; !ret && i < msg->len; i++) { /* disable ACK for final READ */ if (i == msg->len - 1) clrbits_le32(&i2c->iiccon, I2CCON_ACKGEN); read_write_byte(i2c); ret = WaitForXfer(i2c); msg->buf[i] = readl(&i2c->iicds); } if (ret == I2C_NACK) ret = I2C_OK; /* Normal terminated read */ |
8dfcbaa68 dm: i2c: s3c24x0:... |
258 |
} else { |
45d9ae87c exynos: i2c: Tidy... |
259 260 261 262 263 |
for (i = 0; !ret && i < msg->len; i++) { writel(msg->buf[i], &i2c->iicds); read_write_byte(i2c); ret = WaitForXfer(i2c); } |
8dfcbaa68 dm: i2c: s3c24x0:... |
264 |
} |
45d9ae87c exynos: i2c: Tidy... |
265 266 |
err: return ret; |
8dfcbaa68 dm: i2c: s3c24x0:... |
267 268 269 270 271 272 |
} static int s3c24x0_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) { struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev); |
45d9ae87c exynos: i2c: Tidy... |
273 274 275 |
struct s3c24x0_i2c *i2c = i2c_bus->regs; ulong start_time; int ret, i; |
8dfcbaa68 dm: i2c: s3c24x0:... |
276 |
|
45d9ae87c exynos: i2c: Tidy... |
277 278 279 280 281 282 |
start_time = get_timer(0); while (readl(&i2c->iicstat) & I2CSTAT_BSY) { if (get_timer(start_time) > I2C_TIMEOUT_MS) { debug("Timeout "); return -ETIMEDOUT; |
8dfcbaa68 dm: i2c: s3c24x0:... |
283 |
} |
8dfcbaa68 dm: i2c: s3c24x0:... |
284 |
} |
45d9ae87c exynos: i2c: Tidy... |
285 286 287 288 289 290 291 292 |
for (ret = 0, i = 0; !ret && i < nmsgs; i++) ret = s3c24x0_do_msg(i2c_bus, &msg[i], i); /* Send STOP */ writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); read_write_byte(i2c); return ret ? -EREMOTEIO : 0; |
8dfcbaa68 dm: i2c: s3c24x0:... |
293 294 295 296 297 298 |
} static int s3c_i2c_ofdata_to_platdata(struct udevice *dev) { const void *blob = gd->fdt_blob; struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev); |
37b8eb37f samsung: i2c: Spl... |
299 |
int node; |
8dfcbaa68 dm: i2c: s3c24x0:... |
300 |
|
e160f7d43 dm: core: Replace... |
301 |
node = dev_of_offset(dev); |
8dfcbaa68 dm: i2c: s3c24x0:... |
302 |
|
a821c4af7 dm: Rename dev_ad... |
303 |
i2c_bus->regs = (struct s3c24x0_i2c *)devfdt_get_addr(dev); |
8dfcbaa68 dm: i2c: s3c24x0:... |
304 305 306 307 |
i2c_bus->id = pinmux_decode_periph_id(blob, node); i2c_bus->clock_frequency = fdtdec_get_int(blob, node, |
45d9ae87c exynos: i2c: Tidy... |
308 |
"clock-frequency", 100000); |
8dfcbaa68 dm: i2c: s3c24x0:... |
309 310 |
i2c_bus->node = node; i2c_bus->bus_num = dev->seq; |
37b8eb37f samsung: i2c: Spl... |
311 |
exynos_pinmux_config(i2c_bus->id, 0); |
8dfcbaa68 dm: i2c: s3c24x0:... |
312 313 314 315 316 317 318 319 320 321 322 323 324 |
i2c_bus->active = true; return 0; } static const struct dm_i2c_ops s3c_i2c_ops = { .xfer = s3c24x0_i2c_xfer, .probe_chip = s3c24x0_i2c_probe, .set_bus_speed = s3c24x0_i2c_set_bus_speed, }; static const struct udevice_id s3c_i2c_ids[] = { |
37b8eb37f samsung: i2c: Spl... |
325 |
{ .compatible = "samsung,s3c2440-i2c" }, |
8dfcbaa68 dm: i2c: s3c24x0:... |
326 327 328 329 330 331 332 333 |
{ } }; U_BOOT_DRIVER(i2c_s3c) = { .name = "i2c_s3c", .id = UCLASS_I2C, .of_match = s3c_i2c_ids, .ofdata_to_platdata = s3c_i2c_ofdata_to_platdata, |
8dfcbaa68 dm: i2c: s3c24x0:... |
334 335 336 |
.priv_auto_alloc_size = sizeof(struct s3c24x0_i2c_bus), .ops = &s3c_i2c_ops, }; |