summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCástor Muñoz <cmvidal@gmail.com>2016-05-14 14:24:49 +0200
committerCástor Muñoz <cmvidal@gmail.com>2016-05-14 19:39:09 +0200
commit9a4cd2eaee7d389f2fa6e0d79f0f6ea526f7ef85 (patch)
tree2cf04d46c512cf48d05c1804e03aee952885bf47
parent60fb707203c5b0ce2c32776890611846f0c22566 (diff)
downloadrockbox-9a4cd2eaee7d389f2fa6e0d79f0f6ea526f7ef85.tar.gz
rockbox-9a4cd2eaee7d389f2fa6e0d79f0f6ea526f7ef85.zip
iPod Nano2G: add IPOD_ACCESSORY_PROTOCOL
Change-Id: I78a19972624504bc802d96b9b8e9cec132164c2c
-rw-r--r--firmware/export/config/ipodnano2g.h9
-rw-r--r--firmware/export/s5l8700.h52
-rw-r--r--firmware/target/arm/s5l8700/ipodnano2g/serial-nano2g.c113
-rw-r--r--firmware/target/arm/s5l8700/system-s5l8700.c12
-rw-r--r--firmware/target/arm/s5l8700/uart-s5l8701.c17
5 files changed, 183 insertions, 20 deletions
diff --git a/firmware/export/config/ipodnano2g.h b/firmware/export/config/ipodnano2g.h
index a9c7239056..8e8f40f56c 100644
--- a/firmware/export/config/ipodnano2g.h
+++ b/firmware/export/config/ipodnano2g.h
@@ -231,9 +231,14 @@
231 231
232/* Define this if you can switch on/off the accessory power supply */ 232/* Define this if you can switch on/off the accessory power supply */
233#define HAVE_ACCESSORY_SUPPLY 233#define HAVE_ACCESSORY_SUPPLY
234//#define IPOD_ACCESSORY_PROTOCOL 234
235#ifdef LOGF_SERIAL 235/* Serial */
236#ifndef BOOTLOADER
236#define HAVE_SERIAL 237#define HAVE_SERIAL
238/* Disable iAP when LOGF_SERIAL is enabled to avoid conflicts */
239#ifndef LOGF_SERIAL
240#define IPOD_ACCESSORY_PROTOCOL
241#endif
237#endif 242#endif
238 243
239/* Define this, if you can switch on/off the lineout */ 244/* Define this, if you can switch on/off the lineout */
diff --git a/firmware/export/s5l8700.h b/firmware/export/s5l8700.h
index e8497ff337..3444919bc3 100644
--- a/firmware/export/s5l8700.h
+++ b/firmware/export/s5l8700.h
@@ -133,33 +133,61 @@
133#define PWRCONEXT (*(REG32_PTR_T)(0x3C500040)) /* Clock power control register 2 */ 133#define PWRCONEXT (*(REG32_PTR_T)(0x3C500040)) /* Clock power control register 2 */
134 134
135/* 06. INTERRUPT CONTROLLER UNIT */ 135/* 06. INTERRUPT CONTROLLER UNIT */
136#if CONFIG_CPU==S5L8700
136#define SRCPND (*(REG32_PTR_T)(0x39C00000)) /* Indicates the interrupt request status. */ 137#define SRCPND (*(REG32_PTR_T)(0x39C00000)) /* Indicates the interrupt request status. */
137#define INTMOD (*(REG32_PTR_T)(0x39C00004)) /* Interrupt mode register. */ 138#define INTMOD (*(REG32_PTR_T)(0x39C00004)) /* Interrupt mode register. */
138#define INTMSK (*(REG32_PTR_T)(0x39C00008)) /* Determines which interrupt source is masked. The */ 139#define INTMSK (*(REG32_PTR_T)(0x39C00008)) /* Determines which interrupt source is masked. The */
139#if CONFIG_CPU==S5L8701
140#define INTMSK_TIMERA (1<<5)
141#define INTMSK_TIMERB (1<<5)
142#define INTMSK_TIMERC (1<<5)
143#define INTMSK_TIMERD (1<<5)
144#define INTMSK_ECC (1<<19)
145#define INTMSK_USB_OTG (1<<16)
146#define INTMSK_UART0 (0) /* Unknown */
147#define INTMSK_UART1 (1<<12)
148#define INTMSK_UART2 (1<<7)
149#else
150#define INTMSK_TIMERA (1<<5) 140#define INTMSK_TIMERA (1<<5)
151#define INTMSK_TIMERB (1<<7) 141#define INTMSK_TIMERB (1<<7)
152#define INTMSK_TIMERC (1<<8) 142#define INTMSK_TIMERC (1<<8)
153#define INTMSK_TIMERD (1<<9) 143#define INTMSK_TIMERD (1<<9)
154#define INTMSK_UART0 (1<<22) 144#define INTMSK_UART0 (1<<22)
155#define INTMSK_UART1 (1<<14) 145#define INTMSK_UART1 (1<<14)
156#endif
157#define PRIORITY (*(REG32_PTR_T)(0x39C0000C)) /* IRQ priority control register */ 146#define PRIORITY (*(REG32_PTR_T)(0x39C0000C)) /* IRQ priority control register */
158#define INTPND (*(REG32_PTR_T)(0x39C00010)) /* Indicates the interrupt request status. */ 147#define INTPND (*(REG32_PTR_T)(0x39C00010)) /* Indicates the interrupt request status. */
159#define INTOFFSET (*(REG32_PTR_T)(0x39C00014)) /* Indicates the IRQ interrupt request source */ 148#define INTOFFSET (*(REG32_PTR_T)(0x39C00014)) /* Indicates the IRQ interrupt request source */
160#define EINTPOL (*(REG32_PTR_T)(0x39C00018)) /* Indicates external interrupt polarity */ 149#define EINTPOL (*(REG32_PTR_T)(0x39C00018)) /* Indicates external interrupt polarity */
161#define EINTPEND (*(REG32_PTR_T)(0x39C0001C)) /* Indicates whether external interrupts are pending. */ 150#define EINTPEND (*(REG32_PTR_T)(0x39C0001C)) /* Indicates whether external interrupts are pending. */
162#define EINTMSK (*(REG32_PTR_T)(0x39C00020)) /* Indicates whether external interrupts are masked */ 151#define EINTMSK (*(REG32_PTR_T)(0x39C00020)) /* Indicates whether external interrupts are masked */
152#else /* S5L8701 */
153#define SRCPND (*(REG32_PTR_T)(0x39C00000)) /* Indicates the interrupt request status. */
154#define INTMOD (*(REG32_PTR_T)(0x39C00004)) /* Interrupt mode register. */
155#define INTMSK (*(REG32_PTR_T)(0x39C00008)) /* Determines which interrupt source is masked. The */
156#define INTMSK_EINTG0 (1<<1)
157#define INTMSK_EINTG1 (1<<2)
158#define INTMSK_EINTG2 (1<<3)
159#define INTMSK_EINTG3 (1<<4)
160#define INTMSK_TIMERA (1<<5)
161#define INTMSK_TIMERB (1<<5)
162#define INTMSK_TIMERC (1<<5)
163#define INTMSK_TIMERD (1<<5)
164#define INTMSK_ECC (1<<19)
165#define INTMSK_USB_OTG (1<<16)
166#define INTMSK_UART0 (0) /* (AFAIK) no IRQ to ICU, uses EINTG0 */
167#define INTMSK_UART1 (1<<12)
168#define INTMSK_UART2 (1<<7)
169#define PRIORITY (*(REG32_PTR_T)(0x39C0000C)) /* IRQ priority control register */
170#define INTPND (*(REG32_PTR_T)(0x39C00010)) /* Indicates the interrupt request status. */
171#define INTOFFSET (*(REG32_PTR_T)(0x39C00014)) /* Indicates the IRQ interrupt request source */
172/*
173 * s5l8701 GPIO (External) Interrupt Controller.
174 *
175 * At first glance it looks very similar to gpio-s5l8702, but
176 * not fully tested, so this information could be wrong.
177 *
178 * Group0[31:10] Not used
179 * [9] UART0 IRQ
180 * [8] VBUS
181 * [7:0] PDAT1
182 * Group1[31:0] PDAT5:PDAT4:PDAT3:PDAT2
183 * Group2[31:0] PDAT11:PDAT10:PDAT7:PDAT6
184 * Group3[31:0] PDAT15:PDAT14:PDAT13:PDAT12
185 */
186#define GPIOIC_INTLEVEL(g) (*(REG32_PTR_T)(0x39C00018 + 4*(g)))
187#define GPIOIC_INTSTAT(g) (*(REG32_PTR_T)(0x39C00028 + 4*(g)))
188#define GPIOIC_INTEN(g) (*(REG32_PTR_T)(0x39C00038 + 4*(g)))
189#define GPIOIC_INTTYPE(g) (*(REG32_PTR_T)(0x39C00048 + 4*(g)))
190#endif
163 191
164/* 07. MEMORY INTERFACE UNIT (MIU) */ 192/* 07. MEMORY INTERFACE UNIT (MIU) */
165 193
diff --git a/firmware/target/arm/s5l8700/ipodnano2g/serial-nano2g.c b/firmware/target/arm/s5l8700/ipodnano2g/serial-nano2g.c
index 487984fbe3..5ff0b0ca25 100644
--- a/firmware/target/arm/s5l8700/ipodnano2g/serial-nano2g.c
+++ b/firmware/target/arm/s5l8700/ipodnano2g/serial-nano2g.c
@@ -47,6 +47,9 @@
47 47
48 48
49extern const struct uartc s5l8701_uartc0; 49extern const struct uartc s5l8701_uartc0;
50#ifdef IPOD_ACCESSORY_PROTOCOL
51void iap_rx_isr(int, char*, char*, uint32_t);
52#endif
50 53
51struct uartc_port ser_port IDATA_ATTR = 54struct uartc_port ser_port IDATA_ATTR =
52{ 55{
@@ -61,7 +64,11 @@ struct uartc_port ser_port IDATA_ATTR =
61 .clkhz = NANO2G_UART_CLK_HZ, 64 .clkhz = NANO2G_UART_CLK_HZ,
62 65
63 /* interrupt callbacks */ 66 /* interrupt callbacks */
67#ifdef IPOD_ACCESSORY_PROTOCOL
68 .rx_cb = iap_rx_isr,
69#else
64 .rx_cb = NULL, 70 .rx_cb = NULL,
71#endif
65 .tx_cb = NULL, /* polling */ 72 .tx_cb = NULL, /* polling */
66}; 73};
67 74
@@ -94,3 +101,109 @@ void tx_writec(unsigned char c)
94{ 101{
95 uartc_port_tx_byte(&ser_port, c); 102 uartc_port_tx_byte(&ser_port, c);
96} 103}
104
105
106#ifdef IPOD_ACCESSORY_PROTOCOL
107#include "iap.h"
108
109static enum {
110 ABR_STATUS_LAUNCHED, /* ST_SYNC */
111 ABR_STATUS_SYNCING, /* ST_SOF */
112 ABR_STATUS_DONE
113} abr_status;
114
115void serial_bitrate(int rate)
116{
117 logf("[%lu] serial_bitrate(%d)", (uint32_t)USEC_TIMER, rate);
118
119 if (rate == 0) {
120 /* Using auto-bitrate (ABR) to detect accessory Tx speed:
121 *
122 * + Here:
123 * - Disable Rx logic to clean the FIFO and the shift
124 * register, thus no Rx data interrupts are generated.
125 * - Launch ABR and wait for a low pulse in Rx line.
126 *
127 * + In ISR, when a low pulse is detected (ideally it is the
128 * start bit of 0xff):
129 * - Calculate and configure detected speed.
130 * - Enable Rx to verify that the next received data frame
131 * is 0x55 or 0xff:
132 * - If so, it's assumed bit rate is correctly detected,
133 * it will not be modified until speed is changed using
134 * RB options menu.
135 * - If not, reset iAP state machine and launch a new ABR.
136 */
137 uartc_port_set_rx_mode(&ser_port, UCON_MODE_DISABLED);
138 uartc_port_abr_start(&ser_port);
139 abr_status = ABR_STATUS_LAUNCHED;
140 }
141 else {
142 uint32_t brdata;
143 if (rate == 57600) brdata = BRDATA_57600;
144 else if (rate == 38400) brdata = BRDATA_38400;
145 else if (rate == 19200) brdata = BRDATA_19200;
146 else brdata = BRDATA_9600;
147 uartc_port_abr_stop(&ser_port); /* abort ABR if already launched */
148 uartc_port_set_bitrate_raw(&ser_port, brdata);
149 uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ);
150 abr_status = ABR_STATUS_DONE;
151 }
152}
153
154void iap_rx_isr(int len, char *data, char *err, uint32_t abr_cnt)
155{
156 /* ignore Rx errors, upper layer will discard bad packets */
157 (void) err;
158
159 static int sync_retry;
160
161 if (abr_status == ABR_STATUS_LAUNCHED) {
162 /* autobauding */
163 if (abr_cnt) {
164 #define BR2CNT(s) (NANO2G_UART_CLK_HZ / (unsigned)(s))
165 if (abr_cnt < BR2CNT(57600*1.1) || abr_cnt > BR2CNT(9600*0.9)) {
166 /* detected speed out of range, relaunch ABR */
167 uartc_port_abr_start(&ser_port);
168 return;
169 }
170 /* valid speed detected, select it */
171 uint32_t brdata;
172 if (abr_cnt < BR2CNT(48000)) brdata = BRDATA_57600;
173 else if (abr_cnt < BR2CNT(33600)) brdata = BRDATA_38400;
174 else if (abr_cnt < BR2CNT(24000)) brdata = BRDATA_28800;
175 else if (abr_cnt < BR2CNT(14400)) brdata = BRDATA_19200;
176 else brdata = BRDATA_9600;
177
178 /* set detected speed */
179 uartc_port_set_bitrate_raw(&ser_port, brdata);
180 uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ);
181
182 /* enter SOF state */
183 iap_getc(0xff);
184
185 abr_status = ABR_STATUS_SYNCING;
186 sync_retry = 2; /* we are expecting [0xff] 0x55 */
187 }
188 }
189
190 /* process received data */
191 while (len--)
192 {
193 bool sync_done = !iap_getc(*data++);
194
195 if (abr_status == ABR_STATUS_SYNCING)
196 {
197 if (sync_done) {
198 abr_status = ABR_STATUS_DONE;
199 }
200 else if (--sync_retry == 0) {
201 /* invalid speed detected, relaunch ABR
202 discarding remaining data (if any) */
203 serial_bitrate(0);
204 break;
205 }
206 }
207 }
208}
209#endif /* IPOD_ACCESSORY_PROTOCOL */
diff --git a/firmware/target/arm/s5l8700/system-s5l8700.c b/firmware/target/arm/s5l8700/system-s5l8700.c
index 728fea0432..226464fb08 100644
--- a/firmware/target/arm/s5l8700/system-s5l8700.c
+++ b/firmware/target/arm/s5l8700/system-s5l8700.c
@@ -85,6 +85,10 @@ default_interrupt(INT_ADC);
85#if CONFIG_CPU==S5L8701 85#if CONFIG_CPU==S5L8701
86default_interrupt(INT_UNK); 86default_interrupt(INT_UNK);
87default_interrupt(INT_UART2); 87default_interrupt(INT_UART2);
88default_interrupt(EINT_G0);
89default_interrupt(EINT_G1);
90default_interrupt(EINT_G2);
91default_interrupt(EINT_G3);
88#endif 92#endif
89 93
90 94
@@ -99,8 +103,8 @@ void INT_TIMER(void)
99 103
100#if CONFIG_CPU==S5L8701 104#if CONFIG_CPU==S5L8701
101static void (* const irqvector[])(void) = 105static void (* const irqvector[])(void) =
102{ /* still 90% unverified and probably incorrect */ 106{ /* still 80% unverified and probably incorrect */
103 EXT0,EXT1,EXT2,EINT_VBUS,EINTG,INT_TIMER,INT_WDT,INT_UART2, 107 EXT0,EINT_G0,EINT_G1,EINT_G2,EINT_G3,INT_TIMER,INT_WDT,INT_UART2,
104 INT_UNK,INT_UNK,INT_DMA,INT_ALARM_RTC,INT_UART1,INT_UNK,INT_UNK,INT_USB_HOST, 108 INT_UNK,INT_UNK,INT_DMA,INT_ALARM_RTC,INT_UART1,INT_UNK,INT_UNK,INT_USB_HOST,
105 INT_USB_FUNC,INT_LCDC_0,INT_LCDC_1,INT_CALM,INT_ATA,INT_UNK,INT_SPDIF_OUT,INT_ECC, 109 INT_USB_FUNC,INT_LCDC_0,INT_LCDC_1,INT_CALM,INT_ATA,INT_UNK,INT_SPDIF_OUT,INT_ECC,
106 INT_SDCI,INT_LCD,INT_WHEEL,INT_IIC,RESERVED2,INT_MSTICK,INT_ADC_WAKEUP,INT_ADC 110 INT_SDCI,INT_LCD,INT_WHEEL,INT_IIC,RESERVED2,INT_MSTICK,INT_ADC_WAKEUP,INT_ADC
@@ -117,8 +121,8 @@ static void (* const irqvector[])(void) =
117 121
118#if CONFIG_CPU==S5L8701 122#if CONFIG_CPU==S5L8701
119static const char * const irqname[] = 123static const char * const irqname[] =
120{ /* still 90% unverified and probably incorrect */ 124{ /* still 80% unverified and probably incorrect */
121 "EXT0","EXT1","EXT2","EINT_VBUS","EINTG","INT_TIMER","INT_WDT","INT_UART2", 125 "EXT0","EINT_G0","EINT_G1","EINT_G2","EINT_G3","INT_TIMER","INT_WDT","INT_UART2",
122 "INT_UNK1","INT_UNK2","INT_DMA","INT_ALARM_RTC","INT_UART1","INT_UNK3","INT_UNK4","INT_USB_HOST", 126 "INT_UNK1","INT_UNK2","INT_DMA","INT_ALARM_RTC","INT_UART1","INT_UNK3","INT_UNK4","INT_USB_HOST",
123 "INT_USB_FUNC","INT_LCDC_0","INT_LCDC_1","INT_CALM","INT_ATA","INT_UNK5","INT_SPDIF_OUT","INT_ECC", 127 "INT_USB_FUNC","INT_LCDC_0","INT_LCDC_1","INT_CALM","INT_ATA","INT_UNK5","INT_SPDIF_OUT","INT_ECC",
124 "INT_SDCI","INT_LCD","INT_WHEEL","INT_IIC","Reserved","INT_MSTICK","INT_ADC_WAKEUP","INT_ADC" 128 "INT_SDCI","INT_LCD","INT_WHEEL","INT_IIC","Reserved","INT_MSTICK","INT_ADC_WAKEUP","INT_ADC"
diff --git a/firmware/target/arm/s5l8700/uart-s5l8701.c b/firmware/target/arm/s5l8700/uart-s5l8701.c
index 00c9322e47..b9fb1632ac 100644
--- a/firmware/target/arm/s5l8700/uart-s5l8701.c
+++ b/firmware/target/arm/s5l8700/uart-s5l8701.c
@@ -68,7 +68,7 @@ static uint8_t clockgate_uartc[S5L8701_N_UARTC] = {
68 CLOCKGATE_UARTC0, CLOCKGATE_UARTC1, CLOCKGATE_UARTC2 }; 68 CLOCKGATE_UARTC0, CLOCKGATE_UARTC1, CLOCKGATE_UARTC2 };
69 69
70static int intmsk_uart[S5L8701_N_UARTC] = { 70static int intmsk_uart[S5L8701_N_UARTC] = {
71 INTMSK_UART0, INTMSK_UART1, INTMSK_UART2 }; 71 INTMSK_EINTG0, INTMSK_UART1, INTMSK_UART2 };
72 72
73/* 73/*
74 * Device level functions specific to S5L8701 74 * Device level functions specific to S5L8701
@@ -107,6 +107,7 @@ void uart_target_disable_gpio(int uart_id, int port_id)
107void uart_target_enable_irq(int uart_id, int port_id) 107void uart_target_enable_irq(int uart_id, int port_id)
108{ 108{
109 (void) port_id; 109 (void) port_id;
110 if (uart_id == 0) GPIOIC_INTEN(0) = 0x200;
110 INTMSK |= intmsk_uart[uart_id]; 111 INTMSK |= intmsk_uart[uart_id];
111} 112}
112 113
@@ -114,11 +115,13 @@ void uart_target_disable_irq(int uart_id, int port_id)
114{ 115{
115 (void) port_id; 116 (void) port_id;
116 INTMSK &= ~intmsk_uart[uart_id]; 117 INTMSK &= ~intmsk_uart[uart_id];
118 if (uart_id == 0) GPIOIC_INTEN(0) = 0;
117} 119}
118 120
119void uart_target_clear_irq(int uart_id, int port_id) 121void uart_target_clear_irq(int uart_id, int port_id)
120{ 122{
121 (void) port_id; 123 (void) port_id;
124 if (uart_id == 0) GPIOIC_INTSTAT(0) = 0x200;
122 SRCPND |= intmsk_uart[uart_id]; 125 SRCPND |= intmsk_uart[uart_id];
123} 126}
124 127
@@ -135,10 +138,20 @@ void uart_target_disable_clocks(int uart_id)
135/* 138/*
136 * ISRs 139 * ISRs
137 */ 140 */
138void ICODE_ATTR INT_UART0(void) 141
142/* On Nano2G, PORT0 interrupts are not used when iAP is disabled */
143#if !defined(IPOD_NANO2G) || defined(IPOD_ACCESSORY_PROTOCOL)
144/*
145 * UART0 IRQ is connected to EINTG0, this is a quick patch, a "real"
146 * EINT handler will be needed if in future we use more than one IRQ
147 * on this group.
148 */
149void ICODE_ATTR EINT_G0(void)
139{ 150{
151 GPIOIC_INTSTAT(0) = 0x200; /* clear external IRQ */
140 uartc_callback(&s5l8701_uartc0, 0); 152 uartc_callback(&s5l8701_uartc0, 0);
141} 153}
154#endif
142 155
143/* UARTC1,2 not used on Nano2G */ 156/* UARTC1,2 not used on Nano2G */
144#ifndef IPOD_NANO2G 157#ifndef IPOD_NANO2G