summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--firmware/SOURCES1
-rw-r--r--firmware/export/s5l8702.h7
-rw-r--r--firmware/target/arm/s5l8702/gpio-s5l8702.c187
-rw-r--r--firmware/target/arm/s5l8702/gpio-s5l8702.h145
-rw-r--r--firmware/target/arm/s5l8702/system-s5l8702.c22
5 files changed, 352 insertions, 10 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 1bb8563f24..d3fb69f9e8 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -1597,6 +1597,7 @@ target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c
target/arm/s5l8702/ipod6g/power-ipod6g.c
target/arm/s5l8702/kernel-s5l8702.c
target/arm/s5l8702/system-s5l8702.c
+target/arm/s5l8702/gpio-s5l8702.c
target/arm/s5l8702/ipod6g/lcd-ipod6g.c
target/arm/s5l8702/ipod6g/lcd-asm-ipod6g.S
#if 0 //TODO
diff --git a/firmware/export/s5l8702.h b/firmware/export/s5l8702.h
index a83fe371e0..a176a9bf29 100644
--- a/firmware/export/s5l8702.h
+++ b/firmware/export/s5l8702.h
@@ -853,6 +853,13 @@ struct dma_lli
#define IRQ_ATA 29
#define IRQ_MMC 44
+#define IRQ_EXT0 0
+#define IRQ_EXT1 1
+#define IRQ_EXT2 2
+#define IRQ_EXT3 3
+#define IRQ_EXT4 31
+#define IRQ_EXT5 32
+#define IRQ_EXT6 33
#endif
diff --git a/firmware/target/arm/s5l8702/gpio-s5l8702.c b/firmware/target/arm/s5l8702/gpio-s5l8702.c
new file mode 100644
index 0000000000..d3ccb032bc
--- /dev/null
+++ b/firmware/target/arm/s5l8702/gpio-s5l8702.c
@@ -0,0 +1,187 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2014 Cástor Muñoz
+ * Code based on openiBoot project
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include <stdint.h>
+
+#include "config.h"
+#include "system.h"
+#include "gpio-s5l8702.h"
+#include "panic.h"
+
+
+/*
+ * XXX: disabled, not used and never tested!
+ */
+#if 0
+/* handlers list */
+#define GPIOIC_MAX_HANDLERS 2
+
+#define FLAG_TYPE_LEVEL (1 << 0)
+#define FLAG_AUTOFLIP (1 << 1)
+
+struct gpio_handler {
+ int gpio_n;
+ void (*isr)(void);
+ int flags;
+};
+static struct gpio_handler l_handlers[GPIOIC_MAX_HANDLERS] IDATA_ATTR;
+static int n_handlers = 0;
+
+
+/* API */
+void INIT_ATTR gpio_init(void)
+{
+ /* TODO: initialize GPIO ports for minimum power consumption */
+}
+
+uint32_t gpio_group_get(int group)
+{
+ uint32_t pcon = PCON(group);
+ uint32_t pdat = PDAT(group);
+ uint32_t group_cfg = 0;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ int pin_cfg = pcon & 0xF;
+ if (pin_cfg == 1) /* output */
+ pin_cfg = 0xE | ((pdat >> i) & 1);
+ group_cfg = (group_cfg >> 4) | (pin_cfg << 28);
+ pcon >>= 4;
+ }
+
+ return group_cfg;
+}
+
+void gpio_group_set(int group, uint32_t mask, uint32_t cfg)
+{
+ PCON(group) = (PCON(group) & ~mask) | (cfg & mask);
+}
+
+void gpio_int_register(int gpio_n, void *isr, int type, int level, int autoflip)
+{
+ if (n_handlers >= GPIOIC_MAX_HANDLERS)
+ panicf("gpio_int_register(): too many handlers!");
+
+ int group = IC_GROUP(gpio_n);
+ int index = IC_IDX(gpio_n);
+
+ int flags = disable_irq_save();
+
+ /* fill handler struct */
+ struct gpio_handler *handler = &l_handlers[n_handlers++];
+
+ handler->gpio_n = gpio_n;
+ handler->isr = isr;
+ handler->flags = (type ? FLAG_TYPE_LEVEL : 0)
+ | (autoflip ? FLAG_AUTOFLIP : 0);
+
+ /* configure */
+ GPIOIC_INTTYPE(group) |=
+ (type ? GPIOIC_INTTYPE_LEVEL : GPIOIC_INTTYPE_EDGE) << index;
+ GPIOIC_INTLEVEL(group) |=
+ (level ? GPIOIC_INTLEVEL_HIGH : GPIOIC_INTLEVEL_LOW) << index;
+
+ restore_irq(flags);
+
+ /* XXX: valid only for gpio_n = 0..127 (IRQ_EXT0..IRQ_EXT3) */
+ VIC0INTENABLE = 1 << (IRQ_EXT0 + (gpio_n >> 5));
+}
+
+void gpio_int_enable(int gpio_n)
+{
+ int group = IC_GROUP(gpio_n);
+ uint32_t bit_mask = 1 << IC_IDX(gpio_n);
+
+ int flags = disable_irq_save();
+ GPIOIC_INTSTAT(group) = bit_mask; /* clear */
+ GPIOIC_INTEN(group) |= bit_mask; /* enable */
+ restore_irq(flags);
+}
+
+void gpio_int_disable(int gpio_n)
+{
+ int group = IC_GROUP(gpio_n);
+ uint32_t bit_mask = 1 << IC_IDX(gpio_n);
+
+ int flags = disable_irq_save();
+ GPIOIC_INTEN(group) &= ~bit_mask; /* disable */
+ GPIOIC_INTSTAT(group) = bit_mask; /* clear */
+ restore_irq(flags);
+}
+
+
+/* ISR */
+void ICODE_ATTR gpio_handler(int group)
+{
+ int i;
+ struct gpio_handler *handler;
+ uint32_t ints, bit_mask;
+
+ ints = GPIOIC_INTSTAT(group) & GPIOIC_INTEN(group);
+
+ for (i = 0; i < n_handlers; i++) {
+ handler = &l_handlers[i];
+
+ if (IC_GROUP(handler->gpio_n) != group)
+ continue;
+
+ bit_mask = 1 << IC_IDX(handler->gpio_n);
+
+ if (ints & bit_mask) {
+ if ((handler->flags & FLAG_TYPE_LEVEL) == 0)
+ GPIOIC_INTSTAT(group) = bit_mask; /* clear */
+
+ if (handler->flags & FLAG_AUTOFLIP)
+ GPIOIC_INTLEVEL(group) ^= bit_mask; /* swap level */
+
+ if (handler->isr)
+ handler->isr(); /* exec GPIO handler */
+
+ GPIOIC_INTSTAT(group) = bit_mask; /* clear */
+ }
+ }
+}
+
+void ICODE_ATTR INT_EXT0(void)
+{
+ gpio_handler(6);
+}
+
+void ICODE_ATTR INT_EXT1(void)
+{
+ gpio_handler(5);
+}
+
+void ICODE_ATTR INT_EXT2(void)
+{
+ gpio_handler(4);
+}
+
+void ICODE_ATTR INT_EXT3(void)
+{
+ gpio_handler(3);
+}
+
+void ICODE_ATTR INT_EXT6(void)
+{
+ gpio_handler(0);
+}
+#endif
diff --git a/firmware/target/arm/s5l8702/gpio-s5l8702.h b/firmware/target/arm/s5l8702/gpio-s5l8702.h
new file mode 100644
index 0000000000..00f5ba18f3
--- /dev/null
+++ b/firmware/target/arm/s5l8702/gpio-s5l8702.h
@@ -0,0 +1,145 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2014 Cástor Muñoz
+ * Code based on openiBoot project
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __GPIO_S5L8702_H__
+#define __GPIO_S5L8702_H__
+#include <stdint.h>
+
+/* This is very preliminary work in progress, ATM this region is called
+ * system 'alive' because it seems there are similiarities when mixing
+ * concepts from:
+ * - s3c2440 datasheet (figure 7-12, Sleep mode) and
+ * - ARM-DDI-0287B (2.1.8 System Mode Control, Sleep an Doze modes)
+ *
+ * Known components:
+ * - independent clocking
+ * - 32-bit timer
+ * - level/edge configurable interrupt controller
+ *
+ *
+ * OSCSEL
+ * |\ CKSEL
+ * OSC0 -->| | |\
+ * | |--->| | _________ ___________
+ * OSC1 -->| | | | | | SClk | |
+ * |/ | |--->| 1/CKDIV |---------->| 1/ALVTDIV |--> Timer
+ * | | |_________| | |___________| counter
+ * PClk --------->| | | ___________
+ * |/ | | |
+ * +-->| 1/UNKDIV |--> Unknown
+ * |___________|
+ */
+
+#define REG32_PTR_T volatile uint32_t *
+
+#define SYSALV_BASE 0x39a00000
+
+#define ALVCON (*((REG32_PTR_T)(SYSALV_BASE + 0x0)))
+#define ALVUNK4 (*((REG32_PTR_T)(SYSALV_BASE + 0x4)))
+#define ALVUNK100 (*((REG32_PTR_T)(SYSALV_BASE + 0x100)))
+#define ALVUNK104 (*((REG32_PTR_T)(SYSALV_BASE + 0x104)))
+
+
+/*
+ * System Alive control register
+ */
+#define ALVCON_CKSEL_BIT (1 << 25) /* 0 -> S5L8702_OSCx, 1 -> PClk */
+#define ALVCON_CKDIVEN_BIT (1 << 24) /* 0 -> CK divider Off, 1 -> On */
+#define ALVCON_CKDIV_POS 20 /* real_val = reg_val+1 */
+#define ALVCON_CKDIV_MSK 0xf
+
+/* UNKDIV: real_val = reg_val+1 (TBC), valid reg_val=0,1,2 */
+/* experimental: for registers in this region, read/write speed is
+ * scaled by this divider, so probably it is related with internal
+ * 'working' frequency.
+ */
+#define ALVCON_UNKDIV_POS 16
+#define ALVCON_UNKDIV_MSK 0x3
+
+/* bits[14:1] are UNKNOWN */
+
+#define ALVCON_OSCSEL_BIT (1 << 0) /* 0 -> OSC0, 1 -> OSC1 */
+
+
+/*
+ * System Alive timer
+ */
+/* ALVCOM_RUN_BIT starts/stops count on ALVTCNT, counter frequency
+ * is SClk / ALVTDIV. When count reachs ALVTEND then ALVTSTAT[0]
+ * and ALVUNK4[0] are set, optionally an interrupt is generated (see
+ * GPIO_IC below). Writing 1 to ALVTCOM_RST_BIT clears ALVSTAT[0]
+ * and ALVUNK4[0] and initializes ALVTCNT to zero.
+ */
+#define ALVTCOM (*((REG32_PTR_T)(SYSALV_BASE + 0x6c)))
+#define ALVTCOM_RUN_BIT (1 << 0) /* 0 -> Stop, 1 -> Start */
+#define ALVTCOM_RST_BIT (1 << 1) /* 1 -> Reset */
+
+#define ALVTEND (*((REG32_PTR_T)(SYSALV_BASE + 0x70)))
+#define ALVTDIV (*((REG32_PTR_T)(SYSALV_BASE + 0x74)))
+
+#define ALVTCNT (*((REG32_PTR_T)(SYSALV_BASE + 0x78)))
+#define ALVTSTAT (*((REG32_PTR_T)(SYSALV_BASE + 0x7c)))
+
+
+/*
+ * s5l8702 GPIO Interrupt Controller
+ */
+#define GPIOIC_BASE 0x39a00000 /* probably a part of the system controller */
+
+#define GPIOIC_INTLEVEL(g) (*((REG32_PTR_T)(GPIOIC_BASE + 0x80 + 4*(g))))
+#define GPIOIC_INTSTAT(g) (*((REG32_PTR_T)(GPIOIC_BASE + 0xA0 + 4*(g))))
+#define GPIOIC_INTEN(g) (*((REG32_PTR_T)(GPIOIC_BASE + 0xC0 + 4*(g))))
+#define GPIOIC_INTTYPE(g) (*((REG32_PTR_T)(GPIOIC_BASE + 0xE0 + 4*(g))))
+
+#define GPIOIC_INTLEVEL_LOW 0
+#define GPIOIC_INTLEVEL_HIGH 1
+
+#define GPIOIC_INTTYPE_EDGE 0
+#define GPIOIC_INTTYPE_LEVEL 1
+
+/* 7 groups of 32 interrupts, GPIO pins are seen as 'wired'
+ * to groups 6..3 in reverse order.
+ * On group 3, last four bits are dissbled (GPIO 124..127).
+ * All bits in groups 1 and 2 are disabled (not used).
+ * On group 0, all bits are masked except bits 0 and 2:
+ * bit 0: if unmasked, EINT6 is generated when ALVTCNT
+ * reachs ALVTEND.
+ * bit 2: if unmasked, EINT6 is generated when USB cable
+ * is plugged and/or(TBC) unplugged.
+ *
+ * IC_GROUP0..6 are connected to EINT6..0 of the VIC.
+ */
+
+/* get GPIOIC group and bit for a given GPIO port */
+#define IC_GROUP(n) (6 - (n >> 5))
+#define IC_IDX(n) ((0x18 - (n & 0x18)) | (n & 0x7))
+
+void gpio_init(void);
+void gpio_int_register(int gpio_n, void *isr,
+ int type, int level, int autoflip);
+void gpio_int_enable(int gpio_n);
+void gpio_int_disable(int gpio_n);
+
+/* get/set configuration for GPIO groups (0..15) */
+uint32_t gpio_group_get(int group);
+void gpio_group_set(int group, uint32_t mask, uint32_t cfg);
+
+#endif /* __GPIO_S5L8702_H__ */
diff --git a/firmware/target/arm/s5l8702/system-s5l8702.c b/firmware/target/arm/s5l8702/system-s5l8702.c
index 5b03f3bc5c..f93a6a70ec 100644
--- a/firmware/target/arm/s5l8702/system-s5l8702.c
+++ b/firmware/target/arm/s5l8702/system-s5l8702.c
@@ -24,6 +24,7 @@
#include "panic.h"
#include "system-target.h"
#include "pmu-target.h"
+#include "gpio-s5l8702.h"
#define default_interrupt(name) \
extern __attribute__((weak,alias("UIRQ"))) void name (void)
@@ -32,10 +33,10 @@ void irq_handler(void) __attribute__((interrupt ("IRQ"), naked));
void fiq_handler(void) __attribute__((interrupt ("FIQ"), naked, \
weak, alias("fiq_dummy")));
-default_interrupt(INT_IRQ0);
-default_interrupt(INT_IRQ1);
-default_interrupt(INT_IRQ2);
-default_interrupt(INT_IRQ3);
+default_interrupt(INT_EXT0); /* GPIOIC group 6 (GPIO 0..31) */
+default_interrupt(INT_EXT1); /* GPIOIC group 5 (GPIO 32..63) */
+default_interrupt(INT_EXT2); /* GPIOIC group 4 (GPIO 64..95) */
+default_interrupt(INT_EXT3); /* GPIOIC group 3 (GPIO 96..123) */
default_interrupt(INT_IRQ4);
default_interrupt(INT_IRQ5);
default_interrupt(INT_IRQ6);
@@ -83,9 +84,9 @@ default_interrupt(INT_IRQ27);
default_interrupt(INT_IRQ28);
default_interrupt(INT_ATA);
default_interrupt(INT_IRQ30);
-default_interrupt(INT_IRQ31);
-default_interrupt(INT_IRQ32);
-default_interrupt(INT_IRQ33);
+default_interrupt(INT_EXT4); /* GPIOIC group 2 (not used) */
+default_interrupt(INT_EXT5); /* GPIOIC group 1 (not used) */
+default_interrupt(INT_EXT6); /* GPIOIC group 0 */
default_interrupt(INT_IRQ34);
default_interrupt(INT_IRQ35);
default_interrupt(INT_IRQ36);
@@ -170,11 +171,11 @@ void INT_DMAC1()
static void (* const irqvector[])(void) =
{
- INT_IRQ0,INT_IRQ1,INT_IRQ2,INT_IRQ3,INT_IRQ4,INT_IRQ5,INT_IRQ6,INT_TIMER32,
+ INT_EXT0,INT_EXT1,INT_EXT2,INT_EXT3,INT_IRQ4,INT_IRQ5,INT_IRQ6,INT_TIMER32,
INT_TIMER,INT_IRQ9,INT_IRQ10,INT_IRQ11,INT_IRQ12,INT_IRQ13,INT_IRQ14,INT_IRQ15,
INT_DMAC0,INT_DMAC1,INT_IRQ18,INT_USB_FUNC,INT_IRQ20,INT_IRQ21,INT_IRQ22,INT_WHEEL,
- INT_IRQ24,INT_IRQ25,INT_IRQ26,INT_IRQ27,INT_IRQ28,INT_ATA,INT_IRQ30,INT_IRQ31,
- INT_IRQ32,INT_IRQ33,INT_IRQ34,INT_IRQ35,INT_IRQ36,INT_IRQ37,INT_IRQ38,INT_IRQ39,
+ INT_IRQ24,INT_IRQ25,INT_IRQ26,INT_IRQ27,INT_IRQ28,INT_ATA,INT_IRQ30,INT_EXT4,
+ INT_EXT5,INT_EXT6,INT_IRQ34,INT_IRQ35,INT_IRQ36,INT_IRQ37,INT_IRQ38,INT_IRQ39,
INT_IRQ40,INT_IRQ41,INT_IRQ42,INT_IRQ43,INT_MMC,INT_IRQ45,INT_IRQ46,INT_IRQ47,
INT_IRQ48,INT_IRQ49,INT_IRQ50,INT_IRQ51,INT_IRQ52,INT_IRQ53,INT_IRQ54,INT_IRQ55,
INT_IRQ56,INT_IRQ57,INT_IRQ58,INT_IRQ59,INT_IRQ60,INT_IRQ61,INT_IRQ62,INT_IRQ63
@@ -222,6 +223,7 @@ void fiq_dummy(void)
void system_init(void)
{
+ /*gpio_init();*/
pmu_init();
VIC0INTENABLE = 1 << IRQ_WHEEL;
VIC0INTENABLE = 1 << IRQ_ATA;