2007-09-21 15:51:53 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (c) 2007 Will Robertson
|
|
|
|
*
|
|
|
|
* All files in this archive are subject to the GNU General Public License.
|
|
|
|
* See the file COPYING in the source tree root for full license agreement.
|
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
2008-04-27 21:32:10 +00:00
|
|
|
#include "config.h"
|
|
|
|
#include "system.h"
|
2007-09-21 15:51:53 +00:00
|
|
|
#include "spi-imx31.h"
|
2008-04-11 08:51:27 +00:00
|
|
|
#include "avic-imx31.h"
|
|
|
|
#include "clkctl-imx31.h"
|
2007-09-21 15:51:53 +00:00
|
|
|
#include "debug.h"
|
|
|
|
#include "kernel.h"
|
|
|
|
|
2008-04-11 08:51:27 +00:00
|
|
|
/* 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
|
|
|
|
{
|
2008-04-27 21:32:10 +00:00
|
|
|
struct cspi_map * const base;
|
2008-04-11 08:51:27 +00:00
|
|
|
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)
|
|
|
|
{
|
2008-04-27 21:32:10 +00:00
|
|
|
.base = (struct cspi_map *)CSPI1_BASE_ADDR,
|
2008-04-11 08:51:27 +00:00
|
|
|
.cg = CG_CSPI1,
|
|
|
|
.ints = CSPI1,
|
|
|
|
.handler = CSPI1_HANDLER,
|
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#if (SPI_MODULE_MASK & USE_CSPI2_MODULE)
|
|
|
|
{
|
2008-04-27 21:32:10 +00:00
|
|
|
.base = (struct cspi_map *)CSPI2_BASE_ADDR,
|
2008-04-11 08:51:27 +00:00
|
|
|
.cg = CG_CSPI2,
|
|
|
|
.ints = CSPI2,
|
|
|
|
.handler = CSPI2_HANDLER,
|
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#if (SPI_MODULE_MASK & USE_CSPI3_MODULE)
|
|
|
|
{
|
2008-04-27 21:32:10 +00:00
|
|
|
.base = (struct cspi_map *)CSPI3_BASE_ADDR,
|
2008-04-11 08:51:27 +00:00
|
|
|
.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];
|
2008-04-27 21:32:10 +00:00
|
|
|
struct cspi_map * const base = desc->base;
|
2008-04-11 08:51:27 +00:00
|
|
|
struct spi_transfer *trans = desc->trans;
|
|
|
|
int inc = desc->byte_size + 1;
|
|
|
|
|
|
|
|
if (desc->rxcount > 0)
|
|
|
|
{
|
|
|
|
/* Data received - empty out RXFIFO */
|
2008-04-27 21:32:10 +00:00
|
|
|
while ((base->statreg & CSPI_STATREG_RR) != 0)
|
2008-04-11 08:51:27 +00:00
|
|
|
{
|
2008-04-27 21:32:10 +00:00
|
|
|
uint32_t word = base->rxdata;
|
2008-04-11 08:51:27 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2008-04-27 21:32:10 +00:00
|
|
|
unsigned long intreg = base->intreg;
|
2008-04-11 08:51:27 +00:00
|
|
|
|
|
|
|
if (desc->rxcount <= 0)
|
|
|
|
{
|
|
|
|
/* No more to receive - stop RX interrupts */
|
|
|
|
intreg &= ~(CSPI_INTREG_RHEN | CSPI_INTREG_RREN);
|
2008-04-27 21:32:10 +00:00
|
|
|
base->intreg = intreg;
|
2008-04-11 08:51:27 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (!(intreg & CSPI_INTREG_RREN))
|
|
|
|
{
|
|
|
|
/* < 4 words expected - switch to RX ready */
|
|
|
|
intreg &= ~CSPI_INTREG_RHEN;
|
2008-04-27 21:32:10 +00:00
|
|
|
base->intreg = intreg | CSPI_INTREG_RREN;
|
2008-04-11 08:51:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (trans->count > 0)
|
|
|
|
{
|
|
|
|
/* Data to transmit - fill TXFIFO or write until exhausted */
|
2008-04-27 21:32:10 +00:00
|
|
|
while ((base->statreg & CSPI_STATREG_TF) == 0)
|
2008-04-11 08:51:27 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2008-04-27 21:32:10 +00:00
|
|
|
base->txdata = word;
|
2008-04-11 08:51:27 +00:00
|
|
|
|
|
|
|
if (--trans->count <= 0)
|
|
|
|
{
|
|
|
|
/* Out of data - stop TX interrupts */
|
2008-04-27 21:32:10 +00:00
|
|
|
base->intreg &= ~CSPI_INTREG_THEN;
|
2008-04-11 08:51:27 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If all interrupts have been remasked - we're done */
|
2008-04-27 21:32:10 +00:00
|
|
|
if (base->intreg == 0)
|
2008-04-11 08:51:27 +00:00
|
|
|
{
|
2008-04-27 21:32:10 +00:00
|
|
|
base->statreg = CSPI_STATREG_TC | CSPI_STATREG_BO;
|
2008-04-11 08:51:27 +00:00
|
|
|
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)
|
|
|
|
{
|
2008-04-27 21:32:10 +00:00
|
|
|
struct cspi_map * const base = desc->base;
|
2008-04-11 08:51:27 +00:00
|
|
|
|
2008-04-27 21:32:10 +00:00
|
|
|
if ((base->conreg & CSPI_CONREG_EN) == 0)
|
2008-04-11 08:51:27 +00:00
|
|
|
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. */
|
2008-04-27 21:32:10 +00:00
|
|
|
base->conreg =
|
2008-04-11 08:51:27 +00:00
|
|
|
(node->conreg & ~(0xfcc8e000 | CSPI_CONREG_XCH | CSPI_CONREG_SMC))
|
|
|
|
| CSPI_CONREG_EN;
|
|
|
|
/* Set the wait-states */
|
2008-04-27 21:32:10 +00:00
|
|
|
base->periodreg = node->periodreg & 0xffff;
|
2008-04-11 08:51:27 +00:00
|
|
|
/* Clear out any spuriously-pending interrupts */
|
2008-04-27 21:32:10 +00:00
|
|
|
base->statreg = CSPI_STATREG_TC | CSPI_STATREG_BO;
|
2008-04-11 08:51:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2007-09-21 15:51:53 +00:00
|
|
|
}
|
|
|
|
|
2008-04-11 08:51:27 +00:00
|
|
|
/* Initialize each of the used SPI descriptors */
|
|
|
|
void spi_init(void)
|
|
|
|
{
|
|
|
|
int i;
|
2008-02-09 07:59:53 +00:00
|
|
|
|
2008-04-11 08:51:27 +00:00
|
|
|
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);
|
2007-09-21 15:51:53 +00:00
|
|
|
}
|
2008-04-11 08:51:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 */
|
2008-04-27 21:32:10 +00:00
|
|
|
struct cspi_map * const base = desc->base;
|
2008-04-11 08:51:27 +00:00
|
|
|
|
|
|
|
/* Enable clock-gating register */
|
|
|
|
imx31_clkctl_module_clock_gating(desc->cg, CGM_ON_ALL);
|
|
|
|
|
|
|
|
/* Reset */
|
2008-04-27 21:32:10 +00:00
|
|
|
base->conreg &= ~CSPI_CONREG_EN;
|
|
|
|
base->conreg |= CSPI_CONREG_EN;
|
|
|
|
base->intreg = 0;
|
|
|
|
base->statreg = CSPI_STATREG_TC | CSPI_STATREG_BO;
|
2008-04-11 08:51:27 +00:00
|
|
|
|
|
|
|
/* Enable interrupt at controller level */
|
|
|
|
avic_enable_int(desc->ints, IRQ, 6, desc->handler);
|
2007-09-21 15:51:53 +00:00
|
|
|
}
|
2008-04-11 08:51:27 +00:00
|
|
|
|
|
|
|
mutex_unlock(&desc->m);
|
2007-09-21 15:51:53 +00:00
|
|
|
}
|
|
|
|
|
2008-04-11 08:51:27 +00:00
|
|
|
/* 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 */
|
2008-04-27 21:32:10 +00:00
|
|
|
struct cspi_map * const base = desc->base;
|
2008-04-11 08:51:27 +00:00
|
|
|
|
|
|
|
/* Disable interrupt at controller level */
|
|
|
|
avic_disable_int(desc->ints);
|
|
|
|
|
|
|
|
/* Disable interface */
|
2008-04-27 21:32:10 +00:00
|
|
|
base->conreg &= ~CSPI_CONREG_EN;
|
2008-04-11 08:51:27 +00:00
|
|
|
|
|
|
|
/* Disable interface clock */
|
|
|
|
imx31_clkctl_module_clock_gating(desc->cg, CGM_OFF);
|
2007-09-21 15:51:53 +00:00
|
|
|
}
|
2008-04-11 08:51:27 +00:00
|
|
|
|
|
|
|
mutex_unlock(&desc->m);
|
2007-09-21 15:51:53 +00:00
|
|
|
}
|
|
|
|
|
2008-04-11 08:51:27 +00:00
|
|
|
/* 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)
|
|
|
|
{
|
2008-04-27 21:32:10 +00:00
|
|
|
struct cspi_map * const base = desc->base;
|
2008-04-11 08:51:27 +00:00
|
|
|
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 */
|
|
|
|
|
2008-04-27 21:32:10 +00:00
|
|
|
base->intreg = intreg;
|
2008-04-11 08:51:27 +00:00
|
|
|
|
|
|
|
/* Start transfer */
|
2008-04-27 21:32:10 +00:00
|
|
|
base->conreg |= CSPI_CONREG_XCH;
|
2008-04-11 08:51:27 +00:00
|
|
|
|
|
|
|
if (wakeup_wait(&desc->w, HZ) != OBJ_WAIT_SUCCEEDED)
|
|
|
|
{
|
2008-04-27 21:32:10 +00:00
|
|
|
base->intreg = 0;
|
|
|
|
base->conreg &= ~CSPI_CONREG_XCH;
|
2008-04-11 08:51:27 +00:00
|
|
|
retval = false;
|
|
|
|
}
|
2007-09-21 15:51:53 +00:00
|
|
|
}
|
2008-04-11 08:51:27 +00:00
|
|
|
|
|
|
|
mutex_unlock(&desc->m);
|
|
|
|
|
|
|
|
return retval;
|
2007-09-21 15:51:53 +00:00
|
|
|
}
|