xref: /openbmc/qemu/hw/gpio/mpc8xxx.c (revision e9206163)
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.1 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 #include "qom/object.h"
28 
29 #define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio"
30 OBJECT_DECLARE_SIMPLE_TYPE(MPC8XXXGPIOState, MPC8XXX_GPIO)
31 
32 struct MPC8XXXGPIOState {
33     SysBusDevice parent_obj;
34 
35     MemoryRegion iomem;
36     qemu_irq irq;
37     qemu_irq out[32];
38 
39     uint32_t dir;
40     uint32_t odr;
41     uint32_t dat;
42     uint32_t ier;
43     uint32_t imr;
44     uint32_t icr;
45 };
46 
47 static const VMStateDescription vmstate_mpc8xxx_gpio = {
48     .name = "mpc8xxx_gpio",
49     .version_id = 1,
50     .minimum_version_id = 1,
51     .fields = (const VMStateField[]) {
52         VMSTATE_UINT32(dir, MPC8XXXGPIOState),
53         VMSTATE_UINT32(odr, MPC8XXXGPIOState),
54         VMSTATE_UINT32(dat, MPC8XXXGPIOState),
55         VMSTATE_UINT32(ier, MPC8XXXGPIOState),
56         VMSTATE_UINT32(imr, MPC8XXXGPIOState),
57         VMSTATE_UINT32(icr, MPC8XXXGPIOState),
58         VMSTATE_END_OF_LIST()
59     }
60 };
61 
62 static void mpc8xxx_gpio_update(MPC8XXXGPIOState *s)
63 {
64     qemu_set_irq(s->irq, !!(s->ier & s->imr));
65 }
66 
67 static uint64_t mpc8xxx_gpio_read(void *opaque, hwaddr offset,
68                                   unsigned size)
69 {
70     MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
71 
72     if (size != 4) {
73         /* All registers are 32bit */
74         return 0;
75     }
76 
77     switch (offset) {
78     case 0x0: /* Direction */
79         return s->dir;
80     case 0x4: /* Open Drain */
81         return s->odr;
82     case 0x8: /* Data */
83         return s->dat;
84     case 0xC: /* Interrupt Event */
85         return s->ier;
86     case 0x10: /* Interrupt Mask */
87         return s->imr;
88     case 0x14: /* Interrupt Control */
89         return s->icr;
90     default:
91         return 0;
92     }
93 }
94 
95 static void mpc8xxx_write_data(MPC8XXXGPIOState *s, uint32_t new_data)
96 {
97     uint32_t old_data = s->dat;
98     uint32_t diff = old_data ^ new_data;
99     int i;
100 
101     for (i = 0; i < 32; i++) {
102         uint32_t mask = 0x80000000 >> i;
103         if (!(diff & mask)) {
104             continue;
105         }
106 
107         if (s->dir & mask) {
108             /* Output */
109             qemu_set_irq(s->out[i], (new_data & mask) != 0);
110         }
111     }
112 
113     s->dat = new_data;
114 }
115 
116 static void mpc8xxx_gpio_write(void *opaque, hwaddr offset,
117                         uint64_t value, unsigned size)
118 {
119     MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
120 
121     if (size != 4) {
122         /* All registers are 32bit */
123         return;
124     }
125 
126     switch (offset) {
127     case 0x0: /* Direction */
128         s->dir = value;
129         break;
130     case 0x4: /* Open Drain */
131         s->odr = value;
132         break;
133     case 0x8: /* Data */
134         mpc8xxx_write_data(s, value);
135         break;
136     case 0xC: /* Interrupt Event */
137         s->ier &= ~value;
138         break;
139     case 0x10: /* Interrupt Mask */
140         s->imr = value;
141         break;
142     case 0x14: /* Interrupt Control */
143         s->icr = value;
144         break;
145     }
146 
147     mpc8xxx_gpio_update(s);
148 }
149 
150 static void mpc8xxx_gpio_reset(DeviceState *dev)
151 {
152     MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev);
153 
154     s->dir = 0;
155     s->odr = 0;
156     s->dat = 0;
157     s->ier = 0;
158     s->imr = 0;
159     s->icr = 0;
160 }
161 
162 static void mpc8xxx_gpio_set_irq(void * opaque, int irq, int level)
163 {
164     MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
165     uint32_t mask;
166 
167     mask = 0x80000000 >> irq;
168     if ((s->dir & mask) == 0) {
169         uint32_t old_value = s->dat & mask;
170 
171         s->dat &= ~mask;
172         if (level)
173             s->dat |= mask;
174 
175         if (!(s->icr & irq) || (old_value && !level)) {
176             s->ier |= mask;
177         }
178 
179         mpc8xxx_gpio_update(s);
180     }
181 }
182 
183 static const MemoryRegionOps mpc8xxx_gpio_ops = {
184     .read = mpc8xxx_gpio_read,
185     .write = mpc8xxx_gpio_write,
186     .endianness = DEVICE_BIG_ENDIAN,
187 };
188 
189 static void mpc8xxx_gpio_initfn(Object *obj)
190 {
191     DeviceState *dev = DEVICE(obj);
192     MPC8XXXGPIOState *s = MPC8XXX_GPIO(obj);
193     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
194 
195     memory_region_init_io(&s->iomem, obj, &mpc8xxx_gpio_ops,
196                           s, "mpc8xxx_gpio", 0x1000);
197     sysbus_init_mmio(sbd, &s->iomem);
198     sysbus_init_irq(sbd, &s->irq);
199     qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32);
200     qdev_init_gpio_out(dev, s->out, 32);
201 }
202 
203 static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data)
204 {
205     DeviceClass *dc = DEVICE_CLASS(klass);
206 
207     dc->vmsd = &vmstate_mpc8xxx_gpio;
208     dc->reset = mpc8xxx_gpio_reset;
209 }
210 
211 static const TypeInfo mpc8xxx_gpio_info = {
212     .name          = TYPE_MPC8XXX_GPIO,
213     .parent        = TYPE_SYS_BUS_DEVICE,
214     .instance_size = sizeof(MPC8XXXGPIOState),
215     .instance_init = mpc8xxx_gpio_initfn,
216     .class_init    = mpc8xxx_gpio_class_init,
217 };
218 
219 static void mpc8xxx_gpio_register_types(void)
220 {
221     type_register_static(&mpc8xxx_gpio_info);
222 }
223 
224 type_init(mpc8xxx_gpio_register_types)
225