iPod Nano2G: add IPOD_ACCESSORY_PROTOCOL

Change-Id: I78a19972624504bc802d96b9b8e9cec132164c2c
This commit is contained in:
Cástor Muñoz 2016-05-14 14:24:49 +02:00
parent 60fb707203
commit 9a4cd2eaee
5 changed files with 183 additions and 20 deletions

View file

@ -231,9 +231,14 @@
/* Define this if you can switch on/off the accessory power supply */
#define HAVE_ACCESSORY_SUPPLY
//#define IPOD_ACCESSORY_PROTOCOL
#ifdef LOGF_SERIAL
/* Serial */
#ifndef BOOTLOADER
#define HAVE_SERIAL
/* Disable iAP when LOGF_SERIAL is enabled to avoid conflicts */
#ifndef LOGF_SERIAL
#define IPOD_ACCESSORY_PROTOCOL
#endif
#endif
/* Define this, if you can switch on/off the lineout */

View file

@ -133,33 +133,61 @@
#define PWRCONEXT (*(REG32_PTR_T)(0x3C500040)) /* Clock power control register 2 */
/* 06. INTERRUPT CONTROLLER UNIT */
#if CONFIG_CPU==S5L8700
#define SRCPND (*(REG32_PTR_T)(0x39C00000)) /* Indicates the interrupt request status. */
#define INTMOD (*(REG32_PTR_T)(0x39C00004)) /* Interrupt mode register. */
#define INTMSK (*(REG32_PTR_T)(0x39C00008)) /* Determines which interrupt source is masked. The */
#if CONFIG_CPU==S5L8701
#define INTMSK_TIMERA (1<<5)
#define INTMSK_TIMERB (1<<5)
#define INTMSK_TIMERC (1<<5)
#define INTMSK_TIMERD (1<<5)
#define INTMSK_ECC (1<<19)
#define INTMSK_USB_OTG (1<<16)
#define INTMSK_UART0 (0) /* Unknown */
#define INTMSK_UART1 (1<<12)
#define INTMSK_UART2 (1<<7)
#else
#define INTMSK_TIMERA (1<<5)
#define INTMSK_TIMERB (1<<7)
#define INTMSK_TIMERC (1<<8)
#define INTMSK_TIMERD (1<<9)
#define INTMSK_UART0 (1<<22)
#define INTMSK_UART1 (1<<14)
#endif
#define PRIORITY (*(REG32_PTR_T)(0x39C0000C)) /* IRQ priority control register */
#define INTPND (*(REG32_PTR_T)(0x39C00010)) /* Indicates the interrupt request status. */
#define INTOFFSET (*(REG32_PTR_T)(0x39C00014)) /* Indicates the IRQ interrupt request source */
#define EINTPOL (*(REG32_PTR_T)(0x39C00018)) /* Indicates external interrupt polarity */
#define EINTPEND (*(REG32_PTR_T)(0x39C0001C)) /* Indicates whether external interrupts are pending. */
#define EINTMSK (*(REG32_PTR_T)(0x39C00020)) /* Indicates whether external interrupts are masked */
#else /* S5L8701 */
#define SRCPND (*(REG32_PTR_T)(0x39C00000)) /* Indicates the interrupt request status. */
#define INTMOD (*(REG32_PTR_T)(0x39C00004)) /* Interrupt mode register. */
#define INTMSK (*(REG32_PTR_T)(0x39C00008)) /* Determines which interrupt source is masked. The */
#define INTMSK_EINTG0 (1<<1)
#define INTMSK_EINTG1 (1<<2)
#define INTMSK_EINTG2 (1<<3)
#define INTMSK_EINTG3 (1<<4)
#define INTMSK_TIMERA (1<<5)
#define INTMSK_TIMERB (1<<5)
#define INTMSK_TIMERC (1<<5)
#define INTMSK_TIMERD (1<<5)
#define INTMSK_ECC (1<<19)
#define INTMSK_USB_OTG (1<<16)
#define INTMSK_UART0 (0) /* (AFAIK) no IRQ to ICU, uses EINTG0 */
#define INTMSK_UART1 (1<<12)
#define INTMSK_UART2 (1<<7)
#define PRIORITY (*(REG32_PTR_T)(0x39C0000C)) /* IRQ priority control register */
#define INTPND (*(REG32_PTR_T)(0x39C00010)) /* Indicates the interrupt request status. */
#define INTOFFSET (*(REG32_PTR_T)(0x39C00014)) /* Indicates the IRQ interrupt request source */
/*
* s5l8701 GPIO (External) Interrupt Controller.
*
* At first glance it looks very similar to gpio-s5l8702, but
* not fully tested, so this information could be wrong.
*
* Group0[31:10] Not used
* [9] UART0 IRQ
* [8] VBUS
* [7:0] PDAT1
* Group1[31:0] PDAT5:PDAT4:PDAT3:PDAT2
* Group2[31:0] PDAT11:PDAT10:PDAT7:PDAT6
* Group3[31:0] PDAT15:PDAT14:PDAT13:PDAT12
*/
#define GPIOIC_INTLEVEL(g) (*(REG32_PTR_T)(0x39C00018 + 4*(g)))
#define GPIOIC_INTSTAT(g) (*(REG32_PTR_T)(0x39C00028 + 4*(g)))
#define GPIOIC_INTEN(g) (*(REG32_PTR_T)(0x39C00038 + 4*(g)))
#define GPIOIC_INTTYPE(g) (*(REG32_PTR_T)(0x39C00048 + 4*(g)))
#endif
/* 07. MEMORY INTERFACE UNIT (MIU) */

