xref: /qemu/hw/i2c/exynos4210_i2c.c (revision e3a6e0daf47f43cdb02b42c4dd3694a8f00063d5)
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