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