View file

@ -47,6 +47,9 @@
extern const struct uartc s5l8701_uartc0;
#ifdef IPOD_ACCESSORY_PROTOCOL
void iap_rx_isr(int, char*, char*, uint32_t);
#endif
struct uartc_port ser_port IDATA_ATTR =
{
@ -61,7 +64,11 @@ struct uartc_port ser_port IDATA_ATTR =
.clkhz = NANO2G_UART_CLK_HZ,
/* interrupt callbacks */
#ifdef IPOD_ACCESSORY_PROTOCOL
.rx_cb = iap_rx_isr,
#else
.rx_cb = NULL,
#endif
.tx_cb = NULL, /* polling */
};
@ -94,3 +101,109 @@ void tx_writec(unsigned char c)
{
uartc_port_tx_byte(&ser_port, c);
}
#ifdef IPOD_ACCESSORY_PROTOCOL
#include "iap.h"
static enum {
ABR_STATUS_LAUNCHED, /* ST_SYNC */
ABR_STATUS_SYNCING, /* ST_SOF */
ABR_STATUS_DONE
} abr_status;
void serial_bitrate(int rate)
{
logf("[%lu] serial_bitrate(%d)", (uint32_t)USEC_TIMER, rate);
if (rate == 0) {
/* Using auto-bitrate (ABR) to detect accessory Tx speed:
*
* + Here:
* - Disable Rx logic to clean the FIFO and the shift
* register, thus no Rx data interrupts are generated.
* - Launch ABR and wait for a low pulse in Rx line.
*
* + In ISR, when a low pulse is detected (ideally it is the
* start bit of 0xff):
* - Calculate and configure detected speed.
* - Enable Rx to verify that the next received data frame
* is 0x55 or 0xff:
* - If so, it's assumed bit rate is correctly detected,
* it will not be modified until speed is changed using
* RB options menu.
* - If not, reset iAP state machine and launch a new ABR.
*/
uartc_port_set_rx_mode(&ser_port, UCON_MODE_DISABLED);
uartc_port_abr_start(&ser_port);
abr_status = ABR_STATUS_LAUNCHED;
}
else {
uint32_t brdata;
if (rate == 57600) brdata = BRDATA_57600;
else if (rate == 38400) brdata = BRDATA_38400;
else if (rate == 19200) brdata = BRDATA_19200;
else brdata = BRDATA_9600;
uartc_port_abr_stop(&ser_port); /* abort ABR if already launched */
uartc_port_set_bitrate_raw(&ser_port, brdata);
uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ);
abr_status = ABR_STATUS_DONE;
}
}
void iap_rx_isr(int len, char *data, char *err, uint32_t abr_cnt)
{
/* ignore Rx errors, upper layer will discard bad packets */
(void) err;
static int sync_retry;
if (abr_status == ABR_STATUS_LAUNCHED) {
/* autobauding */
if (abr_cnt) {
#define BR2CNT(s) (NANO2G_UART_CLK_HZ / (unsigned)(s))
if (abr_cnt < BR2CNT(57600*1.1) || abr_cnt > BR2CNT(9600*0.9)) {
/* detected speed out of range, relaunch ABR */
uartc_port_abr_start(&ser_port);
return;
}
/* valid speed detected, select it */
uint32_t brdata;
if (abr_cnt < BR2CNT(48000)) brdata = BRDATA_57600;
else if (abr_cnt < BR2CNT(33600)) brdata = BRDATA_38400;
else if (abr_cnt < BR2CNT(24000)) brdata = BRDATA_28800;
else if (abr_cnt < BR2CNT(14400)) brdata = BRDATA_19200;
else brdata = BRDATA_9600;
/* set detected speed */
uartc_port_set_bitrate_raw(&ser_port, brdata);
uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ);
/* enter SOF state */
iap_getc(0xff);
abr_status = ABR_STATUS_SYNCING;
sync_retry = 2; /* we are expecting [0xff] 0x55 */
}
}
/* process received data */
while (len--)
{
bool sync_done = !iap_getc(*data++);
if (abr_status == ABR_STATUS_SYNCING)
{
if (sync_done) {
abr_status = ABR_STATUS_DONE;
}
else if (--sync_retry == 0) {
/* invalid speed detected, relaunch ABR
discarding remaining data (if any) */
serial_bitrate(0);
break;
}
}
}
}
#endif /* IPOD_ACCESSORY_PROTOCOL */

