xref: /qemu/hw/gpio/mpc8xxx.c (revision 0b8fa32f551e863bb548a11394239239270dd3dc)
1228aa992SAlexander Graf /*
2228aa992SAlexander Graf  *  GPIO Controller for a lot of Freescale SoCs
3228aa992SAlexander Graf  *
4228aa992SAlexander Graf  * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved.
5228aa992SAlexander Graf  *
6228aa992SAlexander Graf  * Author: Alexander Graf, <agraf@suse.de>
7228aa992SAlexander Graf  *
8228aa992SAlexander Graf  * This library is free software; you can redistribute it and/or
9228aa992SAlexander Graf  * modify it under the terms of the GNU Lesser General Public
10228aa992SAlexander Graf  * License as published by the Free Software Foundation; either
11228aa992SAlexander Graf  * version 2 of the License, or (at your option) any later version.
12228aa992SAlexander Graf  *
13228aa992SAlexander Graf  * This library is distributed in the hope that it will be useful,
14228aa992SAlexander Graf  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15228aa992SAlexander Graf  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16228aa992SAlexander Graf  * Lesser General Public License for more details.
17228aa992SAlexander Graf  *
18228aa992SAlexander Graf  * You should have received a copy of the GNU Lesser General Public
19228aa992SAlexander Graf  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20228aa992SAlexander Graf  */
21228aa992SAlexander Graf 
220430891cSPeter Maydell #include "qemu/osdep.h"
23228aa992SAlexander Graf #include "hw/sysbus.h"
24*0b8fa32fSMarkus Armbruster #include "qemu/module.h"
25228aa992SAlexander Graf 
26228aa992SAlexander Graf #define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio"
27228aa992SAlexander Graf #define MPC8XXX_GPIO(obj) OBJECT_CHECK(MPC8XXXGPIOState, (obj), TYPE_MPC8XXX_GPIO)
28228aa992SAlexander Graf 
29228aa992SAlexander Graf typedef struct MPC8XXXGPIOState {
30228aa992SAlexander Graf     SysBusDevice parent_obj;
31228aa992SAlexander Graf 
32228aa992SAlexander Graf     MemoryRegion iomem;
33228aa992SAlexander Graf     qemu_irq irq;
34228aa992SAlexander Graf     qemu_irq out[32];
35228aa992SAlexander Graf 
36228aa992SAlexander Graf     uint32_t dir;
37228aa992SAlexander Graf     uint32_t odr;
38228aa992SAlexander Graf     uint32_t dat;
39228aa992SAlexander Graf     uint32_t ier;
40228aa992SAlexander Graf     uint32_t imr;
41228aa992SAlexander Graf     uint32_t icr;
42228aa992SAlexander Graf } MPC8XXXGPIOState;
43228aa992SAlexander Graf 
44228aa992SAlexander Graf static const VMStateDescription vmstate_mpc8xxx_gpio = {
45228aa992SAlexander Graf     .name = "mpc8xxx_gpio",
46228aa992SAlexander Graf     .version_id = 1,
47228aa992SAlexander Graf     .minimum_version_id = 1,
48228aa992SAlexander Graf     .fields = (VMStateField[]) {
49228aa992SAlexander Graf         VMSTATE_UINT32(dir, MPC8XXXGPIOState),
50228aa992SAlexander Graf         VMSTATE_UINT32(odr, MPC8XXXGPIOState),
51228aa992SAlexander Graf         VMSTATE_UINT32(dat, MPC8XXXGPIOState),
52228aa992SAlexander Graf         VMSTATE_UINT32(ier, MPC8XXXGPIOState),
53228aa992SAlexander Graf         VMSTATE_UINT32(imr, MPC8XXXGPIOState),
54228aa992SAlexander Graf         VMSTATE_UINT32(icr, MPC8XXXGPIOState),
55228aa992SAlexander Graf         VMSTATE_END_OF_LIST()
56228aa992SAlexander Graf     }
57228aa992SAlexander Graf };
58228aa992SAlexander Graf 
59228aa992SAlexander Graf static void mpc8xxx_gpio_update(MPC8XXXGPIOState *s)
60228aa992SAlexander Graf {
61228aa992SAlexander Graf     qemu_set_irq(s->irq, !!(s->ier & s->imr));
62228aa992SAlexander Graf }
63228aa992SAlexander Graf 
64228aa992SAlexander Graf static uint64_t mpc8xxx_gpio_read(void *opaque, hwaddr offset,
65228aa992SAlexander Graf                                   unsigned size)
66228aa992SAlexander Graf {
67228aa992SAlexander Graf     MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
68228aa992SAlexander Graf 
69228aa992SAlexander Graf     if (size != 4) {
70228aa992SAlexander Graf         /* All registers are 32bit */
71228aa992SAlexander Graf         return 0;
72228aa992SAlexander Graf     }
73228aa992SAlexander Graf 
74228aa992SAlexander Graf     switch (offset) {
75228aa992SAlexander Graf     case 0x0: /* Direction */
76228aa992SAlexander Graf         return s->dir;
77228aa992SAlexander Graf     case 0x4: /* Open Drain */
78228aa992SAlexander Graf         return s->odr;
79228aa992SAlexander Graf     case 0x8: /* Data */
80228aa992SAlexander Graf         return s->dat;
81228aa992SAlexander Graf     case 0xC: /* Interrupt Event */
82228aa992SAlexander Graf         return s->ier;
83228aa992SAlexander Graf     case 0x10: /* Interrupt Mask */
84228aa992SAlexander Graf         return s->imr;
85228aa992SAlexander Graf     case 0x14: /* Interrupt Control */
86228aa992SAlexander Graf         return s->icr;
87228aa992SAlexander Graf     default:
88228aa992SAlexander Graf         return 0;
89228aa992SAlexander Graf     }
90228aa992SAlexander Graf }
91228aa992SAlexander Graf 
92228aa992SAlexander Graf static void mpc8xxx_write_data(MPC8XXXGPIOState *s, uint32_t new_data)
93228aa992SAlexander Graf {
94228aa992SAlexander Graf     uint32_t old_data = s->dat;
95228aa992SAlexander Graf     uint32_t diff = old_data ^ new_data;
96228aa992SAlexander Graf     int i;
97228aa992SAlexander Graf 
98228aa992SAlexander Graf     for (i = 0; i < 32; i++) {
99228aa992SAlexander Graf         uint32_t mask = 0x80000000 >> i;
100228aa992SAlexander Graf         if (!(diff & mask)) {
101228aa992SAlexander Graf             continue;
102228aa992SAlexander Graf         }
103228aa992SAlexander Graf 
104228aa992SAlexander Graf         if (s->dir & mask) {
105228aa992SAlexander Graf             /* Output */
106228aa992SAlexander Graf             qemu_set_irq(s->out[i], (new_data & mask) != 0);
107228aa992SAlexander Graf         }
108228aa992SAlexander Graf     }
109228aa992SAlexander Graf 
110228aa992SAlexander Graf     s->dat = new_data;
111228aa992SAlexander Graf }
112228aa992SAlexander Graf 
113228aa992SAlexander Graf static void mpc8xxx_gpio_write(void *opaque, hwaddr offset,
114228aa992SAlexander Graf                         uint64_t value, unsigned size)
115228aa992SAlexander Graf {
116228aa992SAlexander Graf     MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
117228aa992SAlexander Graf 
118228aa992SAlexander Graf     if (size != 4) {
119228aa992SAlexander Graf         /* All registers are 32bit */
120228aa992SAlexander Graf         return;
121228aa992SAlexander Graf     }
122228aa992SAlexander Graf 
123228aa992SAlexander Graf     switch (offset) {
124228aa992SAlexander Graf     case 0x0: /* Direction */
125228aa992SAlexander Graf         s->dir = value;
126228aa992SAlexander Graf         break;
127228aa992SAlexander Graf     case 0x4: /* Open Drain */
128228aa992SAlexander Graf         s->odr = value;
129228aa992SAlexander Graf         break;
130228aa992SAlexander Graf     case 0x8: /* Data */
131228aa992SAlexander Graf         mpc8xxx_write_data(s, value);
132228aa992SAlexander Graf         break;
133228aa992SAlexander Graf     case 0xC: /* Interrupt Event */
134228aa992SAlexander Graf         s->ier &= ~value;
135228aa992SAlexander Graf         break;
136228aa992SAlexander Graf     case 0x10: /* Interrupt Mask */
137228aa992SAlexander Graf         s->imr = value;
138228aa992SAlexander Graf         break;
139228aa992SAlexander Graf     case 0x14: /* Interrupt Control */
140228aa992SAlexander Graf         s->icr = value;
141228aa992SAlexander Graf         break;
142228aa992SAlexander Graf     }
143228aa992SAlexander Graf 
144228aa992SAlexander Graf     mpc8xxx_gpio_update(s);
145228aa992SAlexander Graf }
146228aa992SAlexander Graf 
147396781f6Sxiaoqiang zhao static void mpc8xxx_gpio_reset(DeviceState *dev)
148228aa992SAlexander Graf {
149396781f6Sxiaoqiang zhao     MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev);
150396781f6Sxiaoqiang zhao 
151228aa992SAlexander Graf     s->dir = 0;
152228aa992SAlexander Graf     s->odr = 0;
153228aa992SAlexander Graf     s->dat = 0;
154228aa992SAlexander Graf     s->ier = 0;
155228aa992SAlexander Graf     s->imr = 0;
156228aa992SAlexander Graf     s->icr = 0;
157228aa992SAlexander Graf }
158228aa992SAlexander Graf 
159228aa992SAlexander Graf static void mpc8xxx_gpio_set_irq(void * opaque, int irq, int level)
160228aa992SAlexander Graf {
161228aa992SAlexander Graf     MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
162228aa992SAlexander Graf     uint32_t mask;
163228aa992SAlexander Graf 
164228aa992SAlexander Graf     mask = 0x80000000 >> irq;
165228aa992SAlexander Graf     if ((s->dir & mask) == 0) {
166228aa992SAlexander Graf         uint32_t old_value = s->dat & mask;
167228aa992SAlexander Graf 
168228aa992SAlexander Graf         s->dat &= ~mask;
169228aa992SAlexander Graf         if (level)
170228aa992SAlexander Graf             s->dat |= mask;
171228aa992SAlexander Graf 
172228aa992SAlexander Graf         if (!(s->icr & irq) || (old_value && !level)) {
173228aa992SAlexander Graf             s->ier |= mask;
174228aa992SAlexander Graf         }
175228aa992SAlexander Graf 
176228aa992SAlexander Graf         mpc8xxx_gpio_update(s);
177228aa992SAlexander Graf     }
178228aa992SAlexander Graf }
179228aa992SAlexander Graf 
180228aa992SAlexander Graf static const MemoryRegionOps mpc8xxx_gpio_ops = {
181228aa992SAlexander Graf     .read = mpc8xxx_gpio_read,
182228aa992SAlexander Graf     .write = mpc8xxx_gpio_write,
183228aa992SAlexander Graf     .endianness = DEVICE_BIG_ENDIAN,
184228aa992SAlexander Graf };
185228aa992SAlexander Graf 
186396781f6Sxiaoqiang zhao static void mpc8xxx_gpio_initfn(Object *obj)
187228aa992SAlexander Graf {
188396781f6Sxiaoqiang zhao     DeviceState *dev = DEVICE(obj);
189396781f6Sxiaoqiang zhao     MPC8XXXGPIOState *s = MPC8XXX_GPIO(obj);
190396781f6Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
191228aa992SAlexander Graf 
192396781f6Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &mpc8xxx_gpio_ops,
193396781f6Sxiaoqiang zhao                           s, "mpc8xxx_gpio", 0x1000);
194228aa992SAlexander Graf     sysbus_init_mmio(sbd, &s->iomem);
195228aa992SAlexander Graf     sysbus_init_irq(sbd, &s->irq);
196228aa992SAlexander Graf     qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32);
197228aa992SAlexander Graf     qdev_init_gpio_out(dev, s->out, 32);
198228aa992SAlexander Graf }
199228aa992SAlexander Graf 
200228aa992SAlexander Graf static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data)
201228aa992SAlexander Graf {
202228aa992SAlexander Graf     DeviceClass *dc = DEVICE_CLASS(klass);
203228aa992SAlexander Graf 
204228aa992SAlexander Graf     dc->vmsd = &vmstate_mpc8xxx_gpio;
205396781f6Sxiaoqiang zhao     dc->reset = mpc8xxx_gpio_reset;
206228aa992SAlexander Graf }
207228aa992SAlexander Graf 
208228aa992SAlexander Graf static const TypeInfo mpc8xxx_gpio_info = {
209228aa992SAlexander Graf     .name          = TYPE_MPC8XXX_GPIO,
210228aa992SAlexander Graf     .parent        = TYPE_SYS_BUS_DEVICE,
211228aa992SAlexander Graf     .instance_size = sizeof(MPC8XXXGPIOState),
212396781f6Sxiaoqiang zhao     .instance_init = mpc8xxx_gpio_initfn,
213228aa992SAlexander Graf     .class_init    = mpc8xxx_gpio_class_init,
214228aa992SAlexander Graf };
215228aa992SAlexander Graf 
216228aa992SAlexander Graf static void mpc8xxx_gpio_register_types(void)
217228aa992SAlexander Graf {
218228aa992SAlexander Graf     type_register_static(&mpc8xxx_gpio_info);
219228aa992SAlexander Graf }
220228aa992SAlexander Graf 
221228aa992SAlexander Graf type_init(mpc8xxx_gpio_register_types)
222