root / hw / i2c / omap_i2c.c @ 1b111dc1
History | View | Annotate | Download (13.8 kB)
1 |
/*
|
---|---|
2 |
* TI OMAP on-chip I2C controller. Only "new I2C" mode supported.
|
3 |
*
|
4 |
* Copyright (C) 2007 Andrzej Zaborowski <balrog@zabor.org>
|
5 |
*
|
6 |
* This program is free software; you can redistribute it and/or
|
7 |
* modify it under the terms of the GNU General Public License as
|
8 |
* published by the Free Software Foundation; either version 2 of
|
9 |
* the License, or (at your option) any later version.
|
10 |
*
|
11 |
* This program is distributed in the hope that it will be useful,
|
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14 |
* GNU General Public License for more details.
|
15 |
*
|
16 |
* You should have received a copy of the GNU General Public License along
|
17 |
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
18 |
*/
|
19 |
#include "hw/hw.h" |
20 |
#include "hw/i2c/i2c.h" |
21 |
#include "hw/arm/omap.h" |
22 |
#include "hw/sysbus.h" |
23 |
|
24 |
#define TYPE_OMAP_I2C "omap_i2c" |
25 |
#define OMAP_I2C(obj) OBJECT_CHECK(OMAPI2CState, (obj), TYPE_OMAP_I2C)
|
26 |
|
27 |
typedef struct OMAPI2CState { |
28 |
SysBusDevice parent_obj; |
29 |
|
30 |
MemoryRegion iomem; |
31 |
qemu_irq irq; |
32 |
qemu_irq drq[2];
|
33 |
i2c_bus *bus; |
34 |
|
35 |
uint8_t revision; |
36 |
void *iclk;
|
37 |
void *fclk;
|
38 |
|
39 |
uint8_t mask; |
40 |
uint16_t stat; |
41 |
uint16_t dma; |
42 |
uint16_t count; |
43 |
int count_cur;
|
44 |
uint32_t fifo; |
45 |
int rxlen;
|
46 |
int txlen;
|
47 |
uint16_t control; |
48 |
uint16_t addr[2];
|
49 |
uint8_t divider; |
50 |
uint8_t times[2];
|
51 |
uint16_t test; |
52 |
} OMAPI2CState; |
53 |
|
54 |
#define OMAP2_INTR_REV 0x34 |
55 |
#define OMAP2_GC_REV 0x34 |
56 |
|
57 |
static void omap_i2c_interrupts_update(OMAPI2CState *s) |
58 |
{ |
59 |
qemu_set_irq(s->irq, s->stat & s->mask); |
60 |
if ((s->dma >> 15) & 1) /* RDMA_EN */ |
61 |
qemu_set_irq(s->drq[0], (s->stat >> 3) & 1); /* RRDY */ |
62 |
if ((s->dma >> 7) & 1) /* XDMA_EN */ |
63 |
qemu_set_irq(s->drq[1], (s->stat >> 4) & 1); /* XRDY */ |
64 |
} |
65 |
|
66 |
static void omap_i2c_fifo_run(OMAPI2CState *s) |
67 |
{ |
68 |
int ack = 1; |
69 |
|
70 |
if (!i2c_bus_busy(s->bus))
|
71 |
return;
|
72 |
|
73 |
if ((s->control >> 2) & 1) { /* RM */ |
74 |
if ((s->control >> 1) & 1) { /* STP */ |
75 |
i2c_end_transfer(s->bus); |
76 |
s->control &= ~(1 << 1); /* STP */ |
77 |
s->count_cur = s->count; |
78 |
s->txlen = 0;
|
79 |
} else if ((s->control >> 9) & 1) { /* TRX */ |
80 |
while (ack && s->txlen)
|
81 |
ack = (i2c_send(s->bus, |
82 |
(s->fifo >> ((-- s->txlen) << 3)) &
|
83 |
0xff) >= 0); |
84 |
s->stat |= 1 << 4; /* XRDY */ |
85 |
} else {
|
86 |
while (s->rxlen < 4) |
87 |
s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3);
|
88 |
s->stat |= 1 << 3; /* RRDY */ |
89 |
} |
90 |
} else {
|
91 |
if ((s->control >> 9) & 1) { /* TRX */ |
92 |
while (ack && s->count_cur && s->txlen) {
|
93 |
ack = (i2c_send(s->bus, |
94 |
(s->fifo >> ((-- s->txlen) << 3)) &
|
95 |
0xff) >= 0); |
96 |
s->count_cur --; |
97 |
} |
98 |
if (ack && s->count_cur)
|
99 |
s->stat |= 1 << 4; /* XRDY */ |
100 |
else
|
101 |
s->stat &= ~(1 << 4); /* XRDY */ |
102 |
if (!s->count_cur) {
|
103 |
s->stat |= 1 << 2; /* ARDY */ |
104 |
s->control &= ~(1 << 10); /* MST */ |
105 |
} |
106 |
} else {
|
107 |
while (s->count_cur && s->rxlen < 4) { |
108 |
s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3);
|
109 |
s->count_cur --; |
110 |
} |
111 |
if (s->rxlen)
|
112 |
s->stat |= 1 << 3; /* RRDY */ |
113 |
else
|
114 |
s->stat &= ~(1 << 3); /* RRDY */ |
115 |
} |
116 |
if (!s->count_cur) {
|
117 |
if ((s->control >> 1) & 1) { /* STP */ |
118 |
i2c_end_transfer(s->bus); |
119 |
s->control &= ~(1 << 1); /* STP */ |
120 |
s->count_cur = s->count; |
121 |
s->txlen = 0;
|
122 |
} else {
|
123 |
s->stat |= 1 << 2; /* ARDY */ |
124 |
s->control &= ~(1 << 10); /* MST */ |
125 |
} |
126 |
} |
127 |
} |
128 |
|
129 |
s->stat |= (!ack) << 1; /* NACK */ |
130 |
if (!ack)
|
131 |
s->control &= ~(1 << 1); /* STP */ |
132 |
} |
133 |
|
134 |
static void omap_i2c_reset(DeviceState *dev) |
135 |
{ |
136 |
OMAPI2CState *s = OMAP_I2C(dev); |
137 |
|
138 |
s->mask = 0;
|
139 |
s->stat = 0;
|
140 |
s->dma = 0;
|
141 |
s->count = 0;
|
142 |
s->count_cur = 0;
|
143 |
s->fifo = 0;
|
144 |
s->rxlen = 0;
|
145 |
s->txlen = 0;
|
146 |
s->control = 0;
|
147 |
s->addr[0] = 0; |
148 |
s->addr[1] = 0; |
149 |
s->divider = 0;
|
150 |
s->times[0] = 0; |
151 |
s->times[1] = 0; |
152 |
s->test = 0;
|
153 |
} |
154 |
|
155 |
static uint32_t omap_i2c_read(void *opaque, hwaddr addr) |
156 |
{ |
157 |
OMAPI2CState *s = opaque; |
158 |
int offset = addr & OMAP_MPUI_REG_MASK;
|
159 |
uint16_t ret; |
160 |
|
161 |
switch (offset) {
|
162 |
case 0x00: /* I2C_REV */ |
163 |
return s->revision; /* REV */ |
164 |
|
165 |
case 0x04: /* I2C_IE */ |
166 |
return s->mask;
|
167 |
|
168 |
case 0x08: /* I2C_STAT */ |
169 |
return s->stat | (i2c_bus_busy(s->bus) << 12); |
170 |
|
171 |
case 0x0c: /* I2C_IV */ |
172 |
if (s->revision >= OMAP2_INTR_REV)
|
173 |
break;
|
174 |
ret = ffs(s->stat & s->mask); |
175 |
if (ret)
|
176 |
s->stat ^= 1 << (ret - 1); |
177 |
omap_i2c_interrupts_update(s); |
178 |
return ret;
|
179 |
|
180 |
case 0x10: /* I2C_SYSS */ |
181 |
return (s->control >> 15) & 1; /* I2C_EN */ |
182 |
|
183 |
case 0x14: /* I2C_BUF */ |
184 |
return s->dma;
|
185 |
|
186 |
case 0x18: /* I2C_CNT */ |
187 |
return s->count_cur; /* DCOUNT */ |
188 |
|
189 |
case 0x1c: /* I2C_DATA */ |
190 |
ret = 0;
|
191 |
if (s->control & (1 << 14)) { /* BE */ |
192 |
ret |= ((s->fifo >> 0) & 0xff) << 8; |
193 |
ret |= ((s->fifo >> 8) & 0xff) << 0; |
194 |
} else {
|
195 |
ret |= ((s->fifo >> 8) & 0xff) << 8; |
196 |
ret |= ((s->fifo >> 0) & 0xff) << 0; |
197 |
} |
198 |
if (s->rxlen == 1) { |
199 |
s->stat |= 1 << 15; /* SBD */ |
200 |
s->rxlen = 0;
|
201 |
} else if (s->rxlen > 1) { |
202 |
if (s->rxlen > 2) |
203 |
s->fifo >>= 16;
|
204 |
s->rxlen -= 2;
|
205 |
} else {
|
206 |
/* XXX: remote access (qualifier) error - what's that? */
|
207 |
} |
208 |
if (!s->rxlen) {
|
209 |
s->stat &= ~(1 << 3); /* RRDY */ |
210 |
if (((s->control >> 10) & 1) && /* MST */ |
211 |
((~s->control >> 9) & 1)) { /* TRX */ |
212 |
s->stat |= 1 << 2; /* ARDY */ |
213 |
s->control &= ~(1 << 10); /* MST */ |
214 |
} |
215 |
} |
216 |
s->stat &= ~(1 << 11); /* ROVR */ |
217 |
omap_i2c_fifo_run(s); |
218 |
omap_i2c_interrupts_update(s); |
219 |
return ret;
|
220 |
|
221 |
case 0x20: /* I2C_SYSC */ |
222 |
return 0; |
223 |
|
224 |
case 0x24: /* I2C_CON */ |
225 |
return s->control;
|
226 |
|
227 |
case 0x28: /* I2C_OA */ |
228 |
return s->addr[0]; |
229 |
|
230 |
case 0x2c: /* I2C_SA */ |
231 |
return s->addr[1]; |
232 |
|
233 |
case 0x30: /* I2C_PSC */ |
234 |
return s->divider;
|
235 |
|
236 |
case 0x34: /* I2C_SCLL */ |
237 |
return s->times[0]; |
238 |
|
239 |
case 0x38: /* I2C_SCLH */ |
240 |
return s->times[1]; |
241 |
|
242 |
case 0x3c: /* I2C_SYSTEST */ |
243 |
if (s->test & (1 << 15)) { /* ST_EN */ |
244 |
s->test ^= 0xa;
|
245 |
return s->test;
|
246 |
} else
|
247 |
return s->test & ~0x300f; |
248 |
} |
249 |
|
250 |
OMAP_BAD_REG(addr); |
251 |
return 0; |
252 |
} |
253 |
|
254 |
static void omap_i2c_write(void *opaque, hwaddr addr, |
255 |
uint32_t value) |
256 |
{ |
257 |
OMAPI2CState *s = opaque; |
258 |
int offset = addr & OMAP_MPUI_REG_MASK;
|
259 |
int nack;
|
260 |
|
261 |
switch (offset) {
|
262 |
case 0x00: /* I2C_REV */ |
263 |
case 0x0c: /* I2C_IV */ |
264 |
case 0x10: /* I2C_SYSS */ |
265 |
OMAP_RO_REG(addr); |
266 |
return;
|
267 |
|
268 |
case 0x04: /* I2C_IE */ |
269 |
s->mask = value & (s->revision < OMAP2_GC_REV ? 0x1f : 0x3f); |
270 |
break;
|
271 |
|
272 |
case 0x08: /* I2C_STAT */ |
273 |
if (s->revision < OMAP2_INTR_REV) {
|
274 |
OMAP_RO_REG(addr); |
275 |
return;
|
276 |
} |
277 |
|
278 |
/* RRDY and XRDY are reset by hardware. (in all versions???) */
|
279 |
s->stat &= ~(value & 0x27);
|
280 |
omap_i2c_interrupts_update(s); |
281 |
break;
|
282 |
|
283 |
case 0x14: /* I2C_BUF */ |
284 |
s->dma = value & 0x8080;
|
285 |
if (value & (1 << 15)) /* RDMA_EN */ |
286 |
s->mask &= ~(1 << 3); /* RRDY_IE */ |
287 |
if (value & (1 << 7)) /* XDMA_EN */ |
288 |
s->mask &= ~(1 << 4); /* XRDY_IE */ |
289 |
break;
|
290 |
|
291 |
case 0x18: /* I2C_CNT */ |
292 |
s->count = value; /* DCOUNT */
|
293 |
break;
|
294 |
|
295 |
case 0x1c: /* I2C_DATA */ |
296 |
if (s->txlen > 2) { |
297 |
/* XXX: remote access (qualifier) error - what's that? */
|
298 |
break;
|
299 |
} |
300 |
s->fifo <<= 16;
|
301 |
s->txlen += 2;
|
302 |
if (s->control & (1 << 14)) { /* BE */ |
303 |
s->fifo |= ((value >> 8) & 0xff) << 8; |
304 |
s->fifo |= ((value >> 0) & 0xff) << 0; |
305 |
} else {
|
306 |
s->fifo |= ((value >> 0) & 0xff) << 8; |
307 |
s->fifo |= ((value >> 8) & 0xff) << 0; |
308 |
} |
309 |
s->stat &= ~(1 << 10); /* XUDF */ |
310 |
if (s->txlen > 2) |
311 |
s->stat &= ~(1 << 4); /* XRDY */ |
312 |
omap_i2c_fifo_run(s); |
313 |
omap_i2c_interrupts_update(s); |
314 |
break;
|
315 |
|
316 |
case 0x20: /* I2C_SYSC */ |
317 |
if (s->revision < OMAP2_INTR_REV) {
|
318 |
OMAP_BAD_REG(addr); |
319 |
return;
|
320 |
} |
321 |
|
322 |
if (value & 2) { |
323 |
omap_i2c_reset(DEVICE(s)); |
324 |
} |
325 |
break;
|
326 |
|
327 |
case 0x24: /* I2C_CON */ |
328 |
s->control = value & 0xcf87;
|
329 |
if (~value & (1 << 15)) { /* I2C_EN */ |
330 |
if (s->revision < OMAP2_INTR_REV) {
|
331 |
omap_i2c_reset(DEVICE(s)); |
332 |
} |
333 |
break;
|
334 |
} |
335 |
if ((value & (1 << 15)) && !(value & (1 << 10))) { /* MST */ |
336 |
fprintf(stderr, "%s: I^2C slave mode not supported\n",
|
337 |
__FUNCTION__); |
338 |
break;
|
339 |
} |
340 |
if ((value & (1 << 15)) && value & (1 << 8)) { /* XA */ |
341 |
fprintf(stderr, "%s: 10-bit addressing mode not supported\n",
|
342 |
__FUNCTION__); |
343 |
break;
|
344 |
} |
345 |
if ((value & (1 << 15)) && value & (1 << 0)) { /* STT */ |
346 |
nack = !!i2c_start_transfer(s->bus, s->addr[1], /* SA */ |
347 |
(~value >> 9) & 1); /* TRX */ |
348 |
s->stat |= nack << 1; /* NACK */ |
349 |
s->control &= ~(1 << 0); /* STT */ |
350 |
s->fifo = 0;
|
351 |
if (nack)
|
352 |
s->control &= ~(1 << 1); /* STP */ |
353 |
else {
|
354 |
s->count_cur = s->count; |
355 |
omap_i2c_fifo_run(s); |
356 |
} |
357 |
omap_i2c_interrupts_update(s); |
358 |
} |
359 |
break;
|
360 |
|
361 |
case 0x28: /* I2C_OA */ |
362 |
s->addr[0] = value & 0x3ff; |
363 |
break;
|
364 |
|
365 |
case 0x2c: /* I2C_SA */ |
366 |
s->addr[1] = value & 0x3ff; |
367 |
break;
|
368 |
|
369 |
case 0x30: /* I2C_PSC */ |
370 |
s->divider = value; |
371 |
break;
|
372 |
|
373 |
case 0x34: /* I2C_SCLL */ |
374 |
s->times[0] = value;
|
375 |
break;
|
376 |
|
377 |
case 0x38: /* I2C_SCLH */ |
378 |
s->times[1] = value;
|
379 |
break;
|
380 |
|
381 |
case 0x3c: /* I2C_SYSTEST */ |
382 |
s->test = value & 0xf80f;
|
383 |
if (value & (1 << 11)) /* SBB */ |
384 |
if (s->revision >= OMAP2_INTR_REV) {
|
385 |
s->stat |= 0x3f;
|
386 |
omap_i2c_interrupts_update(s); |
387 |
} |
388 |
if (value & (1 << 15)) /* ST_EN */ |
389 |
fprintf(stderr, "%s: System Test not supported\n", __FUNCTION__);
|
390 |
break;
|
391 |
|
392 |
default:
|
393 |
OMAP_BAD_REG(addr); |
394 |
return;
|
395 |
} |
396 |
} |
397 |
|
398 |
static void omap_i2c_writeb(void *opaque, hwaddr addr, |
399 |
uint32_t value) |
400 |
{ |
401 |
OMAPI2CState *s = opaque; |
402 |
int offset = addr & OMAP_MPUI_REG_MASK;
|
403 |
|
404 |
switch (offset) {
|
405 |
case 0x1c: /* I2C_DATA */ |
406 |
if (s->txlen > 2) { |
407 |
/* XXX: remote access (qualifier) error - what's that? */
|
408 |
break;
|
409 |
} |
410 |
s->fifo <<= 8;
|
411 |
s->txlen += 1;
|
412 |
s->fifo |= value & 0xff;
|
413 |
s->stat &= ~(1 << 10); /* XUDF */ |
414 |
if (s->txlen > 2) |
415 |
s->stat &= ~(1 << 4); /* XRDY */ |
416 |
omap_i2c_fifo_run(s); |
417 |
omap_i2c_interrupts_update(s); |
418 |
break;
|
419 |
|
420 |
default:
|
421 |
OMAP_BAD_REG(addr); |
422 |
return;
|
423 |
} |
424 |
} |
425 |
|
426 |
static const MemoryRegionOps omap_i2c_ops = { |
427 |
.old_mmio = { |
428 |
.read = { |
429 |
omap_badwidth_read16, |
430 |
omap_i2c_read, |
431 |
omap_badwidth_read16, |
432 |
}, |
433 |
.write = { |
434 |
omap_i2c_writeb, /* Only the last fifo write can be 8 bit. */
|
435 |
omap_i2c_write, |
436 |
omap_badwidth_write16, |
437 |
}, |
438 |
}, |
439 |
.endianness = DEVICE_NATIVE_ENDIAN, |
440 |
}; |
441 |
|
442 |
static int omap_i2c_init(SysBusDevice *sbd) |
443 |
{ |
444 |
DeviceState *dev = DEVICE(sbd); |
445 |
OMAPI2CState *s = OMAP_I2C(dev); |
446 |
|
447 |
if (!s->fclk) {
|
448 |
hw_error("omap_i2c: fclk not connected\n");
|
449 |
} |
450 |
if (s->revision >= OMAP2_INTR_REV && !s->iclk) {
|
451 |
/* Note that OMAP1 doesn't have a separate interface clock */
|
452 |
hw_error("omap_i2c: iclk not connected\n");
|
453 |
} |
454 |
sysbus_init_irq(sbd, &s->irq); |
455 |
sysbus_init_irq(sbd, &s->drq[0]);
|
456 |
sysbus_init_irq(sbd, &s->drq[1]);
|
457 |
memory_region_init_io(&s->iomem, OBJECT(s), &omap_i2c_ops, s, "omap.i2c",
|
458 |
(s->revision < OMAP2_INTR_REV) ? 0x800 : 0x1000); |
459 |
sysbus_init_mmio(sbd, &s->iomem); |
460 |
s->bus = i2c_init_bus(dev, NULL);
|
461 |
return 0; |
462 |
} |
463 |
|
464 |
static Property omap_i2c_properties[] = {
|
465 |
DEFINE_PROP_UINT8("revision", OMAPI2CState, revision, 0), |
466 |
DEFINE_PROP_PTR("iclk", OMAPI2CState, iclk),
|
467 |
DEFINE_PROP_PTR("fclk", OMAPI2CState, fclk),
|
468 |
DEFINE_PROP_END_OF_LIST(), |
469 |
}; |
470 |
|
471 |
static void omap_i2c_class_init(ObjectClass *klass, void *data) |
472 |
{ |
473 |
DeviceClass *dc = DEVICE_CLASS(klass); |
474 |
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); |
475 |
k->init = omap_i2c_init; |
476 |
dc->props = omap_i2c_properties; |
477 |
dc->reset = omap_i2c_reset; |
478 |
/* Reason: pointer properties "iclk", "fclk" */
|
479 |
dc->cannot_instantiate_with_device_add_yet = true;
|
480 |
} |
481 |
|
482 |
static const TypeInfo omap_i2c_info = { |
483 |
.name = TYPE_OMAP_I2C, |
484 |
.parent = TYPE_SYS_BUS_DEVICE, |
485 |
.instance_size = sizeof(OMAPI2CState),
|
486 |
.class_init = omap_i2c_class_init, |
487 |
}; |
488 |
|
489 |
static void omap_i2c_register_types(void) |
490 |
{ |
491 |
type_register_static(&omap_i2c_info); |
492 |
} |
493 |
|
494 |
i2c_bus *omap_i2c_bus(DeviceState *omap_i2c) |
495 |
{ |
496 |
OMAPI2CState *s = OMAP_I2C(omap_i2c); |
497 |
return s->bus;
|
498 |
} |
499 |
|
500 |
type_init(omap_i2c_register_types) |