diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2008-04-11 08:51:27 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2008-04-11 08:51:27 +0000 |
commit | 0b1d7e76d751b9659f4d25be72143cd491704b7e (patch) | |
tree | 0197590c2f32ab7927df1360b86c2fff7902d20f /firmware/target/arm/imx31/gigabeat-s/spi-imx31.c | |
parent | f46a7533a4b53de235c8adf78103e9754909ec7d (diff) |
Serial driver for imx31. Perhaps not 100% but maybe 80-90% (future developments will tell). Factor-out the mc13783 stuff and make that driver a layer above the SPI. TODO: start processing PMIC interrupts. Start a clkctl API for imx31 (we'll see if this sticks around but it seems reasonable here). Misc. stuff for convenience/neatness.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17070 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/imx31/gigabeat-s/spi-imx31.c')
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/spi-imx31.c | 355 |
1 files changed, 307 insertions, 48 deletions
diff --git a/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c b/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c index 10ee3f44c0..bcbe85a76b 100644 --- a/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c @@ -18,66 +18,325 @@ ****************************************************************************/ #include "cpu.h" #include "spi-imx31.h" +#include "avic-imx31.h" +#include "clkctl-imx31.h" #include "debug.h" #include "kernel.h" - /* This is all based on communicating with the MC13783 PMU which is on - * CSPI2 with the chip select at 0. The LCD controller resides on - * CSPI3 cs1, but we have no idea how to communicate to it */ - -void spi_init(void) { - CSPI_CONREG2 |= (2 << 20); // Burst will be triggered at SPI_RDY low - CSPI_CONREG2 |= (2 << 16); // Clock = IPG_CLK/16 - we want about 20mhz - CSPI_CONREG2 |= (31 << 8); // All 32 bits are to be transferred - CSPI_CONREG2 |= (1 << 3); // Start burst on TXFIFO write. - CSPI_CONREG2 |= (1 << 1); // Master mode. - CSPI_CONREG2 |= 1; // Enable CSPI2; +/* Forward interrupt handler declarations */ +#if (SPI_MODULE_MASK & USE_CSPI1_MODULE) +static __attribute__((interrupt("IRQ"))) void CSPI1_HANDLER(void); +#endif +#if (SPI_MODULE_MASK & USE_CSPI2_MODULE) +static __attribute__((interrupt("IRQ"))) void CSPI2_HANDLER(void); +#endif +#if (SPI_MODULE_MASK & USE_CSPI3_MODULE) +static __attribute__((interrupt("IRQ"))) void CSPI3_HANDLER(void); +#endif + +/* State data associatated with each CSPI module */ +static struct spi_module_descriptor +{ + volatile unsigned long *base; + int enab; + struct spi_node *last; + enum IMX31_CG_LIST cg; + enum IMX31_INT_LIST ints; + int byte_size; + void (*handler)(void); + struct mutex m; + struct wakeup w; + struct spi_transfer *trans; + int rxcount; +} spi_descs[SPI_NUM_CSPI] = +/* Init non-zero members */ +{ +#if (SPI_MODULE_MASK & USE_CSPI1_MODULE) + { + .base = (unsigned long *)CSPI1_BASE_ADDR, + .cg = CG_CSPI1, + .ints = CSPI1, + .handler = CSPI1_HANDLER, + }, +#endif +#if (SPI_MODULE_MASK & USE_CSPI2_MODULE) + { + .base = (unsigned long *)CSPI2_BASE_ADDR, + .cg = CG_CSPI2, + .ints = CSPI2, + .handler = CSPI2_HANDLER, + }, +#endif +#if (SPI_MODULE_MASK & USE_CSPI3_MODULE) + { + .base = (unsigned long *)CSPI3_BASE_ADDR, + .cg = CG_CSPI3, + .ints = CSPI3, + .handler = CSPI3_HANDLER, + }, +#endif +}; + +/* Common code for interrupt handlers */ +static void spi_interrupt(enum spi_module_number spi) +{ + struct spi_module_descriptor *desc = &spi_descs[spi]; + volatile unsigned long * const base = desc->base; + struct spi_transfer *trans = desc->trans; + int inc = desc->byte_size + 1; + + if (desc->rxcount > 0) + { + /* Data received - empty out RXFIFO */ + while ((base[CSPI_STATREG_I] & CSPI_STATREG_RR) != 0) + { + uint32_t word = base[CSPI_RXDATA_I]; + + switch (desc->byte_size & 3) + { + case 3: + *(unsigned char *)(trans->rxbuf + 3) = word >> 24; + case 2: + *(unsigned char *)(trans->rxbuf + 2) = word >> 16; + case 1: + *(unsigned char *)(trans->rxbuf + 1) = word >> 8; + case 0: + *(unsigned char *)(trans->rxbuf + 0) = word; + } + + trans->rxbuf += inc; + + if (--desc->rxcount < 4) + { + unsigned long intreg = base[CSPI_INTREG_I]; + + if (desc->rxcount <= 0) + { + /* No more to receive - stop RX interrupts */ + intreg &= ~(CSPI_INTREG_RHEN | CSPI_INTREG_RREN); + base[CSPI_INTREG_I] = intreg; + break; + } + else if (!(intreg & CSPI_INTREG_RREN)) + { + /* < 4 words expected - switch to RX ready */ + intreg &= ~CSPI_INTREG_RHEN; + base[CSPI_INTREG_I] = intreg | CSPI_INTREG_RREN; + } + } + } + } + + if (trans->count > 0) + { + /* Data to transmit - fill TXFIFO or write until exhausted */ + while ((base[CSPI_STATREG_I] & CSPI_STATREG_TF) == 0) + { + uint32_t word = 0; + + switch (desc->byte_size & 3) + { + case 3: + word = *(unsigned char *)(trans->txbuf + 3) << 24; + case 2: + word |= *(unsigned char *)(trans->txbuf + 2) << 16; + case 1: + word |= *(unsigned char *)(trans->txbuf + 1) << 8; + case 0: + word |= *(unsigned char *)(trans->txbuf + 0); + } + + trans->txbuf += inc; + + base[CSPI_TXDATA_I] = word; + + if (--trans->count <= 0) + { + /* Out of data - stop TX interrupts */ + base[CSPI_INTREG_I] &= ~CSPI_INTREG_THEN; + break; + } + } + } + + /* If all interrupts have been remasked - we're done */ + if (base[CSPI_INTREG_I] == 0) + { + base[CSPI_STATREG_I] = CSPI_STATREG_TC | CSPI_STATREG_BO; + wakeup_signal(&desc->w); + } +} + +/* Interrupt handlers for each CSPI module */ +#if (SPI_MODULE_MASK & USE_CSPI1_MODULE) +static __attribute__((interrupt("IRQ"))) void CSPI1_HANDLER(void) +{ + spi_interrupt(CSPI1_NUM); +} +#endif + +#if (SPI_MODULE_MASK & USE_CSPI2_MODULE) +static __attribute__((interrupt("IRQ"))) void CSPI2_HANDLER(void) +{ + spi_interrupt(CSPI2_NUM); +} +#endif + +#if (SPI_MODULE_MASK & USE_CSPI3_MODULE) +static __attribute__((interrupt("IRQ"))) void CSPI3_HANDLER(void) +{ + spi_interrupt(CSPI3_NUM); +} +#endif + +/* Write the context for the node and remember it to avoid unneeded reconfigure */ +static bool spi_set_context(struct spi_node *node, + struct spi_module_descriptor *desc) +{ + volatile unsigned long * const base = desc->base; + + if ((base[CSPI_CONREG_I] & CSPI_CONREG_EN) == 0) + return false; + + if (node != desc->last) + { + /* Switch the module's node */ + desc->last = node; + desc->byte_size = (((node->conreg >> 8) & 0x1f) + 1 + 7) / 8 - 1; + + /* Keep reserved and start bits cleared. Keep enabled bit. */ + base[CSPI_CONREG_I] = + (node->conreg & ~(0xfcc8e000 | CSPI_CONREG_XCH | CSPI_CONREG_SMC)) + | CSPI_CONREG_EN; + /* Set the wait-states */ + base[CSPI_PERIODREG_I] = node->periodreg & 0xffff; + /* Clear out any spuriously-pending interrupts */ + base[CSPI_STATREG_I] = CSPI_STATREG_TC | CSPI_STATREG_BO; + } + + return true; } -static int spi_transfer(int address, long data, long* buffer, bool read) { - return -1; /* Disable for now - hangs - and we'll use interrupts */ +/* Initialize each of the used SPI descriptors */ +void spi_init(void) +{ + int i; - unsigned long packet = 0; - if(!read) { - /* Set the appropriate bit in the packet to indicate a write */ - packet |= (1<<31); + for (i = 0; i < SPI_NUM_CSPI; i++) + { + struct spi_module_descriptor * const desc = &spi_descs[i]; + mutex_init(&desc->m); + wakeup_init(&desc->w); } - /* Set the address of the packet */ - packet |= (address << 25); - - /* Ensure data only occupies 24 bits, then mash the data into the packet */ - data &= ~(DATAMASK); - packet |= data; - - /* Wait for some room in TXFIFO */ - while(CSPI_STATREG2 & (1<<2)); - - /* Send the packet */ - CSPI_TXDATA2 = packet; - - /* Poll the XCH bit to wait for the end of the transfer, with - * a one second timeout */ - int newtick = current_tick + HZ; - while((CSPI_CONREG2 & (1<<2)) && (current_tick < newtick)); - - if(newtick > current_tick) { - *buffer = CSPI_RXDATA2; - return 0; - } else { - /* Indicate the fact that the transfer timed out */ - return -1; +} + +/* Get mutually-exclusive access to the node */ +void spi_lock(struct spi_node *node) +{ + mutex_lock(&spi_descs[node->num].m); +} + +/* Release mutual exclusion */ +void spi_unlock(struct spi_node *node) +{ + mutex_unlock(&spi_descs[node->num].m); +} + +/* Enable the specified module for the node */ +void spi_enable_module(struct spi_node *node) +{ + struct spi_module_descriptor * const desc = &spi_descs[node->num]; + + mutex_lock(&desc->m); + + if (++desc->enab == 1) + { + /* First enable for this module */ + volatile unsigned long * const base = desc->base; + + /* Enable clock-gating register */ + imx31_clkctl_module_clock_gating(desc->cg, CGM_ON_ALL); + + /* Reset */ + base[CSPI_CONREG_I] &= ~CSPI_CONREG_EN; + base[CSPI_CONREG_I] |= CSPI_CONREG_EN; + base[CSPI_INTREG_I] = 0; + base[CSPI_STATREG_I] = CSPI_STATREG_TC | CSPI_STATREG_BO; + + /* Enable interrupt at controller level */ + avic_enable_int(desc->ints, IRQ, 6, desc->handler); } + + mutex_unlock(&desc->m); } -void spi_send(int address, unsigned long data) { - long dummy; - if(spi_transfer(address, data, &dummy, false)) { - DEBUGF("SPI Send timed out"); +/* Disabled the specified module for the node */ +void spi_disable_module(struct spi_node *node) +{ + struct spi_module_descriptor * const desc = &spi_descs[node->num]; + + mutex_lock(&desc->m); + + if (desc->enab > 0 && --desc->enab == 0) + { + /* Last enable for this module */ + volatile unsigned long * const base = desc->base; + + /* Disable interrupt at controller level */ + avic_disable_int(desc->ints); + + /* Disable interface */ + base[CSPI_CONREG_I] &= ~CSPI_CONREG_EN; + + /* Disable interface clock */ + imx31_clkctl_module_clock_gating(desc->cg, CGM_OFF); } + + mutex_unlock(&desc->m); } -void spi_read(int address, unsigned long* buffer) { - if(spi_transfer(address, 0, buffer, true)) { - DEBUGF("SPI read timed out"); +/* Send and/or receive data on the specified node */ +int spi_transfer(struct spi_node *node, struct spi_transfer *trans) +{ + struct spi_module_descriptor * const desc = &spi_descs[node->num]; + int retval; + + if (trans->count <= 0) + return true; + + mutex_lock(&desc->m); + + retval = spi_set_context(node, desc); + + if (retval) + { + volatile unsigned long * const base = desc->base; + unsigned long intreg; + + desc->trans = trans; + desc->rxcount = trans->count; + + /* Enable needed interrupts - FIFOs will start filling */ + intreg = CSPI_INTREG_THEN; + + intreg |= (trans->count < 4) ? + CSPI_INTREG_RREN : /* Must grab data on every word */ + CSPI_INTREG_RHEN; /* Enough data to wait for half-full */ + + base[CSPI_INTREG_I] = intreg; + + /* Start transfer */ + base[CSPI_CONREG_I] |= CSPI_CONREG_XCH; + + if (wakeup_wait(&desc->w, HZ) != OBJ_WAIT_SUCCEEDED) + { + base[CSPI_INTREG_I] = 0; + base[CSPI_CONREG_I] &= ~CSPI_CONREG_XCH; + retval = false; + } } + + mutex_unlock(&desc->m); + + return retval; } |