diff options
-rw-r--r-- | openpcd/firmware/include/AT91SAM7.h | 6 | ||||
-rw-r--r-- | openpcd/firmware/src/ssc.c | 227 |
2 files changed, 197 insertions, 36 deletions
diff --git a/openpcd/firmware/include/AT91SAM7.h b/openpcd/firmware/include/AT91SAM7.h index 2bc8558..56b738e 100644 --- a/openpcd/firmware/include/AT91SAM7.h +++ b/openpcd/firmware/include/AT91SAM7.h @@ -841,7 +841,8 @@ typedef struct _AT91S_SSC { AT91_REG Reserved1[2]; // AT91_REG SSC_RSHR; // Receive Sync Holding Register AT91_REG SSC_TSHR; // Transmit Sync Holding Register - AT91_REG Reserved2[2]; // + AT91_REG SSC_RC0R; // Receive Compare 0 Register + AT91_REG SSC_RC1R; // Receive Compare 1 Register AT91_REG SSC_SR; // Status Register AT91_REG SSC_IER; // Interrupt Enable Register AT91_REG SSC_IDR; // Interrupt Disable Register @@ -904,6 +905,7 @@ typedef struct _AT91S_SSC { // -------- SSC_TCMR : (SSC Offset: 0x18) SSC Transmit Clock Mode Register -------- // -------- SSC_TFMR : (SSC Offset: 0x1c) SSC Transmit Frame Mode Register -------- #define AT91C_SSC_DATDEF ((unsigned int) 0x1 << 5) // (SSC) Data Default Value +#define AT91C_SSC_MSBF ((unsigned int) 0x1 << 7) // (SSC) MSB First #define AT91C_SSC_FSDEN ((unsigned int) 0x1 << 23) // (SSC) Frame Sync Data Enable // -------- SSC_SR : (SSC Offset: 0x40) SSC Status Register -------- #define AT91C_SSC_TXRDY ((unsigned int) 0x1 << 0) // (SSC) Transmit Ready @@ -914,6 +916,8 @@ typedef struct _AT91S_SSC { #define AT91C_SSC_OVRUN ((unsigned int) 0x1 << 5) // (SSC) Receive Overrun #define AT91C_SSC_ENDRX ((unsigned int) 0x1 << 6) // (SSC) End of Reception #define AT91C_SSC_RXBUFF ((unsigned int) 0x1 << 7) // (SSC) Receive Buffer Full +#define AT91C_SSC_CP0 ((unsigned int) 0x1 << 8) // (SSC) Compare 0 +#define AT91C_SSC_CP1 ((unsigned int) 0x1 << 9) // (SSC) Compare 1 #define AT91C_SSC_TXSYN ((unsigned int) 0x1 << 10) // (SSC) Transmit Sync #define AT91C_SSC_RXSYN ((unsigned int) 0x1 << 11) // (SSC) Receive Sync #define AT91C_SSC_TXENA ((unsigned int) 0x1 << 16) // (SSC) Transmit Enable diff --git a/openpcd/firmware/src/ssc.c b/openpcd/firmware/src/ssc.c index e79eda8..1a4de70 100644 --- a/openpcd/firmware/src/ssc.c +++ b/openpcd/firmware/src/ssc.c @@ -9,7 +9,9 @@ */ #include <errno.h> +#include <string.h> #include <sys/types.h> +#include <AT91SAM7.h> #include <lib_AT91SAM7.h> #include <openpcd.h> @@ -17,24 +19,141 @@ #include "openpcd.h" #include "dbgu.h" -static AT91PS_SSC ssc = AT91C_BASE_SSC; +/* definitions for four-times oversampling */ +#define REQA 0x10410441 +#define WUPA 0x04041041 + +static const AT91PS_SSC ssc = AT91C_BASE_SSC; static AT91PS_PDC rx_pdc; +enum ssc_mode { + SSC_MODE_14443A_SHORT, + SSC_MODE_14443A_STANDARD, + SSC_MODE_14443B, +}; + struct ssc_state { struct req_ctx *rx_ctx[2]; + enum ssc_mode mode; }; - static struct ssc_state ssc_state; +/* This is for four-times oversampling */ +#define ISO14443A_SOF_SAMPLE 0x08 +#define ISO14443A_SOF_LEN 4 + +static void ssc_rx_mode_set(enum ssc_mode ssc_mode) +{ + u_int8_t data_len, num_data, sync_len; + u_int32_t start_cond; + + /* disable Rx */ + ssc->SSC_CR = AT91C_SSC_RXDIS; + + /* disable all Rx related interrupt sources */ + AT91F_SSC_DisableIt(ssc, ssc->SSC_IDR = AT91C_SSC_RXRDY | + AT91C_SSC_OVRUN | AT91C_SSC_ENDRX | + AT91C_SSC_RXBUFF | AT91C_SSC_RXSYN | + AT91C_SSC_CP0 | AT91C_SSC_CP1); + + switch (ssc_mode) { + case SSC_MODE_14443A_SHORT: + start_cond = AT91C_SSC_START_0; + sync_len = ISO14443A_SOF_LEN; + ssc->SSC_RC0R = ISO14443A_SOF_SAMPLE; + data_len = 32; + num_data = 1; + break; + case SSC_MODE_14443A_STANDARD: + start_cond = AT91C_SSC_START_0; + sync_len = ISO14443A_SOF_LEN; + ssc->SSC_RC0R = ISO14443A_SOF_SAMPLE; + data_len = 1; + num_data = 1; /* FIXME */ + break; + case SSC_MODE_14443B: + /* start sampling at first falling data edge */ + //start_cond = + break; + } + ssc->SSC_RFMR = (data_len-1) & 0x1f | + (((num_data-1) & 0x0f) << 8) | + (((sync_len-1) & 0x0f) << 16); + ssc->SSC_RCMR = AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE | start_cond; + + /* Enable RX interrupts */ + AT91F_SSC_EnableIt(ssc, AT91C_SSC_OVRUN); + //AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF); + + ssc_state.mode = ssc_mode; + + AT91F_PDC_EnableRx(rx_pdc); +} + +static void ssc_tx_mode_set(enum ssc_mode ssc_mode) +{ + u_int8_t data_len, num_data, sync_len; + u_int32_t start_cond; + + /* disable Tx */ + ssc->SSC_CR = AT91C_SSC_TXDIS; + + /* disable all Tx related interrupt sources */ + ssc->SSC_IDR = AT91C_SSC_TXRDY | AT91C_SSC_TXEMPTY | AT91C_SSC_ENDTX | + AT91C_SSC_TXBUFE | AT91C_SSC_TXSYN; + + switch (ssc_mode) { + case SSC_MODE_14443A_SHORT: + start_cond = AT91C_SSC_START_RISE_RF; + sync_len = ISO14443A_SOF_LEN; + data_len = 32; + num_data = 1; + break; + case SSC_MODE_14443A_STANDARD: + start_cond = AT91C_SSC_START_0; + sync_len = ISO14443A_SOF_LEN; + ssc->SSC_RC0R = ISO14443A_SOF_SAMPLE; + data_len = 1; + num_data = 1; /* FIXME */ + break; + } + ssc->SSC_TFMR = (data_len-1) & 0x1f | + (((num_data-1) & 0x0f) << 8) | + (((sync_len-1) & 0x0f) << 16); + ssc->SSC_TCMR = AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE | start_cond; + +#if 0 + /* Enable RX interrupts */ + AT91F_SSC_EnableIt(ssc, AT91C_SSC_OVRUN | + AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF); + AT91F_PDC_EnableRx(rx_pdc); + + ssc_state.mode = ssc_mode; +#endif +} + + + + static struct openpcd_hdr opcd_ssc_hdr = { .cmd = OPENPCD_CMD_SSC_READ, }; static inline void init_opcdhdr(struct req_ctx *rctx) { - memcpy(&rctx->tx.data[0], rctx, sizeof(rctx)); + memcpy(&rctx->tx.data[0], &opcd_ssc_hdr, sizeof(opcd_ssc_hdr)); + rctx->tx.tot_len = MAX_HDRSIZE + MAX_REQSIZE -1; } +#ifdef DEBUG_SSC_REFILL +#define DEBUGR(x, args ...) DEBUGPCRF(x, ## args) +#else +#define DEBUGR(x, args ...) +#endif + +static char dmabuf1[512]; +static char dmabuf2[512]; + /* Try to refill RX dma descriptors. Return values: * 0) no dma descriptors empty * 1) filled next/secondary descriptor @@ -44,6 +163,7 @@ static inline void init_opcdhdr(struct req_ctx *rctx) */ static int8_t ssc_rx_refill(void) { +#if 0 struct req_ctx *rctx; rctx = req_ctx_find_get(RCTX_STATE_FREE, RCTX_STATE_SSC_RX_BUSY); @@ -51,12 +171,12 @@ static int8_t ssc_rx_refill(void) DEBUGPCRF("no rctx for refill!"); return -1; } + init_opcdhdr(rctx); if (AT91F_PDC_IsRxEmpty(rx_pdc)) { - init_opcdhdr(rctx); - DEBUGPCRF("filling primary SSC RX dma ctx"); + DEBUGR("filling primary SSC RX dma ctx"); AT91F_PDC_SetRx(rx_pdc, &rctx->rx.data[MAX_HDRSIZE], - MAX_REQSIZE); + (sizeof(rctx->rx.data)-MAX_HDRSIZE)>>2); ssc_state.rx_ctx[0] = rctx; /* If primary is empty, secondary must be empty, too */ @@ -66,13 +186,13 @@ static int8_t ssc_rx_refill(void) DEBUGPCRF("no rctx for secondary refill!"); return -2; } + init_opcdhdr(rctx); } if (AT91F_PDC_IsNextRxEmpty(rx_pdc)) { - DEBUGPCRF("filling secondary SSC RX dma ctx"); - init_opcdhdr(rctx); + DEBUGR("filling secondary SSC RX dma ctx"); AT91F_PDC_SetNextRx(rx_pdc, &rctx->rx.data[MAX_HDRSIZE], - MAX_REQSIZE); + (sizeof(rctx->rx.data)-MAX_HDRSIZE)>2); ssc_state.rx_ctx[1] = rctx; return 2; } else { @@ -81,48 +201,85 @@ static int8_t ssc_rx_refill(void) req_ctx_put(rctx); return 0; } +#else + if (AT91F_PDC_IsRxEmpty(rx_pdc)) + AT91F_PDC_SetRx(rx_pdc, dmabuf1, sizeof(dmabuf1)>>2); + + if (AT91F_PDC_IsNextRxEmpty(rx_pdc)) + AT91F_PDC_SetNextRx(rx_pdc, dmabuf2, sizeof(dmabuf2)>>2); + else + DEBUGPCRF("prim/secnd DMA busy, can't refill"); +#endif } +#define ISO14443A_FDT_SHORT_1 1236 +#define ISO14443A_FDT_SHORT_0 1172 + static void ssc_irq(void) { u_int32_t ssc_sr = ssc->SSC_SR; - DEBUGPCRF("ssc_sr=0x%08x", ssc_sr); - - if (ssc_sr & (AT91C_SSC_ENDRX | AT91C_SSC_TXBUFE)) { - /* Mark primary RCTX as ready to send for usb */ - req_ctx_set_state(ssc_state.rx_ctx[0], - RCTX_STATE_UDP_EP2_PENDING); - /* second buffer gets propagated to primary */ - ssc_state.rx_ctx[0] = ssc_state.rx_ctx[1]; - ssc_state.rx_ctx[1] = NULL; - - if (ssc_sr & AT91C_SSC_TXBUFE) { - DEBUGPCRF("TXBUFE, shouldn't happen!"); - req_ctx_set_state(ssc_state.rx_ctx[0], + DEBUGP("ssc_sr=0x%08x: ", ssc_sr); + + if (ssc_sr & AT91C_SSC_OVRUN) + DEBUGP("RX OVERRUN "); + + switch (ssc_state.mode) { + case SSC_MODE_14443A_SHORT: + if (ssc_sr & AT91C_SSC_RXSYN) + DEBUGP("RXSYN "); + if (ssc_sr & AT91C_SSC_RXRDY) { + u_int32_t sample = ssc->SSC_RHR; + DEBUGP("RXRDY=0x%08x ", sample); + /* Try to set FDT compare register ASAP */ + if (sample == REQA) { + tc_fdt_set(ISO14443A_FDT_SHORT_0); + /* FIXME: prepare and configure ATQA response */ + } else if (sample == WUPA) { + tc_fdt_set(ISO14443A_FDT_SHORT_1); + /* FIXME: prepare and configure ATQA response */ + } else + DEBUGP("<== unknown "); + } + break; + + case SSC_MODE_14443A_STANDARD: + + if (ssc_sr & (AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF)) { +#if 0 + /* Mark primary RCTX as ready to send for usb */ + req_ctx_set_state(ssc_state.rx_ctx[0], RCTX_STATE_UDP_EP2_PENDING); + /* second buffer gets propagated to primary */ + ssc_state.rx_ctx[0] = ssc_state.rx_ctx[1]; + ssc_state.rx_ctx[1] = NULL; +#endif + + if (ssc_sr & AT91C_SSC_RXBUFF) { + DEBUGPCRF("RXBUFF, shouldn't happen!"); +#if 0 + req_ctx_set_state(ssc_state.rx_ctx[0], + RCTX_STATE_UDP_EP2_PENDING); +#endif + } + if (ssc_rx_refill() == -1) + AT91F_AIC_DisableIt(ssc, AT91C_SSC_ENDRX | + AT91C_SSC_RXBUFF | + AT91C_SSC_OVRUN); } - if (ssc_rx_refill() == -1) - AT91F_AIC_DisableIt(ssc, AT91C_SSC_ENDRX | - AT91C_SSC_TXBUFE | - AT91C_SSC_OVRUN); + break; } - - if (ssc_sr & AT91C_SSC_OVRUN) - DEBUGPCRF("Rx Overrun, shouldn't happen!"); } void ssc_rx_unthrottle(void) { - AT91F_AIC_EnableIt(ssc, AT91C_SSC_ENDRX | - AT91C_SSC_TXBUFE | AT91C_SSC_OVRUN); + AT91F_SSC_EnableIt(ssc, AT91C_SSC_ENDRX | + AT91C_SSC_RXBUFF | AT91C_SSC_OVRUN); } void ssc_rx_start(void) { DEBUGPCRF("starting SSC RX\n"); - /* 're'fill DMA descriptors */ - //ssc_rx_refill(); /* Enable Reception */ AT91F_SSC_EnableRx(ssc); @@ -179,8 +336,8 @@ void ssc_rx_init(void) ssc->SSC_RCMR = AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE | AT91C_SSC_START_CONTINOUS; - /* Data bits per Data N = 32-1, Data words per Frame = 16-1*/ - ssc->SSC_RFMR = 31 | AT91C_SSC_MSBF | (15 << 8); + /* Data bits per Data N = 32-1, Data words per Frame = 15-1 (=60 byte)*/ + ssc->SSC_RFMR = 31 | AT91C_SSC_MSBF | (14 << 8); /* Enable RX interrupts */ AT91F_SSC_EnableIt(ssc, AT91C_SSC_OVRUN | |