diff --git a/firmware/SOURCES b/firmware/SOURCES index cd1bf492d8..098eca427f 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1610,7 +1610,7 @@ target/arm/s5l8702/ipod6g/pmu-ipod6g.c target/arm/s5l8702/ipod6g/rtc-ipod6g.c target/arm/s5l8700/usb-nano2g-6g.c #ifdef HAVE_SERIAL -target/arm/s5l8702/uc8702.c +target/arm/uc870x.c target/arm/s5l8702/uart-s5l8702.c target/arm/s5l8702/ipod6g/serial-ipod6g.c #endif diff --git a/firmware/export/s5l8702.h b/firmware/export/s5l8702.h index 00d92a58b1..59170b04ea 100644 --- a/firmware/export/s5l8702.h +++ b/firmware/export/s5l8702.h @@ -711,9 +711,10 @@ /////UART///// -/* UC8702 uart controller */ -#define S5L8702_UART_BASE 0x3cc00000 -#define S5L8702_UART_PORT_MAX 4 +/* s5l8702 UC870X HW: 1 UARTC, 4 ports */ +#define UARTC_BASE_ADDR 0x3CC00000 +#define UARTC_N_PORTS 4 +#define UARTC_PORT_OFFSET 0x4000 /////CLOCK GATES///// @@ -737,7 +738,7 @@ #define CLOCKGATE_TIMER 37 #define CLOCKGATE_I2C1 38 #define CLOCKGATE_I2S0 39 -#define CLOCKGATE_UART 41 +#define CLOCKGATE_UARTC 41 #define CLOCKGATE_I2S1 42 #define CLOCKGATE_SPI1 43 #define CLOCKGATE_GPIO 44 diff --git a/firmware/target/arm/s5l8702/uc8702.h b/firmware/export/uc870x.h similarity index 83% rename from firmware/target/arm/s5l8702/uc8702.h rename to firmware/export/uc870x.h index 36e4f8faa4..ad7168fe64 100644 --- a/firmware/target/arm/s5l8702/uc8702.h +++ b/firmware/export/uc870x.h @@ -18,32 +18,27 @@ * KIND, either express or implied. * ****************************************************************************/ -#ifndef __UC8702_H__ -#define __UC8702_H__ +#ifndef __UC870X_H__ +#define __UC870X_H__ #include #include +#include "config.h" +#include "system.h" +#include "uart-target.h" -/* s5l8702 UART controller (UC8702): + +/* + * UC870x: UART controller for s5l870x * - * This UART is similar to the UART included in s5l8700 (see also - * s3c2416 and s3c6400 datasheets), UC8702 adds fine tunning for - * Tx/Rx bitrate and autobauding. + * This UART is similar to the UART described in s5l8700 datasheet, + * (see also s3c2416 and s3c6400 datasheets). On s5l8701/2 the UC870x + * includes autobauding, and fine tunning for Tx/Rx on s5l8702. */ -/*#define UC8702_DEBUG*/ - /* - * uc8702 HW definitions - */ -#define UART_PORT_MAX 4 -#define UART_FIFO_SIZE 16 - -#define UART_PORT_BASE(b,i) ((b) + 0x4000 * (i)) - -/* - * controller registers + * Controller registers */ #define REG32_PTR_T volatile uint32_t * @@ -58,10 +53,14 @@ #define UTXH(ba) (*((REG32_PTR_T)((ba) + 0x20))) /* transmission hold */ #define URXH(ba) (*((REG32_PTR_T)((ba) + 0x24))) /* receive buffer */ #define UBRDIV(ba) (*((REG32_PTR_T)((ba) + 0x28))) /* baud rate divisor */ +#if CONFIG_CPU != S5L8700 #define UABRCNT(ba) (*((REG32_PTR_T)((ba) + 0x2c))) /* autobaud counter */ #define UABRSTAT(ba) (*((REG32_PTR_T)((ba) + 0x30))) /* autobaud status */ +#endif +#if CONFIG_CPU == S5L8702 #define UBRCONTX(ba) (*((REG32_PTR_T)((ba) + 0x34))) /* Tx frame config */ #define UBRCONRX(ba) (*((REG32_PTR_T)((ba) + 0x38))) /* Rx frame config */ +#endif /* ULCON register */ #define ULCON_DATA_BITS_MASK 0x3 @@ -107,13 +106,31 @@ #define UCON_CLKSEL_PCLK 0 /* internal */ #define UCON_CLKSEL_ECLK 1 /* external */ +#if CONFIG_CPU == S5L8702 #define UCON_RX_TOUT_INT_BIT (1 << 11) /* Rx timeout INT enable */ +#endif #define UCON_RX_INT_BIT (1 << 12) /* Rx INT enable */ #define UCON_TX_INT_BIT (1 << 13) /* Tx INT enable */ #define UCON_ERR_INT_BIT (1 << 14) /* Rx error INT enable */ #define UCON_MODEM_INT_BIT (1 << 15) /* modem INT enable (TBC) */ +#if CONFIG_CPU != S5L8700 #define UCON_AUTOBR_INT_BIT (1 << 16) /* autobauding INT enable */ #define UCON_AUTOBR_START_BIT (1 << 17) /* autobauding start/stop */ +#endif + +#if CONFIG_CPU == S5L8701 +/* WTF! ABR bits are swapped on reads, so don't forget to + always use this workaround to read the UCON register. */ +static inline uint32_t _UCON_RD(uint32_t ba) +{ + uint32_t ucon = UCON(ba); + return ((ucon & 0xffff) | + ((ucon & UCON_AUTOBR_INT_BIT) << 1) | + ((ucon & UCON_AUTOBR_START_BIT) >> 1)); +} +#else +#define _UCON_RD(ba) UCON(ba) +#endif /* UFCON register */ #define UFCON_FIFO_ENABLE_BIT (1 << 0) @@ -142,12 +159,16 @@ #define UTRSTAT_RXBUF_RDY_BIT (1 << 0) #define UTRSTAT_TXBUF_EMPTY_BIT (1 << 1) #define UTRSTAT_TX_EMPTY_BIT (1 << 2) +#if CONFIG_CPU == S5L8702 #define UTRSTAT_RX_TOUT_INT_BIT (1 << 3) /* Rx timeout INT status */ +#endif #define UTRSTAT_RX_INT_BIT (1 << 4) #define UTRSTAT_TX_INT_BIT (1 << 5) #define UTRSTAT_ERR_INT_BIT (1 << 6) -#define UTRSTAT_MODEM_INT_BIT (1 << 7) /* modem INT status (TBC) */ +#define UTRSTAT_MODEM_INT_BIT (1 << 7) /* modem INT status */ +#if CONFIG_CPU != S5L8700 #define UTRSTAT_AUTOBR_INT_BIT (1 << 8) /* autobauding INT status */ +#endif /* UERSTAT register */ #define UERSTAT_OVERRUN_BIT (1 << 0) @@ -170,6 +191,8 @@ #define UMSTAT_CTS_ACTIVE_BIT (1 << 0) #define UMSTAT_CTS_DELTA_BIT (1 << 4) + +#if CONFIG_CPU == S5L8702 /* Bitrate: * * Master UCLK clock is divided by 16 to serialize data, UBRDIV is @@ -192,7 +215,10 @@ #define UBRCON_JITTER_INC 1 /* increment 1/16 bit width */ #define UBRCON_JITTER_UNUSED 2 /* does nothing */ #define UBRCON_JITTER_DEC 3 /* decremet 1/16 bit width */ +#endif /* CONFIG_CPU == S5L8702 */ + +#if CONFIG_CPU != S5L8700 /* Autobauding: * * Initial UABRSTAT is NOT_INIT, it goes to READY when either of @@ -223,6 +249,13 @@ #define UABRSTAT_STATUS_NOT_INIT 0 /* initial status */ #define UABRSTAT_STATUS_READY 1 /* machine is ready */ #define UABRSTAT_STATUS_COUNTING 2 /* count in progress */ +#endif /* CONFIG_CPU != S5L8700 */ + + +/* + * other HW definitions + */ +#define UART_FIFO_SIZE 16 /* @@ -231,58 +264,70 @@ struct uartc { /* static configuration */ - const uint32_t baddr; - /* private */ - struct uartc_port *port_l[UART_PORT_MAX]; + uint8_t id; + uint8_t n_ports; + uint16_t port_off; + uint32_t baddr; + struct uartc_port **port_l; }; struct uartc_port { /* static configuration */ - struct uartc * const uartc; + const struct uartc * const uartc; const uint8_t id; /* port number */ const uint8_t rx_trg; /* UFCON_RX_FIFO_TRG_xxx */ const uint8_t tx_trg; /* UFCON_TX_FIFO_TRG_xxx */ const uint8_t clksel; /* UFCON_CLKSEL_xxx */ const uint32_t clkhz; /* UCLK (PCLK or ECLK) frequency */ void (* const tx_cb) (int len); /* ISRs */ +#if CONFIG_CPU != S5L8700 void (* const rx_cb) (int len, char *data, char *err, uint32_t abr_cnt); +#else + void (* const rx_cb) (int len, char *data, char *err); +#endif /* private */ uint32_t baddr; uint32_t utrstat_int_mask; - bool abr_aborted; uint8_t rx_data[UART_FIFO_SIZE]; /* data buffer for rx_cb */ uint8_t rx_err[UART_FIFO_SIZE]; /* error buffer for rx_cb */ +#if CONFIG_CPU != S5L8700 + bool abr_aborted; +#endif -#ifdef UC8702_DEBUG +#ifdef UC870X_DEBUG uint32_t n_tx_bytes; uint32_t n_rx_bytes; uint32_t n_ovr_err; uint32_t n_parity_err; uint32_t n_frame_err; uint32_t n_break_detect; +#if CONFIG_CPU != S5L8700 + /* autobauding */ uint32_t n_abnormal0; uint32_t n_abnormal1; #endif +#endif }; /* - * uc8702 low level API + * uc870x low level API */ /* Initialization */ -void uartc_open(struct uartc* uartc); -void uartc_close(struct uartc* uartc); +void uartc_open(const struct uartc* uartc); +void uartc_close(const struct uartc* uartc); void uartc_port_open(struct uartc_port *port); void uartc_port_close(struct uartc_port *port); void uartc_port_rx_onoff(struct uartc_port *port, bool onoff); void uartc_port_tx_onoff(struct uartc_port *port, bool onoff); /* Configuration */ -void uartc_port_config(struct uartc_port *port, unsigned int speed, - uint8_t data_bits, uint8_t parity, uint8_t stop_bits); +void uartc_port_config(struct uartc_port *port, + uint8_t data_bits, uint8_t parity, uint8_t stop_bits); +void uartc_port_set_bitrate_raw(struct uartc_port *port, uint32_t brdata); void uartc_port_set_bitrate(struct uartc_port *port, unsigned int speed); void uartc_port_set_rx_mode(struct uartc_port *port, uint32_t mode); void uartc_port_set_tx_mode(struct uartc_port *port, uint32_t mode); @@ -297,14 +342,22 @@ bool uartc_port_rx_ready(struct uartc_port *port); uint8_t uartc_port_rx_byte(struct uartc_port *port); uint8_t uartc_port_read_byte(struct uartc_port *port); +#if CONFIG_CPU != S5L8700 /* Autobauding */ void uartc_port_abr_start(struct uartc_port *port); void uartc_port_abr_stop(struct uartc_port *port); +#endif /* ISR */ -void uartc_callback(struct uartc *uartc, int dev); +void uartc_callback(const struct uartc *uartc, int port_id); -#ifdef UC8702_DEBUG +/* Debug */ +#ifdef UC870X_DEBUG +void uartc_port_get_line_info(struct uartc_port *port, + int *tx_status, int *rx_status, + int *tx_speed, int *rx_speed, char *line_cfg); + +#if CONFIG_CPU != S5L8700 enum { ABR_INFO_ST_IDLE, ABR_INFO_ST_LAUNCHED, @@ -312,10 +365,8 @@ enum { ABR_INFO_ST_ABNORMAL }; -void uartc_port_get_line_info(struct uartc_port *port, - int *tx_status, int *rx_status, - int *tx_speed, int *rx_speed, char *line_cfg); - -int uartc_port_get_abr_info(struct uartc_port *port, unsigned int *abr_cnt); +int uartc_port_get_abr_info(struct uartc_port *port, uint32_t *abr_cnt); #endif -#endif /* __UC8702_H__ */ +#endif /* UC870X_DEBUG */ + +#endif /* __UC870X_H__ */ diff --git a/firmware/target/arm/s5l8702/debug-s5l8702.c b/firmware/target/arm/s5l8702/debug-s5l8702.c index 291c809bce..614019bac1 100644 --- a/firmware/target/arm/s5l8702/debug-s5l8702.c +++ b/firmware/target/arm/s5l8702/debug-s5l8702.c @@ -31,7 +31,8 @@ #include "pmu-target.h" #include "pcm-target.h" #ifdef HAVE_SERIAL -#include "uc8702.h" +#include "uart-target.h" +#include "uc870x.h" #endif #include "clocking-s5l8702.h" @@ -50,7 +51,7 @@ bool dbg_hw_info(void) int line; int i; unsigned int state = 0; -#ifdef UC8702_DEBUG +#ifdef UC870X_DEBUG const unsigned int max_states=3; #else const unsigned int max_states=2; @@ -120,20 +121,18 @@ bool dbg_hw_info(void) _DEBUG_PRINTF("backlight: %s", pmu_read(0x29) ? "on" : "off"); _DEBUG_PRINTF("brightness value: %d", pmu_read(0x28)); } -#ifdef UC8702_DEBUG +#ifdef UC870X_DEBUG else if(state==2) { extern struct uartc_port ser_port; int tx_stat, rx_stat, tx_speed, rx_speed; char line_cfg[4]; int abr_stat; - unsigned int abr_cnt; - + uint32_t abr_cnt; char *abrstatus[] = {"Idle", "Launched", "Counting", "Abnormal"}; - uartc_port_get_line_info(&ser_port, &tx_stat, &rx_stat, - &tx_speed, &rx_speed, line_cfg); - + uartc_port_get_line_info(&ser_port, + &tx_stat, &rx_stat, &tx_speed, &rx_speed, line_cfg); abr_stat = uartc_port_get_abr_info(&ser_port, &abr_cnt); _DEBUG_PRINTF("UART %d:", ser_port.id); @@ -141,7 +140,7 @@ bool dbg_hw_info(void) _DEBUG_PRINTF("line: %s", line_cfg); _DEBUG_PRINTF("Tx: %s, speed: %d", tx_stat ? "On":"Off", tx_speed); _DEBUG_PRINTF("Rx: %s, speed: %d", rx_stat ? "On":"Off", rx_speed); - _DEBUG_PRINTF("ABR: %s, cnt: %d", abrstatus[abr_stat], abr_cnt); + _DEBUG_PRINTF("ABR: %s, cnt: %u", abrstatus[abr_stat], abr_cnt); line++; _DEBUG_PRINTF("n_tx_bytes: %u", ser_port.n_tx_bytes); _DEBUG_PRINTF("n_rx_bytes: %u", ser_port.n_rx_bytes); @@ -149,10 +148,8 @@ bool dbg_hw_info(void) _DEBUG_PRINTF("n_parity_err: %u", ser_port.n_parity_err); _DEBUG_PRINTF("n_frame_err: %u", ser_port.n_frame_err); _DEBUG_PRINTF("n_break_detect: %u", ser_port.n_break_detect); - _DEBUG_PRINTF("n_abnormal0: %u", ser_port.n_abnormal0); - _DEBUG_PRINTF("n_abnormal1: %u", ser_port.n_abnormal1); - - sleep(HZ/20); + _DEBUG_PRINTF("ABR n_abnormal: %u %u", + ser_port.n_abnormal0, ser_port.n_abnormal1); } #endif else diff --git a/firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c index b022fef675..c77b5d0c95 100644 --- a/firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c +++ b/firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c @@ -18,19 +18,16 @@ * KIND, either express or implied. * ****************************************************************************/ -#include -#include -#include +#include +#include #include "config.h" #include "cpu.h" #include "system.h" -#include "kernel.h" - #include "serial.h" + #include "s5l8702.h" -#include "uc8702.h" -#include "uart-s5l8702.h" +#include "uc870x.h" /* Define LOGF_ENABLE to enable logf output in this file */ #define LOGF_ENABLE @@ -38,16 +35,26 @@ /* shall include serial HW configuracion for specific target */ -#define IPOD6G_UART_CLK_HZ 12000000 /* external OSC0 ??? */ +#define IPOD6G_UART_CLK_HZ 12000000 /* external OSC0 ??? */ -extern struct uartc s5l8702_uart; +/* This values below are valid with a UCLK of 12MHz */ +#define BRDATA_9600 (77) /* 9615 */ +#define BRDATA_19200 (38) /* 19231 */ +#define BRDATA_28800 (25) /* 28846 */ +#define BRDATA_38400 (19 | (0xc330c << 8)) /* 38305 */ +#define BRDATA_57600 (12) /* 57692 */ +#define BRDATA_115200 (6 | (0xffffff << 8)) /* 114286 */ + + +extern const struct uartc s5l8702_uartc; #ifdef IPOD_ACCESSORY_PROTOCOL void iap_rx_isr(int, char*, char*, uint32_t); #endif -struct uartc_port ser_port IDATA_ATTR = { +struct uartc_port ser_port IDATA_ATTR = +{ /* location */ - .uartc = &s5l8702_uart, + .uartc = &s5l8702_uartc, .id = 0, /* configuration */ @@ -70,17 +77,18 @@ struct uartc_port ser_port IDATA_ATTR = { */ void serial_setup(void) { - uart_port_init(&ser_port); + uartc_port_open(&ser_port); /* set a default configuration, Tx and Rx modes are disabled when the port is initialized */ - uartc_port_config(&ser_port, 115200, ULCON_DATA_BITS_8, + uartc_port_config(&ser_port, ULCON_DATA_BITS_8, ULCON_PARITY_NONE, ULCON_STOP_BITS_1); + uartc_port_set_bitrate_raw(&ser_port, BRDATA_115200); /* enable Tx interrupt request or POLLING mode */ uartc_port_set_tx_mode(&ser_port, UCON_MODE_INTREQ); - logf("[%lu] serial_setup(): port %d ready!", USEC_TIMER, ser_port.id); + logf("[%lu] "MODEL_NAME" port %d ready!", USEC_TIMER, ser_port.id); } int tx_rdy(void) @@ -93,16 +101,15 @@ void tx_writec(unsigned char c) uartc_port_tx_byte(&ser_port, c); } + #ifdef IPOD_ACCESSORY_PROTOCOL #include "iap.h" -enum { +static enum { ABR_STATUS_LAUNCHED, /* ST_SYNC */ ABR_STATUS_SYNCING, /* ST_SOF */ ABR_STATUS_DONE -}; - -int abr_status; +} abr_status; void serial_bitrate(int rate) { @@ -131,8 +138,13 @@ void serial_bitrate(int rate) 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(&ser_port, rate); + uartc_port_set_bitrate_raw(&ser_port, brdata); uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ); abr_status = ABR_STATUS_DONE; } @@ -149,22 +161,21 @@ void iap_rx_isr(int len, char *data, char *err, uint32_t abr_cnt) /* autobauding */ if (abr_cnt) { #define BR2CNT(s) (IPOD6G_UART_CLK_HZ / (unsigned)(s)) - unsigned speed; - 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 */ - else if (abr_cnt < BR2CNT(48000)) speed = 57600; - else if (abr_cnt < BR2CNT(33600)) speed = 38400; - else if (abr_cnt < BR2CNT(24000)) speed = 28800; - else if (abr_cnt < BR2CNT(14400)) speed = 19200; - else speed = 9600; + 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(&ser_port, speed); + uartc_port_set_bitrate_raw(&ser_port, brdata); uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ); /* enter SOF state */ @@ -176,10 +187,12 @@ void iap_rx_isr(int len, char *data, char *err, uint32_t abr_cnt) } /* process received data */ - while (len--) { + while (len--) + { bool sync_done = !iap_getc(*data++); - if (abr_status == ABR_STATUS_SYNCING) { + if (abr_status == ABR_STATUS_SYNCING) + { if (sync_done) { abr_status = ABR_STATUS_DONE; } diff --git a/firmware/target/arm/s5l8702/system-s5l8702.c b/firmware/target/arm/s5l8702/system-s5l8702.c index 6b20f44acb..fd3a464e2f 100644 --- a/firmware/target/arm/s5l8702/system-s5l8702.c +++ b/firmware/target/arm/s5l8702/system-s5l8702.c @@ -24,9 +24,9 @@ #include "panic.h" #include "system-target.h" #include "pmu-target.h" +#include "uart-target.h" #include "gpio-s5l8702.h" #include "dma-s5l8702.h" -#include "uart-s5l8702.h" #include "clocking-s5l8702.h" #define default_interrupt(name) \ diff --git a/firmware/target/arm/s5l8702/uart-s5l8702.c b/firmware/target/arm/s5l8702/uart-s5l8702.c index 45d61ba627..9d8faaa173 100644 --- a/firmware/target/arm/s5l8702/uart-s5l8702.c +++ b/firmware/target/arm/s5l8702/uart-s5l8702.c @@ -18,110 +18,133 @@ * KIND, either express or implied. * ****************************************************************************/ - -/* Include Standard files */ -#include -#include -#include +#include +#include #include "config.h" #include "cpu.h" #include "system.h" -#include "kernel.h" #include "s5l8702.h" -#include "uc8702.h" -#include "uart-s5l8702.h" +#include "uc870x.h" -/* s5l8702 UART configuration */ -struct uartc s5l8702_uart = { - .baddr = S5L8702_UART_BASE +/* + * s5l8702 UC870X HW: 1 UARTC, 4 ports + */ +static struct uartc_port *uartc_port_l[UARTC_N_PORTS]; +const struct uartc s5l8702_uartc = +{ + .id = 0, + .baddr = UARTC_BASE_ADDR, + .port_off = UARTC_PORT_OFFSET, + .n_ports = UARTC_N_PORTS, + .port_l = uartc_port_l, }; /* * Device level functions specific to S5L8702 */ -void uart_gpio_control(int port_id, bool onoff) +void uart_target_enable_gpio(int uart_id, int port_id) { - if (onoff) { - switch (port_id) { - case 0: - /* configure UART0 Tx/Rx GPIO ports */ - PCON0 = (PCON0 & 0xff00ffff) | 0x00220000; - break; - case 1: - /* configure UART1 GPIO ports, including RTS/CTS signals */ - PCOND = (PCOND & 0xff0000ff) | 0x00222200; - break; - case 2: - case 3: - /* unknown, probably UART3/4 not routed on s5l8702 */ - default: - break; - } + (void) uart_id; + switch (port_id) { + case 0: + /* configure UART0 Tx/Rx GPIO ports */ + PCON0 = (PCON0 & 0xff00ffff) | 0x00220000; + break; + case 1: + /* configure UART1 GPIO ports, including RTS/CTS signals */ + PCOND = (PCOND & 0xff0000ff) | 0x00222200; + break; + case 2: + case 3: + /* unknown */ + default: + break; } - else { +} + +void uart_target_disable_gpio(int uart_id, int port_id) +{ + (void) uart_id; + switch (port_id) { /* configure minimal power consumption */ - switch (port_id) { - case 0: - PCON0 = (PCON0 & 0xff00ffff) | 0x00ee0000; - break; - case 1: - PCOND = (PCOND & 0xff0000ff) | 0x00eeee00; - break; - case 2: - case 3: - default: - break; - } + case 0: + PCON0 = (PCON0 & 0xff00ffff) | 0x00ee0000; + break; + case 1: + PCOND = (PCOND & 0xff0000ff) | 0x00eeee00; + break; + case 2: + case 3: + default: + break; } } -/* reset s5l8702 uart related hardware */ -static void s5l8702_uart_hw_init(void) +void uart_target_enable_irq(int uart_id, int port_id) { - for (int id = 0; id < S5L8702_UART_PORT_MAX; id++) { - VIC0INTENCLEAR = 1 << IRQ_UART(id); /* mask INT */ - uart_gpio_control(id, 0); - } + (void) uart_id; + VIC0INTENABLE = 1 << IRQ_UART(port_id); } -void uart_init(void) +void uart_target_disable_irq(int uart_id, int port_id) { - s5l8702_uart_hw_init(); - PWRCON(1) &= ~(1 << (CLOCKGATE_UART - 32)); /* on */ - uartc_open(&s5l8702_uart); + (void) uart_id; + VIC0INTENCLEAR = 1 << IRQ_UART(port_id); } -void uart_close(void) +void uart_target_clear_irq(int uart_id, int port_id) { - uartc_close(&s5l8702_uart); - PWRCON(1) |= (1 << (CLOCKGATE_UART - 32)); /* off */ - s5l8702_uart_hw_init(); + (void) uart_id; + (void) port_id; } -void uart_port_init(struct uartc_port *port) +void uart_target_enable_clocks(int uart_id) { - uart_gpio_control(port->id, 1); - uartc_port_open(port); - VIC0INTENABLE = 1 << IRQ_UART(port->id); /* unmask INT */ + (void) uart_id; + PWRCON(1) &= ~(1 << (CLOCKGATE_UARTC - 32)); } -void uart_port_close(struct uartc_port *port) +void uart_target_disable_clocks(int uart_id) { - VIC0INTENCLEAR = 1 << IRQ_UART(port->id); /* mask INT */ - uartc_port_close(port); - uart_gpio_control(port->id, 0); + (void) uart_id; + PWRCON(1) |= (1 << (CLOCKGATE_UARTC - 32)); } -/* ISRs */ +/* + * ISRs + */ + +/* On Classic, PORT0 interrupts are not used when iAP is disabled */ +#if !defined(IPOD_6G) || defined(IPOD_ACCESSORY_PROTOCOL) void ICODE_ATTR INT_UART0(void) { - uartc_callback(&s5l8702_uart, 0); + uartc_callback(&s5l8702_uartc, 0); } +#endif +/* PORT1,2,3 not used on Classic */ +#ifndef IPOD_6G void ICODE_ATTR INT_UART1(void) { - uartc_callback(&s5l8702_uart, 1); + uartc_callback(&s5l8702_uartc, 1); +} + +void ICODE_ATTR INT_UART2(void) +{ + uartc_callback(&s5l8702_uartc, 2); +} + +void ICODE_ATTR INT_UART3(void) +{ + uartc_callback(&s5l8702_uartc, 3); +} +#endif + +/* Main init */ +void uart_init(void) +{ + uartc_open(&s5l8702_uartc); } diff --git a/firmware/target/arm/s5l8702/uart-s5l8702.h b/firmware/target/arm/s5l8702/uart-target.h similarity index 62% rename from firmware/target/arm/s5l8702/uart-s5l8702.h rename to firmware/target/arm/s5l8702/uart-target.h index c7ec684284..d6d45b9073 100644 --- a/firmware/target/arm/s5l8702/uart-s5l8702.h +++ b/firmware/target/arm/s5l8702/uart-target.h @@ -18,15 +18,21 @@ * KIND, either express or implied. * ****************************************************************************/ -#ifndef __UART_S5L8702_H__ -#define __UART_S5L8702_H__ +#ifndef __UART_TARGET_H__ +#define __UART_TARGET_H__ -#include "uc8702.h" +/* Define this to show debug data on "View HW Info" */ +/* #define UC870X_DEBUG */ -/* s5l8702 low level routines */ -void uart_init (void); -void uart_close(void); -void uart_port_init (struct uartc_port *port); -void uart_port_close (struct uartc_port *port); +void uart_init(void); -#endif /* __UART_S5L8702_H__ */ +/* s5l870x low level routines */ +void uart_target_enable_clocks(int uart_id); +void uart_target_disable_clocks(int uart_id); +void uart_target_enable_irq(int uart_id, int port_id); +void uart_target_disable_irq(int uart_id, int port_id); +void uart_target_clear_irq(int uart_id, int port_id); +void uart_target_enable_gpio(int uart_id, int port_id); +void uart_target_disable_gpio(int uart_id, int port_id); + +#endif /* __UART_TARGET_H__ */ diff --git a/firmware/target/arm/s5l8702/uc8702.c b/firmware/target/arm/uc870x.c similarity index 69% rename from firmware/target/arm/s5l8702/uc8702.c rename to firmware/target/arm/uc870x.c index 91c4a3c9e6..9537c15e1a 100644 --- a/firmware/target/arm/s5l8702/uc8702.c +++ b/firmware/target/arm/uc870x.c @@ -19,79 +19,100 @@ * ****************************************************************************/ #include -#include "kernel.h" -#include "uc8702.h" +#include + +#include "config.h" +#include "system.h" + +#include "uart-target.h" +#include "uc870x.h" /* - * s5l8702 UART controller (UC8702) + * UC870x: UART controller for s5l870x */ /* Rx related masks */ +#if CONFIG_CPU == S5L8700 +#define UTRSTAT_RX_RELATED_INTS (UTRSTAT_RX_INT_BIT | UTRSTAT_ERR_INT_BIT) +#define UCON_RX_RELATED_INTS (UCON_RX_INT_BIT | UCON_ERR_INT_BIT) + +#elif CONFIG_CPU == S5L8701 #define UTRSTAT_RX_RELATED_INTS \ - (UTRSTAT_RX_INT_BIT | UTRSTAT_RX_TOUT_INT_BIT | \ - UTRSTAT_ERR_INT_BIT | UTRSTAT_AUTOBR_INT_BIT) + (UTRSTAT_RX_INT_BIT | UTRSTAT_ERR_INT_BIT | UTRSTAT_AUTOBR_INT_BIT) #define UCON_RX_RELATED_INTS \ - (UCON_RX_INT_BIT | UCON_RX_TOUT_INT_BIT | \ - UCON_ERR_INT_BIT | UCON_AUTOBR_INT_BIT) + (UCON_RX_INT_BIT | UCON_ERR_INT_BIT | UCON_AUTOBR_INT_BIT) + +#else /* CONFIG_CPU == S5L8702 */ +#define UTRSTAT_RX_RELATED_INTS \ + (UTRSTAT_RX_INT_BIT | UTRSTAT_ERR_INT_BIT | \ + UTRSTAT_AUTOBR_INT_BIT | UTRSTAT_RX_TOUT_INT_BIT) +#define UCON_RX_RELATED_INTS \ + (UCON_RX_INT_BIT | UCON_ERR_INT_BIT | \ + UCON_AUTOBR_INT_BIT | UCON_RX_TOUT_INT_BIT) +#endif + +#define UART_PORT_BASE(u,i) (((u)->baddr) + (u)->port_off * (i)) /* Initialization */ -static void uartc_port_id_reset(struct uartc* uartc, int id) +static void uartc_reset_port_id(const struct uartc* uartc, int port_id) { - uint32_t baddr = UART_PORT_BASE(uartc->baddr, id); + uart_target_disable_irq(uartc->id, port_id); + uart_target_disable_gpio(uartc->id, port_id); /* set port registers to default reset values */ + uint32_t baddr = UART_PORT_BASE(uartc, port_id); UCON(baddr) = 0; ULCON(baddr) = 0; UMCON(baddr) = 0; UFCON(baddr) = UFCON_RX_FIFO_RST_BIT | UFCON_TX_FIFO_RST_BIT; - /* clear all interrupts */ - UTRSTAT(baddr) = UTRSTAT_RX_RELATED_INTS - | UTRSTAT_TX_INT_BIT - | UTRSTAT_MODEM_INT_BIT; + UTRSTAT(baddr) = ~0; /* clear all interrupts */ UBRDIV(baddr) = 0; +#if CONFIG_CPU == S5L8702 UBRCONTX(baddr) = 0; UBRCONRX(baddr) = 0; +#endif - uartc->port_l[id] = (void*)0; + uartc->port_l[port_id] = (void*)0; } -static void uartc_reset(struct uartc* uartc) +static void uartc_reset(const struct uartc* uartc) { - for (int id = 0; id < UART_PORT_MAX; id++) - uartc_port_id_reset(uartc, id); + for (int port_id = 0; port_id < uartc->n_ports; port_id++) + uartc_reset_port_id(uartc, port_id); } -void uartc_open(struct uartc* uartc) - __attribute__((alias("uartc_reset"))); +void uartc_open(const struct uartc *uartc) +{ + uart_target_enable_clocks(uartc->id); + uartc_reset(uartc); +} -void uartc_close(struct uartc* uartc) - __attribute__((alias("uartc_reset"))); +void uartc_close(const struct uartc *uartc) +{ + uartc_reset(uartc); + uart_target_disable_clocks(uartc->id); +} void uartc_port_open(struct uartc_port *port) { - struct uartc *uartc = port->uartc; - uint32_t baddr = UART_PORT_BASE(uartc->baddr, port->id); + const struct uartc *uartc = port->uartc; + uint32_t baddr = UART_PORT_BASE(uartc, port->id); - port->baddr = baddr; - port->utrstat_int_mask = (port->rx_cb ? UTRSTAT_RX_RELATED_INTS : 0) - | (port->tx_cb ? UTRSTAT_TX_INT_BIT : 0); - port->abr_aborted = 0; + uart_target_enable_gpio(uartc->id, port->id); /* disable Tx/Rx and mask all interrupts */ UCON(baddr) = 0; /* clear all interrupts */ - UTRSTAT(baddr) = UTRSTAT_RX_RELATED_INTS - | UTRSTAT_TX_INT_BIT - | UTRSTAT_MODEM_INT_BIT; + UTRSTAT(baddr) = ~0; /* configure registers */ UFCON(baddr) = UFCON_FIFO_ENABLE_BIT - | UFCON_RX_FIFO_RST_BIT - | UFCON_TX_FIFO_RST_BIT - | ((port->rx_trg & UFCON_RX_FIFO_TRG_MASK) << UFCON_RX_FIFO_TRG_POS) - | ((port->tx_trg & UFCON_TX_FIFO_TRG_MASK) << UFCON_TX_FIFO_TRG_POS); + | UFCON_RX_FIFO_RST_BIT + | UFCON_TX_FIFO_RST_BIT + | ((port->rx_trg & UFCON_RX_FIFO_TRG_MASK) << UFCON_RX_FIFO_TRG_POS) + | ((port->tx_trg & UFCON_TX_FIFO_TRG_MASK) << UFCON_TX_FIFO_TRG_POS); UMCON(baddr) = UMCON_RTS_BIT; /* activate nRTS (low level) */ @@ -101,38 +122,59 @@ void uartc_port_open(struct uartc_port *port) | (port->rx_cb ? UCON_RX_RELATED_INTS|UCON_RX_TOUT_EN_BIT : 0) | (port->tx_cb ? UCON_TX_INT_BIT : 0); - /* register port on parent controller */ + /* init and register port struct */ + port->baddr = baddr; + port->utrstat_int_mask = (port->rx_cb ? UTRSTAT_RX_RELATED_INTS : 0) + | (port->tx_cb ? UTRSTAT_TX_INT_BIT : 0); +#if CONFIG_CPU != S5L8700 + port->abr_aborted = 0; +#endif uartc->port_l[port->id] = port; + + /* enable interrupts */ + uart_target_clear_irq(uartc->id, port->id); + /*if (port->utrstat_int_mask)*/ + uart_target_enable_irq(uartc->id, port->id); } void uartc_port_close(struct uartc_port *port) { - uartc_port_id_reset(port->uartc, port->id); + uartc_reset_port_id(port->uartc, port->id); } /* Configuration */ -void uartc_port_config(struct uartc_port *port, unsigned int speed, - uint8_t data_bits, uint8_t parity, uint8_t stop_bits) +void uartc_port_config(struct uartc_port *port, + uint8_t data_bits, uint8_t parity, uint8_t stop_bits) { - uint32_t baddr = port->baddr; - - ULCON(baddr) = ((parity & ULCON_PARITY_MASK) << ULCON_PARITY_POS) - | ((stop_bits & ULCON_STOP_BITS_MASK) << ULCON_STOP_BITS_POS) - | ((data_bits & ULCON_DATA_BITS_MASK) << ULCON_DATA_BITS_POS); - - uartc_port_set_bitrate(port, speed); + ULCON(port->baddr) = ((parity & ULCON_PARITY_MASK) << ULCON_PARITY_POS) + | ((stop_bits & ULCON_STOP_BITS_MASK) << ULCON_STOP_BITS_POS) + | ((data_bits & ULCON_DATA_BITS_MASK) << ULCON_DATA_BITS_POS); } -void uartc_port_set_bitrate(struct uartc_port *port, unsigned int speed) +/* set bitrate using precalculated values */ +void uartc_port_set_bitrate_raw(struct uartc_port *port, uint32_t brdata) { uint32_t baddr = port->baddr; + UBRDIV(baddr) = brdata & 0xff; +#if CONFIG_CPU == S5L8702 + UBRCONRX(baddr) = brdata >> 8; + UBRCONTX(baddr) = brdata >> 8; +#endif +} + +#if 0 +/* calculate values to set real bitrate as close as possible to the + requested speed */ +void uartc_port_set_bitrate(struct uartc_port *port, unsigned int speed) +{ int uclk = port->clkhz; /* Real baud width in UCLK/16 ticks: trunc(UCLK/(16*speed) + 0.5) */ int brdiv = (uclk + (speed << 3)) / (speed << 4); - UBRDIV(baddr) = brdiv - 1; + uint32_t brdata = brdiv - 1; +#if CONFIG_CPU == S5L8702 /* Fine adjust: * * Along the whole frame, insert/remove "jittered" bauds when needed @@ -158,7 +200,6 @@ void uartc_port_set_bitrate(struct uartc_port *port, unsigned int speed) int err_width = 0; uint32_t brcon = 0; - /* TODO: for (bit < configured frame length) */ for (int bit = 0; bit < UC_FRAME_MAX_LEN; bit++) { err_width += baud_err_width; /* adjust to the nearest width */ @@ -168,24 +209,25 @@ void uartc_port_set_bitrate(struct uartc_port *port, unsigned int speed) } } - UBRCONRX(baddr) = brcon; - UBRCONTX(baddr) = brcon; -} + brdata |= (brcon << 8); +#endif /* CONFIG_CPU == S5L8702 */ -/* TODO: uarc_port_set_bitrate_raw() using precalculated values */ + uartc_port_set_rawbr(port, brdata); +} +#endif /* Select Tx/Rx modes: disabling Tx/Rx resets HW, including FIFOs and shift registers */ void uartc_port_set_rx_mode(struct uartc_port *port, uint32_t mode) { UCON(port->baddr) = (mode << UCON_RX_MODE_POS) | - (UCON(port->baddr) & ~(UCON_RX_MODE_MASK << UCON_RX_MODE_POS)); + (_UCON_RD(port->baddr) & ~(UCON_RX_MODE_MASK << UCON_RX_MODE_POS)); } void uartc_port_set_tx_mode(struct uartc_port *port, uint32_t mode) { UCON(port->baddr) = (mode << UCON_TX_MODE_POS) | - (UCON(port->baddr) & ~(UCON_TX_MODE_MASK << UCON_TX_MODE_POS)); + (_UCON_RD(port->baddr) & ~(UCON_TX_MODE_MASK << UCON_TX_MODE_POS)); } /* Transmit */ @@ -197,7 +239,7 @@ bool uartc_port_tx_ready(struct uartc_port *port) void uartc_port_tx_byte(struct uartc_port *port, uint8_t ch) { UTXH(port->baddr) = ch; -#ifdef UC8702_DEBUG +#ifdef UC870X_DEBUG port->n_tx_bytes++; #endif } @@ -227,6 +269,7 @@ uint8_t uartc_port_read_byte(struct uartc_port *port) return uartc_port_rx_byte(port); } +#if CONFIG_CPU != S5L8700 /* Autobauding */ static inline int uartc_port_abr_status(struct uartc_port *port) { @@ -236,7 +279,7 @@ static inline int uartc_port_abr_status(struct uartc_port *port) void uartc_port_abr_start(struct uartc_port *port) { port->abr_aborted = 0; - UCON(port->baddr) |= UCON_AUTOBR_START_BIT; + UCON(port->baddr) = _UCON_RD(port->baddr) | UCON_AUTOBR_START_BIT; } void uartc_port_abr_stop(struct uartc_port *port) @@ -255,13 +298,14 @@ void uartc_port_abr_stop(struct uartc_port *port) */ port->abr_aborted = 1; else - UCON(port->baddr) &= ~UCON_AUTOBR_START_BIT; + UCON(port->baddr) = _UCON_RD(port->baddr) & ~UCON_AUTOBR_START_BIT; } +#endif /* CONFIG_CPU != S5L8700 */ /* ISR */ -void ICODE_ATTR uartc_callback(struct uartc *uartc, int id) +void ICODE_ATTR uartc_callback(const struct uartc* uartc, int port_id) { - struct uartc_port *port = uartc->port_l[id]; + struct uartc_port *port = uartc->port_l[port_id]; uint32_t baddr = port->baddr; /* filter registered interrupts */ @@ -274,23 +318,25 @@ void ICODE_ATTR uartc_callback(struct uartc *uartc, int id) if (ints & UTRSTAT_RX_RELATED_INTS) { int len = 0; +#if CONFIG_CPU != S5L8700 uint32_t abr_cnt = 0; if (ints & UTRSTAT_AUTOBR_INT_BIT) { if (uartc_port_abr_status(port) == UABRSTAT_STATUS_COUNTING) { - #ifdef UC8702_DEBUG - if (UCON(baddr) & UCON_AUTOBR_START_BIT) port->n_abnormal0++; + #ifdef UC870X_DEBUG + if (_UCON_RD(baddr) & UCON_AUTOBR_START_BIT) port->n_abnormal0++; else port->n_abnormal1++; #endif /* try to fix abnormal situations */ - UCON(baddr) |= UCON_AUTOBR_START_BIT; + UCON(baddr) = _UCON_RD(baddr) | UCON_AUTOBR_START_BIT; } else if (!port->abr_aborted) abr_cnt = UABRCNT(baddr); } if (ints & (UTRSTAT_RX_RELATED_INTS ^ UTRSTAT_AUTOBR_INT_BIT)) +#endif /* CONFIG_CPU != S5L8700 */ { /* get FIFO count */ uint32_t ufstat = UFSTAT(baddr); @@ -304,15 +350,19 @@ void ICODE_ATTR uartc_callback(struct uartc *uartc, int id) } } - /* 'abr_cnt' is zero when no ABR interrupt exists, 'len' - * might be zero due to RX_TOUT interrupts are raised by - * the hardware even when RX FIFO is empty. - * When overrun, it is marked on the first readed error: + /* 'len' might be zero due to RX_TOUT interrupts are + * raised by the hardware even when RX FIFO is empty. + * When overrun, it is marked on the first error: * overrun = len ? (rx_err[0] & UERSTAT_OVERRUN_BIT) : 0 */ +#if CONFIG_CPU == S5L8700 + port->rx_cb(len, port->rx_data, port->rx_err); +#else + /* 'abr_cnt' is zero when no ABR interrupt exists */ port->rx_cb(len, port->rx_data, port->rx_err, abr_cnt); +#endif - #ifdef UC8702_DEBUG +#ifdef UC870X_DEBUG if (len) { port->n_rx_bytes += len; if (port->rx_err[0] & UERSTAT_OVERRUN_BIT) @@ -326,24 +376,25 @@ void ICODE_ATTR uartc_callback(struct uartc *uartc, int id) port->n_break_detect++; } } - #endif +#endif } - #if 0 +#if 0 /* not used and not tested */ if (ints & UTRSTAT_TX_INT_BIT) { port->tx_cb(UART_FIFO_SIZE - ((UFSTAT(baddr) & \ UFSTAT_TX_FIFO_CNT_MASK) >> UFSTAT_TX_FIFO_CNT_POS)); } - #endif +#endif } -#ifdef UC8702_DEBUG +#ifdef UC870X_DEBUG /*#define LOGF_ENABLE*/ #include "logf.h" +#if CONFIG_CPU == S5L8702 static int get_bitrate(int uclk, int brdiv, int brcon, int frame_len) { logf("get_bitrate(%d, %d, 0x%08x, %d)", uclk, brdiv, brcon, frame_len); @@ -375,6 +426,7 @@ static int get_bitrate(int uclk, int brdiv, int brcon, int frame_len) return avg_speed; } +#endif /* CONFIG_CPU == S5L8702 */ void uartc_port_get_line_info(struct uartc_port *port, int *tx_status, int *rx_status, @@ -382,25 +434,32 @@ void uartc_port_get_line_info(struct uartc_port *port, { uint32_t baddr = port->baddr; - uint32_t ucon = UCON(baddr); - if (*tx_status) + uint32_t ucon = _UCON_RD(baddr); + if (tx_status) *tx_status = ((ucon >> UCON_TX_MODE_POS) & UCON_TX_MODE_MASK) ? 1 : 0; - if (*rx_status) + if (rx_status) *rx_status = ((ucon >> UCON_RX_MODE_POS) & UCON_RX_MODE_MASK) ? 1 : 0; uint32_t ulcon = ULCON(baddr); int n_data = ((ulcon >> ULCON_DATA_BITS_POS) & ULCON_DATA_BITS_MASK) + 5; int n_stop = ((ulcon >> ULCON_STOP_BITS_POS) & ULCON_STOP_BITS_MASK) + 1; int parity = (ulcon >> ULCON_PARITY_POS) & ULCON_PARITY_MASK; - int frame_len = 1 + n_data + (parity ? 1 : 0) + n_stop; uint32_t brdiv = UBRDIV(baddr) + 1; - if (*tx_speed) +#if CONFIG_CPU == S5L8702 + int frame_len = 1 + n_data + (parity ? 1 : 0) + n_stop; + if (tx_speed) *tx_speed = get_bitrate(port->clkhz, brdiv, UBRCONTX(baddr), frame_len); - if (*rx_speed) + if (rx_speed) *rx_speed = get_bitrate(port->clkhz, brdiv, UBRCONRX(baddr), frame_len); +#else + /* speed = truncate(UCLK/(16*brdiv) + 0.5) */ + int speed = (port->clkhz + (brdiv << 3)) / (brdiv << 4); + if (tx_speed) *tx_speed = speed; + if (rx_speed) *rx_speed = speed; +#endif - if (*line_cfg) { + if (line_cfg) { line_cfg[0] = '0' + n_data; line_cfg[1] = ((parity == ULCON_PARITY_NONE) ? 'N' : ((parity == ULCON_PARITY_EVEN) ? 'E' : @@ -412,18 +471,18 @@ void uartc_port_get_line_info(struct uartc_port *port, } } +#if CONFIG_CPU != S5L8700 /* Autobauding */ -int uartc_port_get_abr_info(struct uartc_port *port, unsigned int *abr_cnt) +int uartc_port_get_abr_info(struct uartc_port *port, uint32_t *abr_cnt) { int status; uint32_t abr_status; - uint32_t baddr = port->baddr; int flags = disable_irq_save(); abr_status = uartc_port_abr_status(port); - if (UCON(port->baddr) & UCON_AUTOBR_START_BIT) { + if (_UCON_RD(port->baddr) & UCON_AUTOBR_START_BIT) { if (abr_status == UABRSTAT_STATUS_COUNTING) status = ABR_INFO_ST_COUNTING; /* waiting for rising edge */ else @@ -436,11 +495,12 @@ int uartc_port_get_abr_info(struct uartc_port *port, unsigned int *abr_cnt) status = ABR_INFO_ST_IDLE; } - if (*abr_cnt) - *abr_cnt = UABRCNT(baddr); + if (abr_cnt) + *abr_cnt = UABRCNT(port->baddr); restore_irq(flags); return status; } -#endif /* UC8702_DEBUG */ +#endif /* CONFIG_CPU != S5L8700 */ +#endif /* UC870X_DEBUG */