2012-11-07 21:30:29 +04:00
/*
* CLPS711X SPI bus driver
*
2014-01-10 17:02:05 +04:00
* Copyright ( C ) 2012 - 2014 Alexander Shiyan < shc_work @ mail . ru >
2012-11-07 21:30:29 +04:00
*
* 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/io.h>
# include <linux/clk.h>
# include <linux/init.h>
# include <linux/gpio.h>
# include <linux/delay.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
# include <linux/spi/spi.h>
# include <linux/platform_data/spi-clps711x.h>
# include <mach/hardware.h>
# define DRIVER_NAME "spi-clps711x"
struct spi_clps711x_data {
struct clk * spi_clk ;
u32 max_speed_hz ;
u8 * tx_buf ;
u8 * rx_buf ;
2014-02-02 10:59:49 +04:00
unsigned int bpw ;
2012-11-07 21:30:29 +04:00
int len ;
} ;
static int spi_clps711x_setup ( struct spi_device * spi )
{
/* We are expect that SPI-device is not selected */
2014-02-02 10:59:50 +04:00
gpio_direction_output ( spi - > cs_gpio , ! ( spi - > mode & SPI_CS_HIGH ) ) ;
2012-11-07 21:30:29 +04:00
return 0 ;
}
2014-02-02 10:59:49 +04:00
static void spi_clps711x_setup_xfer ( struct spi_device * spi ,
struct spi_transfer * xfer )
2012-11-07 21:30:29 +04:00
{
u32 speed = xfer - > speed_hz ? : spi - > max_speed_hz ;
struct spi_clps711x_data * hw = spi_master_get_devdata ( spi - > master ) ;
/* Setup SPI frequency divider */
if ( ! speed | | ( speed > = hw - > max_speed_hz ) )
clps_writel ( ( clps_readl ( SYSCON1 ) & ~ SYSCON1_ADCKSEL_MASK ) |
SYSCON1_ADCKSEL ( 3 ) , SYSCON1 ) ;
else if ( speed > = ( hw - > max_speed_hz / 2 ) )
clps_writel ( ( clps_readl ( SYSCON1 ) & ~ SYSCON1_ADCKSEL_MASK ) |
SYSCON1_ADCKSEL ( 2 ) , SYSCON1 ) ;
else if ( speed > = ( hw - > max_speed_hz / 8 ) )
clps_writel ( ( clps_readl ( SYSCON1 ) & ~ SYSCON1_ADCKSEL_MASK ) |
SYSCON1_ADCKSEL ( 1 ) , SYSCON1 ) ;
else
clps_writel ( ( clps_readl ( SYSCON1 ) & ~ SYSCON1_ADCKSEL_MASK ) |
SYSCON1_ADCKSEL ( 0 ) , SYSCON1 ) ;
}
2014-02-18 13:15:54 +04:00
static int spi_clps711x_prepare_message ( struct spi_master * master ,
struct spi_message * msg )
2012-11-07 21:30:29 +04:00
{
2014-02-02 10:59:49 +04:00
struct spi_device * spi = msg - > spi ;
2012-11-07 21:30:29 +04:00
2014-02-18 13:15:54 +04:00
/* Setup edge for transfer */
if ( spi - > mode & SPI_CPHA )
clps_writew ( clps_readw ( SYSCON3 ) | SYSCON3_ADCCKNSEN , SYSCON3 ) ;
else
clps_writew ( clps_readw ( SYSCON3 ) & ~ SYSCON3_ADCCKNSEN , SYSCON3 ) ;
2012-11-07 21:30:29 +04:00
2014-02-18 13:15:54 +04:00
return 0 ;
}
2012-11-07 21:30:29 +04:00
2014-02-18 13:15:54 +04:00
static int spi_clps711x_transfer_one ( struct spi_master * master ,
struct spi_device * spi ,
struct spi_transfer * xfer )
{
struct spi_clps711x_data * hw = spi_master_get_devdata ( master ) ;
u8 data ;
2012-11-07 21:30:29 +04:00
2014-02-18 13:15:54 +04:00
spi_clps711x_setup_xfer ( spi , xfer ) ;
2012-11-07 21:30:29 +04:00
2014-02-18 13:15:54 +04:00
hw - > len = xfer - > len ;
hw - > bpw = xfer - > bits_per_word ? : spi - > bits_per_word ;
hw - > tx_buf = ( u8 * ) xfer - > tx_buf ;
hw - > rx_buf = ( u8 * ) xfer - > rx_buf ;
2012-11-07 21:30:29 +04:00
2014-02-18 13:15:54 +04:00
/* Initiate transfer */
data = hw - > tx_buf ? * hw - > tx_buf + + : 0 ;
clps_writel ( data | SYNCIO_FRMLEN ( hw - > bpw ) | SYNCIO_TXFRMEN , SYNCIO ) ;
return 1 ;
2012-11-07 21:30:29 +04:00
}
static irqreturn_t spi_clps711x_isr ( int irq , void * dev_id )
{
2014-02-18 13:15:54 +04:00
struct spi_master * master = dev_id ;
struct spi_clps711x_data * hw = spi_master_get_devdata ( master ) ;
2014-02-02 10:59:48 +04:00
u8 data ;
2012-11-07 21:30:29 +04:00
/* Handle RX */
data = clps_readb ( SYNCIO ) ;
if ( hw - > rx_buf )
2014-02-02 10:59:48 +04:00
* hw - > rx_buf + + = data ;
2012-11-07 21:30:29 +04:00
/* Handle TX */
2014-02-02 10:59:48 +04:00
if ( - - hw - > len > 0 ) {
data = hw - > tx_buf ? * hw - > tx_buf + + : 0 ;
2014-02-02 10:59:49 +04:00
clps_writel ( data | SYNCIO_FRMLEN ( hw - > bpw ) | SYNCIO_TXFRMEN ,
SYNCIO ) ;
2012-11-07 21:30:29 +04:00
} else
2014-02-18 13:15:54 +04:00
spi_finalize_current_transfer ( master ) ;
2012-11-07 21:30:29 +04:00
return IRQ_HANDLED ;
}
2012-12-07 20:57:14 +04:00
static int spi_clps711x_probe ( struct platform_device * pdev )
2012-11-07 21:30:29 +04:00
{
int i , ret ;
struct spi_master * master ;
struct spi_clps711x_data * hw ;
struct spi_clps711x_pdata * pdata = dev_get_platdata ( & pdev - > dev ) ;
if ( ! pdata ) {
dev_err ( & pdev - > dev , " No platform data supplied \n " ) ;
return - EINVAL ;
}
if ( pdata - > num_chipselect < 1 ) {
dev_err ( & pdev - > dev , " At least one CS must be defined \n " ) ;
return - EINVAL ;
}
2014-02-02 10:59:50 +04:00
master = spi_alloc_master ( & pdev - > dev , sizeof ( * hw ) ) ;
if ( ! master )
2012-11-07 21:30:29 +04:00
return - ENOMEM ;
2014-02-02 10:59:50 +04:00
master - > cs_gpios = devm_kzalloc ( & pdev - > dev , sizeof ( int ) *
pdata - > num_chipselect , GFP_KERNEL ) ;
if ( ! master - > cs_gpios ) {
ret = - ENOMEM ;
goto err_out ;
2012-11-07 21:30:29 +04:00
}
master - > bus_num = pdev - > id ;
master - > mode_bits = SPI_CPHA | SPI_CS_HIGH ;
2014-02-02 10:59:49 +04:00
master - > bits_per_word_mask = SPI_BPW_RANGE_MASK ( 1 , 8 ) ;
2012-11-07 21:30:29 +04:00
master - > num_chipselect = pdata - > num_chipselect ;
master - > setup = spi_clps711x_setup ;
2014-02-18 13:15:54 +04:00
master - > prepare_message = spi_clps711x_prepare_message ;
master - > transfer_one = spi_clps711x_transfer_one ;
2012-11-07 21:30:29 +04:00
hw = spi_master_get_devdata ( master ) ;
for ( i = 0 ; i < master - > num_chipselect ; i + + ) {
2014-02-02 10:59:50 +04:00
master - > cs_gpios [ i ] = pdata - > chipselect [ i ] ;
2014-03-04 08:59:53 +04:00
ret = devm_gpio_request ( & pdev - > dev , master - > cs_gpios [ i ] ,
DRIVER_NAME ) ;
if ( ret ) {
2012-11-07 21:30:29 +04:00
dev_err ( & pdev - > dev , " Can't get CS GPIO %i \n " , i ) ;
goto err_out ;
}
}
hw - > spi_clk = devm_clk_get ( & pdev - > dev , " spi " ) ;
if ( IS_ERR ( hw - > spi_clk ) ) {
dev_err ( & pdev - > dev , " Can't get clocks \n " ) ;
ret = PTR_ERR ( hw - > spi_clk ) ;
goto err_out ;
}
hw - > max_speed_hz = clk_get_rate ( hw - > spi_clk ) ;
platform_set_drvdata ( pdev , master ) ;
/* Disable extended mode due hardware problems */
clps_writew ( clps_readw ( SYSCON3 ) & ~ SYSCON3_ADCCON , SYSCON3 ) ;
/* Clear possible pending interrupt */
clps_readl ( SYNCIO ) ;
ret = devm_request_irq ( & pdev - > dev , IRQ_SSEOTI , spi_clps711x_isr , 0 ,
2014-02-18 13:15:54 +04:00
dev_name ( & pdev - > dev ) , master ) ;
2012-11-07 21:30:29 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " Can't request IRQ \n " ) ;
2013-09-27 14:02:53 +04:00
goto err_out ;
2012-11-07 21:30:29 +04:00
}
2013-09-24 08:27:48 +04:00
ret = devm_spi_register_master ( & pdev - > dev , master ) ;
2012-11-07 21:30:29 +04:00
if ( ! ret ) {
dev_info ( & pdev - > dev ,
" SPI bus driver initialized. Master clock %u Hz \n " ,
hw - > max_speed_hz ) ;
return 0 ;
}
dev_err ( & pdev - > dev , " Failed to register master \n " ) ;
err_out :
spi_master_put ( master ) ;
return ret ;
}
static struct platform_driver clps711x_spi_driver = {
. driver = {
. name = DRIVER_NAME ,
. owner = THIS_MODULE ,
} ,
. probe = spi_clps711x_probe ,
} ;
module_platform_driver ( clps711x_spi_driver ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Alexander Shiyan <shc_work@mail.ru> " ) ;
MODULE_DESCRIPTION ( " CLPS711X SPI bus driver " ) ;
2014-01-14 13:01:54 +04:00
MODULE_ALIAS ( " platform: " DRIVER_NAME ) ;