summaryrefslogtreecommitdiff
path: root/firmware/target/arm/s5l8702
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 /firmware/target/arm/s5l8702
parent3fdb86ea417cae94a7f7df4475989843e644acdc (diff)
downloadrockbox-609cde94689b20098be1c36cc5157514b6dd63a4.tar.gz
rockbox-609cde94689b20098be1c36cc5157514b6dd63a4.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
Diffstat (limited to 'firmware/target/arm/s5l8702')
-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
3 files changed, 344 insertions, 10 deletions
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 Cástor Muñoz
11 * Code based on openiBoot project
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#include <stdint.h>
23
24#include "config.h"
25#include "system.h"
26#include "gpio-s5l8702.h"
27#include "panic.h"
28
29
30/*
31 * XXX: disabled, not used and never tested!
32 */
33#if 0
34/* handlers list */
35#define GPIOIC_MAX_HANDLERS 2
36
37#define FLAG_TYPE_LEVEL (1 << 0)
38#define FLAG_AUTOFLIP (1 << 1)
39
40struct gpio_handler {
41 int gpio_n;
42 void (*isr)(void);
43 int flags;
44};
45static struct gpio_handler l_handlers[GPIOIC_MAX_HANDLERS] IDATA_ATTR;
46static int n_handlers = 0;
47
48
49/* API */
50void INIT_ATTR gpio_init(void)
51{
52 /* TODO: initialize GPIO ports for minimum power consumption */
53}
54
55uint32_t gpio_group_get(int group)
56{
57 uint32_t pcon = PCON(group);
58 uint32_t pdat = PDAT(group);
59 uint32_t group_cfg = 0;
60 int i;
61
62 for (i = 0; i < 8; i++) {
63 int pin_cfg = pcon & 0xF;
64 if (pin_cfg == 1) /* output */
65 pin_cfg = 0xE | ((pdat >> i) & 1);
66 group_cfg = (group_cfg >> 4) | (pin_cfg << 28);
67 pcon >>= 4;
68 }
69
70 return group_cfg;
71}
72
73void gpio_group_set(int group, uint32_t mask, uint32_t cfg)
74{
75 PCON(group) = (PCON(group) & ~mask) | (cfg & mask);
76}
77
78void gpio_int_register(int gpio_n, void *isr, int type, int level, int autoflip)
79{
80 if (n_handlers >= GPIOIC_MAX_HANDLERS)
81 panicf("gpio_int_register(): too many handlers!");
82
83 int group = IC_GROUP(gpio_n);
84 int index = IC_IDX(gpio_n);
85
86 int flags = disable_irq_save();
87
88 /* fill handler struct */
89 struct gpio_handler *handler = &l_handlers[n_handlers++];
90
91 handler->gpio_n = gpio_n;
92 handler->isr = isr;
93 handler->flags = (type ? FLAG_TYPE_LEVEL : 0)
94 | (autoflip ? FLAG_AUTOFLIP : 0);
95
96 /* configure */
97 GPIOIC_INTTYPE(group) |=
98 (type ? GPIOIC_INTTYPE_LEVEL : GPIOIC_INTTYPE_EDGE) << index;
99 GPIOIC_INTLEVEL(group) |=
100 (level ? GPIOIC_INTLEVEL_HIGH : GPIOIC_INTLEVEL_LOW) << index;
101
102 restore_irq(flags);
103
104 /* XXX: valid only for gpio_n = 0..127 (IRQ_EXT0..IRQ_EXT3) */
105 VIC0INTENABLE = 1 << (IRQ_EXT0 + (gpio_n >> 5));
106}
107
108void gpio_int_enable(int gpio_n)
109{
110 int group = IC_GROUP(gpio_n);
111 uint32_t bit_mask = 1 << IC_IDX(gpio_n);
112
113 int flags = disable_irq_save();
114 GPIOIC_INTSTAT(group) = bit_mask; /* clear */
115 GPIOIC_INTEN(group) |= bit_mask; /* enable */
116 restore_irq(flags);
117}
118
119void gpio_int_disable(int gpio_n)
120{
121 int group = IC_GROUP(gpio_n);
122 uint32_t bit_mask = 1 << IC_IDX(gpio_n);
123
124 int flags = disable_irq_save();
125 GPIOIC_INTEN(group) &= ~bit_mask; /* disable */
126 GPIOIC_INTSTAT(group) = bit_mask; /* clear */
127 restore_irq(flags);
128}
129
130
131/* ISR */
132void ICODE_ATTR gpio_handler(int group)
133{
134 int i;
135 struct gpio_handler *handler;
136 uint32_t ints, bit_mask;
137
138 ints = GPIOIC_INTSTAT(group) & GPIOIC_INTEN(group);
139
140 for (i = 0; i < n_handlers; i++) {
141 handler = &l_handlers[i];
142
143 if (IC_GROUP(handler->gpio_n) != group)
144 continue;
145
146 bit_mask = 1 << IC_IDX(handler->gpio_n);
147
148 if (ints & bit_mask) {
149 if ((handler->flags & FLAG_TYPE_LEVEL) == 0)
150 GPIOIC_INTSTAT(group) = bit_mask; /* clear */
151
152 if (handler->flags & FLAG_AUTOFLIP)
153 GPIOIC_INTLEVEL(group) ^= bit_mask; /* swap level */
154
155 if (handler->isr)
156 handler->isr(); /* exec GPIO handler */
157
158 GPIOIC_INTSTAT(group) = bit_mask; /* clear */
159 }
160 }
161}
162
163void ICODE_ATTR INT_EXT0(void)
164{
165 gpio_handler(6);
166}
167
168void ICODE_ATTR INT_EXT1(void)
169{
170 gpio_handler(5);
171}
172
173void ICODE_ATTR INT_EXT2(void)
174{
175 gpio_handler(4);
176}
177
178void ICODE_ATTR INT_EXT3(void)
179{
180 gpio_handler(3);
181}
182
183void ICODE_ATTR INT_EXT6(void)
184{
185 gpio_handler(0);
186}
187#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 Cástor Muñoz
11 * Code based on openiBoot project
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#ifndef __GPIO_S5L8702_H__
23#define __GPIO_S5L8702_H__
24#include <stdint.h>
25
26/* This is very preliminary work in progress, ATM this region is called
27 * system 'alive' because it seems there are similiarities when mixing
28 * concepts from:
29 * - s3c2440 datasheet (figure 7-12, Sleep mode) and
30 * - ARM-DDI-0287B (2.1.8 System Mode Control, Sleep an Doze modes)
31 *
32 * Known components:
33 * - independent clocking
34 * - 32-bit timer
35 * - level/edge configurable interrupt controller
36 *
37 *
38 * OSCSEL
39 * |\ CKSEL
40 * OSC0 -->| | |\
41 * | |--->| | _________ ___________
42 * OSC1 -->| | | | | | SClk | |
43 * |/ | |--->| 1/CKDIV |---------->| 1/ALVTDIV |--> Timer
44 * | | |_________| | |___________| counter
45 * PClk --------->| | | ___________
46 * |/ | | |
47 * +-->| 1/UNKDIV |--> Unknown
48 * |___________|
49 */
50
51#define REG32_PTR_T volatile uint32_t *
52
53#define SYSALV_BASE 0x39a00000
54
55#define ALVCON (*((REG32_PTR_T)(SYSALV_BASE + 0x0)))
56#define ALVUNK4 (*((REG32_PTR_T)(SYSALV_BASE + 0x4)))
57#define ALVUNK100 (*((REG32_PTR_T)(SYSALV_BASE + 0x100)))
58#define ALVUNK104 (*((REG32_PTR_T)(SYSALV_BASE + 0x104)))
59
60
61/*
62 * System Alive control register
63 */
64#define ALVCON_CKSEL_BIT (1 << 25) /* 0 -> S5L8702_OSCx, 1 -> PClk */
65#define ALVCON_CKDIVEN_BIT (1 << 24) /* 0 -> CK divider Off, 1 -> On */
66#define ALVCON_CKDIV_POS 20 /* real_val = reg_val+1 */
67#define ALVCON_CKDIV_MSK 0xf
68
69/* UNKDIV: real_val = reg_val+1 (TBC), valid reg_val=0,1,2 */
70/* experimental: for registers in this region, read/write speed is
71 * scaled by this divider, so probably it is related with internal
72 * 'working' frequency.
73 */
74#define ALVCON_UNKDIV_POS 16
75#define ALVCON_UNKDIV_MSK 0x3
76
77/* bits[14:1] are UNKNOWN */
78
79#define ALVCON_OSCSEL_BIT (1 << 0) /* 0 -> OSC0, 1 -> OSC1 */
80
81
82/*
83 * System Alive timer
84 */
85/* ALVCOM_RUN_BIT starts/stops count on ALVTCNT, counter frequency
86 * is SClk / ALVTDIV. When count reachs ALVTEND then ALVTSTAT[0]
87 * and ALVUNK4[0] are set, optionally an interrupt is generated (see
88 * GPIO_IC below). Writing 1 to ALVTCOM_RST_BIT clears ALVSTAT[0]
89 * and ALVUNK4[0] and initializes ALVTCNT to zero.
90 */
91#define ALVTCOM (*((REG32_PTR_T)(SYSALV_BASE + 0x6c)))
92#define ALVTCOM_RUN_BIT (1 << 0) /* 0 -> Stop, 1 -> Start */
93#define ALVTCOM_RST_BIT (1 << 1) /* 1 -> Reset */
94
95#define ALVTEND (*((REG32_PTR_T)(SYSALV_BASE + 0x70)))
96#define ALVTDIV (*((REG32_PTR_T)(SYSALV_BASE + 0x74)))
97
98#define ALVTCNT (*((REG32_PTR_T)(SYSALV_BASE + 0x78)))
99#define ALVTSTAT (*((REG32_PTR_T)(SYSALV_BASE + 0x7c)))
100
101
102/*
103 * s5l8702 GPIO Interrupt Controller
104 */
105#define GPIOIC_BASE 0x39a00000 /* probably a part of the system controller */
106
107#define GPIOIC_INTLEVEL(g) (*((REG32_PTR_T)(GPIOIC_BASE + 0x80 + 4*(g))))
108#define GPIOIC_INTSTAT(g) (*((REG32_PTR_T)(GPIOIC_BASE + 0xA0 + 4*(g))))
109#define GPIOIC_INTEN(g) (*((REG32_PTR_T)(GPIOIC_BASE + 0xC0 + 4*(g))))
110#define GPIOIC_INTTYPE(g) (*((REG32_PTR_T)(GPIOIC_BASE + 0xE0 + 4*(g))))
111
112#define GPIOIC_INTLEVEL_LOW 0
113#define GPIOIC_INTLEVEL_HIGH 1
114
115#define GPIOIC_INTTYPE_EDGE 0
116#define GPIOIC_INTTYPE_LEVEL 1
117
118/* 7 groups of 32 interrupts, GPIO pins are seen as 'wired'
119 * to groups 6..3 in reverse order.
120 * On group 3, last four bits are dissbled (GPIO 124..127).
121 * All bits in groups 1 and 2 are disabled (not used).
122 * On group 0, all bits are masked except bits 0 and 2:
123 * bit 0: if unmasked, EINT6 is generated when ALVTCNT
124 * reachs ALVTEND.
125 * bit 2: if unmasked, EINT6 is generated when USB cable
126 * is plugged and/or(TBC) unplugged.
127 *
128 * IC_GROUP0..6 are connected to EINT6..0 of the VIC.
129 */
130
131/* get GPIOIC group and bit for a given GPIO port */
132#define IC_GROUP(n) (6 - (n >> 5))
133#define IC_IDX(n) ((0x18 - (n & 0x18)) | (n & 0x7))
134
135void gpio_init(void);
136void gpio_int_register(int gpio_n, void *isr,
137 int type, int level, int autoflip);
138void gpio_int_enable(int gpio_n);
139void gpio_int_disable(int gpio_n);
140
141/* get/set configuration for GPIO groups (0..15) */
142uint32_t gpio_group_get(int group);
143void gpio_group_set(int group, uint32_t mask, uint32_t cfg);
144
145#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 @@
24#include "panic.h" 24#include "panic.h"
25#include "system-target.h" 25#include "system-target.h"
26#include "pmu-target.h" 26#include "pmu-target.h"
27#include "gpio-s5l8702.h"
27 28
28#define default_interrupt(name) \ 29#define default_interrupt(name) \
29 extern __attribute__((weak,alias("UIRQ"))) void name (void) 30 extern __attribute__((weak,alias("UIRQ"))) void name (void)
@@ -32,10 +33,10 @@ void irq_handler(void) __attribute__((interrupt ("IRQ"), naked));
32void fiq_handler(void) __attribute__((interrupt ("FIQ"), naked, \ 33void fiq_handler(void) __attribute__((interrupt ("FIQ"), naked, \
33 weak, alias("fiq_dummy"))); 34 weak, alias("fiq_dummy")));
34 35
35default_interrupt(INT_IRQ0); 36default_interrupt(INT_EXT0); /* GPIOIC group 6 (GPIO 0..31) */
36default_interrupt(INT_IRQ1); 37default_interrupt(INT_EXT1); /* GPIOIC group 5 (GPIO 32..63) */
37default_interrupt(INT_IRQ2); 38default_interrupt(INT_EXT2); /* GPIOIC group 4 (GPIO 64..95) */
38default_interrupt(INT_IRQ3); 39default_interrupt(INT_EXT3); /* GPIOIC group 3 (GPIO 96..123) */
39default_interrupt(INT_IRQ4); 40default_interrupt(INT_IRQ4);
40default_interrupt(INT_IRQ5); 41default_interrupt(INT_IRQ5);
41default_interrupt(INT_IRQ6); 42default_interrupt(INT_IRQ6);
@@ -83,9 +84,9 @@ default_interrupt(INT_IRQ27);
83default_interrupt(INT_IRQ28); 84default_interrupt(INT_IRQ28);
84default_interrupt(INT_ATA); 85default_interrupt(INT_ATA);
85default_interrupt(INT_IRQ30); 86default_interrupt(INT_IRQ30);
86default_interrupt(INT_IRQ31); 87default_interrupt(INT_EXT4); /* GPIOIC group 2 (not used) */
87default_interrupt(INT_IRQ32); 88default_interrupt(INT_EXT5); /* GPIOIC group 1 (not used) */
88default_interrupt(INT_IRQ33); 89default_interrupt(INT_EXT6); /* GPIOIC group 0 */
89default_interrupt(INT_IRQ34); 90default_interrupt(INT_IRQ34);
90default_interrupt(INT_IRQ35); 91default_interrupt(INT_IRQ35);
91default_interrupt(INT_IRQ36); 92default_interrupt(INT_IRQ36);
@@ -170,11 +171,11 @@ void INT_DMAC1()
170 171
171static void (* const irqvector[])(void) = 172static void (* const irqvector[])(void) =
172{ 173{
173 INT_IRQ0,INT_IRQ1,INT_IRQ2,INT_IRQ3,INT_IRQ4,INT_IRQ5,INT_IRQ6,INT_TIMER32, 174 INT_EXT0,INT_EXT1,INT_EXT2,INT_EXT3,INT_IRQ4,INT_IRQ5,INT_IRQ6,INT_TIMER32,
174 INT_TIMER,INT_IRQ9,INT_IRQ10,INT_IRQ11,INT_IRQ12,INT_IRQ13,INT_IRQ14,INT_IRQ15, 175 INT_TIMER,INT_IRQ9,INT_IRQ10,INT_IRQ11,INT_IRQ12,INT_IRQ13,INT_IRQ14,INT_IRQ15,
175 INT_DMAC0,INT_DMAC1,INT_IRQ18,INT_USB_FUNC,INT_IRQ20,INT_IRQ21,INT_IRQ22,INT_WHEEL, 176 INT_DMAC0,INT_DMAC1,INT_IRQ18,INT_USB_FUNC,INT_IRQ20,INT_IRQ21,INT_IRQ22,INT_WHEEL,
176 INT_IRQ24,INT_IRQ25,INT_IRQ26,INT_IRQ27,INT_IRQ28,INT_ATA,INT_IRQ30,INT_IRQ31, 177 INT_IRQ24,INT_IRQ25,INT_IRQ26,INT_IRQ27,INT_IRQ28,INT_ATA,INT_IRQ30,INT_EXT4,
177 INT_IRQ32,INT_IRQ33,INT_IRQ34,INT_IRQ35,INT_IRQ36,INT_IRQ37,INT_IRQ38,INT_IRQ39, 178 INT_EXT5,INT_EXT6,INT_IRQ34,INT_IRQ35,INT_IRQ36,INT_IRQ37,INT_IRQ38,INT_IRQ39,
178 INT_IRQ40,INT_IRQ41,INT_IRQ42,INT_IRQ43,INT_MMC,INT_IRQ45,INT_IRQ46,INT_IRQ47, 179 INT_IRQ40,INT_IRQ41,INT_IRQ42,INT_IRQ43,INT_MMC,INT_IRQ45,INT_IRQ46,INT_IRQ47,
179 INT_IRQ48,INT_IRQ49,INT_IRQ50,INT_IRQ51,INT_IRQ52,INT_IRQ53,INT_IRQ54,INT_IRQ55, 180 INT_IRQ48,INT_IRQ49,INT_IRQ50,INT_IRQ51,INT_IRQ52,INT_IRQ53,INT_IRQ54,INT_IRQ55,
180 INT_IRQ56,INT_IRQ57,INT_IRQ58,INT_IRQ59,INT_IRQ60,INT_IRQ61,INT_IRQ62,INT_IRQ63 181 INT_IRQ56,INT_IRQ57,INT_IRQ58,INT_IRQ59,INT_IRQ60,INT_IRQ61,INT_IRQ62,INT_IRQ63
@@ -222,6 +223,7 @@ void fiq_dummy(void)
222 223
223void system_init(void) 224void system_init(void)
224{ 225{
226 /*gpio_init();*/
225 pmu_init(); 227 pmu_init();
226 VIC0INTENABLE = 1 << IRQ_WHEEL; 228 VIC0INTENABLE = 1 << IRQ_WHEEL;
227 VIC0INTENABLE = 1 << IRQ_ATA; 229 VIC0INTENABLE = 1 << IRQ_ATA;