/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (c) 2007 Will Robertson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "config.h" #include "system.h" #include "spi-imx31.h" #include "avic-imx31.h" #include "ccm-imx31.h" #include "debug.h" #include "kernel.h" /* 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 { struct cspi_map * const 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 = (struct cspi_map *)CSPI1_BASE_ADDR, .cg = CG_CSPI1, .ints = INT_CSPI1, .handler = CSPI1_HANDLER, }, #endif #if (SPI_MODULE_MASK & USE_CSPI2_MODULE) { .base = (struct cspi_map *)CSPI2_BASE_ADDR, .cg = CG_CSPI2, .ints = INT_CSPI2, .handler = CSPI2_HANDLER, }, #endif #if (SPI_MODULE_MASK & USE_CSPI3_MODULE) { .base = (struct cspi_map *)CSPI3_BASE_ADDR, .cg = CG_CSPI3, .ints = INT_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]; struct cspi_map * 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->statreg & CSPI_STATREG_RR) != 0) { uint32_t word = base->rxdata; 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->intreg; if (desc->rxcount <= 0) { /* No more to receive - stop RX interrupts */ intreg &= ~(CSPI_INTREG_RHEN | CSPI_INTREG_RREN); base->intreg = intreg; break; } else if (!(intreg & CSPI_INTREG_RREN)) { /* < 4 words expected - switch to RX ready */ intreg &= ~CSPI_INTREG_RHEN; base->intreg = intreg | CSPI_INTREG_RREN; } } } } if (trans->count > 0) { /* Data to transmit - fill TXFIFO or write until exhausted */ while ((base->statreg & 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->txdata = word; if (--trans->count <= 0) { /* Out of data - stop TX interrupts */ base->intreg &= ~CSPI_INTREG_THEN; break; } } } /* If all interrupts have been remasked - we're done */ if (base->intreg == 0) { base->statreg = 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) { struct cspi_map * const base = desc->base; if ((base->conreg & 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->conreg = (node->conreg & ~(0xfcc8e000 | CSPI_CONREG_XCH | CSPI_CONREG_SMC)) | CSPI_CONREG_EN; /* Set the wait-states */ base->periodreg = node->periodreg & 0xffff; /* Clear out any spuriously-pending interrupts */ base->statreg = CSPI_STATREG_TC | CSPI_STATREG_BO; } return true; } static void spi_reset(struct cspi_map * const base) { /* Reset */ base->conreg &= ~CSPI_CONREG_EN; base->conreg |= CSPI_CONREG_EN; base->intreg = 0; base->statreg = CSPI_STATREG_TC | CSPI_STATREG_BO; } /* Initialize each of the used SPI descriptors */ void spi_init(void) { int i; 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); } } /* 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 */ struct cspi_map * const base = desc->base; /* Enable clock-gating register */ ccm_module_clock_gating(desc->cg, CGM_ON_RUN_WAIT); /* Reset */ spi_reset(base); desc->last = NULL; /* Enable interrupt at controller level */ avic_enable_int(desc->ints, INT_TYPE_IRQ, INT_PRIO_DEFAULT, desc->handler); } mutex_unlock(&desc->m); } /* 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 */ struct cspi_map * const base = desc->base; /* Disable interrupt at controller level */ avic_disable_int(desc->ints); /* Disable interface */ base->conreg &= ~CSPI_CONREG_EN; /* Disable interface clock */ ccm_module_clock_gating(desc->cg, CGM_OFF); } mutex_unlock(&desc->m); } /* 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) { struct cspi_map * 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->intreg = intreg; /* Start transfer */ base->conreg |= CSPI_CONREG_XCH; if (wakeup_wait(&desc->w, HZ) != OBJ_WAIT_SUCCEEDED) { base->intreg = 0; /* Stop SPI ints */ spi_reset(base); /* Reset module (esp. to empty FIFOs) */ desc->last = NULL; /* Force reconfigure */ retval = false; } } mutex_unlock(&desc->m); return retval; }