2009-09-23 03:46:02 +04:00
/*
* Copyright 2004 - 2007 Freescale Semiconductor , Inc . All Rights Reserved .
* Copyright ( C ) 2008 Juergen Beisert
*
* 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 program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the
* Free Software Foundation
* 51 Franklin Street , Fifth Floor
* Boston , MA 02110 - 1301 , USA .
*/
# include <linux/clk.h>
# include <linux/completion.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/gpio.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/spi/spi.h>
# include <linux/spi/spi_bitbang.h>
# include <linux/types.h>
# include <mach/spi.h>
# define DRIVER_NAME "spi_imx"
# define MXC_CSPIRXDATA 0x00
# define MXC_CSPITXDATA 0x04
# define MXC_CSPICTRL 0x08
# define MXC_CSPIINT 0x0c
# define MXC_RESET 0x1c
/* generic defines to abstract from the different register layouts */
# define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */
# define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */
2009-10-02 02:44:28 +04:00
struct spi_imx_config {
2009-09-23 03:46:02 +04:00
unsigned int speed_hz ;
unsigned int bpw ;
unsigned int mode ;
int cs ;
} ;
2009-10-02 02:44:28 +04:00
struct spi_imx_data {
2009-09-23 03:46:02 +04:00
struct spi_bitbang bitbang ;
struct completion xfer_done ;
void * base ;
int irq ;
struct clk * clk ;
unsigned long spi_clk ;
int * chipselect ;
unsigned int count ;
2009-10-02 02:44:28 +04:00
void ( * tx ) ( struct spi_imx_data * ) ;
void ( * rx ) ( struct spi_imx_data * ) ;
2009-09-23 03:46:02 +04:00
void * rx_buf ;
const void * tx_buf ;
unsigned int txfifo ; /* number of words pushed in tx FIFO */
/* SoC specific functions */
2009-10-02 02:44:28 +04:00
void ( * intctrl ) ( struct spi_imx_data * , int ) ;
int ( * config ) ( struct spi_imx_data * , struct spi_imx_config * ) ;
void ( * trigger ) ( struct spi_imx_data * ) ;
int ( * rx_available ) ( struct spi_imx_data * ) ;
2009-09-23 03:46:02 +04:00
} ;
# define MXC_SPI_BUF_RX(type) \
2009-10-02 02:44:28 +04:00
static void spi_imx_buf_rx_ # # type ( struct spi_imx_data * spi_imx ) \
2009-09-23 03:46:02 +04:00
{ \
2009-10-02 02:44:28 +04:00
unsigned int val = readl ( spi_imx - > base + MXC_CSPIRXDATA ) ; \
2009-09-23 03:46:02 +04:00
\
2009-10-02 02:44:28 +04:00
if ( spi_imx - > rx_buf ) { \
* ( type * ) spi_imx - > rx_buf = val ; \
spi_imx - > rx_buf + = sizeof ( type ) ; \
2009-09-23 03:46:02 +04:00
} \
}
# define MXC_SPI_BUF_TX(type) \
2009-10-02 02:44:28 +04:00
static void spi_imx_buf_tx_ # # type ( struct spi_imx_data * spi_imx ) \
2009-09-23 03:46:02 +04:00
{ \
type val = 0 ; \
\
2009-10-02 02:44:28 +04:00
if ( spi_imx - > tx_buf ) { \
val = * ( type * ) spi_imx - > tx_buf ; \
spi_imx - > tx_buf + = sizeof ( type ) ; \
2009-09-23 03:46:02 +04:00
} \
\
2009-10-02 02:44:28 +04:00
spi_imx - > count - = sizeof ( type ) ; \
2009-09-23 03:46:02 +04:00
\
2009-10-02 02:44:28 +04:00
writel ( val , spi_imx - > base + MXC_CSPITXDATA ) ; \
2009-09-23 03:46:02 +04:00
}
MXC_SPI_BUF_RX ( u8 )
MXC_SPI_BUF_TX ( u8 )
MXC_SPI_BUF_RX ( u16 )
MXC_SPI_BUF_TX ( u16 )
MXC_SPI_BUF_RX ( u32 )
MXC_SPI_BUF_TX ( u32 )
/* First entry is reserved, second entry is valid only if SDHC_SPIEN is set
* ( which is currently not the case in this driver )
*/
static int mxc_clkdivs [ ] = { 0 , 3 , 4 , 6 , 8 , 12 , 16 , 24 , 32 , 48 , 64 , 96 , 128 , 192 ,
256 , 384 , 512 , 768 , 1024 } ;
/* MX21, MX27 */
2009-10-02 02:44:28 +04:00
static unsigned int spi_imx_clkdiv_1 ( unsigned int fin ,
2009-09-23 03:46:02 +04:00
unsigned int fspi )
{
int i , max ;
if ( cpu_is_mx21 ( ) )
max = 18 ;
else
max = 16 ;
for ( i = 2 ; i < max ; i + + )
if ( fspi * mxc_clkdivs [ i ] > = fin )
return i ;
return max ;
}
/* MX1, MX31, MX35 */
2009-10-02 02:44:28 +04:00
static unsigned int spi_imx_clkdiv_2 ( unsigned int fin ,
2009-09-23 03:46:02 +04:00
unsigned int fspi )
{
int i , div = 4 ;
for ( i = 0 ; i < 7 ; i + + ) {
if ( fspi * div > = fin )
return i ;
div < < = 1 ;
}
return 7 ;
}
# define MX31_INTREG_TEEN (1 << 0)
# define MX31_INTREG_RREN (1 << 3)
# define MX31_CSPICTRL_ENABLE (1 << 0)
# define MX31_CSPICTRL_MASTER (1 << 1)
# define MX31_CSPICTRL_XCH (1 << 2)
# define MX31_CSPICTRL_POL (1 << 4)
# define MX31_CSPICTRL_PHA (1 << 5)
# define MX31_CSPICTRL_SSCTL (1 << 6)
# define MX31_CSPICTRL_SSPOL (1 << 7)
# define MX31_CSPICTRL_BC_SHIFT 8
# define MX35_CSPICTRL_BL_SHIFT 20
# define MX31_CSPICTRL_CS_SHIFT 24
# define MX35_CSPICTRL_CS_SHIFT 12
# define MX31_CSPICTRL_DR_SHIFT 16
# define MX31_CSPISTATUS 0x14
# define MX31_STATUS_RR (1 << 3)
/* These functions also work for the i.MX35, but be aware that
* the i . MX35 has a slightly different register layout for bits
* we do not use here .
*/
2009-10-02 02:44:28 +04:00
static void mx31_intctrl ( struct spi_imx_data * spi_imx , int enable )
2009-09-23 03:46:02 +04:00
{
unsigned int val = 0 ;
if ( enable & MXC_INT_TE )
val | = MX31_INTREG_TEEN ;
if ( enable & MXC_INT_RR )
val | = MX31_INTREG_RREN ;
2009-10-02 02:44:28 +04:00
writel ( val , spi_imx - > base + MXC_CSPIINT ) ;
2009-09-23 03:46:02 +04:00
}
2009-10-02 02:44:28 +04:00
static void mx31_trigger ( struct spi_imx_data * spi_imx )
2009-09-23 03:46:02 +04:00
{
unsigned int reg ;
2009-10-02 02:44:28 +04:00
reg = readl ( spi_imx - > base + MXC_CSPICTRL ) ;
2009-09-23 03:46:02 +04:00
reg | = MX31_CSPICTRL_XCH ;
2009-10-02 02:44:28 +04:00
writel ( reg , spi_imx - > base + MXC_CSPICTRL ) ;
2009-09-23 03:46:02 +04:00
}
2009-10-02 02:44:28 +04:00
static int mx31_config ( struct spi_imx_data * spi_imx ,
struct spi_imx_config * config )
2009-09-23 03:46:02 +04:00
{
unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER ;
2009-10-02 02:44:28 +04:00
reg | = spi_imx_clkdiv_2 ( spi_imx - > spi_clk , config - > speed_hz ) < <
2009-09-23 03:46:02 +04:00
MX31_CSPICTRL_DR_SHIFT ;
if ( cpu_is_mx31 ( ) )
reg | = ( config - > bpw - 1 ) < < MX31_CSPICTRL_BC_SHIFT ;
else if ( cpu_is_mx35 ( ) ) {
reg | = ( config - > bpw - 1 ) < < MX35_CSPICTRL_BL_SHIFT ;
reg | = MX31_CSPICTRL_SSCTL ;
}
if ( config - > mode & SPI_CPHA )
reg | = MX31_CSPICTRL_PHA ;
if ( config - > mode & SPI_CPOL )
reg | = MX31_CSPICTRL_POL ;
if ( config - > mode & SPI_CS_HIGH )
reg | = MX31_CSPICTRL_SSPOL ;
if ( config - > cs < 0 ) {
if ( cpu_is_mx31 ( ) )
reg | = ( config - > cs + 32 ) < < MX31_CSPICTRL_CS_SHIFT ;
else if ( cpu_is_mx35 ( ) )
reg | = ( config - > cs + 32 ) < < MX35_CSPICTRL_CS_SHIFT ;
}
2009-10-02 02:44:28 +04:00
writel ( reg , spi_imx - > base + MXC_CSPICTRL ) ;
2009-09-23 03:46:02 +04:00
return 0 ;
}
2009-10-02 02:44:28 +04:00
static int mx31_rx_available ( struct spi_imx_data * spi_imx )
2009-09-23 03:46:02 +04:00
{
2009-10-02 02:44:28 +04:00
return readl ( spi_imx - > base + MX31_CSPISTATUS ) & MX31_STATUS_RR ;
2009-09-23 03:46:02 +04:00
}
# define MX27_INTREG_RR (1 << 4)
# define MX27_INTREG_TEEN (1 << 9)
# define MX27_INTREG_RREN (1 << 13)
# define MX27_CSPICTRL_POL (1 << 5)
# define MX27_CSPICTRL_PHA (1 << 6)
# define MX27_CSPICTRL_SSPOL (1 << 8)
# define MX27_CSPICTRL_XCH (1 << 9)
# define MX27_CSPICTRL_ENABLE (1 << 10)
# define MX27_CSPICTRL_MASTER (1 << 11)
# define MX27_CSPICTRL_DR_SHIFT 14
# define MX27_CSPICTRL_CS_SHIFT 19
2009-10-02 02:44:28 +04:00
static void mx27_intctrl ( struct spi_imx_data * spi_imx , int enable )
2009-09-23 03:46:02 +04:00
{
unsigned int val = 0 ;
if ( enable & MXC_INT_TE )
val | = MX27_INTREG_TEEN ;
if ( enable & MXC_INT_RR )
val | = MX27_INTREG_RREN ;
2009-10-02 02:44:28 +04:00
writel ( val , spi_imx - > base + MXC_CSPIINT ) ;
2009-09-23 03:46:02 +04:00
}
2009-10-02 02:44:28 +04:00
static void mx27_trigger ( struct spi_imx_data * spi_imx )
2009-09-23 03:46:02 +04:00
{
unsigned int reg ;
2009-10-02 02:44:28 +04:00
reg = readl ( spi_imx - > base + MXC_CSPICTRL ) ;
2009-09-23 03:46:02 +04:00
reg | = MX27_CSPICTRL_XCH ;
2009-10-02 02:44:28 +04:00
writel ( reg , spi_imx - > base + MXC_CSPICTRL ) ;
2009-09-23 03:46:02 +04:00
}
2009-10-02 02:44:28 +04:00
static int mx27_config ( struct spi_imx_data * spi_imx ,
struct spi_imx_config * config )
2009-09-23 03:46:02 +04:00
{
unsigned int reg = MX27_CSPICTRL_ENABLE | MX27_CSPICTRL_MASTER ;
2009-10-02 02:44:28 +04:00
reg | = spi_imx_clkdiv_1 ( spi_imx - > spi_clk , config - > speed_hz ) < <
2009-09-23 03:46:02 +04:00
MX27_CSPICTRL_DR_SHIFT ;
reg | = config - > bpw - 1 ;
if ( config - > mode & SPI_CPHA )
reg | = MX27_CSPICTRL_PHA ;
if ( config - > mode & SPI_CPOL )
reg | = MX27_CSPICTRL_POL ;
if ( config - > mode & SPI_CS_HIGH )
reg | = MX27_CSPICTRL_SSPOL ;
if ( config - > cs < 0 )
reg | = ( config - > cs + 32 ) < < MX27_CSPICTRL_CS_SHIFT ;
2009-10-02 02:44:28 +04:00
writel ( reg , spi_imx - > base + MXC_CSPICTRL ) ;
2009-09-23 03:46:02 +04:00
return 0 ;
}
2009-10-02 02:44:28 +04:00
static int mx27_rx_available ( struct spi_imx_data * spi_imx )
2009-09-23 03:46:02 +04:00
{
2009-10-02 02:44:28 +04:00
return readl ( spi_imx - > base + MXC_CSPIINT ) & MX27_INTREG_RR ;
2009-09-23 03:46:02 +04:00
}
# define MX1_INTREG_RR (1 << 3)
# define MX1_INTREG_TEEN (1 << 8)
# define MX1_INTREG_RREN (1 << 11)
# define MX1_CSPICTRL_POL (1 << 4)
# define MX1_CSPICTRL_PHA (1 << 5)
# define MX1_CSPICTRL_XCH (1 << 8)
# define MX1_CSPICTRL_ENABLE (1 << 9)
# define MX1_CSPICTRL_MASTER (1 << 10)
# define MX1_CSPICTRL_DR_SHIFT 13
2009-10-02 02:44:28 +04:00
static void mx1_intctrl ( struct spi_imx_data * spi_imx , int enable )
2009-09-23 03:46:02 +04:00
{
unsigned int val = 0 ;
if ( enable & MXC_INT_TE )
val | = MX1_INTREG_TEEN ;
if ( enable & MXC_INT_RR )
val | = MX1_INTREG_RREN ;
2009-10-02 02:44:28 +04:00
writel ( val , spi_imx - > base + MXC_CSPIINT ) ;
2009-09-23 03:46:02 +04:00
}
2009-10-02 02:44:28 +04:00
static void mx1_trigger ( struct spi_imx_data * spi_imx )
2009-09-23 03:46:02 +04:00
{
unsigned int reg ;
2009-10-02 02:44:28 +04:00
reg = readl ( spi_imx - > base + MXC_CSPICTRL ) ;
2009-09-23 03:46:02 +04:00
reg | = MX1_CSPICTRL_XCH ;
2009-10-02 02:44:28 +04:00
writel ( reg , spi_imx - > base + MXC_CSPICTRL ) ;
2009-09-23 03:46:02 +04:00
}
2009-10-02 02:44:28 +04:00
static int mx1_config ( struct spi_imx_data * spi_imx ,
struct spi_imx_config * config )
2009-09-23 03:46:02 +04:00
{
unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_MASTER ;
2009-10-02 02:44:28 +04:00
reg | = spi_imx_clkdiv_2 ( spi_imx - > spi_clk , config - > speed_hz ) < <
2009-09-23 03:46:02 +04:00
MX1_CSPICTRL_DR_SHIFT ;
reg | = config - > bpw - 1 ;
if ( config - > mode & SPI_CPHA )
reg | = MX1_CSPICTRL_PHA ;
if ( config - > mode & SPI_CPOL )
reg | = MX1_CSPICTRL_POL ;
2009-10-02 02:44:28 +04:00
writel ( reg , spi_imx - > base + MXC_CSPICTRL ) ;
2009-09-23 03:46:02 +04:00
return 0 ;
}
2009-10-02 02:44:28 +04:00
static int mx1_rx_available ( struct spi_imx_data * spi_imx )
2009-09-23 03:46:02 +04:00
{
2009-10-02 02:44:28 +04:00
return readl ( spi_imx - > base + MXC_CSPIINT ) & MX1_INTREG_RR ;
2009-09-23 03:46:02 +04:00
}
2009-10-02 02:44:28 +04:00
static void spi_imx_chipselect ( struct spi_device * spi , int is_active )
2009-09-23 03:46:02 +04:00
{
2009-10-02 02:44:28 +04:00
struct spi_imx_data * spi_imx = spi_master_get_devdata ( spi - > master ) ;
2009-09-23 03:46:02 +04:00
unsigned int cs = 0 ;
2009-10-02 02:44:28 +04:00
int gpio = spi_imx - > chipselect [ spi - > chip_select ] ;
struct spi_imx_config config ;
2009-09-23 03:46:02 +04:00
if ( spi - > mode & SPI_CS_HIGH )
cs = 1 ;
if ( is_active = = BITBANG_CS_INACTIVE ) {
if ( gpio > = 0 )
gpio_set_value ( gpio , ! cs ) ;
return ;
}
config . bpw = spi - > bits_per_word ;
config . speed_hz = spi - > max_speed_hz ;
config . mode = spi - > mode ;
2009-10-02 02:44:28 +04:00
config . cs = spi_imx - > chipselect [ spi - > chip_select ] ;
2009-09-23 03:46:02 +04:00
2009-10-02 02:44:28 +04:00
spi_imx - > config ( spi_imx , & config ) ;
2009-09-23 03:46:02 +04:00
/* Initialize the functions for transfer */
if ( config . bpw < = 8 ) {
2009-10-02 02:44:28 +04:00
spi_imx - > rx = spi_imx_buf_rx_u8 ;
spi_imx - > tx = spi_imx_buf_tx_u8 ;
2009-09-23 03:46:02 +04:00
} else if ( config . bpw < = 16 ) {
2009-10-02 02:44:28 +04:00
spi_imx - > rx = spi_imx_buf_rx_u16 ;
spi_imx - > tx = spi_imx_buf_tx_u16 ;
2009-09-23 03:46:02 +04:00
} else if ( config . bpw < = 32 ) {
2009-10-02 02:44:28 +04:00
spi_imx - > rx = spi_imx_buf_rx_u32 ;
spi_imx - > tx = spi_imx_buf_tx_u32 ;
2009-09-23 03:46:02 +04:00
} else
BUG ( ) ;
if ( gpio > = 0 )
gpio_set_value ( gpio , cs ) ;
return ;
}
2009-10-02 02:44:28 +04:00
static void spi_imx_push ( struct spi_imx_data * spi_imx )
2009-09-23 03:46:02 +04:00
{
2009-10-02 02:44:28 +04:00
while ( spi_imx - > txfifo < 8 ) {
if ( ! spi_imx - > count )
2009-09-23 03:46:02 +04:00
break ;
2009-10-02 02:44:28 +04:00
spi_imx - > tx ( spi_imx ) ;
spi_imx - > txfifo + + ;
2009-09-23 03:46:02 +04:00
}
2009-10-02 02:44:28 +04:00
spi_imx - > trigger ( spi_imx ) ;
2009-09-23 03:46:02 +04:00
}
2009-10-02 02:44:28 +04:00
static irqreturn_t spi_imx_isr ( int irq , void * dev_id )
2009-09-23 03:46:02 +04:00
{
2009-10-02 02:44:28 +04:00
struct spi_imx_data * spi_imx = dev_id ;
2009-09-23 03:46:02 +04:00
2009-10-02 02:44:28 +04:00
while ( spi_imx - > rx_available ( spi_imx ) ) {
spi_imx - > rx ( spi_imx ) ;
spi_imx - > txfifo - - ;
2009-09-23 03:46:02 +04:00
}
2009-10-02 02:44:28 +04:00
if ( spi_imx - > count ) {
spi_imx_push ( spi_imx ) ;
2009-09-23 03:46:02 +04:00
return IRQ_HANDLED ;
}
2009-10-02 02:44:28 +04:00
if ( spi_imx - > txfifo ) {
2009-09-23 03:46:02 +04:00
/* No data left to push, but still waiting for rx data,
* enable receive data available interrupt .
*/
2009-10-02 02:44:28 +04:00
spi_imx - > intctrl ( spi_imx , MXC_INT_RR ) ;
2009-09-23 03:46:02 +04:00
return IRQ_HANDLED ;
}
2009-10-02 02:44:28 +04:00
spi_imx - > intctrl ( spi_imx , 0 ) ;
complete ( & spi_imx - > xfer_done ) ;
2009-09-23 03:46:02 +04:00
return IRQ_HANDLED ;
}
2009-10-02 02:44:28 +04:00
static int spi_imx_setupxfer ( struct spi_device * spi ,
2009-09-23 03:46:02 +04:00
struct spi_transfer * t )
{
2009-10-02 02:44:28 +04:00
struct spi_imx_data * spi_imx = spi_master_get_devdata ( spi - > master ) ;
struct spi_imx_config config ;
2009-09-23 03:46:02 +04:00
config . bpw = t ? t - > bits_per_word : spi - > bits_per_word ;
config . speed_hz = t ? t - > speed_hz : spi - > max_speed_hz ;
config . mode = spi - > mode ;
2009-10-02 02:44:29 +04:00
if ( ! config . speed_hz )
config . speed_hz = spi - > max_speed_hz ;
if ( ! config . bpw )
config . bpw = spi - > bits_per_word ;
if ( ! config . speed_hz )
config . speed_hz = spi - > max_speed_hz ;
2009-10-02 02:44:28 +04:00
spi_imx - > config ( spi_imx , & config ) ;
2009-09-23 03:46:02 +04:00
return 0 ;
}
2009-10-02 02:44:28 +04:00
static int spi_imx_transfer ( struct spi_device * spi ,
2009-09-23 03:46:02 +04:00
struct spi_transfer * transfer )
{
2009-10-02 02:44:28 +04:00
struct spi_imx_data * spi_imx = spi_master_get_devdata ( spi - > master ) ;
2009-09-23 03:46:02 +04:00
2009-10-02 02:44:28 +04:00
spi_imx - > tx_buf = transfer - > tx_buf ;
spi_imx - > rx_buf = transfer - > rx_buf ;
spi_imx - > count = transfer - > len ;
spi_imx - > txfifo = 0 ;
2009-09-23 03:46:02 +04:00
2009-10-02 02:44:28 +04:00
init_completion ( & spi_imx - > xfer_done ) ;
2009-09-23 03:46:02 +04:00
2009-10-02 02:44:28 +04:00
spi_imx_push ( spi_imx ) ;
2009-09-23 03:46:02 +04:00
2009-10-02 02:44:28 +04:00
spi_imx - > intctrl ( spi_imx , MXC_INT_TE ) ;
2009-09-23 03:46:02 +04:00
2009-10-02 02:44:28 +04:00
wait_for_completion ( & spi_imx - > xfer_done ) ;
2009-09-23 03:46:02 +04:00
return transfer - > len ;
}
2009-10-02 02:44:28 +04:00
static int spi_imx_setup ( struct spi_device * spi )
2009-09-23 03:46:02 +04:00
{
2009-10-02 02:44:29 +04:00
struct spi_imx_data * spi_imx = spi_master_get_devdata ( spi - > master ) ;
int gpio = spi_imx - > chipselect [ spi - > chip_select ] ;
2009-09-23 03:46:02 +04:00
if ( ! spi - > bits_per_word )
spi - > bits_per_word = 8 ;
pr_debug ( " %s: mode %d, %u bpw, %d hz \n " , __func__ ,
spi - > mode , spi - > bits_per_word , spi - > max_speed_hz ) ;
2009-10-02 02:44:29 +04:00
if ( gpio > = 0 )
gpio_direction_output ( gpio , spi - > mode & SPI_CS_HIGH ? 0 : 1 ) ;
2009-10-02 02:44:28 +04:00
spi_imx_chipselect ( spi , BITBANG_CS_INACTIVE ) ;
2009-09-23 03:46:02 +04:00
return 0 ;
}
2009-10-02 02:44:28 +04:00
static void spi_imx_cleanup ( struct spi_device * spi )
2009-09-23 03:46:02 +04:00
{
}
2009-10-02 02:44:28 +04:00
static int __init spi_imx_probe ( struct platform_device * pdev )
2009-09-23 03:46:02 +04:00
{
struct spi_imx_master * mxc_platform_info ;
struct spi_master * master ;
2009-10-02 02:44:28 +04:00
struct spi_imx_data * spi_imx ;
2009-09-23 03:46:02 +04:00
struct resource * res ;
int i , ret ;
mxc_platform_info = ( struct spi_imx_master * ) pdev - > dev . platform_data ;
if ( ! mxc_platform_info ) {
dev_err ( & pdev - > dev , " can't get the platform data \n " ) ;
return - EINVAL ;
}
2009-10-02 02:44:28 +04:00
master = spi_alloc_master ( & pdev - > dev , sizeof ( struct spi_imx_data ) ) ;
2009-09-23 03:46:02 +04:00
if ( ! master )
return - ENOMEM ;
platform_set_drvdata ( pdev , master ) ;
master - > bus_num = pdev - > id ;
master - > num_chipselect = mxc_platform_info - > num_chipselect ;
2009-10-02 02:44:28 +04:00
spi_imx = spi_master_get_devdata ( master ) ;
spi_imx - > bitbang . master = spi_master_get ( master ) ;
spi_imx - > chipselect = mxc_platform_info - > chipselect ;
2009-09-23 03:46:02 +04:00
for ( i = 0 ; i < master - > num_chipselect ; i + + ) {
2009-10-02 02:44:28 +04:00
if ( spi_imx - > chipselect [ i ] < 0 )
2009-09-23 03:46:02 +04:00
continue ;
2009-10-02 02:44:28 +04:00
ret = gpio_request ( spi_imx - > chipselect [ i ] , DRIVER_NAME ) ;
2009-09-23 03:46:02 +04:00
if ( ret ) {
i - - ;
while ( i > 0 )
2009-10-02 02:44:28 +04:00
if ( spi_imx - > chipselect [ i ] > = 0 )
gpio_free ( spi_imx - > chipselect [ i - - ] ) ;
2009-09-23 03:46:02 +04:00
dev_err ( & pdev - > dev , " can't get cs gpios " ) ;
goto out_master_put ;
}
}
2009-10-02 02:44:28 +04:00
spi_imx - > bitbang . chipselect = spi_imx_chipselect ;
spi_imx - > bitbang . setup_transfer = spi_imx_setupxfer ;
spi_imx - > bitbang . txrx_bufs = spi_imx_transfer ;
spi_imx - > bitbang . master - > setup = spi_imx_setup ;
spi_imx - > bitbang . master - > cleanup = spi_imx_cleanup ;
2009-09-23 03:46:02 +04:00
2009-10-02 02:44:28 +04:00
init_completion ( & spi_imx - > xfer_done ) ;
2009-09-23 03:46:02 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " can't get platform resource \n " ) ;
ret = - ENOMEM ;
goto out_gpio_free ;
}
if ( ! request_mem_region ( res - > start , resource_size ( res ) , pdev - > name ) ) {
dev_err ( & pdev - > dev , " request_mem_region failed \n " ) ;
ret = - EBUSY ;
goto out_gpio_free ;
}
2009-10-02 02:44:28 +04:00
spi_imx - > base = ioremap ( res - > start , resource_size ( res ) ) ;
if ( ! spi_imx - > base ) {
2009-09-23 03:46:02 +04:00
ret = - EINVAL ;
goto out_release_mem ;
}
2009-10-02 02:44:28 +04:00
spi_imx - > irq = platform_get_irq ( pdev , 0 ) ;
if ( ! spi_imx - > irq ) {
2009-09-23 03:46:02 +04:00
ret = - EINVAL ;
goto out_iounmap ;
}
2009-10-02 02:44:28 +04:00
ret = request_irq ( spi_imx - > irq , spi_imx_isr , 0 , DRIVER_NAME , spi_imx ) ;
2009-09-23 03:46:02 +04:00
if ( ret ) {
2009-10-02 02:44:28 +04:00
dev_err ( & pdev - > dev , " can't get irq%d: %d \n " , spi_imx - > irq , ret ) ;
2009-09-23 03:46:02 +04:00
goto out_iounmap ;
}
if ( cpu_is_mx31 ( ) | | cpu_is_mx35 ( ) ) {
2009-10-02 02:44:28 +04:00
spi_imx - > intctrl = mx31_intctrl ;
spi_imx - > config = mx31_config ;
spi_imx - > trigger = mx31_trigger ;
spi_imx - > rx_available = mx31_rx_available ;
2009-09-23 03:46:02 +04:00
} else if ( cpu_is_mx27 ( ) | | cpu_is_mx21 ( ) ) {
2009-10-02 02:44:28 +04:00
spi_imx - > intctrl = mx27_intctrl ;
spi_imx - > config = mx27_config ;
spi_imx - > trigger = mx27_trigger ;
spi_imx - > rx_available = mx27_rx_available ;
2009-09-23 03:46:02 +04:00
} else if ( cpu_is_mx1 ( ) ) {
2009-10-02 02:44:28 +04:00
spi_imx - > intctrl = mx1_intctrl ;
spi_imx - > config = mx1_config ;
spi_imx - > trigger = mx1_trigger ;
spi_imx - > rx_available = mx1_rx_available ;
2009-09-23 03:46:02 +04:00
} else
BUG ( ) ;
2009-10-02 02:44:28 +04:00
spi_imx - > clk = clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( spi_imx - > clk ) ) {
2009-09-23 03:46:02 +04:00
dev_err ( & pdev - > dev , " unable to get clock \n " ) ;
2009-10-02 02:44:28 +04:00
ret = PTR_ERR ( spi_imx - > clk ) ;
2009-09-23 03:46:02 +04:00
goto out_free_irq ;
}
2009-10-02 02:44:28 +04:00
clk_enable ( spi_imx - > clk ) ;
spi_imx - > spi_clk = clk_get_rate ( spi_imx - > clk ) ;
2009-09-23 03:46:02 +04:00
if ( ! cpu_is_mx31 ( ) | | ! cpu_is_mx35 ( ) )
2009-10-02 02:44:28 +04:00
writel ( 1 , spi_imx - > base + MXC_RESET ) ;
2009-09-23 03:46:02 +04:00
2009-10-02 02:44:28 +04:00
spi_imx - > intctrl ( spi_imx , 0 ) ;
2009-09-23 03:46:02 +04:00
2009-10-02 02:44:28 +04:00
ret = spi_bitbang_start ( & spi_imx - > bitbang ) ;
2009-09-23 03:46:02 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " bitbang start failed with %d \n " , ret ) ;
goto out_clk_put ;
}
dev_info ( & pdev - > dev , " probed \n " ) ;
return ret ;
out_clk_put :
2009-10-02 02:44:28 +04:00
clk_disable ( spi_imx - > clk ) ;
clk_put ( spi_imx - > clk ) ;
2009-09-23 03:46:02 +04:00
out_free_irq :
2009-10-02 02:44:28 +04:00
free_irq ( spi_imx - > irq , spi_imx ) ;
2009-09-23 03:46:02 +04:00
out_iounmap :
2009-10-02 02:44:28 +04:00
iounmap ( spi_imx - > base ) ;
2009-09-23 03:46:02 +04:00
out_release_mem :
release_mem_region ( res - > start , resource_size ( res ) ) ;
out_gpio_free :
for ( i = 0 ; i < master - > num_chipselect ; i + + )
2009-10-02 02:44:28 +04:00
if ( spi_imx - > chipselect [ i ] > = 0 )
gpio_free ( spi_imx - > chipselect [ i ] ) ;
2009-09-23 03:46:02 +04:00
out_master_put :
spi_master_put ( master ) ;
kfree ( master ) ;
platform_set_drvdata ( pdev , NULL ) ;
return ret ;
}
2009-10-02 02:44:28 +04:00
static int __exit spi_imx_remove ( struct platform_device * pdev )
2009-09-23 03:46:02 +04:00
{
struct spi_master * master = platform_get_drvdata ( pdev ) ;
struct resource * res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2009-10-02 02:44:28 +04:00
struct spi_imx_data * spi_imx = spi_master_get_devdata ( master ) ;
2009-09-23 03:46:02 +04:00
int i ;
2009-10-02 02:44:28 +04:00
spi_bitbang_stop ( & spi_imx - > bitbang ) ;
2009-09-23 03:46:02 +04:00
2009-10-02 02:44:28 +04:00
writel ( 0 , spi_imx - > base + MXC_CSPICTRL ) ;
clk_disable ( spi_imx - > clk ) ;
clk_put ( spi_imx - > clk ) ;
free_irq ( spi_imx - > irq , spi_imx ) ;
iounmap ( spi_imx - > base ) ;
2009-09-23 03:46:02 +04:00
for ( i = 0 ; i < master - > num_chipselect ; i + + )
2009-10-02 02:44:28 +04:00
if ( spi_imx - > chipselect [ i ] > = 0 )
gpio_free ( spi_imx - > chipselect [ i ] ) ;
2009-09-23 03:46:02 +04:00
spi_master_put ( master ) ;
release_mem_region ( res - > start , resource_size ( res ) ) ;
platform_set_drvdata ( pdev , NULL ) ;
return 0 ;
}
2009-10-02 02:44:28 +04:00
static struct platform_driver spi_imx_driver = {
2009-09-23 03:46:02 +04:00
. driver = {
. name = DRIVER_NAME ,
. owner = THIS_MODULE ,
} ,
2009-10-02 02:44:28 +04:00
. probe = spi_imx_probe ,
. remove = __exit_p ( spi_imx_remove ) ,
2009-09-23 03:46:02 +04:00
} ;
2009-10-02 02:44:28 +04:00
static int __init spi_imx_init ( void )
2009-09-23 03:46:02 +04:00
{
2009-10-02 02:44:28 +04:00
return platform_driver_register ( & spi_imx_driver ) ;
2009-09-23 03:46:02 +04:00
}
2009-10-02 02:44:28 +04:00
static void __exit spi_imx_exit ( void )
2009-09-23 03:46:02 +04:00
{
2009-10-02 02:44:28 +04:00
platform_driver_unregister ( & spi_imx_driver ) ;
2009-09-23 03:46:02 +04:00
}
2009-10-02 02:44:28 +04:00
module_init ( spi_imx_init ) ;
module_exit ( spi_imx_exit ) ;
2009-09-23 03:46:02 +04:00
MODULE_DESCRIPTION ( " SPI Master Controller driver " ) ;
MODULE_AUTHOR ( " Sascha Hauer, Pengutronix " ) ;
MODULE_LICENSE ( " GPL " ) ;