diff options
-rw-r--r-- | firmware/SOURCES | 1 | ||||
-rw-r--r-- | firmware/export/s5l8702.h | 7 | ||||
-rw-r--r-- | firmware/target/arm/s5l8702/gpio-s5l8702.c | 187 | ||||
-rw-r--r-- | firmware/target/arm/s5l8702/gpio-s5l8702.h | 145 | ||||
-rw-r--r-- | firmware/target/arm/s5l8702/system-s5l8702.c | 22 |
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; |