]> Git Repo - qemu.git/blame - hw/omap_i2c.c
sh_serial: enable tx after reset (Magnus Damm).
[qemu.git] / hw / omap_i2c.c
CommitLineData
02645926
AZ
1/*
2 * TI OMAP on-chip I2C controller. Only "new I2C" mode supported.
3 *
4 * Copyright (C) 2007 Andrzej Zaborowski <[email protected]>
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
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19 * MA 02111-1307 USA
20 */
87ecb68b
PB
21#include "hw.h"
22#include "i2c.h"
23#include "omap.h"
02645926
AZ
24
25struct omap_i2c_s {
26 target_phys_addr_t base;
27 qemu_irq irq;
28 qemu_irq drq[2];
29 i2c_slave slave;
30 i2c_bus *bus;
31
32 uint8_t mask;
33 uint16_t stat;
34 uint16_t dma;
35 uint16_t count;
36 int count_cur;
37 uint32_t fifo;
38 int rxlen;
39 int txlen;
40 uint16_t control;
41 uint16_t addr[2];
42 uint8_t divider;
43 uint8_t times[2];
44 uint16_t test;
45};
46
47static void omap_i2c_interrupts_update(struct omap_i2c_s *s)
48{
49 qemu_set_irq(s->irq, s->stat & s->mask);
50 if ((s->dma >> 15) & 1) /* RDMA_EN */
51 qemu_set_irq(s->drq[0], (s->stat >> 3) & 1); /* RRDY */
52 if ((s->dma >> 7) & 1) /* XDMA_EN */
53 qemu_set_irq(s->drq[1], (s->stat >> 4) & 1); /* XRDY */
54}
55
56/* These are only stubs now. */
57static void omap_i2c_event(i2c_slave *i2c, enum i2c_event event)
58{
59 struct omap_i2c_s *s = (struct omap_i2c_s *) i2c;
60
61 if ((~s->control >> 15) & 1) /* I2C_EN */
62 return;
63
64 switch (event) {
65 case I2C_START_SEND:
66 case I2C_START_RECV:
67 s->stat |= 1 << 9; /* AAS */
68 break;
69 case I2C_FINISH:
70 s->stat |= 1 << 2; /* ARDY */
71 break;
72 case I2C_NACK:
73 s->stat |= 1 << 1; /* NACK */
74 break;
75 }
76
77 omap_i2c_interrupts_update(s);
78}
79
80static int omap_i2c_rx(i2c_slave *i2c)
81{
82 struct omap_i2c_s *s = (struct omap_i2c_s *) i2c;
83 uint8_t ret = 0;
84
85 if ((~s->control >> 15) & 1) /* I2C_EN */
86 return -1;
87
88 if (s->txlen)
89 ret = s->fifo >> ((-- s->txlen) << 3) & 0xff;
90 else
91 s->stat |= 1 << 10; /* XUDF */
92 s->stat |= 1 << 4; /* XRDY */
93
94 omap_i2c_interrupts_update(s);
95 return ret;
96}
97
98static int omap_i2c_tx(i2c_slave *i2c, uint8_t data)
99{
100 struct omap_i2c_s *s = (struct omap_i2c_s *) i2c;
101
102 if ((~s->control >> 15) & 1) /* I2C_EN */
103 return 1;
104
105 if (s->rxlen < 4)
106 s->fifo |= data << ((s->rxlen ++) << 3);
107 else
108 s->stat |= 1 << 11; /* ROVR */
109 s->stat |= 1 << 3; /* RRDY */
110
111 omap_i2c_interrupts_update(s);
112 return 1;
113}
114
115static void omap_i2c_fifo_run(struct omap_i2c_s *s)
116{
117 int ack = 1;
118
119 if (!i2c_bus_busy(s->bus))
120 return;
121
122 if ((s->control >> 2) & 1) { /* RM */
123 if ((s->control >> 1) & 1) { /* STP */
124 i2c_end_transfer(s->bus);
125 s->control &= ~(1 << 1); /* STP */
126 s->count_cur = s->count;
127 } else if ((s->control >> 9) & 1) { /* TRX */
128 while (ack && s->txlen)
129 ack = (i2c_send(s->bus,
130 (s->fifo >> ((-- s->txlen) << 3)) &
131 0xff) >= 0);
132 s->stat |= 1 << 4; /* XRDY */
133 } else {
134 while (s->rxlen < 4)
135 s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3);
136 s->stat |= 1 << 3; /* RRDY */
137 }
138 } else {
139 if ((s->control >> 9) & 1) { /* TRX */
140 while (ack && s->count_cur && s->txlen) {
141 ack = (i2c_send(s->bus,
142 (s->fifo >> ((-- s->txlen) << 3)) &
143 0xff) >= 0);
144 s->count_cur --;
145 }
146 if (ack && s->count_cur)
147 s->stat |= 1 << 4; /* XRDY */
148 if (!s->count_cur) {
149 s->stat |= 1 << 2; /* ARDY */
150 s->control &= ~(1 << 10); /* MST */
151 }
152 } else {
153 while (s->count_cur && s->rxlen < 4) {
154 s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3);
155 s->count_cur --;
156 }
157 if (s->rxlen)
158 s->stat |= 1 << 3; /* RRDY */
159 }
160 if (!s->count_cur) {
161 if ((s->control >> 1) & 1) { /* STP */
162 i2c_end_transfer(s->bus);
163 s->control &= ~(1 << 1); /* STP */
164 s->count_cur = s->count;
165 } else {
166 s->stat |= 1 << 2; /* ARDY */
167 s->control &= ~(1 << 10); /* MST */
168 }
169 }
170 }
171
172 s->stat |= (!ack) << 1; /* NACK */
173 if (!ack)
174 s->control &= ~(1 << 1); /* STP */
175}
176
177void omap_i2c_reset(struct omap_i2c_s *s)
178{
179 s->mask = 0;
180 s->stat = 0;
181 s->dma = 0;
182 s->count = 0;
183 s->count_cur = 0;
184 s->fifo = 0;
185 s->rxlen = 0;
186 s->txlen = 0;
187 s->control = 0;
188 s->addr[0] = 0;
189 s->addr[1] = 0;
190 s->divider = 0;
191 s->times[0] = 0;
192 s->times[1] = 0;
193 s->test = 0;
194}
195
196static uint32_t omap_i2c_read(void *opaque, target_phys_addr_t addr)
197{
198 struct omap_i2c_s *s = (struct omap_i2c_s *) opaque;
cf965d24 199 int offset = addr & OMAP_MPUI_REG_MASK;
02645926
AZ
200 uint16_t ret;
201
202 switch (offset) {
203 case 0x00: /* I2C_REV */
204 /* TODO: set a value greater or equal to real hardware */
205 return 0x11; /* REV */
206
207 case 0x04: /* I2C_IE */
208 return s->mask;
209
210 case 0x08: /* I2C_STAT */
211 return s->stat | (i2c_bus_busy(s->bus) << 12);
212
213 case 0x0c: /* I2C_IV */
214 ret = ffs(s->stat & s->mask);
215 if (ret)
216 s->stat ^= 1 << (ret - 1);
217 omap_i2c_interrupts_update(s);
218 return ret;
219
220 case 0x14: /* I2C_BUF */
221 return s->dma;
222
223 case 0x18: /* I2C_CNT */
224 return s->count_cur; /* DCOUNT */
225
226 case 0x1c: /* I2C_DATA */
227 ret = 0;
228 if (s->control & (1 << 14)) { /* BE */
229 ret |= ((s->fifo >> 0) & 0xff) << 8;
230 ret |= ((s->fifo >> 8) & 0xff) << 0;
231 } else {
232 ret |= ((s->fifo >> 8) & 0xff) << 8;
233 ret |= ((s->fifo >> 0) & 0xff) << 0;
234 }
235 if (s->rxlen == 1) {
236 s->stat |= 1 << 15; /* SBD */
237 s->rxlen = 0;
238 } else if (s->rxlen > 1) {
239 if (s->rxlen > 2)
240 s->fifo >>= 16;
241 s->rxlen -= 2;
242 } else
243 /* XXX: remote access (qualifier) error - what's that? */;
244 if (!s->rxlen) {
245 s->stat |= ~(1 << 3); /* RRDY */
246 if (((s->control >> 10) & 1) && /* MST */
247 ((~s->control >> 9) & 1)) { /* TRX */
248 s->stat |= 1 << 2; /* ARDY */
249 s->control &= ~(1 << 10); /* MST */
250 }
251 }
252 s->stat &= ~(1 << 11); /* ROVR */
253 omap_i2c_fifo_run(s);
254 omap_i2c_interrupts_update(s);
255 return ret;
256
257 case 0x24: /* I2C_CON */
258 return s->control;
259
260 case 0x28: /* I2C_OA */
261 return s->addr[0];
262
263 case 0x2c: /* I2C_SA */
264 return s->addr[1];
265
266 case 0x30: /* I2C_PSC */
267 return s->divider;
268
269 case 0x34: /* I2C_SCLL */
270 return s->times[0];
271
272 case 0x38: /* I2C_SCLH */
273 return s->times[1];
274
275 case 0x3c: /* I2C_SYSTEST */
276 if (s->test & (1 << 15)) { /* ST_EN */
277 s->test ^= 0xa;
278 return s->test;
279 } else
280 return s->test & ~0x300f;
281 }
282
283 OMAP_BAD_REG(addr);
284 return 0;
285}
286
287static void omap_i2c_write(void *opaque, target_phys_addr_t addr,
288 uint32_t value)
289{
290 struct omap_i2c_s *s = (struct omap_i2c_s *) opaque;
cf965d24 291 int offset = addr & OMAP_MPUI_REG_MASK;
02645926
AZ
292 int nack;
293
294 switch (offset) {
295 case 0x00: /* I2C_REV */
296 case 0x08: /* I2C_STAT */
297 case 0x0c: /* I2C_IV */
298 OMAP_BAD_REG(addr);
299 return;
300
301 case 0x04: /* I2C_IE */
302 s->mask = value & 0x1f;
303 break;
304
305 case 0x14: /* I2C_BUF */
306 s->dma = value & 0x8080;
307 if (value & (1 << 15)) /* RDMA_EN */
308 s->mask &= ~(1 << 3); /* RRDY_IE */
309 if (value & (1 << 7)) /* XDMA_EN */
310 s->mask &= ~(1 << 4); /* XRDY_IE */
311 break;
312
313 case 0x18: /* I2C_CNT */
314 s->count = value; /* DCOUNT */
315 break;
316
317 case 0x1c: /* I2C_DATA */
318 if (s->txlen > 2) {
319 /* XXX: remote access (qualifier) error - what's that? */
320 break;
321 }
322 s->fifo <<= 16;
323 s->txlen += 2;
324 if (s->control & (1 << 14)) { /* BE */
325 s->fifo |= ((value >> 8) & 0xff) << 8;
326 s->fifo |= ((value >> 0) & 0xff) << 0;
327 } else {
328 s->fifo |= ((value >> 0) & 0xff) << 8;
329 s->fifo |= ((value >> 8) & 0xff) << 0;
330 }
331 s->stat &= ~(1 << 10); /* XUDF */
332 if (s->txlen > 2)
333 s->stat &= ~(1 << 4); /* XRDY */
334 omap_i2c_fifo_run(s);
335 omap_i2c_interrupts_update(s);
336 break;
337
338 case 0x24: /* I2C_CON */
339 s->control = value & 0xcf07;
340 if (~value & (1 << 15)) { /* I2C_EN */
341 omap_i2c_reset(s);
342 break;
343 }
344 if (~value & (1 << 10)) { /* MST */
345 printf("%s: I^2C slave mode not supported\n", __FUNCTION__);
346 break;
347 }
348 if (value & (1 << 9)) { /* XA */
349 printf("%s: 10-bit addressing mode not supported\n", __FUNCTION__);
350 break;
351 }
352 if (value & (1 << 0)) { /* STT */
353 nack = !!i2c_start_transfer(s->bus, s->addr[1], /* SA */
354 (~value >> 9) & 1); /* TRX */
355 s->stat |= nack << 1; /* NACK */
356 s->control &= ~(1 << 0); /* STT */
357 if (nack)
358 s->control &= ~(1 << 1); /* STP */
359 else
360 omap_i2c_fifo_run(s);
361 omap_i2c_interrupts_update(s);
362 }
363 break;
364
365 case 0x28: /* I2C_OA */
366 s->addr[0] = value & 0x3ff;
367 i2c_set_slave_address(&s->slave, value & 0x7f);
368 break;
369
370 case 0x2c: /* I2C_SA */
371 s->addr[1] = value & 0x3ff;
372 break;
373
374 case 0x30: /* I2C_PSC */
375 s->divider = value;
376 break;
377
378 case 0x34: /* I2C_SCLL */
379 s->times[0] = value;
380 break;
381
382 case 0x38: /* I2C_SCLH */
383 s->times[1] = value;
384 break;
385
386 case 0x3c: /* I2C_SYSTEST */
387 s->test = value & 0xf00f;
388 if (value & (1 << 15)) /* ST_EN */
389 printf("%s: System Test not supported\n", __FUNCTION__);
390 break;
391
392 default:
393 OMAP_BAD_REG(addr);
394 return;
395 }
396}
397
398static CPUReadMemoryFunc *omap_i2c_readfn[] = {
399 omap_badwidth_read16,
400 omap_i2c_read,
401 omap_badwidth_read16,
402};
403
404static CPUWriteMemoryFunc *omap_i2c_writefn[] = {
405 omap_badwidth_write16,
406 omap_i2c_write,
407 omap_i2c_write, /* TODO: Only the last fifo write can be 8 bit. */
408};
409
410struct omap_i2c_s *omap_i2c_init(target_phys_addr_t base,
411 qemu_irq irq, qemu_irq *dma, omap_clk clk)
412{
413 int iomemtype;
414 struct omap_i2c_s *s = (struct omap_i2c_s *)
415 qemu_mallocz(sizeof(struct omap_i2c_s));
416
417 s->base = base;
418 s->irq = irq;
419 s->drq[0] = dma[0];
420 s->drq[1] = dma[1];
421 s->slave.event = omap_i2c_event;
422 s->slave.recv = omap_i2c_rx;
423 s->slave.send = omap_i2c_tx;
424 s->bus = i2c_init_bus();
425 omap_i2c_reset(s);
426
427 iomemtype = cpu_register_io_memory(0, omap_i2c_readfn,
428 omap_i2c_writefn, s);
429 cpu_register_physical_memory(s->base, 0x800, iomemtype);
430
431 return s;
432}
433
434i2c_bus *omap_i2c_bus(struct omap_i2c_s *s)
435{
436 return s->bus;
437}
This page took 0.081528 seconds and 4 git commands to generate.