xref: /openbmc/qemu/hw/gpio/mpc8xxx.c (revision 63dc36944383f70f1c7a20f6104966d8560300fa)
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"
26db1015e9SEduardo Habkost #include "qom/object.h"
27228aa992SAlexander Graf 
28228aa992SAlexander Graf #define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio"
298063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(MPC8XXXGPIOState, MPC8XXX_GPIO)
30228aa992SAlexander Graf 
31db1015e9SEduardo Habkost struct MPC8XXXGPIOState {
32228aa992SAlexander Graf     SysBusDevice parent_obj;
33228aa992SAlexander Graf 
34228aa992SAlexander Graf     MemoryRegion iomem;
35228aa992SAlexander Graf     qemu_irq irq;
36228aa992SAlexander Graf     qemu_irq out[32];
37228aa992SAlexander Graf 
38228aa992SAlexander Graf     uint32_t dir;
39228aa992SAlexander Graf     uint32_t odr;
40228aa992SAlexander Graf     uint32_t dat;
41228aa992SAlexander Graf     uint32_t ier;
42228aa992SAlexander Graf     uint32_t imr;
43228aa992SAlexander Graf     uint32_t icr;
44db1015e9SEduardo Habkost };
45228aa992SAlexander Graf 
46228aa992SAlexander Graf static const VMStateDescription vmstate_mpc8xxx_gpio = {
47228aa992SAlexander Graf     .name = "mpc8xxx_gpio",
48228aa992SAlexander Graf     .version_id = 1,
49228aa992SAlexander Graf     .minimum_version_id = 1,
503b9e779bSRichard Henderson     .fields = (const VMStateField[]) {
51228aa992SAlexander Graf         VMSTATE_UINT32(dir, MPC8XXXGPIOState),
52228aa992SAlexander Graf         VMSTATE_UINT32(odr, MPC8XXXGPIOState),
53228aa992SAlexander Graf         VMSTATE_UINT32(dat, MPC8XXXGPIOState),
54228aa992SAlexander Graf         VMSTATE_UINT32(ier, MPC8XXXGPIOState),
55228aa992SAlexander Graf         VMSTATE_UINT32(imr, MPC8XXXGPIOState),
56228aa992SAlexander Graf         VMSTATE_UINT32(icr, MPC8XXXGPIOState),
57228aa992SAlexander Graf         VMSTATE_END_OF_LIST()
58228aa992SAlexander Graf     }
59228aa992SAlexander Graf };
60228aa992SAlexander Graf 
mpc8xxx_gpio_update(MPC8XXXGPIOState * s)61228aa992SAlexander Graf static void mpc8xxx_gpio_update(MPC8XXXGPIOState *s)
62228aa992SAlexander Graf {
63228aa992SAlexander Graf     qemu_set_irq(s->irq, !!(s->ier & s->imr));
64228aa992SAlexander Graf }
65228aa992SAlexander Graf 
mpc8xxx_gpio_read(void * opaque,hwaddr offset,unsigned size)66228aa992SAlexander Graf static uint64_t mpc8xxx_gpio_read(void *opaque, hwaddr offset,
67228aa992SAlexander Graf                                   unsigned size)
68228aa992SAlexander Graf {
69228aa992SAlexander Graf     MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
70228aa992SAlexander Graf 
71228aa992SAlexander Graf     if (size != 4) {
72228aa992SAlexander Graf         /* All registers are 32bit */
73228aa992SAlexander Graf         return 0;
74228aa992SAlexander Graf     }
75228aa992SAlexander Graf 
76228aa992SAlexander Graf     switch (offset) {
77228aa992SAlexander Graf     case 0x0: /* Direction */
78228aa992SAlexander Graf         return s->dir;
79228aa992SAlexander Graf     case 0x4: /* Open Drain */
80228aa992SAlexander Graf         return s->odr;
81228aa992SAlexander Graf     case 0x8: /* Data */
82228aa992SAlexander Graf         return s->dat;
83228aa992SAlexander Graf     case 0xC: /* Interrupt Event */
84228aa992SAlexander Graf         return s->ier;
85228aa992SAlexander Graf     case 0x10: /* Interrupt Mask */
86228aa992SAlexander Graf         return s->imr;
87228aa992SAlexander Graf     case 0x14: /* Interrupt Control */
88228aa992SAlexander Graf         return s->icr;
89228aa992SAlexander Graf     default:
90228aa992SAlexander Graf         return 0;
91228aa992SAlexander Graf     }
92228aa992SAlexander Graf }
93228aa992SAlexander Graf 
mpc8xxx_write_data(MPC8XXXGPIOState * s,uint32_t new_data)94228aa992SAlexander Graf static void mpc8xxx_write_data(MPC8XXXGPIOState *s, uint32_t new_data)
95228aa992SAlexander Graf {
96228aa992SAlexander Graf     uint32_t old_data = s->dat;
97228aa992SAlexander Graf     uint32_t diff = old_data ^ new_data;
98228aa992SAlexander Graf     int i;
99228aa992SAlexander Graf 
100228aa992SAlexander Graf     for (i = 0; i < 32; i++) {
101228aa992SAlexander Graf         uint32_t mask = 0x80000000 >> i;
102228aa992SAlexander Graf         if (!(diff & mask)) {
103228aa992SAlexander Graf             continue;
104228aa992SAlexander Graf         }
105228aa992SAlexander Graf 
106228aa992SAlexander Graf         if (s->dir & mask) {
107228aa992SAlexander Graf             /* Output */
108228aa992SAlexander Graf             qemu_set_irq(s->out[i], (new_data & mask) != 0);
109228aa992SAlexander Graf         }
110228aa992SAlexander Graf     }
111228aa992SAlexander Graf 
112228aa992SAlexander Graf     s->dat = new_data;
113228aa992SAlexander Graf }
114228aa992SAlexander Graf 
mpc8xxx_gpio_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)115228aa992SAlexander Graf static void mpc8xxx_gpio_write(void *opaque, hwaddr offset,
116228aa992SAlexander Graf                         uint64_t value, unsigned size)
117228aa992SAlexander Graf {
118228aa992SAlexander Graf     MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
119228aa992SAlexander Graf 
120228aa992SAlexander Graf     if (size != 4) {
121228aa992SAlexander Graf         /* All registers are 32bit */
122228aa992SAlexander Graf         return;
123228aa992SAlexander Graf     }
124228aa992SAlexander Graf 
125228aa992SAlexander Graf     switch (offset) {
126228aa992SAlexander Graf     case 0x0: /* Direction */
127228aa992SAlexander Graf         s->dir = value;
128228aa992SAlexander Graf         break;
129228aa992SAlexander Graf     case 0x4: /* Open Drain */
130228aa992SAlexander Graf         s->odr = value;
131228aa992SAlexander Graf         break;
132228aa992SAlexander Graf     case 0x8: /* Data */
133228aa992SAlexander Graf         mpc8xxx_write_data(s, value);
134228aa992SAlexander Graf         break;
135228aa992SAlexander Graf     case 0xC: /* Interrupt Event */
136228aa992SAlexander Graf         s->ier &= ~value;
137228aa992SAlexander Graf         break;
138228aa992SAlexander Graf     case 0x10: /* Interrupt Mask */
139228aa992SAlexander Graf         s->imr = value;
140228aa992SAlexander Graf         break;
141228aa992SAlexander Graf     case 0x14: /* Interrupt Control */
142228aa992SAlexander Graf         s->icr = value;
143228aa992SAlexander Graf         break;
144228aa992SAlexander Graf     }
145228aa992SAlexander Graf 
146228aa992SAlexander Graf     mpc8xxx_gpio_update(s);
147228aa992SAlexander Graf }
148228aa992SAlexander Graf 
mpc8xxx_gpio_reset(DeviceState * dev)149396781f6Sxiaoqiang zhao static void mpc8xxx_gpio_reset(DeviceState *dev)
150228aa992SAlexander Graf {
151396781f6Sxiaoqiang zhao     MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev);
152396781f6Sxiaoqiang zhao 
153228aa992SAlexander Graf     s->dir = 0;
154228aa992SAlexander Graf     s->odr = 0;
155228aa992SAlexander Graf     s->dat = 0;
156228aa992SAlexander Graf     s->ier = 0;
157228aa992SAlexander Graf     s->imr = 0;
158228aa992SAlexander Graf     s->icr = 0;
159228aa992SAlexander Graf }
160228aa992SAlexander Graf 
mpc8xxx_gpio_set_irq(void * opaque,int irq,int level)161228aa992SAlexander Graf static void mpc8xxx_gpio_set_irq(void * opaque, int irq, int level)
162228aa992SAlexander Graf {
163228aa992SAlexander Graf     MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
164228aa992SAlexander Graf     uint32_t mask;
165228aa992SAlexander Graf 
166228aa992SAlexander Graf     mask = 0x80000000 >> irq;
167228aa992SAlexander Graf     if ((s->dir & mask) == 0) {
168228aa992SAlexander Graf         uint32_t old_value = s->dat & mask;
169228aa992SAlexander Graf 
170228aa992SAlexander Graf         s->dat &= ~mask;
171228aa992SAlexander Graf         if (level)
172228aa992SAlexander Graf             s->dat |= mask;
173228aa992SAlexander Graf 
174228aa992SAlexander Graf         if (!(s->icr & irq) || (old_value && !level)) {
175228aa992SAlexander Graf             s->ier |= mask;
176228aa992SAlexander Graf         }
177228aa992SAlexander Graf 
178228aa992SAlexander Graf         mpc8xxx_gpio_update(s);
179228aa992SAlexander Graf     }
180228aa992SAlexander Graf }
181228aa992SAlexander Graf 
182228aa992SAlexander Graf static const MemoryRegionOps mpc8xxx_gpio_ops = {
183228aa992SAlexander Graf     .read = mpc8xxx_gpio_read,
184228aa992SAlexander Graf     .write = mpc8xxx_gpio_write,
185228aa992SAlexander Graf     .endianness = DEVICE_BIG_ENDIAN,
186228aa992SAlexander Graf };
187228aa992SAlexander Graf 
mpc8xxx_gpio_initfn(Object * obj)188396781f6Sxiaoqiang zhao static void mpc8xxx_gpio_initfn(Object *obj)
189228aa992SAlexander Graf {
190396781f6Sxiaoqiang zhao     DeviceState *dev = DEVICE(obj);
191396781f6Sxiaoqiang zhao     MPC8XXXGPIOState *s = MPC8XXX_GPIO(obj);
192396781f6Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
193228aa992SAlexander Graf 
194396781f6Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &mpc8xxx_gpio_ops,
195396781f6Sxiaoqiang zhao                           s, "mpc8xxx_gpio", 0x1000);
196228aa992SAlexander Graf     sysbus_init_mmio(sbd, &s->iomem);
197228aa992SAlexander Graf     sysbus_init_irq(sbd, &s->irq);
198228aa992SAlexander Graf     qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32);
199228aa992SAlexander Graf     qdev_init_gpio_out(dev, s->out, 32);
200228aa992SAlexander Graf }
201228aa992SAlexander Graf 
mpc8xxx_gpio_class_init(ObjectClass * klass,void * data)202228aa992SAlexander Graf static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data)
203228aa992SAlexander Graf {
204228aa992SAlexander Graf     DeviceClass *dc = DEVICE_CLASS(klass);
205228aa992SAlexander Graf 
206228aa992SAlexander Graf     dc->vmsd = &vmstate_mpc8xxx_gpio;
207e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, mpc8xxx_gpio_reset);
208228aa992SAlexander Graf }
209228aa992SAlexander Graf 
210*c267da0eSBernhard Beschow static const TypeInfo mpc8xxx_gpio_types[] = {
211*c267da0eSBernhard Beschow     {
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,
217*c267da0eSBernhard Beschow     },
218228aa992SAlexander Graf };
219228aa992SAlexander Graf 
220*c267da0eSBernhard Beschow DEFINE_TYPES(mpc8xxx_gpio_types)
221