[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 4/5] exynos4210: add exynos4210 GPIO implementation
From: |
Igor Mitsyanko |
Subject: |
[Qemu-devel] [PATCH 4/5] exynos4210: add exynos4210 GPIO implementation |
Date: |
Fri, 02 Mar 2012 15:35:44 +0400 |
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 <address@hidden>
---
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, <address@hidden>
+ * Igor Mitsyanko, <address@hidden>
+ *
+ * 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
- [Qemu-devel] [PATCH 0/5] Exynos: i2c, gpio and touchscreen support for NURI board, Igor Mitsyanko, 2012/03/02
- [Qemu-devel] [PATCH 5/5] hw: add Atmel maxtouch touchscreen implementation, Igor Mitsyanko, 2012/03/02
- [Qemu-devel] [PATCH 4/5] exynos4210: add exynos4210 GPIO implementation,
Igor Mitsyanko <=
- [Qemu-devel] [PATCH 2/5] qom/object.c: rename type_class_init() to type_initialize(), Igor Mitsyanko, 2012/03/02
- [Qemu-devel] [PATCH 3/5] exynos4210: add Exynos4210 i2c implementation, Igor Mitsyanko, 2012/03/02
- [Qemu-devel] [PATCH 1/5] qom: if @instance_size==0, assign size of object to parent object size, Igor Mitsyanko, 2012/03/02