Now that we have GPIO emulation for exynos4210 SoC we can use it to properly hook up IRQ line to lan9215 controller on SMDK board.
Signed-off-by: Igor Mitsyanko <i.mitsya...@samsung.com> --- Makefile.target | 2 +- hw/exynos4210.c | 46 ++ hw/exynos4210.h | 64 +++ hw/exynos4210_gpio.c | 1117 ++++++++++++++++++++++++++++++++++++++++++++++++++ hw/exynos4_boards.c | 2 +- 5 files changed, 1229 insertions(+), 2 deletions(-) create mode 100644 hw/exynos4210_gpio.c diff --git a/Makefile.target b/Makefile.target index 3b309d9..7968120 100644 --- a/Makefile.target +++ b/Makefile.target @@ -348,7 +348,7 @@ obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o obj-arm-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o -obj-arm-y += exynos4210_i2c.o +obj-arm-y += exynos4210_i2c.o exynos4210_gpio.o obj-arm-y += arm_l2x0.o obj-arm-y += arm_mptimer.o a15mpcore.o obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o diff --git a/hw/exynos4210.c b/hw/exynos4210.c index 464f157..9a32eb8 100644 --- a/hw/exynos4210.c +++ b/hw/exynos4210.c @@ -35,6 +35,12 @@ /* MCT */ #define EXYNOS4210_MCT_BASE_ADDR 0x10050000 +/* GPIO */ +#define EXYNOS4210_GPIO1_BASE_ADDR 0x11400000 +#define EXYNOS4210_GPIO2_BASE_ADDR 0x11000000 +#define EXYNOS4210_GPIO2X_BASE_ADDR 0x11000C00 +#define EXYNOS4210_GPIO3_BASE_ADDR 0x03860000 + /* I2C */ #define EXYNOS4210_I2C_SHIFT 0x00010000 #define EXYNOS4210_I2C_BASE_ADDR 0x13860000 @@ -249,6 +255,46 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, s->irq_table[exynos4210_get_irq(35, 3)]); sysbus_mmio_map(busdev, 0, EXYNOS4210_MCT_BASE_ADDR); + /* GPIO */ + s->gpio1 = qdev_create(NULL, "exynos4210.gpio1"); + qdev_init_nofail(s->gpio1); + busdev = sysbus_from_qdev(s->gpio1); + sysbus_connect_irq(busdev, 0, s->irq_table[exynos4210_get_irq(24, 1)]); + sysbus_mmio_map(busdev, 0, EXYNOS4210_GPIO1_BASE_ADDR); + + s->gpio2 = qdev_create(NULL, "exynos4210.gpio2"); + qdev_init_nofail(s->gpio2); + busdev = sysbus_from_qdev(s->gpio2); + sysbus_connect_irq(busdev, 0, s->irq_table[exynos4210_get_irq(24, 0)]); + sysbus_mmio_map(busdev, 0, EXYNOS4210_GPIO2X_BASE_ADDR); + + s->gpio2x = qdev_create(NULL, "exynos4210.gpio2x"); + qdev_init_nofail(s->gpio2x); + busdev = sysbus_from_qdev(s->gpio2x); + sysbus_connect_irq(busdev, 0, s->irq_table[exynos4210_get_irq(40, 0)]); + sysbus_connect_irq(busdev, 1, s->irq_table[exynos4210_get_irq(41, 0)]); + sysbus_connect_irq(busdev, 2, s->irq_table[exynos4210_get_irq(42, 0)]); + sysbus_connect_irq(busdev, 3, s->irq_table[exynos4210_get_irq(43, 0)]); + sysbus_connect_irq(busdev, 4, s->irq_table[exynos4210_get_irq(37, 0)]); + sysbus_connect_irq(busdev, 5, s->irq_table[exynos4210_get_irq(37, 1)]); + sysbus_connect_irq(busdev, 6, s->irq_table[exynos4210_get_irq(37, 2)]); + sysbus_connect_irq(busdev, 7, s->irq_table[exynos4210_get_irq(37, 3)]); + sysbus_connect_irq(busdev, 8, s->irq_table[exynos4210_get_irq(38, 0)]); + sysbus_connect_irq(busdev, 9, s->irq_table[exynos4210_get_irq(38, 1)]); + sysbus_connect_irq(busdev, 10, s->irq_table[exynos4210_get_irq(38, 2)]); + sysbus_connect_irq(busdev, 11, s->irq_table[exynos4210_get_irq(38, 3)]); + sysbus_connect_irq(busdev, 12, s->irq_table[exynos4210_get_irq(38, 4)]); + sysbus_connect_irq(busdev, 13, s->irq_table[exynos4210_get_irq(38, 5)]); + sysbus_connect_irq(busdev, 14, s->irq_table[exynos4210_get_irq(38, 6)]); + sysbus_connect_irq(busdev, 15, s->irq_table[exynos4210_get_irq(38, 7)]); + sysbus_connect_irq(busdev, 16, s->irq_table[exynos4210_get_irq(39, 0)]); + sysbus_mmio_map(busdev, 0, EXYNOS4210_GPIO2X_BASE_ADDR); + + s->gpio3 = qdev_create(NULL, "exynos4210.gpio3"); + qdev_init_nofail(s->gpio3); + busdev = sysbus_from_qdev(s->gpio3); + sysbus_mmio_map(busdev, 0, EXYNOS4210_GPIO3_BASE_ADDR); + /*** I2C ***/ for (n = 0; n < EXYNOS4210_I2C_NUMBER; n++) { uint32_t addr = EXYNOS4210_I2C_BASE_ADDR + EXYNOS4210_I2C_SHIFT * n; diff --git a/hw/exynos4210.h b/hw/exynos4210.h index 07cc579..55b34a6 100644 --- a/hw/exynos4210.h +++ b/hw/exynos4210.h @@ -76,6 +76,69 @@ #define EXYNOS4210_I2C_NUMBER 9 +/* + * Exynos4210 general purpose input/output (GPIO) + */ + +/* GPIO part 1 port group numbers */ +#define EXYNOS4210_GPIO1_GPA0 0 +#define EXYNOS4210_GPIO1_GPA1 1 +#define EXYNOS4210_GPIO1_GPB 2 +#define EXYNOS4210_GPIO1_GPC0 3 +#define EXYNOS4210_GPIO1_GPC1 4 +#define EXYNOS4210_GPIO1_GPD0 5 +#define EXYNOS4210_GPIO1_GPD1 6 +#define EXYNOS4210_GPIO1_GPE0 7 +#define EXYNOS4210_GPIO1_GPE1 8 +#define EXYNOS4210_GPIO1_GPE2 9 +#define EXYNOS4210_GPIO1_GPE3 10 +#define EXYNOS4210_GPIO1_GPE4 11 +#define EXYNOS4210_GPIO1_GPF0 12 +#define EXYNOS4210_GPIO1_GPF1 13 +#define EXYNOS4210_GPIO1_GPF2 14 +#define EXYNOS4210_GPIO1_GPF3 15 +#define EXYNOS4210_GPIO1_ETC0 16 +#define EXYNOS4210_GPIO1_ETC1 17 + +/* GPIO part 2 port group numbers */ +#define EXYNOS4210_GPIO2_GPJ0 0 +#define EXYNOS4210_GPIO2_GPJ1 1 +#define EXYNOS4210_GPIO2_GPK0 2 +#define EXYNOS4210_GPIO2_GPK1 3 +#define EXYNOS4210_GPIO2_GPK2 4 +#define EXYNOS4210_GPIO2_GPK3 5 +#define EXYNOS4210_GPIO2_GPL0 6 +#define EXYNOS4210_GPIO2_GPL1 7 +#define EXYNOS4210_GPIO2_GPL2 8 +#define EXYNOS4210_GPIO2_GPY0 9 +#define EXYNOS4210_GPIO2_GPY1 10 +#define EXYNOS4210_GPIO2_GPY2 11 +#define EXYNOS4210_GPIO2_GPY3 12 +#define EXYNOS4210_GPIO2_GPY4 13 +#define EXYNOS4210_GPIO2_GPY5 14 +#define EXYNOS4210_GPIO2_GPY6 15 +#define EXYNOS4210_GPIO2_ETC6 16 +/* GPIO part 2 X port groups numbers */ +#define EXYNOS4210_GPIO2X_GPX0 0 +#define EXYNOS4210_GPIO2X_GPX1 1 +#define EXYNOS4210_GPIO2X_GPX2 2 +#define EXYNOS4210_GPIO2X_GPX3 3 + +/* GPIO part 3 port group numbers */ +#define EXYNOS4210_GPIO3_GPZ 0 + +/* Get GPIO line number from GPIO port group name and group pin number */ +#define EXYNOS4210_GPIO_MAX_PIN_IN_PORT 8 +#define EXYNOS4210_GPIO1_LINE(group, pin) \ + ((EXYNOS4210_GPIO1_##group * EXYNOS4210_GPIO_MAX_PIN_IN_PORT) + (pin)) +#define EXYNOS4210_GPIO2_LINE(group, pin) \ + ((EXYNOS4210_GPIO2_##group * EXYNOS4210_GPIO_MAX_PIN_IN_PORT) + (pin)) +#define EXYNOS4210_GPIO2X_LINE(group, pin) \ + ((EXYNOS4210_GPIO2X_##group * EXYNOS4210_GPIO_MAX_PIN_IN_PORT) + (pin)) +#define EXYNOS4210_GPIO3_LINE(group, pin) \ + ((EXYNOS4210_GPIO3_##group * EXYNOS4210_GPIO_MAX_PIN_IN_PORT) + (pin)) + + typedef struct Exynos4210Irq { qemu_irq int_combiner_irq[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ]; qemu_irq ext_combiner_irq[EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ]; @@ -98,6 +161,7 @@ typedef struct Exynos4210State { MemoryRegion boot_secondary; MemoryRegion bootreg_mem; i2c_bus *i2c_if[EXYNOS4210_I2C_NUMBER]; + DeviceState *gpio1, *gpio2, *gpio2x, *gpio3; } Exynos4210State; Exynos4210State *exynos4210_init(MemoryRegion *system_mem, diff --git a/hw/exynos4210_gpio.c b/hw/exynos4210_gpio.c new file mode 100644 index 0000000..5dd5387 --- /dev/null +++ b/hw/exynos4210_gpio.c @@ -0,0 +1,1117 @@ +/* + * Exynos4210 General Purpose Input/Output (GPIO) Emulation + * + * Copyright (C) 2012 Samsung Electronics Co Ltd. + * Maksim Kozlov, <m.koz...@samsung.com> + * Igor Mitsyanko, <i.mitsya...@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu-common.h" +#include "sysbus.h" +#include "qdev.h" +#include "irq.h" + +/* Debug messages configuration */ +#define EXYNOS4210_GPIO_DEBUG 0 + +#if EXYNOS4210_GPIO_DEBUG == 0 +#define DPRINT_L1(fmt, args...) do { } while (0) +#define DPRINT_L2(fmt, args...) do { } while (0) +#define DPRINT_ERROR(fmt, args...) do { } while (0) +#elif EXYNOS4210_GPIO_DEBUG == 1 +#define DPRINT_L1(fmt, args...) \ +do {fprintf(stderr, "QEMU GPIO: "fmt, ## args); } while (0) +#define DPRINT_L2(fmt, args...) do { } while (0) +#define DPRINT_ERROR(fmt, args...) \ +do {fprintf(stderr, "QEMU GPIO ERROR: "fmt, ## args); } while (0) +#else +#define DPRINT_L1(fmt, args...) \ +do {fprintf(stderr, "QEMU GPIO: "fmt, ## args); } while (0) +#define DPRINT_L2(fmt, args...) \ +do {fprintf(stderr, "QEMU GPIO: "fmt, ## args); } while (0) +#define DPRINT_ERROR(fmt, args...) \ +do {fprintf(stderr, "QEMU GPIO ERROR: "fmt, ## args); } while (0) +#endif + + +#define TYPE_EXYNOS4210_GPIO "exynos4210.gpio" +#define TYPE_EXYNOS4210_GPIO_1_2 "exynos4210.gpio-1_2" +#define TYPE_EXYNOS4210_GPIO_2X "exynos4210.gpio2x" +#define EXYNOS4210_GPIO(obj) \ +OBJECT_CHECK(Exynos4GpioState, (obj), TYPE_EXYNOS4210_GPIO) +#define EXYNOS4210_GPIO_1_2(obj) \ +OBJECT_CHECK(Exynos4Gpio12State, (obj), TYPE_EXYNOS4210_GPIO_1_2) +#define EXYNOS4210_GPIO2X(obj) \ +OBJECT_CHECK(Exynos4Gpio2XState, (obj), TYPE_EXYNOS4210_GPIO_2X) + +#define GPIO1_REGS_MEM_SIZE 0x0b54 +#define GPIO2_REGS_MEM_SIZE 0x0b38 +#define GPIO2X_REGS_MEM_SIZE 0x0350 +#define GPIO3_REGS_MEM_SIZE 0x0018 + +/* Port group registers offsets */ +#define GPIOCON 0x0000 /* Port Group Configuration Register */ +#define GPIODAT 0x0004 /* Port Group Data Register */ +#define GPIOPUD 0x0008 /* Port Group Pull-up/down Register */ +#define GPIODRV 0x000C /* Port Group Drive Strength Ctrl Reg */ +#define GPIOCONPDN 0x0010 /* Port Pwr Down Mode Config Reg */ +#define GPIOPUDPDN 0x0014 /* Port Pwr Down Mode Pullup/down Reg */ + +/* GPIO pin functions */ +#define GPIOCON_IN 0x0 /* input pin */ +#define GPIOCON_OUT 0x1 /* output pin */ +#define GPIOCON_EXTINT 0xf /* external interrupt pin */ + +/* External interrupts signaling methods */ +#define GPIO_INTCON_LOW 0x0 /* interrupt on low level */ +#define GPIO_INTCON_HIGH 0x1 /* interrupt on high level */ +#define GPIO_INTCON_FALL 0x2 /* interrupt on falling edge */ +#define GPIO_INTCON_RISE 0x3 /* interrupt on rising edge */ +#define GPIO_INTCON_FALLRISE 0x4 /* interrupt on both edges */ + +/* + * Code assumes that each GPIO port group (GPA0, GPA1, e.t.c.) + * has 8 pins. When calculating GPIO line number to pass to function + * qdev_get_gpio_in() or qdev_connect_gpio_out(), you must assume the same, + * even though it's not true for real hardware. + */ +#define GPIO_MAX_PIN_IN_PORT 8 +#define GPIO_PULLUP_STATE 0x3 +#define GPIO_PORTGR_SIZE 0x20 +#define DIV_BY_PORTGR_SIZE(x) ((x) >> 5) +#define MOD_PORTGR_SIZE(x) ((x) & (GPIO_PORTGR_SIZE - 1)) +#define GPIO_NORM_PORT_END 0x0200 /* norm ports mem area end */ +#define GPIO_ETCPORT_END 0x0240 /* etc ports mem area end */ +#define GPIO_EXTINT_CON_START 0x0700 /* extint con mem area start */ +#define GPIO_EXTINT_FLT_START 0x0800 /* extint filter mem area start */ +#define GPIO_EXTINT_MASK_START 0x0900 /* extint mask mem area start */ +#define GPIO_EXTINT_PEND_START 0x0A00 /* extint pend mem area start */ +#define GPIO_EXTINT_SERVICE 0x0B08 /* Current Service Register */ +#define GPIO_EXTINT_SERVICE_PEND 0x0B0C /* Current Service Pending Reg */ +#define GPIO_EXTINT_GRPFIXPRI 0x0B10 /* Ext Int Fixed Priority Ctrl */ +#define GPIO_EXTINT_FIXPRI_START 0x0B14 /* extint fixpri mem area start */ + +/* GPIO part 1 specific defines */ +#define GPIO1_NORM_PORT_NUM 16 +#define GPIO1_ETC_PORT_NUM 2 +#define GPIO1_NUM_OF_PORTS (GPIO1_NORM_PORT_NUM + GPIO1_ETC_PORT_NUM) +#define GPIO1_PORTINT_NUM GPIO1_NORM_PORT_NUM +#define GPIO1_ETCPORT_START 0x0200 +#define GPIO1_EXTINT_CON_END 0x073C +#define GPIO1_EXTINT_FLT_END 0x087C +#define GPIO1_EXTINT_MASK_END 0x093C +#define GPIO1_EXTINT_PEND_END 0x0A3C +#define GPIO1_EXTINT_FIXPRI_END 0x0B50 + +/* GPIO part 2 specific defines */ +#define GPIO2_NORM_PORT_NUM 16 +#define GPIO2_ETC_PORT_NUM 1 +#define GPIO2_NUM_OF_PORTS (GPIO2_NORM_PORT_NUM + GPIO2_ETC_PORT_NUM) +#define GPIO2_PORTINT_NUM 9 +#define GPIO2_ETCPORT_START 0x0220 +#define GPIO2_EXTINT_CON_END 0x0724 +#define GPIO2_EXTINT_FLT_END 0x0848 +#define GPIO2_EXTINT_MASK_END 0x0924 +#define GPIO2_EXTINT_PEND_END 0x0A24 +#define GPIO2_EXTINT_FIXPRI_END 0x0B38 + +/* GPIO part2 XPORT specific defines + * In Exynos documentation X ports are a part of GPIO part2, but we separate + * them to simplify implementation */ +#define GPIO2_X_PORT_NUM 4 +#define GPIO2_X_PORTINT_NUM GPIO2_X_PORT_NUM +#define GPIO2_X_PORT_IRQ_NUM 17 +#define GPIO2_XPORT_END 0x0080 +#define GPIO2_WKPINT_CON_START 0x0200 +#define GPIO2_WKPINT_CON_END 0x0210 +#define GPIO2_WKPINT_FLT_START 0x0280 +#define GPIO2_WKPINT_FLT_END 0x02A0 +#define GPIO2_WKPINT_MASK_START 0x0300 +#define GPIO2_WKPINT_MASK_END 0x0310 +#define GPIO2_WKPINT_PEND_START 0x0340 +#define GPIO2_WKPINT_PEND_END 0x0350 + +/* GPIO part 3 specific defines */ +#define GPIO3_NUM_OF_PORTS 1 +#define GPIO3_NORM_PORT_END 0x0020 + +typedef enum { + GPIO_PART2X = 0, + GPIO_PART1, + GPIO_PART2, + GPIO_PART3, +} Exynos4210GpioPart; + +typedef struct Exynos4PortGroup { + uint32_t con; /* configuration register */ + uint32_t dat; /* data register */ + uint32_t pud; /* pull-up/down register */ + uint32_t drv; /* drive strength control register */ + uint32_t conpdn; /* configuration register in power down mode */ + uint32_t pudpdn; /* pull-up/down register in power down mode */ + + const char *name; /* port specific name */ + const uint32_t def_con; /* default value for configuration register */ + const uint32_t def_pud; /* default value for pull-up/down register */ + const uint32_t def_drv; /* default value for drive strength control */ +} Exynos4PortGroup; + +static const VMStateDescription exynos4_gpio_portgroup_vmstate = { + .name = "exynos4210.gpio-port", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(con, Exynos4PortGroup), + VMSTATE_UINT32(dat, Exynos4PortGroup), + VMSTATE_UINT32(pud, Exynos4PortGroup), + VMSTATE_UINT32(drv, Exynos4PortGroup), + VMSTATE_UINT32(conpdn, Exynos4PortGroup), + VMSTATE_UINT32(pudpdn, Exynos4PortGroup), + VMSTATE_END_OF_LIST() + } +}; + +typedef struct Exynos4PortIntState { + uint32_t con; /* configuration register */ + uint32_t fltcon[2]; /* filter configuraton registers 1,2 */ + uint32_t mask; /* mask register */ + uint32_t pend; /* interrupt pending register */ + uint32_t fixpri; /* fixed priority control register */ + + const uint32_t def_mask; /* default value for mask register */ + const uint8_t int_line_num; /* external interrupt line number */ +} Exynos4PortIntState; + +static const VMStateDescription exynos4_gpio_portint_vmstate = { + .name = "exynos4210.gpio-int", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(con, Exynos4PortIntState), + VMSTATE_UINT32(con, Exynos4PortIntState), + VMSTATE_UINT32_ARRAY(fltcon, Exynos4PortIntState, 2), + VMSTATE_UINT32(mask, Exynos4PortIntState), + VMSTATE_UINT32(pend, Exynos4PortIntState), + VMSTATE_UINT32(fixpri, Exynos4PortIntState), + VMSTATE_END_OF_LIST() + } +}; + +static Exynos4PortGroup gpio1_ports[GPIO1_NUM_OF_PORTS] = { + { .name = "A0", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, + { .name = "A1", .def_con = 0, .def_pud = 0x0555, .def_drv = 0 }, + { .name = "B", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, + { .name = "C0", .def_con = 0, .def_pud = 0x0155, .def_drv = 0 }, + { .name = "C1", .def_con = 0, .def_pud = 0x0155, .def_drv = 0 }, + { .name = "D0", .def_con = 0, .def_pud = 0x0055, .def_drv = 0 }, + { .name = "D1", .def_con = 0, .def_pud = 0x0055, .def_drv = 0 }, + { .name = "E0", .def_con = 0, .def_pud = 0x0155, .def_drv = 0 }, + { .name = "E1", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, + { .name = "E2", .def_con = 0, .def_pud = 0x0555, .def_drv = 0 }, + { .name = "E3", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, + { .name = "E4", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, + { .name = "F0", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, + { .name = "F1", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, + { .name = "F2", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, + { .name = "F3", .def_con = 0, .def_pud = 0x0555, .def_drv = 0 }, + { .name = "ETC0", .def_con = 0, .def_pud = 0x0400, .def_drv = 0 }, + { .name = "ETC1", .def_con = 0, .def_pud = 0x005, .def_drv = 0 }, +}; + +/* All pins of all port groups of GPIO part1 share single GPIO IRQ line */ +static Exynos4PortIntState gpio1_ports_interrupts[GPIO1_PORTINT_NUM] = { + { .int_line_num = 1, .def_mask = 0x000000FF }, + { .int_line_num = 2, .def_mask = 0x0000003F }, + { .int_line_num = 3, .def_mask = 0x000000FF }, + { .int_line_num = 4, .def_mask = 0x0000001F }, + { .int_line_num = 5, .def_mask = 0x0000001F }, + { .int_line_num = 6, .def_mask = 0x0000000F }, + { .int_line_num = 7, .def_mask = 0x0000000F }, + { .int_line_num = 8, .def_mask = 0x0000001F }, + { .int_line_num = 9, .def_mask = 0x000000FF }, + { .int_line_num = 10, .def_mask = 0x0000003F }, + { .int_line_num = 11, .def_mask = 0x000000FF }, + { .int_line_num = 12, .def_mask = 0x000000FF }, + { .int_line_num = 13, .def_mask = 0x000000FF }, + { .int_line_num = 14, .def_mask = 0x000000FF }, + { .int_line_num = 15, .def_mask = 0x000000FF }, + { .int_line_num = 16, .def_mask = 0x0000003F }, +}; + +static Exynos4PortGroup gpio2_ports[GPIO2_NUM_OF_PORTS] = { + { .name = "J0", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, + { .name = "J1", .def_con = 0, .def_pud = 0x0155, .def_drv = 0 }, + { .name = "K0", .def_con = 0, .def_pud = 0x1555, .def_drv = 0x002AAA }, + { .name = "K1", .def_con = 0, .def_pud = 0x1555, .def_drv = 0 }, + { .name = "K2", .def_con = 0, .def_pud = 0x1555, .def_drv = 0 }, + { .name = "K3", .def_con = 0, .def_pud = 0x1555, .def_drv = 0 }, + { .name = "L0", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, + { .name = "L1", .def_con = 0, .def_pud = 0x0015, .def_drv = 0 }, + { .name = "L2", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, + { .name = "Y0", .def_con = 0x00225522, .def_pud = 0, .def_drv = 0x000AAA }, + { .name = "Y1", .def_con = 0x00002222, .def_pud = 0, .def_drv = 0x0000AA }, + { .name = "Y2", .def_con = 0x00255555, .def_pud = 0, .def_drv = 0x000AAA }, + { .name = "Y3", .def_con = 0x22222222, .def_pud = 0, .def_drv = 0x00AAAA }, + { .name = "Y4", .def_con = 0x22222222, .def_pud = 0, .def_drv = 0x00AAAA }, + { .name = "Y5", .def_con = 0x22222222, .def_pud = 0, .def_drv = 0x00AAAA }, + { .name = "Y6", .def_con = 0x22222222, .def_pud = 0, .def_drv = 0x00AAAA }, + { .name = "ETC6", .def_con = 0, .def_pud = 0xC0C0, .def_drv = 0 }, +}; + +/* All pins of all port groups of GPIO part2 share single interrupt line */ +static Exynos4PortIntState gpio2_ports_interrupts[GPIO2_PORTINT_NUM] = { + { .int_line_num = 21, .def_mask = 0x000000FF }, + { .int_line_num = 22, .def_mask = 0x0000001F }, + { .int_line_num = 23, .def_mask = 0x0000007F }, + { .int_line_num = 24, .def_mask = 0x0000007F }, + { .int_line_num = 25, .def_mask = 0x0000007F }, + { .int_line_num = 26, .def_mask = 0x0000007F }, + { .int_line_num = 27, .def_mask = 0x000000FF }, + { .int_line_num = 28, .def_mask = 0x00000007 }, + { .int_line_num = 29, .def_mask = 0x000000FF }, +}; + +static Exynos4PortGroup gpio2x_ports[GPIO2_X_PORT_NUM] = { + { .name = "X0", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, + { .name = "X1", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, + { .name = "X2", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, + { .name = "X3", .def_con = 0, .def_pud = 0x5555, .def_drv = 0 }, +}; + +/* Ports X0 and X1 have separate external irq lines for every pin. + * All pins of ports X2 and X3 share single external irq line */ +static Exynos4PortIntState gpio2x_ports_interrupts[GPIO2_X_PORTINT_NUM] = { + { .int_line_num = 0, .def_mask = 0x000000FF }, + { .int_line_num = 1, .def_mask = 0x000000FF }, + { .int_line_num = 2, .def_mask = 0x000000FF }, + { .int_line_num = 3, .def_mask = 0x000000FF }, +}; + +static Exynos4PortGroup gpio3_ports = { + .name = "Z", .def_con = 0, .def_pud = 0x1555, .def_drv = 0 +}; + +typedef struct Exynos4GpioState { + SysBusDevice busdev; + MemoryRegion iomem; + Exynos4210GpioPart part; + qemu_irq *out_cb; /* Callbacks on writing to GPIO line */ + Exynos4PortGroup *ports; + Exynos4PortIntState *port_ints; + uint16_t num_of_ports; + uint16_t num_of_portints; +} Exynos4GpioState; + +static const VMStateDescription exynos4_gpio_vmstate = { + .name = "exynos4210.gpio", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_VARRAY_POINTER_UINT16(ports, Exynos4GpioState, + num_of_ports, exynos4_gpio_portgroup_vmstate, Exynos4PortGroup), + VMSTATE_STRUCT_VARRAY_POINTER_UINT16(port_ints, Exynos4GpioState, + num_of_portints, exynos4_gpio_portint_vmstate, Exynos4PortIntState), + VMSTATE_END_OF_LIST() + } +}; + +typedef struct Exynos4Gpio12State { + Exynos4GpioState gpio_common; + qemu_irq irq_gpio; /* GPIO interrupt request */ + /* Group index and pin # of highest priority currently pended irq line */ + uint32_t extint_serv; + uint32_t extint_serv_pend; + /* Index of highest priority interrupt group */ + uint32_t extint_grpfixpri; +} Exynos4Gpio12State; + +typedef struct Exynos4Gpio2XState { + Exynos4GpioState gpio_common; + qemu_irq ext_irq[GPIO2_X_PORT_IRQ_NUM]; +} Exynos4Gpio2XState; + +static inline void exynos4_gpio_reset_portgr(Exynos4PortGroup *group) +{ + group->con = group->def_con; + group->dat = 0; + group->pud = group->def_pud; + group->drv = group->def_drv; + group->conpdn = 0; + group->pudpdn = 0; +} + +static inline void exynos4_gpio_reset_portint(Exynos4PortIntState *pint) +{ + pint->con = 0; + pint->mask = pint->def_mask; + pint->fltcon[0] = 0; + pint->fltcon[1] = 0; + pint->pend = 0; + pint->fixpri = 0; +} + +static void exynos4_gpio_reset(DeviceState *dev) +{ + Exynos4GpioState *g = EXYNOS4210_GPIO(dev); + unsigned i; + + DPRINT_L2("GPIO PART%u RESET\n", g->part); + + switch (g->part) { + case GPIO_PART1: case GPIO_PART2: + qemu_irq_lower(EXYNOS4210_GPIO_1_2(g)->irq_gpio); + EXYNOS4210_GPIO_1_2(g)->extint_serv = 0; + EXYNOS4210_GPIO_1_2(g)->extint_grpfixpri = 0; + EXYNOS4210_GPIO_1_2(g)->extint_serv_pend = 0; + break; + case GPIO_PART2X: + for (i = 0; i < GPIO2_X_PORT_IRQ_NUM; i++) { + qemu_irq_lower(EXYNOS4210_GPIO2X(g)->ext_irq[i]); + } + break; + default: + break; + } + + for (i = 0; i < g->num_of_portints; i++) { + exynos4_gpio_reset_portint(&g->port_ints[i]); + } + + for (i = 0; i < g->num_of_ports; i++) { + exynos4_gpio_reset_portgr(&g->ports[i]); + } +} + +static uint32_t +exynos4_gpio_portgr_read(Exynos4PortGroup *group, target_phys_addr_t off) +{ + DPRINT_L2("Port group GP%s read off 0x%x\n", group->name, off); + + switch (off) { + case GPIOCON: + return group->con; + case GPIODAT: + return group->dat & 0xff; + case GPIOPUD: + return group->pud; + case GPIODRV: + return group->drv; + case GPIOCONPDN: + return group->conpdn; + case GPIOPUDPDN: + return group->pudpdn; + default: + DPRINT_ERROR("Port group GP%s bad read off 0x%x\n", group->name, off); + return 0xBAADBAAD; + } +} + +static uint32_t +exynos4_etc_portgroup_read(Exynos4PortGroup *group, target_phys_addr_t off) +{ + DPRINT_L1("Port group %s read off 0x%x\n", group->name, off); + + switch (off) { + case GPIOPUD: + return group->pud; + case GPIODRV: + return group->drv; + default: + DPRINT_ERROR("Port group %s bad read off 0x%x\n", group->name, off); + return 0xBAADBAAD; + } +} + +static void exynos4_etc_portgroup_write(Exynos4PortGroup *group, + target_phys_addr_t off, uint32_t value) +{ + DPRINT_L1("Port group %s write: off 0x%x = %u(0x%x)\n", + group->name, off, value, value); + + switch (off) { + case GPIOPUD: + group->pud = value; + break; + case GPIODRV: + group->drv = value; + break; + default: + DPRINT_ERROR("Port group %s bad write: off 0x%x = %u(0x%x)\n", + group->name, off, value, value); + break; + } +} + +/* Returns index of currently pended external interrupt line with highest + * priority 1..MAX_INDEX for GPIO parts 1 and 2 */ +static unsigned int +gpio_group_get_highest_prio(Exynos4GpioState *g) +{ + Exynos4Gpio12State *g12 = EXYNOS4210_GPIO_1_2(g); + unsigned i; + const unsigned num_of_prio = g->num_of_portints; + uint8_t highest_prio = g12->extint_grpfixpri; + + if (highest_prio == 0) { + /* Zero extint_grpfixpri is equal to extint_grpfixpri == 1 */ + highest_prio = 1; + } + + /* Corresponding group index is less then EXTINT index by one */ + highest_prio--; + for (i = 0; i < num_of_prio; i++) { + if (g->port_ints[highest_prio].pend & + ~g->port_ints[highest_prio].mask) { + return highest_prio + 1; + } + if (++highest_prio >= num_of_prio) { + highest_prio = 0; + } + } + + return 0; +} + +/* Returns line number of highest pended external irq within portgroup */ +static unsigned int gpio_get_highest_intnum(Exynos4GpioState *g, unsigned group) +{ + uint8_t highest_prio = g->port_ints[group].fixpri; + uint8_t pend = g->port_ints[group].pend; + unsigned i; + + for (i = 0; i < GPIO_MAX_PIN_IN_PORT; i++) { + if (pend & (1 << highest_prio)) { + return highest_prio; + } + if (++highest_prio >= GPIO_MAX_PIN_IN_PORT) { + highest_prio = 0; + } + } + + return 0; +} + +/* Clear GPIO IRQ if none of gpio interrupt lines are pended */ +static void exynos4_gpioirq_update(Exynos4GpioState *g) +{ + Exynos4Gpio12State *g12 = EXYNOS4210_GPIO_1_2(g); + unsigned pend_prio = gpio_group_get_highest_prio(g); + + if (pend_prio == 0) { + DPRINT_L2("GPIO part %u interrupt cleared\n", g->part); + g12->extint_serv = g12->extint_serv_pend = 0; + qemu_irq_lower(g12->irq_gpio); + } else if (pend_prio != ((g12->extint_serv >> 3) & 0x1f)) { + g12->extint_serv = (pend_prio << 3) | + gpio_get_highest_intnum(g, pend_prio - 1); + g12->extint_serv_pend = g->port_ints[pend_prio - 1].pend; + } +} + +static void exynos4_gpio_portgr_write(Exynos4GpioState *g, int idx, + unsigned int off, uint32_t value) +{ + Exynos4PortGroup *group = &g->ports[idx]; + unsigned pin; + uint32_t diff, old_con, new_dat; + + DPRINT_L1("Port group GP%s write: off 0x%x = %u(0x%x)\n", + group->name, off, value, value); + + switch (off) { + case GPIOCON: + old_con = group->con; + group->con = value; + for (pin = 0; pin < GPIO_MAX_PIN_IN_PORT; pin++) { + if (((value >> pin * 4) & 0xf) != ((old_con >> pin * 4) & 0xf)) { + qemu_irq_raise(g->out_cb[idx * GPIO_MAX_PIN_IN_PORT + pin]); + } + } + break; + case GPIODAT: + new_dat = group->dat; + value &= (1 << GPIO_MAX_PIN_IN_PORT) - 1; + for (pin = 0; pin < GPIO_MAX_PIN_IN_PORT; pin++) { + if (((group->con >> pin * 4) & 0xf) == GPIOCON_OUT) { + new_dat = (new_dat & ~(1 << pin)) | (value & (1 << pin)); + } + } + diff = group->dat ^ new_dat; + group->dat = new_dat & ((1 << GPIO_MAX_PIN_IN_PORT) - 1); + while ((pin = ffs(diff))) { + pin--; + DPRINT_L2("Port group GP%s pin #%u write callback %s raised\n", + group->name, pin, (g->out_cb[idx * + GPIO_MAX_PIN_IN_PORT + pin] ? "" : "wasn't")); + qemu_set_irq(g->out_cb[idx * GPIO_MAX_PIN_IN_PORT + pin], + (group->dat & (1 << pin))); + diff &= ~(1 << pin); + } + break; + case GPIOPUD: + for (pin = 0; pin < GPIO_MAX_PIN_IN_PORT; pin++) { + if (((value >> 2 * pin) & 0x3) == GPIO_PULLUP_STATE && + ((group->pud >> 2 * pin) & 0x3) != GPIO_PULLUP_STATE) { + group->dat |= 1 << pin; + } + } + group->pud = value; + break; + case GPIODRV: + group->drv = value; + break; + case GPIOCONPDN: + group->conpdn = value; + break; + case GPIOPUDPDN: + group->pudpdn = value; + break; + default: + DPRINT_ERROR("Port group GP%s bad write: offset 0x%x = %u(0x%x)\n", + group->name, off, value, value); + break; + } +} + +static void exynos4_gpio_set_cb(void *opaque, int line, int level) +{ + Exynos4GpioState *g = (Exynos4GpioState *)opaque; + const unsigned group_num = line >> 3; + const unsigned pin = line & (GPIO_MAX_PIN_IN_PORT - 1); + bool irq_is_triggered = false; + const uint32_t dat_prev = g->ports[group_num].dat & (1 << pin); + const unsigned pin_func = (g->ports[group_num].con >> pin * 4) & 0xf; + + /* Check that corresponding pin is in input state */ + if (pin_func != GPIOCON_EXTINT && pin_func != GPIOCON_IN) { + return; + } + + DPRINT_L1("Input pin GPIO%s_PIN%u %s by external device\n", + g->ports[group_num].name, pin, (level ? "set" : "cleared")); + /* Set new value on corresponding gpio pin */ + (level) ? (g->ports[group_num].dat |= (1 << pin)) : + (g->ports[group_num].dat &= ~(1 << pin)); + + /* Check that external interrupt function is active for this pin */ + if (pin_func != GPIOCON_EXTINT || group_num >= g->num_of_portints) { + return; + } + + /* Do nothing if corresponding interrupt line is masked or already pended */ + if ((g->port_ints[group_num].mask & (1 << pin)) || + (g->port_ints[group_num].pend & (1 << pin))) { + return; + } + + /* Get interrupt line signaling method */ + switch ((g->port_ints[group_num].con >> (pin * 4)) & 7) { + case GPIO_INTCON_LOW: + irq_is_triggered = !level; + break; + case GPIO_INTCON_HIGH: + irq_is_triggered = !!level; + break; + case GPIO_INTCON_FALL: + irq_is_triggered = dat_prev && !(g->ports[group_num].dat & (1 << pin)); + break; + case GPIO_INTCON_RISE: + irq_is_triggered = !dat_prev && (g->ports[group_num].dat & (1 << pin)); + break; + case GPIO_INTCON_FALLRISE: + irq_is_triggered = + (dat_prev && !(g->ports[group_num].dat & (1 << pin))) || + (!dat_prev && (g->ports[group_num].dat & (1 << pin))); + break; + default: + DPRINT_ERROR("GPIO PART%u: unknown triggering method of EXT_IRQ_%u\n", + g->part, g->port_ints[group_num].int_line_num); + break; + } + + if (irq_is_triggered) { + g->port_ints[group_num].pend |= 1 << pin; + if (g->part == GPIO_PART2X) { + unsigned irq = group_num * GPIO_MAX_PIN_IN_PORT + pin; + DPRINT_L1("IRQ_EINT%u raised\n", irq); + if (irq >= GPIO2_X_PORT_IRQ_NUM) { + irq = GPIO2_X_PORT_IRQ_NUM - 1; + } + qemu_irq_raise(EXYNOS4210_GPIO2X(g)->ext_irq[irq]); + } else { + Exynos4Gpio12State *g12 = EXYNOS4210_GPIO_1_2(g); + DPRINT_L1("GPIO_INT%u[PIN%u] raised and GPIO_P%u_IRQ raised\n", + g->port_ints[group_num].int_line_num, pin, g->part); + + if ((group_num + 1) == gpio_group_get_highest_prio(g)) { + g12->extint_serv = ((group_num + 1) << 3) | + gpio_get_highest_intnum(g, group_num); + g12->extint_serv_pend = g->port_ints[group_num].pend; + } + qemu_irq_raise(g12->irq_gpio); + } + } +} + +static uint64_t exynos4_gpio_read(void *opaque, target_phys_addr_t off, + unsigned size) +{ + Exynos4GpioState *g = (Exynos4GpioState *)opaque; + unsigned port_end, extint_con_start, extint_con_end; + unsigned extint_flt_start, extint_flt_end; + unsigned extint_mask_start, extint_mask_end; + unsigned extint_pend_start, extint_pend_end; + unsigned etcp_start_addr, etcp_start_idx, extint_pri_end; + unsigned idx; + + DPRINT_L2("GPIO%u read off 0x%x\n", g->part, (uint32_t)off); + + switch (g->part) { + case GPIO_PART2X: + port_end = GPIO2_XPORT_END; + extint_con_start = GPIO2_WKPINT_CON_START; + extint_con_end = GPIO2_WKPINT_CON_END; + extint_flt_start = GPIO2_WKPINT_FLT_START; + extint_flt_end = GPIO2_WKPINT_FLT_END; + extint_mask_start = GPIO2_WKPINT_MASK_START; + extint_mask_end = GPIO2_WKPINT_MASK_END; + extint_pend_start = GPIO2_WKPINT_PEND_START; + extint_pend_end = GPIO2_WKPINT_PEND_END; + etcp_start_addr = etcp_start_idx = extint_pri_end = 0; + break; + case GPIO_PART1: default: + port_end = GPIO_NORM_PORT_END; + extint_con_start = GPIO_EXTINT_CON_START; + extint_con_end = GPIO1_EXTINT_CON_END; + extint_flt_start = GPIO_EXTINT_FLT_START; + extint_flt_end = GPIO1_EXTINT_FLT_END; + extint_mask_start = GPIO_EXTINT_MASK_START; + extint_mask_end = GPIO1_EXTINT_MASK_END; + extint_pend_start = GPIO_EXTINT_PEND_START; + extint_pend_end = GPIO1_EXTINT_PEND_END; + etcp_start_addr = GPIO1_ETCPORT_START; + etcp_start_idx = GPIO1_NORM_PORT_NUM; + extint_pri_end = GPIO1_EXTINT_FIXPRI_END; + break; + case GPIO_PART2: + port_end = GPIO_NORM_PORT_END; + extint_con_start = GPIO_EXTINT_CON_START; + extint_con_end = GPIO2_EXTINT_CON_END; + extint_flt_start = GPIO_EXTINT_FLT_START; + extint_flt_end = GPIO2_EXTINT_FLT_END; + extint_mask_start = GPIO_EXTINT_MASK_START; + extint_mask_end = GPIO2_EXTINT_MASK_END; + extint_pend_start = GPIO_EXTINT_PEND_START; + extint_pend_end = GPIO2_EXTINT_PEND_END; + etcp_start_addr = GPIO2_ETCPORT_START; + etcp_start_idx = GPIO2_NORM_PORT_NUM; + extint_pri_end = GPIO2_EXTINT_FIXPRI_END; + break; + case GPIO_PART3: + if (off < GPIO3_NORM_PORT_END) { + idx = DIV_BY_PORTGR_SIZE(off); + return exynos4_gpio_portgr_read(&g->ports[idx], + MOD_PORTGR_SIZE(off)); + } + DPRINT_ERROR("GPIO part 3 bad read off 0x%x\n", off); + return 0xBAADBAAD; + } + + if (off < port_end) { + idx = DIV_BY_PORTGR_SIZE(off); + return exynos4_gpio_portgr_read(&g->ports[idx], MOD_PORTGR_SIZE(off)); + } else if (off >= extint_mask_start && off < extint_mask_end) { + idx = (off - extint_mask_start) >> 2; + DPRINT_L1("GPIO%u EXTINT%u_MASK register read\n", g->part, + g->port_ints[idx].int_line_num); + return g->port_ints[idx].mask; + } else if (off >= extint_pend_start && off < extint_pend_end) { + idx = (off - extint_pend_start) >> 2; + DPRINT_L1("GPIO%u EXTINT%u_PEND register read\n", g->part, + g->port_ints[idx].int_line_num); + return g->port_ints[idx].pend; + } else if (off >= extint_con_start && off < extint_con_end) { + idx = (off - extint_con_start) >> 2; + DPRINT_L1("GPIO%u EXTINT%u_CON register read\n", g->part, + g->port_ints[idx].int_line_num); + return g->port_ints[idx].con; + } else if (off >= extint_flt_start && off < extint_flt_end) { + unsigned i = ((off - extint_flt_start) >> 2) & 1; + idx = (off - extint_flt_start) >> 3; + DPRINT_L1("GPIO%u EXTINT%u_FLTCON%u register read\n", g->part, + g->port_ints[idx].int_line_num, i); + return g->port_ints[idx].fltcon[i]; + } else if (g->part == GPIO_PART2X) { + /* GPIO group X has no more registers */ + DPRINT_ERROR("GPIO group X bad read off 0x%x\n", (uint32_t)off); + return 0xBAADBAAD; + } else if (off == GPIO_EXTINT_SERVICE) { + DPRINT_L1("GPIO%u EXT_INT_SERVICE register read\n", g->part); + return EXYNOS4210_GPIO_1_2(g)->extint_serv; + } else if (off == GPIO_EXTINT_SERVICE_PEND) { + DPRINT_L1("GPIO%u EXT_INT_SERVICE_PEND register read\n", g->part); + return EXYNOS4210_GPIO_1_2(g)->extint_serv_pend; + } else if (off == GPIO_EXTINT_GRPFIXPRI) { + DPRINT_L1("GPIO%u EXT_INT_GRPFIXPRI register read\n", g->part); + return EXYNOS4210_GPIO_1_2(g)->extint_grpfixpri; + } else if (off >= GPIO_EXTINT_FIXPRI_START && off < extint_pri_end) { + idx = (off - GPIO_EXTINT_FIXPRI_START) >> 2; + DPRINT_L1("GPIO%u EXTINT%u_FIXPRI register read\n", g->part, + g->port_ints[idx].int_line_num); + return g->port_ints[idx].fixpri; + } else if (off >= etcp_start_addr && off < GPIO_ETCPORT_END) { + idx = DIV_BY_PORTGR_SIZE(off - etcp_start_addr) + + etcp_start_idx; + return exynos4_etc_portgroup_read(&g->ports[idx], + MOD_PORTGR_SIZE(off - etcp_start_addr)); + } + + DPRINT_ERROR("GPIO_P%u bad read off 0x%x\n", g->part, (uint32_t)off); + return 0xBAADBAAD; +} + +static void exynos4_gpio_write(void *opaque, target_phys_addr_t off, + uint64_t value, unsigned size) +{ + Exynos4GpioState *g = (Exynos4GpioState *)opaque; + unsigned int port_end, extint_con_start, extint_con_end; + unsigned int extint_flt_start, extint_flt_end; + unsigned int extint_mask_start, extint_mask_end; + unsigned int extint_pend_start, extint_pend_end; + unsigned int etcp_start_addr, etcp_start_idx, extint_pri_end; + unsigned idx; + + DPRINT_L2("GPIO%u write off 0x%x = %u(0x%x)\n", g->part, + (uint32_t)off, (uint32_t)value, (uint32_t)value); + + switch (g->part) { + case GPIO_PART2X: + port_end = GPIO2_XPORT_END; + extint_con_start = GPIO2_WKPINT_CON_START; + extint_con_end = GPIO2_WKPINT_CON_END; + extint_flt_start = GPIO2_WKPINT_FLT_START; + extint_flt_end = GPIO2_WKPINT_FLT_END; + extint_mask_start = GPIO2_WKPINT_MASK_START; + extint_mask_end = GPIO2_WKPINT_MASK_END; + extint_pend_start = GPIO2_WKPINT_PEND_START; + extint_pend_end = GPIO2_WKPINT_PEND_END; + etcp_start_addr = etcp_start_idx = extint_pri_end = 0; + break; + case GPIO_PART1: default: + port_end = GPIO_NORM_PORT_END; + extint_con_start = GPIO_EXTINT_CON_START; + extint_con_end = GPIO1_EXTINT_CON_END; + extint_flt_start = GPIO_EXTINT_FLT_START; + extint_flt_end = GPIO1_EXTINT_FLT_END; + extint_mask_start = GPIO_EXTINT_MASK_START; + extint_mask_end = GPIO1_EXTINT_MASK_END; + extint_pend_start = GPIO_EXTINT_PEND_START; + extint_pend_end = GPIO1_EXTINT_PEND_END; + etcp_start_addr = GPIO1_ETCPORT_START; + etcp_start_idx = GPIO1_NORM_PORT_NUM; + extint_pri_end = GPIO1_EXTINT_FIXPRI_END; + break; + case GPIO_PART2: + port_end = GPIO_NORM_PORT_END; + extint_con_start = GPIO_EXTINT_CON_START; + extint_con_end = GPIO2_EXTINT_CON_END; + extint_flt_start = GPIO_EXTINT_FLT_START; + extint_flt_end = GPIO2_EXTINT_FLT_END; + extint_mask_start = GPIO_EXTINT_MASK_START; + extint_mask_end = GPIO2_EXTINT_MASK_END; + extint_pend_start = GPIO_EXTINT_PEND_START; + extint_pend_end = GPIO2_EXTINT_PEND_END; + etcp_start_addr = GPIO2_ETCPORT_START; + etcp_start_idx = GPIO2_NORM_PORT_NUM; + extint_pri_end = GPIO2_EXTINT_FIXPRI_END; + break; + case GPIO_PART3: + if (off < GPIO3_NORM_PORT_END) { + idx = DIV_BY_PORTGR_SIZE(off); + exynos4_gpio_portgr_write(g, idx, MOD_PORTGR_SIZE(off), value); + } else { + DPRINT_ERROR("GPIO3 bad write off 0x%x = %u(0x%x)\n", + (uint32_t)off, (uint32_t)value, (uint32_t)value); + } + return; + } + + if (off < port_end) { + idx = DIV_BY_PORTGR_SIZE(off); + exynos4_gpio_portgr_write(g, idx, MOD_PORTGR_SIZE(off), value); + } else if (off >= extint_mask_start && off < extint_mask_end) { + idx = (off - extint_mask_start) >> 2; + DPRINT_L1("GPIO%u EXTINT%u_MASK register write = %u(0x%x)\n", g->part, + g->port_ints[idx].int_line_num, (uint32_t)value, (uint32_t)value); + g->port_ints[idx].mask = value; + } else if (off >= extint_pend_start && off < extint_pend_end) { + idx = (off - extint_pend_start) >> 2; + DPRINT_L1("GPIO%u EXTINT%u_PEND register write = %u(0x%x)\n", g->part, + g->port_ints[idx].int_line_num, (uint32_t)value, (uint32_t)value); + if (g->part == GPIO_PART2X) { + unsigned i, irq_n; + Exynos4Gpio2XState *g2 = EXYNOS4210_GPIO2X(g); + for (i = 0; i < GPIO_MAX_PIN_IN_PORT; i++) { + if ((g->port_ints[idx].pend & (1 << i)) && (value & (1 << i))) { + g->port_ints[idx].pend &= ~(1 << i); + irq_n = idx * GPIO_MAX_PIN_IN_PORT + i; + if (irq_n >= GPIO2_X_PORT_IRQ_NUM) { + irq_n = GPIO2_X_PORT_IRQ_NUM - 1; + } + qemu_irq_lower(g2->ext_irq[irq_n]); + } + } + } else { + g->port_ints[idx].pend &= ~value; + exynos4_gpioirq_update(g); + } + } else if (off >= extint_con_start && off < extint_con_end) { + idx = (off - extint_con_start) >> 2; + DPRINT_L1("GPIO%u EXTINT%u_CON register write = %u(0x%x)\n", g->part, + g->port_ints[idx].int_line_num, (uint32_t)value, (uint32_t)value); + g->port_ints[idx].con = value; + } else if (off >= extint_flt_start && off < extint_flt_end) { + unsigned i = ((off - extint_flt_start) >> 2) & 1; + idx = (off - extint_flt_start) >> 3; + DPRINT_L1("GPIO%u EXTINT%u_FLTCON%u reg write = %u(0x%x)\n", g->part, + g->port_ints[idx].int_line_num, i, (uint32_t)value, (uint32_t)value); + g->port_ints[idx].fltcon[i] = value; + } else if (g->part == GPIO_PART2X) { + DPRINT_ERROR("GPIO2 group X bad write off 0x%x = %u(0x%x)\n", + (uint32_t)off, (uint32_t)value, (uint32_t)value); + return; + } else if (off == GPIO_EXTINT_SERVICE) { + DPRINT_L1("GPIO%u EXT_INT_SERVICE register write = %u(0x%x)\n", + g->part, (uint32_t)value, (uint32_t)value); + EXYNOS4210_GPIO_1_2(g)->extint_serv = value; + } else if (off == GPIO_EXTINT_SERVICE_PEND) { + DPRINT_L1("GPIO%u EXT_INT_SERVICE_PEND register write = %u(0x%x)\n", + g->part, (uint32_t)value, (uint32_t)value); + EXYNOS4210_GPIO_1_2(g)->extint_serv_pend = value; + } else if (off == GPIO_EXTINT_GRPFIXPRI) { + DPRINT_L1("GPIO%u EXT_INT_GRPFIXPRI register write = %u(0x%x)\n", + g->part, (uint32_t)value, (uint32_t)value); + EXYNOS4210_GPIO_1_2(g)->extint_grpfixpri = value; + } else if (off >= GPIO_EXTINT_FIXPRI_START && off < extint_pri_end) { + idx = (off - GPIO_EXTINT_FIXPRI_START) >> 2; + DPRINT_L1("GPIO%u EXTINT%u_FIXPRI register write = %u(0x%x)\n", g->part, + g->port_ints[idx].int_line_num, (uint32_t)value, (uint32_t)value); + g->port_ints[idx].fixpri = value; + } else if (off >= etcp_start_addr && off < GPIO_ETCPORT_END) { + idx = etcp_start_idx + DIV_BY_PORTGR_SIZE(off - etcp_start_addr); + exynos4_etc_portgroup_write(&g->ports[idx], + MOD_PORTGR_SIZE(off - etcp_start_addr), value); + } else { + DPRINT_ERROR("GPIO%u bad write off 0x%x = %u(0x%x)\n", g->part, + (uint32_t)off, (uint32_t)value, (uint32_t)value); + } +} + +static const MemoryRegionOps exynos4_gpio_mmio_ops = { + .read = exynos4_gpio_read, + .write = exynos4_gpio_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription exynos4_gpio_1_2_vmstate = { + .name = "exynos4210.gpio-1_2", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(gpio_common, Exynos4Gpio12State, 1, + exynos4_gpio_vmstate, Exynos4GpioState), + VMSTATE_UINT32(extint_serv, Exynos4Gpio12State), + VMSTATE_UINT32(extint_serv_pend, Exynos4Gpio12State), + VMSTATE_UINT32(extint_grpfixpri, Exynos4Gpio12State), + VMSTATE_END_OF_LIST() + } +}; + +static void exynos4_gpio1_init(Object *obj) +{ + Exynos4GpioState *g = EXYNOS4210_GPIO(obj); + + g->part = GPIO_PART1; + g->ports = gpio1_ports; + g->port_ints = gpio1_ports_interrupts; + g->num_of_ports = sizeof(gpio1_ports) / sizeof(Exynos4PortGroup); + g->num_of_portints = + sizeof(gpio1_ports_interrupts) / sizeof(Exynos4PortIntState); +} + +static void exynos4_gpio2_init(Object *obj) +{ + Exynos4GpioState *g = EXYNOS4210_GPIO(obj); + + g->part = GPIO_PART2; + g->ports = gpio2_ports; + g->port_ints = gpio2_ports_interrupts; + g->num_of_ports = sizeof(gpio2_ports) / sizeof(Exynos4PortGroup); + g->num_of_portints = + sizeof(gpio2_ports_interrupts) / sizeof(Exynos4PortIntState); +} + +static void exynos4_gpio2x_init(Object *obj) +{ + Exynos4GpioState *g = EXYNOS4210_GPIO(obj); + + g->part = GPIO_PART2X; + g->ports = gpio2x_ports; + g->port_ints = gpio2x_ports_interrupts; + g->num_of_ports = sizeof(gpio2x_ports) / sizeof(Exynos4PortGroup); + g->num_of_portints = + sizeof(gpio2x_ports_interrupts) / sizeof(Exynos4PortIntState); +} + +static void exynos4_gpio3_init(Object *obj) +{ + Exynos4GpioState *g = EXYNOS4210_GPIO(obj); + + g->part = GPIO_PART3; + g->ports = &gpio3_ports; + g->num_of_ports = sizeof(gpio3_ports) / sizeof(Exynos4PortGroup); + g->num_of_portints = 0; +} + +static int exynos4_gpio_realize(SysBusDevice *busdev) +{ + Exynos4GpioState *g = EXYNOS4210_GPIO(busdev); + unsigned int mem_size, i; + const char *iomem_name; + + switch (g->part) { + case GPIO_PART1: + iomem_name = "exynos4210.gpio1"; + mem_size = GPIO1_REGS_MEM_SIZE; + sysbus_init_irq(busdev, &EXYNOS4210_GPIO_1_2(busdev)->irq_gpio); + break; + case GPIO_PART2: + iomem_name = "exynos4210.gpio2"; + mem_size = GPIO2_REGS_MEM_SIZE; + sysbus_init_irq(busdev, &EXYNOS4210_GPIO_1_2(busdev)->irq_gpio); + break; + case GPIO_PART2X: + iomem_name = "exynos4210.gpio2x"; + mem_size = GPIO2X_REGS_MEM_SIZE; + for (i = 0; i < GPIO2_X_PORT_IRQ_NUM; i++) { + Exynos4Gpio2XState *g2x = EXYNOS4210_GPIO2X(busdev); + sysbus_init_irq(busdev, &g2x->ext_irq[i]); + } + break; + case GPIO_PART3: + iomem_name = "exynos4210.gpio3"; + mem_size = GPIO3_REGS_MEM_SIZE; + break; + default: + hw_error("QEMU GPIO INIT ERROR: unknown GPIO part\n"); + } + + g->out_cb = g_new0(qemu_irq, g->num_of_ports * GPIO_MAX_PIN_IN_PORT); + qdev_init_gpio_in(DEVICE(busdev), exynos4_gpio_set_cb, + g->num_of_ports * GPIO_MAX_PIN_IN_PORT); + qdev_init_gpio_out(DEVICE(busdev), g->out_cb, + g->num_of_ports * GPIO_MAX_PIN_IN_PORT); + memory_region_init_io(&g->iomem, &exynos4_gpio_mmio_ops, g, + iomem_name, mem_size); + sysbus_init_mmio(busdev, &g->iomem); + return 0; +} + +static void exynos4_gpio_finalize(Object *obj) +{ + Exynos4GpioState *g = EXYNOS4210_GPIO(obj); + + if (g->out_cb) { + g_free(g->out_cb); + g->out_cb = NULL; + } +} + +static void exynos4_gpio_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(class); + SysBusDeviceClass *sbd = SYS_BUS_DEVICE_CLASS(class); + dc->reset = exynos4_gpio_reset; + dc->vmsd = &exynos4_gpio_vmstate; + sbd->init = exynos4_gpio_realize; +} + +static void exynos4_gpio_1_2_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(class); + + dc->vmsd = &exynos4_gpio_1_2_vmstate; +} + +static TypeInfo exynos4_gpio_type_info = { + .name = TYPE_EXYNOS4210_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4GpioState), + .class_init = exynos4_gpio_class_init, + .instance_finalize = exynos4_gpio_finalize, + .abstract = true +}; + +static TypeInfo exynos4_gpio_1_2_type_info = { + .name = TYPE_EXYNOS4210_GPIO_1_2, + .parent = TYPE_EXYNOS4210_GPIO, + .instance_size = sizeof(Exynos4Gpio12State), + .class_init = exynos4_gpio_1_2_class_init, + .abstract = true +}; + +static TypeInfo exynos4_gpio1_type_info = { + .name = "exynos4210.gpio1", + .parent = TYPE_EXYNOS4210_GPIO_1_2, + .instance_init = exynos4_gpio1_init, +}; + +static TypeInfo exynos4_gpio2_type_info = { + .name = "exynos4210.gpio2", + .parent = TYPE_EXYNOS4210_GPIO_1_2, + .instance_init = exynos4_gpio2_init, +}; + +static TypeInfo exynos4_gpio2x_type_info = { + .name = TYPE_EXYNOS4210_GPIO_2X, + .parent = TYPE_EXYNOS4210_GPIO, + .instance_size = sizeof(Exynos4Gpio2XState), + .instance_init = exynos4_gpio2x_init, +}; + +static TypeInfo exynos4_gpio3_type_info = { + .name = "exynos4210.gpio3", + .parent = TYPE_EXYNOS4210_GPIO, + .instance_init = exynos4_gpio3_init, +}; + +static void exynos4_gpio_register_types(void) +{ + type_register_static(&exynos4_gpio_type_info); + type_register_static(&exynos4_gpio_1_2_type_info); + type_register_static(&exynos4_gpio1_type_info); + type_register_static(&exynos4_gpio2_type_info); + type_register_static(&exynos4_gpio2x_type_info); + type_register_static(&exynos4_gpio3_type_info); +} + +type_init(exynos4_gpio_register_types) diff --git a/hw/exynos4_boards.c b/hw/exynos4_boards.c index 553a02b..b438361 100644 --- a/hw/exynos4_boards.c +++ b/hw/exynos4_boards.c @@ -149,7 +149,7 @@ static void smdkc210_init(ram_addr_t ram_size, kernel_cmdline, initrd_filename, EXYNOS4_BOARD_SMDKC210); lan9215_init(SMDK_LAN9118_BASE_ADDR, - qemu_irq_invert(s->irq_table[exynos4210_get_irq(37, 1)])); + qdev_get_gpio_in(s->gpio2x, EXYNOS4210_GPIO2X_LINE(GPX0, 5))); arm_load_kernel(first_cpu, &exynos4_board_binfo); } -- 1.7.4.1