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