2007-05-11 09:22:52 +04:00
/*
2008-05-15 03:05:29 +04:00
* MPC52xx PSC in SPI mode driver .
2007-05-11 09:22:52 +04:00
*
* Maintainer : Dragos Carp
*
* Copyright ( C ) 2006 TOPTICA Photonics AG .
*
* 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 .
*/
# include <linux/module.h>
# include <linux/init.h>
2009-06-18 03:26:05 +04:00
# include <linux/types.h>
2007-05-11 09:22:52 +04:00
# include <linux/errno.h>
# include <linux/interrupt.h>
2008-05-23 10:35:47 +04:00
# include <linux/of_platform.h>
2009-11-05 01:23:37 +03:00
# include <linux/of_spi.h>
2007-05-11 09:22:52 +04:00
# include <linux/workqueue.h>
# include <linux/completion.h>
# include <linux/io.h>
# include <linux/delay.h>
# include <linux/spi/spi.h>
# include <linux/fsl_devices.h>
# include <asm/mpc52xx.h>
# include <asm/mpc52xx_psc.h>
# define MCLK 20000000 /* PSC port MClk in hz */
struct mpc52xx_psc_spi {
/* fsl_spi_platform data */
2009-06-18 03:26:05 +04:00
void ( * cs_control ) ( struct spi_device * spi , bool on ) ;
2007-05-11 09:22:52 +04:00
u32 sysclk ;
/* driver internal data */
struct mpc52xx_psc __iomem * psc ;
2008-03-05 01:28:42 +03:00
struct mpc52xx_psc_fifo __iomem * fifo ;
2007-05-11 09:22:52 +04:00
unsigned int irq ;
u8 bits_per_word ;
u8 busy ;
struct workqueue_struct * workqueue ;
struct work_struct work ;
struct list_head queue ;
spinlock_t lock ;
struct completion done ;
} ;
/* controller state */
struct mpc52xx_psc_spi_cs {
int bits_per_word ;
int speed_hz ;
} ;
/* set clock freq, clock ramp, bits per work
* if t is NULL then reset the values to the default values
*/
static int mpc52xx_psc_spi_transfer_setup ( struct spi_device * spi ,
struct spi_transfer * t )
{
struct mpc52xx_psc_spi_cs * cs = spi - > controller_state ;
cs - > speed_hz = ( t & & t - > speed_hz )
? t - > speed_hz : spi - > max_speed_hz ;
cs - > bits_per_word = ( t & & t - > bits_per_word )
? t - > bits_per_word : spi - > bits_per_word ;
cs - > bits_per_word = ( ( cs - > bits_per_word + 7 ) / 8 ) * 8 ;
return 0 ;
}
static void mpc52xx_psc_spi_activate_cs ( struct spi_device * spi )
{
struct mpc52xx_psc_spi_cs * cs = spi - > controller_state ;
struct mpc52xx_psc_spi * mps = spi_master_get_devdata ( spi - > master ) ;
struct mpc52xx_psc __iomem * psc = mps - > psc ;
u32 sicr ;
u16 ccr ;
sicr = in_be32 ( & psc - > sicr ) ;
/* Set clock phase and polarity */
if ( spi - > mode & SPI_CPHA )
sicr | = 0x00001000 ;
else
sicr & = ~ 0x00001000 ;
if ( spi - > mode & SPI_CPOL )
sicr | = 0x00002000 ;
else
sicr & = ~ 0x00002000 ;
if ( spi - > mode & SPI_LSB_FIRST )
sicr | = 0x10000000 ;
else
sicr & = ~ 0x10000000 ;
out_be32 ( & psc - > sicr , sicr ) ;
/* Set clock frequency and bits per word
* Because psc - > ccr is defined as 16 bit register instead of 32 bit
* just set the lower byte of BitClkDiv
*/
2008-10-08 19:02:11 +04:00
ccr = in_be16 ( ( u16 __iomem * ) & psc - > ccr ) ;
2007-05-11 09:22:52 +04:00
ccr & = 0xFF00 ;
if ( cs - > speed_hz )
ccr | = ( MCLK / cs - > speed_hz - 1 ) & 0xFF ;
else /* by default SPI Clk 1MHz */
ccr | = ( MCLK / 1000000 - 1 ) & 0xFF ;
2008-10-08 19:02:11 +04:00
out_be16 ( ( u16 __iomem * ) & psc - > ccr , ccr ) ;
2007-05-11 09:22:52 +04:00
mps - > bits_per_word = cs - > bits_per_word ;
2009-06-18 03:26:05 +04:00
if ( mps - > cs_control )
mps - > cs_control ( spi , ( spi - > mode & SPI_CS_HIGH ) ? 1 : 0 ) ;
2007-05-11 09:22:52 +04:00
}
static void mpc52xx_psc_spi_deactivate_cs ( struct spi_device * spi )
{
struct mpc52xx_psc_spi * mps = spi_master_get_devdata ( spi - > master ) ;
2009-06-18 03:26:05 +04:00
if ( mps - > cs_control )
mps - > cs_control ( spi , ( spi - > mode & SPI_CS_HIGH ) ? 0 : 1 ) ;
2007-05-11 09:22:52 +04:00
}
# define MPC52xx_PSC_BUFSIZE (MPC52xx_PSC_RFNUM_MASK + 1)
/* wake up when 80% fifo full */
# define MPC52xx_PSC_RFALARM (MPC52xx_PSC_BUFSIZE * 20 / 100)
static int mpc52xx_psc_spi_transfer_rxtx ( struct spi_device * spi ,
struct spi_transfer * t )
{
struct mpc52xx_psc_spi * mps = spi_master_get_devdata ( spi - > master ) ;
struct mpc52xx_psc __iomem * psc = mps - > psc ;
2008-03-05 01:28:42 +03:00
struct mpc52xx_psc_fifo __iomem * fifo = mps - > fifo ;
2007-05-11 09:22:52 +04:00
unsigned rb = 0 ; /* number of bytes receieved */
unsigned sb = 0 ; /* number of bytes sent */
unsigned char * rx_buf = ( unsigned char * ) t - > rx_buf ;
unsigned char * tx_buf = ( unsigned char * ) t - > tx_buf ;
unsigned rfalarm ;
unsigned send_at_once = MPC52xx_PSC_BUFSIZE ;
unsigned recv_at_once ;
2008-12-02 00:13:53 +03:00
int last_block = 0 ;
2007-05-11 09:22:52 +04:00
if ( ! t - > tx_buf & & ! t - > rx_buf & & t - > len )
return - EINVAL ;
/* enable transmiter/receiver */
out_8 ( & psc - > command , MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE ) ;
while ( rb < t - > len ) {
if ( t - > len - rb > MPC52xx_PSC_BUFSIZE ) {
rfalarm = MPC52xx_PSC_RFALARM ;
2008-12-02 00:13:53 +03:00
last_block = 0 ;
2007-05-11 09:22:52 +04:00
} else {
send_at_once = t - > len - sb ;
rfalarm = MPC52xx_PSC_BUFSIZE - ( t - > len - rb ) ;
2008-12-02 00:13:53 +03:00
last_block = 1 ;
2007-05-11 09:22:52 +04:00
}
dev_dbg ( & spi - > dev , " send %d bytes... \n " , send_at_once ) ;
2008-07-29 02:46:32 +04:00
for ( ; send_at_once ; sb + + , send_at_once - - ) {
/* set EOF flag before the last word is sent */
2008-12-02 00:13:53 +03:00
if ( send_at_once = = 1 & & last_block )
2008-07-29 02:46:32 +04:00
out_8 ( & psc - > ircr2 , 0x01 ) ;
if ( tx_buf )
2007-05-11 09:22:52 +04:00
out_8 ( & psc - > mpc52xx_psc_buffer_8 , tx_buf [ sb ] ) ;
2008-07-29 02:46:32 +04:00
else
2007-05-11 09:22:52 +04:00
out_8 ( & psc - > mpc52xx_psc_buffer_8 , 0 ) ;
}
2007-10-20 01:10:43 +04:00
/* enable interrupts and wait for wake up
2007-05-11 09:22:52 +04:00
* if just one byte is expected the Rx FIFO genererates no
* FFULL interrupt , so activate the RxRDY interrupt
*/
out_8 ( & psc - > command , MPC52xx_PSC_SEL_MODE_REG_1 ) ;
if ( t - > len - rb = = 1 ) {
out_8 ( & psc - > mode , 0 ) ;
} else {
out_8 ( & psc - > mode , MPC52xx_PSC_MODE_FFULL ) ;
2008-03-05 01:28:42 +03:00
out_be16 ( & fifo - > rfalarm , rfalarm ) ;
2007-05-11 09:22:52 +04:00
}
out_be16 ( & psc - > mpc52xx_psc_imr , MPC52xx_PSC_IMR_RXRDY ) ;
wait_for_completion ( & mps - > done ) ;
2008-03-05 01:28:42 +03:00
recv_at_once = in_be16 ( & fifo - > rfnum ) ;
2007-05-11 09:22:52 +04:00
dev_dbg ( & spi - > dev , " %d bytes received \n " , recv_at_once ) ;
send_at_once = recv_at_once ;
if ( rx_buf ) {
for ( ; recv_at_once ; rb + + , recv_at_once - - )
rx_buf [ rb ] = in_8 ( & psc - > mpc52xx_psc_buffer_8 ) ;
} else {
for ( ; recv_at_once ; rb + + , recv_at_once - - )
in_8 ( & psc - > mpc52xx_psc_buffer_8 ) ;
}
}
/* disable transmiter/receiver */
out_8 ( & psc - > command , MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE ) ;
return 0 ;
}
static void mpc52xx_psc_spi_work ( struct work_struct * work )
{
struct mpc52xx_psc_spi * mps =
container_of ( work , struct mpc52xx_psc_spi , work ) ;
spin_lock_irq ( & mps - > lock ) ;
mps - > busy = 1 ;
while ( ! list_empty ( & mps - > queue ) ) {
struct spi_message * m ;
struct spi_device * spi ;
struct spi_transfer * t = NULL ;
unsigned cs_change ;
int status ;
m = container_of ( mps - > queue . next , struct spi_message , queue ) ;
list_del_init ( & m - > queue ) ;
spin_unlock_irq ( & mps - > lock ) ;
spi = m - > spi ;
cs_change = 1 ;
status = 0 ;
list_for_each_entry ( t , & m - > transfers , transfer_list ) {
if ( t - > bits_per_word | | t - > speed_hz ) {
status = mpc52xx_psc_spi_transfer_setup ( spi , t ) ;
if ( status < 0 )
break ;
}
if ( cs_change )
mpc52xx_psc_spi_activate_cs ( spi ) ;
cs_change = t - > cs_change ;
status = mpc52xx_psc_spi_transfer_rxtx ( spi , t ) ;
if ( status )
break ;
m - > actual_length + = t - > len ;
if ( t - > delay_usecs )
udelay ( t - > delay_usecs ) ;
if ( cs_change )
mpc52xx_psc_spi_deactivate_cs ( spi ) ;
}
m - > status = status ;
m - > complete ( m - > context ) ;
if ( status | | ! cs_change )
mpc52xx_psc_spi_deactivate_cs ( spi ) ;
mpc52xx_psc_spi_transfer_setup ( spi , NULL ) ;
spin_lock_irq ( & mps - > lock ) ;
}
mps - > busy = 0 ;
spin_unlock_irq ( & mps - > lock ) ;
}
static int mpc52xx_psc_spi_setup ( struct spi_device * spi )
{
struct mpc52xx_psc_spi * mps = spi_master_get_devdata ( spi - > master ) ;
struct mpc52xx_psc_spi_cs * cs = spi - > controller_state ;
unsigned long flags ;
if ( spi - > bits_per_word % 8 )
return - EINVAL ;
if ( ! cs ) {
cs = kzalloc ( sizeof * cs , GFP_KERNEL ) ;
if ( ! cs )
return - ENOMEM ;
spi - > controller_state = cs ;
}
cs - > bits_per_word = spi - > bits_per_word ;
cs - > speed_hz = spi - > max_speed_hz ;
spin_lock_irqsave ( & mps - > lock , flags ) ;
if ( ! mps - > busy )
mpc52xx_psc_spi_deactivate_cs ( spi ) ;
spin_unlock_irqrestore ( & mps - > lock , flags ) ;
return 0 ;
}
static int mpc52xx_psc_spi_transfer ( struct spi_device * spi ,
struct spi_message * m )
{
struct mpc52xx_psc_spi * mps = spi_master_get_devdata ( spi - > master ) ;
unsigned long flags ;
m - > actual_length = 0 ;
m - > status = - EINPROGRESS ;
spin_lock_irqsave ( & mps - > lock , flags ) ;
list_add_tail ( & m - > queue , & mps - > queue ) ;
queue_work ( mps - > workqueue , & mps - > work ) ;
spin_unlock_irqrestore ( & mps - > lock , flags ) ;
return 0 ;
}
static void mpc52xx_psc_spi_cleanup ( struct spi_device * spi )
{
kfree ( spi - > controller_state ) ;
}
static int mpc52xx_psc_spi_port_config ( int psc_id , struct mpc52xx_psc_spi * mps )
{
struct mpc52xx_psc __iomem * psc = mps - > psc ;
2008-03-05 01:28:42 +03:00
struct mpc52xx_psc_fifo __iomem * fifo = mps - > fifo ;
2007-05-11 09:22:52 +04:00
u32 mclken_div ;
2009-11-02 06:53:11 +03:00
int ret ;
2007-05-11 09:22:52 +04:00
/* default sysclk is 512MHz */
2008-01-25 08:25:31 +03:00
mclken_div = ( mps - > sysclk ? mps - > sysclk : 512000000 ) / MCLK ;
2009-11-02 06:53:11 +03:00
ret = mpc52xx_set_psc_clkdiv ( psc_id , mclken_div ) ;
if ( ret )
return ret ;
2007-05-11 09:22:52 +04:00
/* Reset the PSC into a known state */
out_8 ( & psc - > command , MPC52xx_PSC_RST_RX ) ;
out_8 ( & psc - > command , MPC52xx_PSC_RST_TX ) ;
out_8 ( & psc - > command , MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE ) ;
/* Disable interrupts, interrupts are based on alarm level */
out_be16 ( & psc - > mpc52xx_psc_imr , 0 ) ;
out_8 ( & psc - > command , MPC52xx_PSC_SEL_MODE_REG_1 ) ;
2008-03-05 01:28:42 +03:00
out_8 ( & fifo - > rfcntl , 0 ) ;
2007-05-11 09:22:52 +04:00
out_8 ( & psc - > mode , MPC52xx_PSC_MODE_FFULL ) ;
/* Configure 8bit codec mode as a SPI master and use EOF flags */
/* SICR_SIM_CODEC8|SICR_GENCLK|SICR_SPI|SICR_MSTR|SICR_USEEOF */
out_be32 ( & psc - > sicr , 0x0180C800 ) ;
2008-10-08 19:02:11 +04:00
out_be16 ( ( u16 __iomem * ) & psc - > ccr , 0x070F ) ; /* default SPI Clk 1MHz */
2007-05-11 09:22:52 +04:00
/* Set 2ms DTL delay */
out_8 ( & psc - > ctur , 0x00 ) ;
out_8 ( & psc - > ctlr , 0x84 ) ;
mps - > bits_per_word = 8 ;
2009-11-02 06:53:11 +03:00
return 0 ;
2007-05-11 09:22:52 +04:00
}
static irqreturn_t mpc52xx_psc_spi_isr ( int irq , void * dev_id )
{
struct mpc52xx_psc_spi * mps = ( struct mpc52xx_psc_spi * ) dev_id ;
struct mpc52xx_psc __iomem * psc = mps - > psc ;
/* disable interrupt and wake up the work queue */
if ( in_be16 ( & psc - > mpc52xx_psc_isr ) & MPC52xx_PSC_IMR_RXRDY ) {
out_be16 ( & psc - > mpc52xx_psc_imr , 0 ) ;
complete ( & mps - > done ) ;
return IRQ_HANDLED ;
}
return IRQ_NONE ;
}
/* bus_num is used only for the case dev->platform_data == NULL */
static int __init mpc52xx_psc_spi_do_probe ( struct device * dev , u32 regaddr ,
u32 size , unsigned int irq , s16 bus_num )
{
struct fsl_spi_platform_data * pdata = dev - > platform_data ;
struct mpc52xx_psc_spi * mps ;
struct spi_master * master ;
int ret ;
master = spi_alloc_master ( dev , sizeof * mps ) ;
if ( master = = NULL )
return - ENOMEM ;
dev_set_drvdata ( dev , master ) ;
mps = spi_master_get_devdata ( master ) ;
2009-06-18 03:26:04 +04:00
/* the spi->mode bits understood by this driver: */
master - > mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST ;
2007-05-11 09:22:52 +04:00
mps - > irq = irq ;
if ( pdata = = NULL ) {
dev_warn ( dev , " probe called without platform data, no "
2009-06-18 03:26:05 +04:00
" cs_control function will be called \n " ) ;
mps - > cs_control = NULL ;
2007-05-11 09:22:52 +04:00
mps - > sysclk = 0 ;
master - > bus_num = bus_num ;
master - > num_chipselect = 255 ;
} else {
2009-06-18 03:26:05 +04:00
mps - > cs_control = pdata - > cs_control ;
2007-05-11 09:22:52 +04:00
mps - > sysclk = pdata - > sysclk ;
master - > bus_num = pdata - > bus_num ;
master - > num_chipselect = pdata - > max_chipselect ;
}
master - > setup = mpc52xx_psc_spi_setup ;
master - > transfer = mpc52xx_psc_spi_transfer ;
master - > cleanup = mpc52xx_psc_spi_cleanup ;
mps - > psc = ioremap ( regaddr , size ) ;
if ( ! mps - > psc ) {
dev_err ( dev , " could not ioremap I/O port range \n " ) ;
ret = - EFAULT ;
goto free_master ;
}
2008-03-05 01:28:42 +03:00
/* On the 5200, fifo regs are immediately ajacent to the psc regs */
mps - > fifo = ( ( void __iomem * ) mps - > psc ) + sizeof ( struct mpc52xx_psc ) ;
2007-05-11 09:22:52 +04:00
ret = request_irq ( mps - > irq , mpc52xx_psc_spi_isr , 0 , " mpc52xx-psc-spi " ,
mps ) ;
if ( ret )
goto free_master ;
ret = mpc52xx_psc_spi_port_config ( master - > bus_num , mps ) ;
2009-11-02 06:53:11 +03:00
if ( ret < 0 ) {
dev_err ( dev , " can't configure PSC! Is it capable of SPI? \n " ) ;
2007-05-11 09:22:52 +04:00
goto free_irq ;
2009-11-02 06:53:11 +03:00
}
2007-05-11 09:22:52 +04:00
spin_lock_init ( & mps - > lock ) ;
init_completion ( & mps - > done ) ;
INIT_WORK ( & mps - > work , mpc52xx_psc_spi_work ) ;
INIT_LIST_HEAD ( & mps - > queue ) ;
mps - > workqueue = create_singlethread_workqueue (
2009-03-25 02:38:21 +03:00
dev_name ( master - > dev . parent ) ) ;
2007-05-11 09:22:52 +04:00
if ( mps - > workqueue = = NULL ) {
ret = - EBUSY ;
goto free_irq ;
}
ret = spi_register_master ( master ) ;
if ( ret < 0 )
goto unreg_master ;
return ret ;
unreg_master :
destroy_workqueue ( mps - > workqueue ) ;
free_irq :
free_irq ( mps - > irq , mps ) ;
free_master :
if ( mps - > psc )
iounmap ( mps - > psc ) ;
spi_master_put ( master ) ;
return ret ;
}
static int __exit mpc52xx_psc_spi_do_remove ( struct device * dev )
{
struct spi_master * master = dev_get_drvdata ( dev ) ;
struct mpc52xx_psc_spi * mps = spi_master_get_devdata ( master ) ;
flush_workqueue ( mps - > workqueue ) ;
destroy_workqueue ( mps - > workqueue ) ;
spi_unregister_master ( master ) ;
free_irq ( mps - > irq , mps ) ;
if ( mps - > psc )
iounmap ( mps - > psc ) ;
return 0 ;
}
static int __init mpc52xx_psc_spi_of_probe ( struct of_device * op ,
const struct of_device_id * match )
{
const u32 * regaddr_p ;
u64 regaddr64 , size64 ;
s16 id = - 1 ;
2009-11-05 01:23:37 +03:00
int rc ;
2007-05-11 09:22:52 +04:00
regaddr_p = of_get_address ( op - > node , 0 , & size64 , NULL ) ;
if ( ! regaddr_p ) {
2009-11-04 02:49:20 +03:00
dev_err ( & op - > dev , " Invalid PSC address \n " ) ;
2007-05-11 09:22:52 +04:00
return - EINVAL ;
}
regaddr64 = of_translate_address ( op - > node , regaddr_p ) ;
2007-05-24 00:57:32 +04:00
/* get PSC id (1..6, used by port_config) */
2007-05-11 09:22:52 +04:00
if ( op - > dev . platform_data = = NULL ) {
2007-05-24 00:57:32 +04:00
const u32 * psc_nump ;
2007-05-11 09:22:52 +04:00
2007-05-24 00:57:32 +04:00
psc_nump = of_get_property ( op - > node , " cell-index " , NULL ) ;
if ( ! psc_nump | | * psc_nump > 5 ) {
2009-11-04 02:49:20 +03:00
dev_err ( & op - > dev , " Invalid cell-index property \n " ) ;
2007-05-24 00:57:32 +04:00
return - EINVAL ;
2007-05-11 09:22:52 +04:00
}
2007-05-24 00:57:32 +04:00
id = * psc_nump + 1 ;
2007-05-11 09:22:52 +04:00
}
2009-11-05 01:23:37 +03:00
rc = mpc52xx_psc_spi_do_probe ( & op - > dev , ( u32 ) regaddr64 , ( u32 ) size64 ,
2007-05-11 09:22:52 +04:00
irq_of_parse_and_map ( op - > node , 0 ) , id ) ;
2009-11-05 01:23:37 +03:00
if ( rc = = 0 )
of_register_spi_devices ( dev_get_drvdata ( & op - > dev ) , op - > node ) ;
return rc ;
2007-05-11 09:22:52 +04:00
}
static int __exit mpc52xx_psc_spi_of_remove ( struct of_device * op )
{
return mpc52xx_psc_spi_do_remove ( & op - > dev ) ;
}
static struct of_device_id mpc52xx_psc_spi_of_match [ ] = {
2008-01-25 08:25:31 +03:00
{ . compatible = " fsl,mpc5200-psc-spi " , } ,
{ . compatible = " mpc5200-psc-spi " , } , /* old */
{ }
2007-05-11 09:22:52 +04:00
} ;
MODULE_DEVICE_TABLE ( of , mpc52xx_psc_spi_of_match ) ;
static struct of_platform_driver mpc52xx_psc_spi_of_driver = {
. owner = THIS_MODULE ,
. name = " mpc52xx-psc-spi " ,
. match_table = mpc52xx_psc_spi_of_match ,
. probe = mpc52xx_psc_spi_of_probe ,
. remove = __exit_p ( mpc52xx_psc_spi_of_remove ) ,
. driver = {
. name = " mpc52xx-psc-spi " ,
. owner = THIS_MODULE ,
} ,
} ;
static int __init mpc52xx_psc_spi_init ( void )
{
return of_register_platform_driver ( & mpc52xx_psc_spi_of_driver ) ;
}
module_init ( mpc52xx_psc_spi_init ) ;
static void __exit mpc52xx_psc_spi_exit ( void )
{
of_unregister_platform_driver ( & mpc52xx_psc_spi_of_driver ) ;
}
module_exit ( mpc52xx_psc_spi_exit ) ;
MODULE_AUTHOR ( " Dragos Carp " ) ;
MODULE_DESCRIPTION ( " MPC52xx PSC SPI Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;