xref: /openbmc/qemu/hw/gpio/mpc8xxx.c (revision 650d103d)
1  /*
2   *  GPIO Controller for a lot of Freescale SoCs
3   *
4   * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved.
5   *
6   * Author: Alexander Graf, <agraf@suse.de>
7   *
8   * This library is free software; you can redistribute it and/or
9   * modify it under the terms of the GNU Lesser General Public
10   * License as published by the Free Software Foundation; either
11   * version 2 of the License, or (at your option) any later version.
12   *
13   * This library is distributed in the hope that it will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16   * Lesser General Public License for more details.
17   *
18   * You should have received a copy of the GNU Lesser General Public
19   * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20   */
21  
22  #include "qemu/osdep.h"
23  #include "hw/irq.h"
24  #include "hw/sysbus.h"
25  #include "migration/vmstate.h"
26  #include "qemu/module.h"
27  
28  #define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio"
29  #define MPC8XXX_GPIO(obj) OBJECT_CHECK(MPC8XXXGPIOState, (obj), TYPE_MPC8XXX_GPIO)
30  
31  typedef struct MPC8XXXGPIOState {
32      SysBusDevice parent_obj;
33  
34      MemoryRegion iomem;
35      qemu_irq irq;
36      qemu_irq out[32];
37  
38      uint32_t dir;
39      uint32_t odr;
40      uint32_t dat;
41      uint32_t ier;
42      uint32_t imr;
43      uint32_t icr;
44  } MPC8XXXGPIOState;
45  
46  static const VMStateDescription vmstate_mpc8xxx_gpio = {
47      .name = "mpc8xxx_gpio",
48      .version_id = 1,
49      .minimum_version_id = 1,
50      .fields = (VMStateField[]) {
51          VMSTATE_UINT32(dir, MPC8XXXGPIOState),
52          VMSTATE_UINT32(odr, MPC8XXXGPIOState),
53          VMSTATE_UINT32(dat, MPC8XXXGPIOState),
54          VMSTATE_UINT32(ier, MPC8XXXGPIOState),
55          VMSTATE_UINT32(imr, MPC8XXXGPIOState),
56          VMSTATE_UINT32(icr, MPC8XXXGPIOState),
57          VMSTATE_END_OF_LIST()
58      }
59  };
60  
61  static void mpc8xxx_gpio_update(MPC8XXXGPIOState *s)
62  {
63      qemu_set_irq(s->irq, !!(s->ier & s->imr));
64  }
65  
66  static uint64_t mpc8xxx_gpio_read(void *opaque, hwaddr offset,
67                                    unsigned size)
68  {
69      MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
70  
71      if (size != 4) {
72          /* All registers are 32bit */
73          return 0;
74      }
75  
76      switch (offset) {
77      case 0x0: /* Direction */
78          return s->dir;
79      case 0x4: /* Open Drain */
80          return s->odr;
81      case 0x8: /* Data */
82          return s->dat;
83      case 0xC: /* Interrupt Event */
84          return s->ier;
85      case 0x10: /* Interrupt Mask */
86          return s->imr;
87      case 0x14: /* Interrupt Control */
88          return s->icr;
89      default:
90          return 0;
91      }
92  }
93  
94  static void mpc8xxx_write_data(MPC8XXXGPIOState *s, uint32_t new_data)
95  {
96      uint32_t old_data = s->dat;
97      uint32_t diff = old_data ^ new_data;
98      int i;
99  
100      for (i = 0; i < 32; i++) {
101          uint32_t mask = 0x80000000 >> i;
102          if (!(diff & mask)) {
103              continue;
104          }
105  
106          if (s->dir & mask) {
107              /* Output */
108              qemu_set_irq(s->out[i], (new_data & mask) != 0);
109          }
110      }
111  
112      s->dat = new_data;
113  }
114  
115  static void mpc8xxx_gpio_write(void *opaque, hwaddr offset,
116                          uint64_t value, unsigned size)
117  {
118      MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
119  
120      if (size != 4) {
121          /* All registers are 32bit */
122          return;
123      }
124  
125      switch (offset) {
126      case 0x0: /* Direction */
127          s->dir = value;
128          break;
129      case 0x4: /* Open Drain */
130          s->odr = value;
131          break;
132      case 0x8: /* Data */
133          mpc8xxx_write_data(s, value);
134          break;
135      case 0xC: /* Interrupt Event */
136          s->ier &= ~value;
137          break;
138      case 0x10: /* Interrupt Mask */
139          s->imr = value;
140          break;
141      case 0x14: /* Interrupt Control */
142          s->icr = value;
143          break;
144      }
145  
146      mpc8xxx_gpio_update(s);
147  }
148  
149  static void mpc8xxx_gpio_reset(DeviceState *dev)
150  {
151      MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev);
152  
153      s->dir = 0;
154      s->odr = 0;
155      s->dat = 0;
156      s->ier = 0;
157      s->imr = 0;
158      s->icr = 0;
159  }
160  
161  static void mpc8xxx_gpio_set_irq(void * opaque, int irq, int level)
162  {
163      MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
164      uint32_t mask;
165  
166      mask = 0x80000000 >> irq;
167      if ((s->dir & mask) == 0) {
168          uint32_t old_value = s->dat & mask;
169  
170          s->dat &= ~mask;
171          if (level)
172              s->dat |= mask;
173  
174          if (!(s->icr & irq) || (old_value && !level)) {
175              s->ier |= mask;
176          }
177  
178          mpc8xxx_gpio_update(s);
179      }
180  }
181  
182  static const MemoryRegionOps mpc8xxx_gpio_ops = {
183      .read = mpc8xxx_gpio_read,
184      .write = mpc8xxx_gpio_write,
185      .endianness = DEVICE_BIG_ENDIAN,
186  };
187  
188  static void mpc8xxx_gpio_initfn(Object *obj)
189  {
190      DeviceState *dev = DEVICE(obj);
191      MPC8XXXGPIOState *s = MPC8XXX_GPIO(obj);
192      SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
193  
194      memory_region_init_io(&s->iomem, obj, &mpc8xxx_gpio_ops,
195                            s, "mpc8xxx_gpio", 0x1000);
196      sysbus_init_mmio(sbd, &s->iomem);
197      sysbus_init_irq(sbd, &s->irq);
198      qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32);
199      qdev_init_gpio_out(dev, s->out, 32);
200  }
201  
202  static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data)
203  {
204      DeviceClass *dc = DEVICE_CLASS(klass);
205  
206      dc->vmsd = &vmstate_mpc8xxx_gpio;
207      dc->reset = mpc8xxx_gpio_reset;
208  }
209  
210  static const TypeInfo mpc8xxx_gpio_info = {
211      .name          = TYPE_MPC8XXX_GPIO,
212      .parent        = TYPE_SYS_BUS_DEVICE,
213      .instance_size = sizeof(MPC8XXXGPIOState),
214      .instance_init = mpc8xxx_gpio_initfn,
215      .class_init    = mpc8xxx_gpio_class_init,
216  };
217  
218  static void mpc8xxx_gpio_register_types(void)
219  {
220      type_register_static(&mpc8xxx_gpio_info);
221  }
222  
223  type_init(mpc8xxx_gpio_register_types)
224