xref: /openbmc/qemu/hw/i2c/exynos4210_i2c.c (revision b8bcf811)
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/timer.h"
24 #include "hw/sysbus.h"
25 #include "hw/i2c/i2c.h"
26 
27 #ifndef EXYNOS4_I2C_DEBUG
28 #define EXYNOS4_I2C_DEBUG                 0
29 #endif
30 
31 #define TYPE_EXYNOS4_I2C                  "exynos4210.i2c"
32 #define EXYNOS4_I2C(obj)                  \
33     OBJECT_CHECK(Exynos4210I2CState, (obj), TYPE_EXYNOS4_I2C)
34 
35 /* Exynos4210 I2C memory map */
36 #define EXYNOS4_I2C_MEM_SIZE              0x14
37 #define I2CCON_ADDR                       0x00  /* control register */
38 #define I2CSTAT_ADDR                      0x04  /* control/status register */
39 #define I2CADD_ADDR                       0x08  /* address register */
40 #define I2CDS_ADDR                        0x0c  /* data shift register */
41 #define I2CLC_ADDR                        0x10  /* line control register */
42 
43 #define I2CCON_ACK_GEN                    (1 << 7)
44 #define I2CCON_INTRS_EN                   (1 << 5)
45 #define I2CCON_INT_PEND                   (1 << 4)
46 
47 #define EXYNOS4_I2C_MODE(reg)             (((reg) >> 6) & 3)
48 #define I2C_IN_MASTER_MODE(reg)           (((reg) >> 6) & 2)
49 #define I2CMODE_MASTER_Rx                 0x2
50 #define I2CMODE_MASTER_Tx                 0x3
51 #define I2CSTAT_LAST_BIT                  (1 << 0)
52 #define I2CSTAT_OUTPUT_EN                 (1 << 4)
53 #define I2CSTAT_START_BUSY                (1 << 5)
54 
55 
56 #if EXYNOS4_I2C_DEBUG
57 #define DPRINT(fmt, args...)              \
58     do { fprintf(stderr, "QEMU I2C: "fmt, ## args); } while (0)
59 
60 static const char *exynos4_i2c_get_regname(unsigned offset)
61 {
62     switch (offset) {
63     case I2CCON_ADDR:
64         return "I2CCON";
65     case I2CSTAT_ADDR:
66         return "I2CSTAT";
67     case I2CADD_ADDR:
68         return "I2CADD";
69     case I2CDS_ADDR:
70         return "I2CDS";
71     case I2CLC_ADDR:
72         return "I2CLC";
73     default:
74         return "[?]";
75     }
76 }
77 
78 #else
79 #define DPRINT(fmt, args...)              do { } while (0)
80 #endif
81 
82 typedef struct Exynos4210I2CState {
83     SysBusDevice parent_obj;
84 
85     MemoryRegion iomem;
86     I2CBus *bus;
87     qemu_irq irq;
88 
89     uint8_t i2ccon;
90     uint8_t i2cstat;
91     uint8_t i2cadd;
92     uint8_t i2cds;
93     uint8_t i2clc;
94     bool scl_free;
95 } Exynos4210I2CState;
96 
97 static inline void exynos4210_i2c_raise_interrupt(Exynos4210I2CState *s)
98 {
99     if (s->i2ccon & I2CCON_INTRS_EN) {
100         s->i2ccon |= I2CCON_INT_PEND;
101         qemu_irq_raise(s->irq);
102     }
103 }
104 
105 static void exynos4210_i2c_data_receive(void *opaque)
106 {
107     Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
108     int ret;
109 
110     s->i2cstat &= ~I2CSTAT_LAST_BIT;
111     s->scl_free = false;
112     ret = i2c_recv(s->bus);
113     if (ret < 0 && (s->i2ccon & I2CCON_ACK_GEN)) {
114         s->i2cstat |= I2CSTAT_LAST_BIT;  /* Data is not acknowledged */
115     } else {
116         s->i2cds = ret;
117     }
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 int exynos4210_i2c_realize(SysBusDevice *sbd)
302 {
303     DeviceState *dev = DEVICE(sbd);
304     Exynos4210I2CState *s = EXYNOS4_I2C(dev);
305 
306     memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_i2c_ops, s,
307                           TYPE_EXYNOS4_I2C, EXYNOS4_I2C_MEM_SIZE);
308     sysbus_init_mmio(sbd, &s->iomem);
309     sysbus_init_irq(sbd, &s->irq);
310     s->bus = i2c_init_bus(dev, "i2c");
311     return 0;
312 }
313 
314 static void exynos4210_i2c_class_init(ObjectClass *klass, void *data)
315 {
316     DeviceClass *dc = DEVICE_CLASS(klass);
317     SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass);
318 
319     dc->vmsd = &exynos4210_i2c_vmstate;
320     dc->reset = exynos4210_i2c_reset;
321     sbdc->init = exynos4210_i2c_realize;
322 }
323 
324 static const TypeInfo exynos4210_i2c_type_info = {
325     .name = TYPE_EXYNOS4_I2C,
326     .parent = TYPE_SYS_BUS_DEVICE,
327     .instance_size = sizeof(Exynos4210I2CState),
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