xref: /qemu/hw/i2c/exynos4210_i2c.c (revision e3a6e0da)
1 /*
2  *  Exynos4210 I2C Bus Serial Interface Emulation
3  *
4  *  Copyright (C) 2012 Samsung Electronics Co Ltd.
5  *    Maksim Kozlov, <m.kozlov@samsung.com>
6  *    Igor Mitsyanko, <i.mitsyanko@samsung.com>
7  *
8  *  This program is free software; you can redistribute it and/or modify it
9  *  under the terms of the GNU General Public License as published by the
10  *  Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful, but WITHOUT
14  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16  *  for more details.
17  *
18  *  You should have received a copy of the GNU General Public License along
19  *  with this program; if not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22 
23 #include "qemu/osdep.h"
24 #include "qemu/module.h"
25 #include "qemu/timer.h"
26 #include "hw/sysbus.h"
27 #include "migration/vmstate.h"
28 #include "hw/i2c/i2c.h"
29 #include "hw/irq.h"
30 #include "qom/object.h"
31 
32 #ifndef EXYNOS4_I2C_DEBUG
33 #define EXYNOS4_I2C_DEBUG                 0
34 #endif
35 
36 #define TYPE_EXYNOS4_I2C                  "exynos4210.i2c"
37 typedef struct Exynos4210I2CState Exynos4210I2CState;
38 DECLARE_INSTANCE_CHECKER(Exynos4210I2CState, EXYNOS4_I2C,
39                          TYPE_EXYNOS4_I2C)
40 
41 /* Exynos4210 I2C memory map */
42 #define EXYNOS4_I2C_MEM_SIZE              0x14
43 #define I2CCON_ADDR                       0x00  /* control register */
44 #define I2CSTAT_ADDR                      0x04  /* control/status register */
45 #define I2CADD_ADDR                       0x08  /* address register */
46 #define I2CDS_ADDR                        0x0c  /* data shift register */
47 #define I2CLC_ADDR                        0x10  /* line control register */
48 
49 #define I2CCON_ACK_GEN                    (1 << 7)
50 #define I2CCON_INTRS_EN                   (1 << 5)
51 #define I2CCON_INT_PEND                   (1 << 4)
52 
53 #define EXYNOS4_I2C_MODE(reg)             (((reg) >> 6) & 3)
54 #define I2C_IN_MASTER_MODE(reg)           (((reg) >> 6) & 2)
55 #define I2CMODE_MASTER_Rx                 0x2
56 #define I2CMODE_MASTER_Tx                 0x3
57 #define I2CSTAT_LAST_BIT                  (1 << 0)
58 #define I2CSTAT_OUTPUT_EN                 (1 << 4)
59 #define I2CSTAT_START_BUSY                (1 << 5)
60 
61 
62 #if EXYNOS4_I2C_DEBUG
63 #define DPRINT(fmt, args...)              \
64     do { fprintf(stderr, "QEMU I2C: "fmt, ## args); } while (0)
65 
66 static const char *exynos4_i2c_get_regname(unsigned offset)
67 {
68     switch (offset) {
69     case I2CCON_ADDR:
70         return "I2CCON";
71     case I2CSTAT_ADDR:
72         return "I2CSTAT";
73     case I2CADD_ADDR:
74         return "I2CADD";
75     case I2CDS_ADDR:
76         return "I2CDS";
77     case I2CLC_ADDR:
78         return "I2CLC";
79     default:
80         return "[?]";
81     }
82 }
83 
84 #else
85 #define DPRINT(fmt, args...)              do { } while (0)
86 #endif
87 
88 struct Exynos4210I2CState {
89     SysBusDevice parent_obj;
90 
91     MemoryRegion iomem;
92     I2CBus *bus;
93     qemu_irq irq;
94 
95     uint8_t i2ccon;
96     uint8_t i2cstat;
97     uint8_t i2cadd;
98     uint8_t i2cds;
99     uint8_t i2clc;
100     bool scl_free;
101 };
102 
103 static inline void exynos4210_i2c_raise_interrupt(Exynos4210I2CState *s)
104 {
105     if (s->i2ccon & I2CCON_INTRS_EN) {
106         s->i2ccon |= I2CCON_INT_PEND;
107         qemu_irq_raise(s->irq);
108     }
109 }
110 
111 static void exynos4210_i2c_data_receive(void *opaque)
112 {
113     Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
114 
115     s->i2cstat &= ~I2CSTAT_LAST_BIT;
116     s->scl_free = false;
117     s->i2cds = i2c_recv(s->bus);
118     exynos4210_i2c_raise_interrupt(s);
119 }
120 
121 static void exynos4210_i2c_data_send(void *opaque)
122 {
123     Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
124 
125     s->i2cstat &= ~I2CSTAT_LAST_BIT;
126     s->scl_free = false;
127     if (i2c_send(s->bus, s->i2cds) < 0 && (s->i2ccon & I2CCON_ACK_GEN)) {
128         s->i2cstat |= I2CSTAT_LAST_BIT;
129     }
130     exynos4210_i2c_raise_interrupt(s);
131 }
132 
133 static uint64_t exynos4210_i2c_read(void *opaque, hwaddr offset,
134                                  unsigned size)
135 {
136     Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
137     uint8_t value;
138 
139     switch (offset) {
140     case I2CCON_ADDR:
141         value = s->i2ccon;
142         break;
143     case I2CSTAT_ADDR:
144         value = s->i2cstat;
145         break;
146     case I2CADD_ADDR:
147         value = s->i2cadd;
148         break;
149     case I2CDS_ADDR:
150         value = s->i2cds;
151         s->scl_free = true;
152         if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Rx &&
153                (s->i2cstat & I2CSTAT_START_BUSY) &&
154                !(s->i2ccon & I2CCON_INT_PEND)) {
155             exynos4210_i2c_data_receive(s);
156         }
157         break;
158     case I2CLC_ADDR:
159         value = s->i2clc;
160         break;
161     default:
162         value = 0;
163         DPRINT("ERROR: Bad read offset 0x%x\n", (unsigned int)offset);
164         break;
165     }
166 
167     DPRINT("read %s [0x%02x] -> 0x%02x\n", exynos4_i2c_get_regname(offset),
168             (unsigned int)offset, value);
169     return value;
170 }
171 
172 static void exynos4210_i2c_write(void *opaque, hwaddr offset,
173                               uint64_t value, unsigned size)
174 {
175     Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
176     uint8_t v = value & 0xff;
177 
178     DPRINT("write %s [0x%02x] <- 0x%02x\n", exynos4_i2c_get_regname(offset),
179             (unsigned int)offset, v);
180 
181     switch (offset) {
182     case I2CCON_ADDR:
183         s->i2ccon = (v & ~I2CCON_INT_PEND) | (s->i2ccon & I2CCON_INT_PEND);
184         if ((s->i2ccon & I2CCON_INT_PEND) && !(v & I2CCON_INT_PEND)) {
185             s->i2ccon &= ~I2CCON_INT_PEND;
186             qemu_irq_lower(s->irq);
187             if (!(s->i2ccon & I2CCON_INTRS_EN)) {
188                 s->i2cstat &= ~I2CSTAT_START_BUSY;
189             }
190 
191             if (s->i2cstat & I2CSTAT_START_BUSY) {
192                 if (s->scl_free) {
193                     if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Tx) {
194                         exynos4210_i2c_data_send(s);
195                     } else if (EXYNOS4_I2C_MODE(s->i2cstat) ==
196                             I2CMODE_MASTER_Rx) {
197                         exynos4210_i2c_data_receive(s);
198                     }
199                 } else {
200                     s->i2ccon |= I2CCON_INT_PEND;
201                     qemu_irq_raise(s->irq);
202                 }
203             }
204         }
205         break;
206     case I2CSTAT_ADDR:
207         s->i2cstat =
208                 (s->i2cstat & I2CSTAT_START_BUSY) | (v & ~I2CSTAT_START_BUSY);
209 
210         if (!(s->i2cstat & I2CSTAT_OUTPUT_EN)) {
211             s->i2cstat &= ~I2CSTAT_START_BUSY;
212             s->scl_free = true;
213             qemu_irq_lower(s->irq);
214             break;
215         }
216 
217         /* Nothing to do if in i2c slave mode */
218         if (!I2C_IN_MASTER_MODE(s->i2cstat)) {
219             break;
220         }
221 
222         if (v & I2CSTAT_START_BUSY) {
223             s->i2cstat &= ~I2CSTAT_LAST_BIT;
224             s->i2cstat |= I2CSTAT_START_BUSY;    /* Line is busy */
225             s->scl_free = false;
226 
227             /* Generate start bit and send slave address */
228             if (i2c_start_transfer(s->bus, s->i2cds >> 1, s->i2cds & 0x1) &&
229                     (s->i2ccon & I2CCON_ACK_GEN)) {
230                 s->i2cstat |= I2CSTAT_LAST_BIT;
231             } else if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Rx) {
232                 exynos4210_i2c_data_receive(s);
233             }
234             exynos4210_i2c_raise_interrupt(s);
235         } else {
236             i2c_end_transfer(s->bus);
237             if (!(s->i2ccon & I2CCON_INT_PEND)) {
238                 s->i2cstat &= ~I2CSTAT_START_BUSY;
239             }
240             s->scl_free = true;
241         }
242         break;
243     case I2CADD_ADDR:
244         if ((s->i2cstat & I2CSTAT_OUTPUT_EN) == 0) {
245             s->i2cadd = v;
246         }
247         break;
248     case I2CDS_ADDR:
249         if (s->i2cstat & I2CSTAT_OUTPUT_EN) {
250             s->i2cds = v;
251             s->scl_free = true;
252             if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Tx &&
253                     (s->i2cstat & I2CSTAT_START_BUSY) &&
254                     !(s->i2ccon & I2CCON_INT_PEND)) {
255                 exynos4210_i2c_data_send(s);
256             }
257         }
258         break;
259     case I2CLC_ADDR:
260         s->i2clc = v;
261         break;
262     default:
263         DPRINT("ERROR: Bad write offset 0x%x\n", (unsigned int)offset);
264         break;
265     }
266 }
267 
268 static const MemoryRegionOps exynos4210_i2c_ops = {
269     .read = exynos4210_i2c_read,
270     .write = exynos4210_i2c_write,
271     .endianness = DEVICE_NATIVE_ENDIAN,
272 };
273 
274 static const VMStateDescription exynos4210_i2c_vmstate = {
275     .name = "exynos4210.i2c",
276     .version_id = 1,
277     .minimum_version_id = 1,
278     .fields = (VMStateField[]) {
279         VMSTATE_UINT8(i2ccon, Exynos4210I2CState),
280         VMSTATE_UINT8(i2cstat, Exynos4210I2CState),
281         VMSTATE_UINT8(i2cds, Exynos4210I2CState),
282         VMSTATE_UINT8(i2cadd, Exynos4210I2CState),
283         VMSTATE_UINT8(i2clc, Exynos4210I2CState),
284         VMSTATE_BOOL(scl_free, Exynos4210I2CState),
285         VMSTATE_END_OF_LIST()
286     }
287 };
288 
289 static void exynos4210_i2c_reset(DeviceState *d)
290 {
291     Exynos4210I2CState *s = EXYNOS4_I2C(d);
292 
293     s->i2ccon  = 0x00;
294     s->i2cstat = 0x00;
295     s->i2cds   = 0xFF;
296     s->i2clc   = 0x00;
297     s->i2cadd  = 0xFF;
298     s->scl_free = true;
299 }
300 
301 static void exynos4210_i2c_init(Object *obj)
302 {
303     DeviceState *dev = DEVICE(obj);
304     Exynos4210I2CState *s = EXYNOS4_I2C(obj);
305     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
306 
307     memory_region_init_io(&s->iomem, obj, &exynos4210_i2c_ops, s,
308                           TYPE_EXYNOS4_I2C, EXYNOS4_I2C_MEM_SIZE);
309     sysbus_init_mmio(sbd, &s->iomem);
310     sysbus_init_irq(sbd, &s->irq);
311     s->bus = i2c_init_bus(dev, "i2c");
312 }
313 
314 static void exynos4210_i2c_class_init(ObjectClass *klass, void *data)
315 {
316     DeviceClass *dc = DEVICE_CLASS(klass);
317 
318     dc->vmsd = &exynos4210_i2c_vmstate;
319     dc->reset = exynos4210_i2c_reset;
320 }
321 
322 static const TypeInfo exynos4210_i2c_type_info = {
323     .name = TYPE_EXYNOS4_I2C,
324     .parent = TYPE_SYS_BUS_DEVICE,
325     .instance_size = sizeof(Exynos4210I2CState),
326     .instance_init = exynos4210_i2c_init,
327     .class_init = exynos4210_i2c_class_init,
328 };
329 
330 static void exynos4210_i2c_register_types(void)
331 {
332     type_register_static(&exynos4210_i2c_type_info);
333 }
334 
335 type_init(exynos4210_i2c_register_types)
336