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