summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCástor Muñoz <cmvidal@gmail.com>2014-11-10 04:35:51 +0100
committerCástor Muñoz <cmvidal@gmail.com>2015-10-07 06:15:03 +0200
commit609cde94689b20098be1c36cc5157514b6dd63a4 (patch)
tree88d559823f2938626c1e80095d6ec46b6bc908f7
parent3fdb86ea417cae94a7f7df4475989843e644acdc (diff)
downloadrockbox-609cde9.tar.gz
rockbox-609cde9.zip
iPod Classic: s5l8702 GPIO interrupt controller.
This patch implements a simple API to use the external interrupt hardware present on s5l8702 (GPIO interrupt controller). This GPIOIC has been fully tested using emcore apps. Code is based on openiBoot project, there are a few modifications to optimize space considering we will only use two or three external interrupts. The API compiles and works, but has been never used, therefore probably will need some changes to the final version. External interrupts are necessary for jack remote+mic controller (see iAP Interface Specifiction: Headphone Remote and Mic System), this controller is located at I2C bus address 0x72, there is a IRQ line for remote button press/release events routed to GPIO E6. At this moment, the functionallity of this controller has been extensively tested using emcore, getting a lot of information about how it works. Microphone is already working on RB, jack accessory detection and button events are work in progress. PMU IRQ line is also routed to GPIO F3, it signals many events: holdswitch, usb plug, wall adapter, low battery... The use of PMU interrupts is the orthodox way of doing things, at this moment there is no work done in this direction, there are a lot of PMU events and i think it is a matter of discursion what to do and how. Change-Id: Icc2e48965e664ca56c9518d84a81c9d9fdd31736
-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;