xref: /qemu/hw/gpio/mpc8xxx.c (revision 3b9e779b86700d217cf824e7d77ed3ba316ef918)
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
1161f3c91aSChetan Pant  * version 2.1 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"
2364552b6bSMarkus Armbruster #include "hw/irq.h"
24228aa992SAlexander Graf #include "hw/sysbus.h"
25d6454270SMarkus Armbruster #include "migration/vmstate.h"
260b8fa32fSMarkus Armbruster #include "qemu/module.h"
27db1015e9SEduardo Habkost #include "qom/object.h"
28228aa992SAlexander Graf 
29228aa992SAlexander Graf #define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio"
308063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(MPC8XXXGPIOState, MPC8XXX_GPIO)
31228aa992SAlexander Graf 
32db1015e9SEduardo Habkost struct MPC8XXXGPIOState {
33228aa992SAlexander Graf     SysBusDevice parent_obj;
34228aa992SAlexander Graf 
35228aa992SAlexander Graf     MemoryRegion iomem;
36228aa992SAlexander Graf     qemu_irq irq;
37228aa992SAlexander Graf     qemu_irq out[32];
38228aa992SAlexander Graf 
39228aa992SAlexander Graf     uint32_t dir;
40228aa992SAlexander Graf     uint32_t odr;
41228aa992SAlexander Graf     uint32_t dat;
42228aa992SAlexander Graf     uint32_t ier;
43228aa992SAlexander Graf     uint32_t imr;
44228aa992SAlexander Graf     uint32_t icr;
45db1015e9SEduardo Habkost };
46228aa992SAlexander Graf 
47228aa992SAlexander Graf static const VMStateDescription vmstate_mpc8xxx_gpio = {
48228aa992SAlexander Graf     .name = "mpc8xxx_gpio",
49228aa992SAlexander Graf     .version_id = 1,
50228aa992SAlexander Graf     .minimum_version_id = 1,
51*3b9e779bSRichard Henderson     .fields = (const VMStateField[]) {
52228aa992SAlexander Graf         VMSTATE_UINT32(dir, MPC8XXXGPIOState),
53228aa992SAlexander Graf         VMSTATE_UINT32(odr, MPC8XXXGPIOState),
54228aa992SAlexander Graf         VMSTATE_UINT32(dat, MPC8XXXGPIOState),
55228aa992SAlexander Graf         VMSTATE_UINT32(ier, MPC8XXXGPIOState),
56228aa992SAlexander Graf         VMSTATE_UINT32(imr, MPC8XXXGPIOState),
57228aa992SAlexander Graf         VMSTATE_UINT32(icr, MPC8XXXGPIOState),
58228aa992SAlexander Graf         VMSTATE_END_OF_LIST()
59228aa992SAlexander Graf     }
60228aa992SAlexander Graf };
61228aa992SAlexander Graf 
62228aa992SAlexander Graf static void mpc8xxx_gpio_update(MPC8XXXGPIOState *s)
63228aa992SAlexander Graf {
64228aa992SAlexander Graf     qemu_set_irq(s->irq, !!(s->ier & s->imr));
65228aa992SAlexander Graf }
66228aa992SAlexander Graf 
67228aa992SAlexander Graf static uint64_t mpc8xxx_gpio_read(void *opaque, hwaddr offset,
68228aa992SAlexander Graf                                   unsigned size)
69228aa992SAlexander Graf {
70228aa992SAlexander Graf     MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
71228aa992SAlexander Graf 
72228aa992SAlexander Graf     if (size != 4) {
73228aa992SAlexander Graf         /* All registers are 32bit */
74228aa992SAlexander Graf         return 0;
75228aa992SAlexander Graf     }
76228aa992SAlexander Graf 
77228aa992SAlexander Graf     switch (offset) {
78228aa992SAlexander Graf     case 0x0: /* Direction */
79228aa992SAlexander Graf         return s->dir;
80228aa992SAlexander Graf     case 0x4: /* Open Drain */
81228aa992SAlexander Graf         return s->odr;
82228aa992SAlexander Graf     case 0x8: /* Data */
83228aa992SAlexander Graf         return s->dat;
84228aa992SAlexander Graf     case 0xC: /* Interrupt Event */
85228aa992SAlexander Graf         return s->ier;
86228aa992SAlexander Graf     case 0x10: /* Interrupt Mask */
87228aa992SAlexander Graf         return s->imr;
88228aa992SAlexander Graf     case 0x14: /* Interrupt Control */
89228aa992SAlexander Graf         return s->icr;
90228aa992SAlexander Graf     default:
91228aa992SAlexander Graf         return 0;
92228aa992SAlexander Graf     }
93228aa992SAlexander Graf }
94228aa992SAlexander Graf 
95228aa992SAlexander Graf static void mpc8xxx_write_data(MPC8XXXGPIOState *s, uint32_t new_data)
96228aa992SAlexander Graf {
97228aa992SAlexander Graf     uint32_t old_data = s->dat;
98228aa992SAlexander Graf     uint32_t diff = old_data ^ new_data;
99228aa992SAlexander Graf     int i;
100228aa992SAlexander Graf 
101228aa992SAlexander Graf     for (i = 0; i < 32; i++) {
102228aa992SAlexander Graf         uint32_t mask = 0x80000000 >> i;
103228aa992SAlexander Graf         if (!(diff & mask)) {
104228aa992SAlexander Graf             continue;
105228aa992SAlexander Graf         }
106228aa992SAlexander Graf 
107228aa992SAlexander Graf         if (s->dir & mask) {
108228aa992SAlexander Graf             /* Output */
109228aa992SAlexander Graf             qemu_set_irq(s->out[i], (new_data & mask) != 0);
110228aa992SAlexander Graf         }
111228aa992SAlexander Graf     }
112228aa992SAlexander Graf 
113228aa992SAlexander Graf     s->dat = new_data;
114228aa992SAlexander Graf }
115228aa992SAlexander Graf 
116228aa992SAlexander Graf static void mpc8xxx_gpio_write(void *opaque, hwaddr offset,
117228aa992SAlexander Graf                         uint64_t value, unsigned size)
118228aa992SAlexander Graf {
119228aa992SAlexander Graf     MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
120228aa992SAlexander Graf 
121228aa992SAlexander Graf     if (size != 4) {
122228aa992SAlexander Graf         /* All registers are 32bit */
123228aa992SAlexander Graf         return;
124228aa992SAlexander Graf     }
125228aa992SAlexander Graf 
126228aa992SAlexander Graf     switch (offset) {
127228aa992SAlexander Graf     case 0x0: /* Direction */
128228aa992SAlexander Graf         s->dir = value;
129228aa992SAlexander Graf         break;
130228aa992SAlexander Graf     case 0x4: /* Open Drain */
131228aa992SAlexander Graf         s->odr = value;
132228aa992SAlexander Graf         break;
133228aa992SAlexander Graf     case 0x8: /* Data */
134228aa992SAlexander Graf         mpc8xxx_write_data(s, value);
135228aa992SAlexander Graf         break;
136228aa992SAlexander Graf     case 0xC: /* Interrupt Event */
137228aa992SAlexander Graf         s->ier &= ~value;
138228aa992SAlexander Graf         break;
139228aa992SAlexander Graf     case 0x10: /* Interrupt Mask */
140228aa992SAlexander Graf         s->imr = value;
141228aa992SAlexander Graf         break;
142228aa992SAlexander Graf     case 0x14: /* Interrupt Control */
143228aa992SAlexander Graf         s->icr = value;
144228aa992SAlexander Graf         break;
145228aa992SAlexander Graf     }
146228aa992SAlexander Graf 
147228aa992SAlexander Graf     mpc8xxx_gpio_update(s);
148228aa992SAlexander Graf }
149228aa992SAlexander Graf 
150396781f6Sxiaoqiang zhao static void mpc8xxx_gpio_reset(DeviceState *dev)
151228aa992SAlexander Graf {
152396781f6Sxiaoqiang zhao     MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev);
153396781f6Sxiaoqiang zhao 
154228aa992SAlexander Graf     s->dir = 0;
155228aa992SAlexander Graf     s->odr = 0;
156228aa992SAlexander Graf     s->dat = 0;
157228aa992SAlexander Graf     s->ier = 0;
158228aa992SAlexander Graf     s->imr = 0;
159228aa992SAlexander Graf     s->icr = 0;
160228aa992SAlexander Graf }
161228aa992SAlexander Graf 
162228aa992SAlexander Graf static void mpc8xxx_gpio_set_irq(void * opaque, int irq, int level)
163228aa992SAlexander Graf {
164228aa992SAlexander Graf     MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
165228aa992SAlexander Graf     uint32_t mask;
166228aa992SAlexander Graf 
167228aa992SAlexander Graf     mask = 0x80000000 >> irq;
168228aa992SAlexander Graf     if ((s->dir & mask) == 0) {
169228aa992SAlexander Graf         uint32_t old_value = s->dat & mask;
170228aa992SAlexander Graf 
171228aa992SAlexander Graf         s->dat &= ~mask;
172228aa992SAlexander Graf         if (level)
173228aa992SAlexander Graf             s->dat |= mask;
174228aa992SAlexander Graf 
175228aa992SAlexander Graf         if (!(s->icr & irq) || (old_value && !level)) {
176228aa992SAlexander Graf             s->ier |= mask;
177228aa992SAlexander Graf         }
178228aa992SAlexander Graf 
179228aa992SAlexander Graf         mpc8xxx_gpio_update(s);
180228aa992SAlexander Graf     }
181228aa992SAlexander Graf }
182228aa992SAlexander Graf 
183228aa992SAlexander Graf static const MemoryRegionOps mpc8xxx_gpio_ops = {
184228aa992SAlexander Graf     .read = mpc8xxx_gpio_read,
185228aa992SAlexander Graf     .write = mpc8xxx_gpio_write,
186228aa992SAlexander Graf     .endianness = DEVICE_BIG_ENDIAN,
187228aa992SAlexander Graf };
188228aa992SAlexander Graf 
189396781f6Sxiaoqiang zhao static void mpc8xxx_gpio_initfn(Object *obj)
190228aa992SAlexander Graf {
191396781f6Sxiaoqiang zhao     DeviceState *dev = DEVICE(obj);
192396781f6Sxiaoqiang zhao     MPC8XXXGPIOState *s = MPC8XXX_GPIO(obj);
193396781f6Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
194228aa992SAlexander Graf 
195396781f6Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &mpc8xxx_gpio_ops,
196396781f6Sxiaoqiang zhao                           s, "mpc8xxx_gpio", 0x1000);
197228aa992SAlexander Graf     sysbus_init_mmio(sbd, &s->iomem);
198228aa992SAlexander Graf     sysbus_init_irq(sbd, &s->irq);
199228aa992SAlexander Graf     qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32);
200228aa992SAlexander Graf     qdev_init_gpio_out(dev, s->out, 32);
201228aa992SAlexander Graf }
202228aa992SAlexander Graf 
203228aa992SAlexander Graf static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data)
204228aa992SAlexander Graf {
205228aa992SAlexander Graf     DeviceClass *dc = DEVICE_CLASS(klass);
206228aa992SAlexander Graf 
207228aa992SAlexander Graf     dc->vmsd = &vmstate_mpc8xxx_gpio;
208396781f6Sxiaoqiang zhao     dc->reset = mpc8xxx_gpio_reset;
209228aa992SAlexander Graf }
210228aa992SAlexander Graf 
211228aa992SAlexander Graf static const TypeInfo mpc8xxx_gpio_info = {
212228aa992SAlexander Graf     .name          = TYPE_MPC8XXX_GPIO,
213228aa992SAlexander Graf     .parent        = TYPE_SYS_BUS_DEVICE,
214228aa992SAlexander Graf     .instance_size = sizeof(MPC8XXXGPIOState),
215396781f6Sxiaoqiang zhao     .instance_init = mpc8xxx_gpio_initfn,
216228aa992SAlexander Graf     .class_init    = mpc8xxx_gpio_class_init,
217228aa992SAlexander Graf };
218228aa992SAlexander Graf 
219228aa992SAlexander Graf static void mpc8xxx_gpio_register_types(void)
220228aa992SAlexander Graf {
221228aa992SAlexander Graf     type_register_static(&mpc8xxx_gpio_info);
222228aa992SAlexander Graf }
223228aa992SAlexander Graf 
224228aa992SAlexander Graf type_init(mpc8xxx_gpio_register_types)
225