View file

@ -85,6 +85,10 @@ default_interrupt(INT_ADC);
#if CONFIG_CPU==S5L8701
default_interrupt(INT_UNK);
default_interrupt(INT_UART2);
default_interrupt(EINT_G0);
default_interrupt(EINT_G1);
default_interrupt(EINT_G2);
default_interrupt(EINT_G3);
#endif
@ -99,8 +103,8 @@ void INT_TIMER(void)
#if CONFIG_CPU==S5L8701
static void (* const irqvector[])(void) =
{ /* still 90% unverified and probably incorrect */
EXT0,EXT1,EXT2,EINT_VBUS,EINTG,INT_TIMER,INT_WDT,INT_UART2,
{ /* still 80% unverified and probably incorrect */
EXT0,EINT_G0,EINT_G1,EINT_G2,EINT_G3,INT_TIMER,INT_WDT,INT_UART2,
INT_UNK,INT_UNK,INT_DMA,INT_ALARM_RTC,INT_UART1,INT_UNK,INT_UNK,INT_USB_HOST,
INT_USB_FUNC,INT_LCDC_0,INT_LCDC_1,INT_CALM,INT_ATA,INT_UNK,INT_SPDIF_OUT,INT_ECC,
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) =
#if CONFIG_CPU==S5L8701
static const char * const irqname[] =
{ /* still 90% unverified and probably incorrect */
"EXT0","EXT1","EXT2","EINT_VBUS","EINTG","INT_TIMER","INT_WDT","INT_UART2",
{ /* still 80% unverified and probably incorrect */
"EXT0","EINT_G0","EINT_G1","EINT_G2","EINT_G3","INT_TIMER","INT_WDT","INT_UART2",
"INT_UNK1","INT_UNK2","INT_DMA","INT_ALARM_RTC","INT_UART1","INT_UNK3","INT_UNK4","INT_USB_HOST",
"INT_USB_FUNC","INT_LCDC_0","INT_LCDC_1","INT_CALM","INT_ATA","INT_UNK5","INT_SPDIF_OUT","INT_ECC",
"INT_SDCI","INT_LCD","INT_WHEEL","INT_IIC","Reserved","INT_MSTICK","INT_ADC_WAKEUP","INT_ADC"

View file

@ -68,7 +68,7 @@ static uint8_t clockgate_uartc[S5L8701_N_UARTC] = {
CLOCKGATE_UARTC0, CLOCKGATE_UARTC1, CLOCKGATE_UARTC2 };
static int intmsk_uart[S5L8701_N_UARTC] = {
INTMSK_UART0, INTMSK_UART1, INTMSK_UART2 };
INTMSK_EINTG0, INTMSK_UART1, INTMSK_UART2 };
/*
* Device level functions specific to S5L8701
@ -107,6 +107,7 @@ void uart_target_disable_gpio(int uart_id, int port_id)
void uart_target_enable_irq(int uart_id, int port_id)
{
(void) port_id;
if (uart_id == 0) GPIOIC_INTEN(0) = 0x200;
INTMSK |= intmsk_uart[uart_id];
}
@ -114,11 +115,13 @@ void uart_target_disable_irq(int uart_id, int port_id)
{
(void) port_id;
INTMSK &= ~intmsk_uart[uart_id];
if (uart_id == 0) GPIOIC_INTEN(0) = 0;
}
void uart_target_clear_irq(int uart_id, int port_id)
{
(void) port_id;
if (uart_id == 0) GPIOIC_INTSTAT(0) = 0x200;
SRCPND |= intmsk_uart[uart_id];
}
@ -135,10 +138,20 @@ void uart_target_disable_clocks(int uart_id)
/*
* ISRs
*/
void ICODE_ATTR INT_UART0(void)
/* On Nano2G, PORT0 interrupts are not used when iAP is disabled */
#if !defined(IPOD_NANO2G) || defined(IPOD_ACCESSORY_PROTOCOL)
/*
* UART0 IRQ is connected to EINTG0, this is a quick patch, a "real"
* EINT handler will be needed if in future we use more than one IRQ
* on this group.
*/
void ICODE_ATTR EINT_G0(void)
{
GPIOIC_INTSTAT(0) = 0x200; /* clear external IRQ */
uartc_callback(&s5l8701_uartc0, 0);
}
#endif
/* UARTC1,2 not used on Nano2G */
#ifndef IPOD_NANO2G