iPod Classic: updates for uc8702 driver

- Small rework on the UC8702 UART controller to make it compatible with
  other s5l870x SOCs. Files moved and renamed, many conditional code
  added to deal with capabilities and 'features' of the different CPUs.

- A couple of optimizacions that should not affect the functionality.

Change-Id: I705169f7e8b18d5d1da642f81ffc31c4089780a6
This commit is contained in:
Cástor Muñoz 2016-05-12 06:47:38 +02:00
parent 2a1e9eb8a8
commit 8fb67f48ab
9 changed files with 395 additions and 244 deletions

View File

@ -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

View File

@ -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

View File

@ -18,32 +18,27 @@
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __UC8702_H__
#define __UC8702_H__
#ifndef __UC870X_H__
#define __UC870X_H__
#include <stdint.h>
#include <stdbool.h>
#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__ */

View File

@ -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

View File

@ -18,19 +18,16 @@
* KIND, either express or implied.
*
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdbool.h>
#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;
}

View File

@ -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) \

View File

@ -18,110 +18,133 @@
* KIND, either express or implied.
*
****************************************************************************/
/* Include Standard files */
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdbool.h>
#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);
}

View File

@ -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__ */

View File

@ -19,79 +19,100 @@
*
****************************************************************************/
#include <stdint.h>
#include "kernel.h"
#include "uc8702.h"
#include <stdbool.h>
#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 */