diff options
author | Alexander Graf <agraf@suse.de> | 2014-10-01 15:52:12 +0200 |
---|---|---|
committer | Alexander Graf <agraf@suse.de> | 2014-11-04 23:26:12 +0100 |
commit | 228aa992fc5be408888c423b6a5b30daf18a96cf (patch) | |
tree | ff243ae76912ce9fdda9e71bc09061e2ed983450 /hw | |
parent | 81f194dd69756677cc36ff0827bf970f0f048914 (diff) |
PPC: Add MPC8XXX gpio controller
On e500 systems most SoCs implement a common GPIO controller that Linux
calls the "mpc8xxx" gpio controller. This patch adds an emulation model
for this device.
Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/gpio/Makefile.objs | 1 | ||||
-rw-r--r-- | hw/gpio/mpc8xxx.c | 217 |
2 files changed, 218 insertions, 0 deletions
diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs index 2c8b51f09a..1abcf17988 100644 --- a/hw/gpio/Makefile.objs +++ b/hw/gpio/Makefile.objs @@ -2,5 +2,6 @@ common-obj-$(CONFIG_MAX7310) += max7310.o common-obj-$(CONFIG_PL061) += pl061.o common-obj-$(CONFIG_PUV3) += puv3_gpio.o common-obj-$(CONFIG_ZAURUS) += zaurus.o +common-obj-$(CONFIG_E500) += mpc8xxx.o obj-$(CONFIG_OMAP) += omap_gpio.o diff --git a/hw/gpio/mpc8xxx.c b/hw/gpio/mpc8xxx.c new file mode 100644 index 0000000000..1aeaaaaf03 --- /dev/null +++ b/hw/gpio/mpc8xxx.c @@ -0,0 +1,217 @@ +/* + * GPIO Controller for a lot of Freescale SoCs + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Alexander Graf, <agraf@suse.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/sysbus.h" + +#define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio" +#define MPC8XXX_GPIO(obj) OBJECT_CHECK(MPC8XXXGPIOState, (obj), TYPE_MPC8XXX_GPIO) + +typedef struct MPC8XXXGPIOState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq irq; + qemu_irq out[32]; + + uint32_t dir; + uint32_t odr; + uint32_t dat; + uint32_t ier; + uint32_t imr; + uint32_t icr; +} MPC8XXXGPIOState; + +static const VMStateDescription vmstate_mpc8xxx_gpio = { + .name = "mpc8xxx_gpio", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(dir, MPC8XXXGPIOState), + VMSTATE_UINT32(odr, MPC8XXXGPIOState), + VMSTATE_UINT32(dat, MPC8XXXGPIOState), + VMSTATE_UINT32(ier, MPC8XXXGPIOState), + VMSTATE_UINT32(imr, MPC8XXXGPIOState), + VMSTATE_UINT32(icr, MPC8XXXGPIOState), + VMSTATE_END_OF_LIST() + } +}; + +static void mpc8xxx_gpio_update(MPC8XXXGPIOState *s) +{ + qemu_set_irq(s->irq, !!(s->ier & s->imr)); +} + +static uint64_t mpc8xxx_gpio_read(void *opaque, hwaddr offset, + unsigned size) +{ + MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque; + + if (size != 4) { + /* All registers are 32bit */ + return 0; + } + + switch (offset) { + case 0x0: /* Direction */ + return s->dir; + case 0x4: /* Open Drain */ + return s->odr; + case 0x8: /* Data */ + return s->dat; + case 0xC: /* Interrupt Event */ + return s->ier; + case 0x10: /* Interrupt Mask */ + return s->imr; + case 0x14: /* Interrupt Control */ + return s->icr; + default: + return 0; + } +} + +static void mpc8xxx_write_data(MPC8XXXGPIOState *s, uint32_t new_data) +{ + uint32_t old_data = s->dat; + uint32_t diff = old_data ^ new_data; + int i; + + for (i = 0; i < 32; i++) { + uint32_t mask = 0x80000000 >> i; + if (!(diff & mask)) { + continue; + } + + if (s->dir & mask) { + /* Output */ + qemu_set_irq(s->out[i], (new_data & mask) != 0); + } + } + + s->dat = new_data; +} + +static void mpc8xxx_gpio_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque; + + if (size != 4) { + /* All registers are 32bit */ + return; + } + + switch (offset) { + case 0x0: /* Direction */ + s->dir = value; + break; + case 0x4: /* Open Drain */ + s->odr = value; + break; + case 0x8: /* Data */ + mpc8xxx_write_data(s, value); + break; + case 0xC: /* Interrupt Event */ + s->ier &= ~value; + break; + case 0x10: /* Interrupt Mask */ + s->imr = value; + break; + case 0x14: /* Interrupt Control */ + s->icr = value; + break; + } + + mpc8xxx_gpio_update(s); +} + +static void mpc8xxx_gpio_reset(MPC8XXXGPIOState *s) +{ + s->dir = 0; + s->odr = 0; + s->dat = 0; + s->ier = 0; + s->imr = 0; + s->icr = 0; +} + +static void mpc8xxx_gpio_set_irq(void * opaque, int irq, int level) +{ + MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque; + uint32_t mask; + + mask = 0x80000000 >> irq; + if ((s->dir & mask) == 0) { + uint32_t old_value = s->dat & mask; + + s->dat &= ~mask; + if (level) + s->dat |= mask; + + if (!(s->icr & irq) || (old_value && !level)) { + s->ier |= mask; + } + + mpc8xxx_gpio_update(s); + } +} + +static const MemoryRegionOps mpc8xxx_gpio_ops = { + .read = mpc8xxx_gpio_read, + .write = mpc8xxx_gpio_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static int mpc8xxx_gpio_initfn(SysBusDevice *sbd) +{ + DeviceState *dev = DEVICE(sbd); + MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &mpc8xxx_gpio_ops, s, "mpc8xxx_gpio", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32); + qdev_init_gpio_out(dev, s->out, 32); + mpc8xxx_gpio_reset(s); + return 0; +} + +static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = mpc8xxx_gpio_initfn; + dc->vmsd = &vmstate_mpc8xxx_gpio; +} + +static const TypeInfo mpc8xxx_gpio_info = { + .name = TYPE_MPC8XXX_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MPC8XXXGPIOState), + .class_init = mpc8xxx_gpio_class_init, +}; + +static void mpc8xxx_gpio_register_types(void) +{ + type_register_static(&mpc8xxx_gpio_info); +} + +type_init(mpc8xxx_gpio_register_types) |