2007-02-14 11:33:09 +03:00
/*
* Driver for Atmel AT32 and AT91 SPI Controllers
*
* Copyright ( C ) 2006 Atmel Corporation
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/clk.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/delay.h>
# include <linux/dma-mapping.h>
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/spi/spi.h>
# include <asm/io.h>
# include <asm/arch/board.h>
# include <asm/arch/gpio.h>
2007-02-21 00:58:19 +03:00
# include <asm/arch/cpu.h>
2007-02-14 11:33:09 +03:00
# include "atmel_spi.h"
/*
* The core SPI transfer engine just talks to a register bank to set up
* DMA transfers ; transfer queue progress is driven by IRQs . The clock
* framework provides the base clock , subdivided for each spi_device .
*
* Newer controllers , marked with " new_1 " flag , have :
* - CR . LASTXFER
* - SPI_MR . DIV32 may become FDIV or must - be - zero ( here : always zero )
* - SPI_SR . TXEMPTY , SPI_SR . NSSR ( and corresponding irqs )
* - SPI_CSRx . CSAAT
* - SPI_CSRx . SBCR allows faster clocking
*/
struct atmel_spi {
spinlock_t lock ;
void __iomem * regs ;
int irq ;
struct clk * clk ;
struct platform_device * pdev ;
unsigned new_1 : 1 ;
u8 stopping ;
struct list_head queue ;
struct spi_transfer * current_transfer ;
unsigned long remaining_bytes ;
void * buffer ;
dma_addr_t buffer_dma ;
} ;
# define BUFFER_SIZE PAGE_SIZE
# define INVALID_DMA_ADDRESS 0xffffffff
/*
* Earlier SPI controllers ( e . g . on at91rm9200 ) have a design bug whereby
* they assume that spi slave device state will not change on deselect , so
* that automagic deselection is OK . Not so ! Workaround uses nCSx pins
* as GPIOs ; or newer controllers have CSAAT and friends .
*
* Since the CSAAT functionality is a bit weird on newer controllers
* as well , we use GPIO to control nCSx pins on all controllers .
*/
static inline void cs_activate ( struct spi_device * spi )
{
unsigned gpio = ( unsigned ) spi - > controller_data ;
unsigned active = spi - > mode & SPI_CS_HIGH ;
dev_dbg ( & spi - > dev , " activate %u%s \n " , gpio , active ? " (high) " : " " ) ;
gpio_set_value ( gpio , active ) ;
}
static inline void cs_deactivate ( struct spi_device * spi )
{
unsigned gpio = ( unsigned ) spi - > controller_data ;
unsigned active = spi - > mode & SPI_CS_HIGH ;
dev_dbg ( & spi - > dev , " DEactivate %u%s \n " , gpio , active ? " (low) " : " " ) ;
gpio_set_value ( gpio , ! active ) ;
}
/*
* Submit next transfer for DMA .
* lock is held , spi irq is blocked
*/
static void atmel_spi_next_xfer ( struct spi_master * master ,
struct spi_message * msg )
{
struct atmel_spi * as = spi_master_get_devdata ( master ) ;
struct spi_transfer * xfer ;
u32 len ;
dma_addr_t tx_dma , rx_dma ;
xfer = as - > current_transfer ;
if ( ! xfer | | as - > remaining_bytes = = 0 ) {
if ( xfer )
xfer = list_entry ( xfer - > transfer_list . next ,
struct spi_transfer , transfer_list ) ;
else
xfer = list_entry ( msg - > transfers . next ,
struct spi_transfer , transfer_list ) ;
as - > remaining_bytes = xfer - > len ;
as - > current_transfer = xfer ;
}
len = as - > remaining_bytes ;
tx_dma = xfer - > tx_dma ;
rx_dma = xfer - > rx_dma ;
/* use scratch buffer only when rx or tx data is unspecified */
if ( rx_dma = = INVALID_DMA_ADDRESS ) {
rx_dma = as - > buffer_dma ;
if ( len > BUFFER_SIZE )
len = BUFFER_SIZE ;
}
if ( tx_dma = = INVALID_DMA_ADDRESS ) {
tx_dma = as - > buffer_dma ;
if ( len > BUFFER_SIZE )
len = BUFFER_SIZE ;
memset ( as - > buffer , 0 , len ) ;
dma_sync_single_for_device ( & as - > pdev - > dev ,
as - > buffer_dma , len , DMA_TO_DEVICE ) ;
}
spi_writel ( as , RPR , rx_dma ) ;
spi_writel ( as , TPR , tx_dma ) ;
as - > remaining_bytes - = len ;
if ( msg - > spi - > bits_per_word > 8 )
len > > = 1 ;
/* REVISIT: when xfer->delay_usecs == 0, the PDC "next transfer"
* mechanism might help avoid the IRQ latency between transfers
*
* We ' re also waiting for ENDRX before we start the next
* transfer because we need to handle some difficult timing
* issues otherwise . If we wait for ENDTX in one transfer and
* then starts waiting for ENDRX in the next , it ' s difficult
* to tell the difference between the ENDRX interrupt we ' re
* actually waiting for and the ENDRX interrupt of the
* previous transfer .
*
* It should be doable , though . Just not now . . .
*/
spi_writel ( as , TNCR , 0 ) ;
spi_writel ( as , RNCR , 0 ) ;
spi_writel ( as , IER , SPI_BIT ( ENDRX ) | SPI_BIT ( OVRES ) ) ;
dev_dbg ( & msg - > spi - > dev ,
" start xfer %p: len %u tx %p/%08x rx %p/%08x imr %03x \n " ,
xfer , xfer - > len , xfer - > tx_buf , xfer - > tx_dma ,
xfer - > rx_buf , xfer - > rx_dma , spi_readl ( as , IMR ) ) ;
spi_writel ( as , TCR , len ) ;
spi_writel ( as , RCR , len ) ;
spi_writel ( as , PTCR , SPI_BIT ( TXTEN ) | SPI_BIT ( RXTEN ) ) ;
}
static void atmel_spi_next_message ( struct spi_master * master )
{
struct atmel_spi * as = spi_master_get_devdata ( master ) ;
struct spi_message * msg ;
u32 mr ;
BUG_ON ( as - > current_transfer ) ;
msg = list_entry ( as - > queue . next , struct spi_message , queue ) ;
/* Select the chip */
mr = spi_readl ( as , MR ) ;
mr = SPI_BFINS ( PCS , ~ ( 1 < < msg - > spi - > chip_select ) , mr ) ;
spi_writel ( as , MR , mr ) ;
cs_activate ( msg - > spi ) ;
atmel_spi_next_xfer ( master , msg ) ;
}
static void
atmel_spi_dma_map_xfer ( struct atmel_spi * as , struct spi_transfer * xfer )
{
xfer - > tx_dma = xfer - > rx_dma = INVALID_DMA_ADDRESS ;
if ( xfer - > tx_buf )
xfer - > tx_dma = dma_map_single ( & as - > pdev - > dev ,
( void * ) xfer - > tx_buf , xfer - > len ,
DMA_TO_DEVICE ) ;
if ( xfer - > rx_buf )
xfer - > rx_dma = dma_map_single ( & as - > pdev - > dev ,
xfer - > rx_buf , xfer - > len ,
DMA_FROM_DEVICE ) ;
}
static void atmel_spi_dma_unmap_xfer ( struct spi_master * master ,
struct spi_transfer * xfer )
{
if ( xfer - > tx_dma ! = INVALID_DMA_ADDRESS )
dma_unmap_single ( master - > cdev . dev , xfer - > tx_dma ,
xfer - > len , DMA_TO_DEVICE ) ;
if ( xfer - > rx_dma ! = INVALID_DMA_ADDRESS )
dma_unmap_single ( master - > cdev . dev , xfer - > rx_dma ,
xfer - > len , DMA_FROM_DEVICE ) ;
}
static void
atmel_spi_msg_done ( struct spi_master * master , struct atmel_spi * as ,
struct spi_message * msg , int status )
{
cs_deactivate ( msg - > spi ) ;
list_del ( & msg - > queue ) ;
msg - > status = status ;
dev_dbg ( master - > cdev . dev ,
" xfer complete: %u bytes transferred \n " ,
msg - > actual_length ) ;
spin_unlock ( & as - > lock ) ;
msg - > complete ( msg - > context ) ;
spin_lock ( & as - > lock ) ;
as - > current_transfer = NULL ;
/* continue if needed */
if ( list_empty ( & as - > queue ) | | as - > stopping )
spi_writel ( as , PTCR , SPI_BIT ( RXTDIS ) | SPI_BIT ( TXTDIS ) ) ;
else
atmel_spi_next_message ( master ) ;
}
static irqreturn_t
atmel_spi_interrupt ( int irq , void * dev_id )
{
struct spi_master * master = dev_id ;
struct atmel_spi * as = spi_master_get_devdata ( master ) ;
struct spi_message * msg ;
struct spi_transfer * xfer ;
u32 status , pending , imr ;
int ret = IRQ_NONE ;
spin_lock ( & as - > lock ) ;
xfer = as - > current_transfer ;
msg = list_entry ( as - > queue . next , struct spi_message , queue ) ;
imr = spi_readl ( as , IMR ) ;
status = spi_readl ( as , SR ) ;
pending = status & imr ;
if ( pending & SPI_BIT ( OVRES ) ) {
int timeout ;
ret = IRQ_HANDLED ;
spi_writel ( as , IDR , ( SPI_BIT ( ENDTX ) | SPI_BIT ( ENDRX )
| SPI_BIT ( OVRES ) ) ) ;
/*
* When we get an overrun , we disregard the current
* transfer . Data will not be copied back from any
* bounce buffer and msg - > actual_len will not be
* updated with the last xfer .
*
* We will also not process any remaning transfers in
* the message .
*
* First , stop the transfer and unmap the DMA buffers .
*/
spi_writel ( as , PTCR , SPI_BIT ( RXTDIS ) | SPI_BIT ( TXTDIS ) ) ;
if ( ! msg - > is_dma_mapped )
atmel_spi_dma_unmap_xfer ( master , xfer ) ;
/* REVISIT: udelay in irq is unfriendly */
if ( xfer - > delay_usecs )
udelay ( xfer - > delay_usecs ) ;
dev_warn ( master - > cdev . dev , " fifo overrun (%u/%u remaining) \n " ,
spi_readl ( as , TCR ) , spi_readl ( as , RCR ) ) ;
/*
* Clean up DMA registers and make sure the data
* registers are empty .
*/
spi_writel ( as , RNCR , 0 ) ;
spi_writel ( as , TNCR , 0 ) ;
spi_writel ( as , RCR , 0 ) ;
spi_writel ( as , TCR , 0 ) ;
for ( timeout = 1000 ; timeout ; timeout - - )
if ( spi_readl ( as , SR ) & SPI_BIT ( TXEMPTY ) )
break ;
if ( ! timeout )
dev_warn ( master - > cdev . dev ,
" timeout waiting for TXEMPTY " ) ;
while ( spi_readl ( as , SR ) & SPI_BIT ( RDRF ) )
spi_readl ( as , RDR ) ;
/* Clear any overrun happening while cleaning up */
spi_readl ( as , SR ) ;
atmel_spi_msg_done ( master , as , msg , - EIO ) ;
} else if ( pending & SPI_BIT ( ENDRX ) ) {
ret = IRQ_HANDLED ;
spi_writel ( as , IDR , pending ) ;
if ( as - > remaining_bytes = = 0 ) {
msg - > actual_length + = xfer - > len ;
if ( ! msg - > is_dma_mapped )
atmel_spi_dma_unmap_xfer ( master , xfer ) ;
/* REVISIT: udelay in irq is unfriendly */
if ( xfer - > delay_usecs )
udelay ( xfer - > delay_usecs ) ;
if ( msg - > transfers . prev = = & xfer - > transfer_list ) {
/* report completed message */
atmel_spi_msg_done ( master , as , msg , 0 ) ;
} else {
if ( xfer - > cs_change ) {
cs_deactivate ( msg - > spi ) ;
udelay ( 1 ) ;
cs_activate ( msg - > spi ) ;
}
/*
* Not done yet . Submit the next transfer .
*
* FIXME handle protocol options for xfer
*/
atmel_spi_next_xfer ( master , msg ) ;
}
} else {
/*
* Keep going , we still have data to send in
* the current transfer .
*/
atmel_spi_next_xfer ( master , msg ) ;
}
}
spin_unlock ( & as - > lock ) ;
return ret ;
}
# define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)
static int atmel_spi_setup ( struct spi_device * spi )
{
struct atmel_spi * as ;
u32 scbr , csr ;
unsigned int bits = spi - > bits_per_word ;
unsigned long bus_hz , sck_hz ;
unsigned int npcs_pin ;
int ret ;
as = spi_master_get_devdata ( spi - > master ) ;
if ( as - > stopping )
return - ESHUTDOWN ;
if ( spi - > chip_select > spi - > master - > num_chipselect ) {
dev_dbg ( & spi - > dev ,
" setup: invalid chipselect %u (%u defined) \n " ,
spi - > chip_select , spi - > master - > num_chipselect ) ;
return - EINVAL ;
}
if ( bits = = 0 )
bits = 8 ;
if ( bits < 8 | | bits > 16 ) {
dev_dbg ( & spi - > dev ,
" setup: invalid bits_per_word %u (8 to 16) \n " ,
bits ) ;
return - EINVAL ;
}
if ( spi - > mode & ~ MODEBITS ) {
dev_dbg ( & spi - > dev , " setup: unsupported mode bits %x \n " ,
spi - > mode & ~ MODEBITS ) ;
return - EINVAL ;
}
/* speed zero convention is used by some upper layers */
bus_hz = clk_get_rate ( as - > clk ) ;
if ( spi - > max_speed_hz ) {
/* assume div32/fdiv/mbz == 0 */
if ( ! as - > new_1 )
bus_hz / = 2 ;
scbr = ( ( bus_hz + spi - > max_speed_hz - 1 )
/ spi - > max_speed_hz ) ;
if ( scbr > = ( 1 < < SPI_SCBR_SIZE ) ) {
dev_dbg ( & spi - > dev , " setup: %d Hz too slow, scbr %u \n " ,
spi - > max_speed_hz , scbr ) ;
return - EINVAL ;
}
} else
scbr = 0xff ;
sck_hz = bus_hz / scbr ;
csr = SPI_BF ( SCBR , scbr ) | SPI_BF ( BITS , bits - 8 ) ;
if ( spi - > mode & SPI_CPOL )
csr | = SPI_BIT ( CPOL ) ;
if ( ! ( spi - > mode & SPI_CPHA ) )
csr | = SPI_BIT ( NCPHA ) ;
/* TODO: DLYBS and DLYBCT */
csr | = SPI_BF ( DLYBS , 10 ) ;
csr | = SPI_BF ( DLYBCT , 10 ) ;
/* chipselect must have been muxed as GPIO (e.g. in board setup) */
npcs_pin = ( unsigned int ) spi - > controller_data ;
if ( ! spi - > controller_state ) {
ret = gpio_request ( npcs_pin , " spi_npcs " ) ;
if ( ret )
return ret ;
spi - > controller_state = ( void * ) npcs_pin ;
2007-03-17 00:38:14 +03:00
gpio_direction_output ( npcs_pin , ! ( spi - > mode & SPI_CS_HIGH ) ) ;
2007-02-14 11:33:09 +03:00
}
dev_dbg ( & spi - > dev ,
" setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x \n " ,
sck_hz , bits , spi - > mode , spi - > chip_select , csr ) ;
spi_writel ( as , CSR0 + 4 * spi - > chip_select , csr ) ;
return 0 ;
}
static int atmel_spi_transfer ( struct spi_device * spi , struct spi_message * msg )
{
struct atmel_spi * as ;
struct spi_transfer * xfer ;
unsigned long flags ;
struct device * controller = spi - > master - > cdev . dev ;
as = spi_master_get_devdata ( spi - > master ) ;
dev_dbg ( controller , " new message %p submitted for %s \n " ,
msg , spi - > dev . bus_id ) ;
if ( unlikely ( list_empty ( & msg - > transfers )
| | ! spi - > max_speed_hz ) )
return - EINVAL ;
if ( as - > stopping )
return - ESHUTDOWN ;
list_for_each_entry ( xfer , & msg - > transfers , transfer_list ) {
if ( ! ( xfer - > tx_buf | | xfer - > rx_buf ) ) {
dev_dbg ( & spi - > dev , " missing rx or tx buf \n " ) ;
return - EINVAL ;
}
/* FIXME implement these protocol options!! */
if ( xfer - > bits_per_word | | xfer - > speed_hz ) {
dev_dbg ( & spi - > dev , " no protocol options yet \n " ) ;
return - ENOPROTOOPT ;
}
}
/* scrub dcache "early" */
if ( ! msg - > is_dma_mapped ) {
list_for_each_entry ( xfer , & msg - > transfers , transfer_list )
atmel_spi_dma_map_xfer ( as , xfer ) ;
}
list_for_each_entry ( xfer , & msg - > transfers , transfer_list ) {
dev_dbg ( controller ,
" xfer %p: len %u tx %p/%08x rx %p/%08x \n " ,
xfer , xfer - > len ,
xfer - > tx_buf , xfer - > tx_dma ,
xfer - > rx_buf , xfer - > rx_dma ) ;
}
msg - > status = - EINPROGRESS ;
msg - > actual_length = 0 ;
spin_lock_irqsave ( & as - > lock , flags ) ;
list_add_tail ( & msg - > queue , & as - > queue ) ;
if ( ! as - > current_transfer )
atmel_spi_next_message ( spi - > master ) ;
spin_unlock_irqrestore ( & as - > lock , flags ) ;
return 0 ;
}
2007-02-21 00:58:19 +03:00
static void atmel_spi_cleanup ( struct spi_device * spi )
2007-02-14 11:33:09 +03:00
{
if ( spi - > controller_state )
gpio_free ( ( unsigned int ) spi - > controller_data ) ;
}
/*-------------------------------------------------------------------------*/
static int __init atmel_spi_probe ( struct platform_device * pdev )
{
struct resource * regs ;
int irq ;
struct clk * clk ;
int ret ;
struct spi_master * master ;
struct atmel_spi * as ;
regs = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! regs )
return - ENXIO ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 )
return irq ;
clk = clk_get ( & pdev - > dev , " spi_clk " ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
/* setup spi core then atmel-specific driver state */
ret = - ENOMEM ;
master = spi_alloc_master ( & pdev - > dev , sizeof * as ) ;
if ( ! master )
goto out_free ;
master - > bus_num = pdev - > id ;
master - > num_chipselect = 4 ;
master - > setup = atmel_spi_setup ;
master - > transfer = atmel_spi_transfer ;
master - > cleanup = atmel_spi_cleanup ;
platform_set_drvdata ( pdev , master ) ;
as = spi_master_get_devdata ( master ) ;
as - > buffer = dma_alloc_coherent ( & pdev - > dev , BUFFER_SIZE ,
& as - > buffer_dma , GFP_KERNEL ) ;
if ( ! as - > buffer )
goto out_free ;
spin_lock_init ( & as - > lock ) ;
INIT_LIST_HEAD ( & as - > queue ) ;
as - > pdev = pdev ;
as - > regs = ioremap ( regs - > start , ( regs - > end - regs - > start ) + 1 ) ;
if ( ! as - > regs )
goto out_free_buffer ;
as - > irq = irq ;
as - > clk = clk ;
if ( ! cpu_is_at91rm9200 ( ) )
as - > new_1 = 1 ;
ret = request_irq ( irq , atmel_spi_interrupt , 0 ,
pdev - > dev . bus_id , master ) ;
if ( ret )
goto out_unmap_regs ;
/* Initialize the hardware */
clk_enable ( clk ) ;
spi_writel ( as , CR , SPI_BIT ( SWRST ) ) ;
spi_writel ( as , MR , SPI_BIT ( MSTR ) | SPI_BIT ( MODFDIS ) ) ;
spi_writel ( as , PTCR , SPI_BIT ( RXTDIS ) | SPI_BIT ( TXTDIS ) ) ;
spi_writel ( as , CR , SPI_BIT ( SPIEN ) ) ;
/* go! */
dev_info ( & pdev - > dev , " Atmel SPI Controller at 0x%08lx (irq %d) \n " ,
( unsigned long ) regs - > start , irq ) ;
ret = spi_register_master ( master ) ;
if ( ret )
goto out_reset_hw ;
return 0 ;
out_reset_hw :
spi_writel ( as , CR , SPI_BIT ( SWRST ) ) ;
clk_disable ( clk ) ;
free_irq ( irq , master ) ;
out_unmap_regs :
iounmap ( as - > regs ) ;
out_free_buffer :
dma_free_coherent ( & pdev - > dev , BUFFER_SIZE , as - > buffer ,
as - > buffer_dma ) ;
out_free :
clk_put ( clk ) ;
spi_master_put ( master ) ;
return ret ;
}
static int __exit atmel_spi_remove ( struct platform_device * pdev )
{
struct spi_master * master = platform_get_drvdata ( pdev ) ;
struct atmel_spi * as = spi_master_get_devdata ( master ) ;
struct spi_message * msg ;
/* reset the hardware and block queue progress */
spin_lock_irq ( & as - > lock ) ;
as - > stopping = 1 ;
spi_writel ( as , CR , SPI_BIT ( SWRST ) ) ;
spi_readl ( as , SR ) ;
spin_unlock_irq ( & as - > lock ) ;
/* Terminate remaining queued transfers */
list_for_each_entry ( msg , & as - > queue , queue ) {
/* REVISIT unmapping the dma is a NOP on ARM and AVR32
* but we shouldn ' t depend on that . . .
*/
msg - > status = - ESHUTDOWN ;
msg - > complete ( msg - > context ) ;
}
dma_free_coherent ( & pdev - > dev , BUFFER_SIZE , as - > buffer ,
as - > buffer_dma ) ;
clk_disable ( as - > clk ) ;
clk_put ( as - > clk ) ;
free_irq ( as - > irq , master ) ;
iounmap ( as - > regs ) ;
spi_unregister_master ( master ) ;
return 0 ;
}
# ifdef CONFIG_PM
static int atmel_spi_suspend ( struct platform_device * pdev , pm_message_t mesg )
{
struct spi_master * master = platform_get_drvdata ( pdev ) ;
struct atmel_spi * as = spi_master_get_devdata ( master ) ;
clk_disable ( as - > clk ) ;
return 0 ;
}
static int atmel_spi_resume ( struct platform_device * pdev )
{
struct spi_master * master = platform_get_drvdata ( pdev ) ;
struct atmel_spi * as = spi_master_get_devdata ( master ) ;
clk_enable ( as - > clk ) ;
return 0 ;
}
# else
# define atmel_spi_suspend NULL
# define atmel_spi_resume NULL
# endif
static struct platform_driver atmel_spi_driver = {
. driver = {
. name = " atmel_spi " ,
. owner = THIS_MODULE ,
} ,
. suspend = atmel_spi_suspend ,
. resume = atmel_spi_resume ,
. remove = __exit_p ( atmel_spi_remove ) ,
} ;
static int __init atmel_spi_init ( void )
{
return platform_driver_probe ( & atmel_spi_driver , atmel_spi_probe ) ;
}
module_init ( atmel_spi_init ) ;
static void __exit atmel_spi_exit ( void )
{
platform_driver_unregister ( & atmel_spi_driver ) ;
}
module_exit ( atmel_spi_exit ) ;
MODULE_DESCRIPTION ( " Atmel AT32/AT91 SPI Controller driver " ) ;
MODULE_AUTHOR ( " Haavard Skinnemoen <hskinnemoen@atmel.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;