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/gpio.h>
# include <linux/delay.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
2014-03-22 10:57:35 +04:00
# include <linux/regmap.h>
# include <linux/mfd/syscon.h>
# include <linux/mfd/syscon/clps711x.h>
2012-11-07 21:30:29 +04:00
# include <linux/spi/spi.h>
# include <linux/platform_data/spi-clps711x.h>
# define DRIVER_NAME "spi-clps711x"
2014-03-22 10:57:35 +04:00
# define SYNCIO_FRMLEN(x) ((x) << 8)
# define SYNCIO_TXFRMEN (1 << 14)
2012-11-07 21:30:29 +04:00
struct spi_clps711x_data {
2014-03-22 10:57:35 +04:00
void __iomem * syncio ;
struct regmap * syscon ;
2012-11-07 21:30:29 +04:00
struct clk * spi_clk ;
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-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-03-22 10:57:35 +04:00
struct spi_clps711x_data * hw = spi_master_get_devdata ( master ) ;
2014-02-02 10:59:49 +04:00
struct spi_device * spi = msg - > spi ;
2012-11-07 21:30:29 +04:00
2014-03-22 10:57:35 +04:00
/* Setup mode for transfer */
return regmap_update_bits ( hw - > syscon , SYSCON_OFFSET , SYSCON3_ADCCKNSEN ,
( spi - > mode & SPI_CPHA ) ?
SYSCON3_ADCCKNSEN : 0 ) ;
2014-02-18 13:15:54 +04:00
}
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-09-20 11:05:09 +04:00
clk_set_rate ( hw - > spi_clk , xfer - > speed_hz ? : spi - > max_speed_hz ) ;
2012-11-07 21:30:29 +04:00
2014-02-18 13:15:54 +04:00
hw - > len = xfer - > len ;
2014-03-02 19:24:18 +04:00
hw - > bpw = xfer - > bits_per_word ;
2014-02-18 13:15:54 +04:00
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 ;
2014-03-22 10:57:35 +04:00
writel ( data | SYNCIO_FRMLEN ( hw - > bpw ) | SYNCIO_TXFRMEN , hw - > syncio ) ;
2014-02-18 13:15:54 +04:00
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 */
2014-03-22 10:57:35 +04:00
data = readb ( hw - > syncio ) ;
2012-11-07 21:30:29 +04:00
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-03-22 10:57:35 +04:00
writel ( data | SYNCIO_FRMLEN ( hw - > bpw ) | SYNCIO_TXFRMEN ,
hw - > 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
{
struct spi_clps711x_data * hw ;
struct spi_clps711x_pdata * pdata = dev_get_platdata ( & pdev - > dev ) ;
2014-03-22 10:57:35 +04:00
struct spi_master * master ;
struct resource * res ;
int i , irq , ret ;
2012-11-07 21:30:29 +04:00
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-03-22 10:57:35 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 )
return irq ;
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 ;
}
}
2014-09-20 11:05:09 +04:00
hw - > spi_clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2012-11-07 21:30:29 +04:00
if ( IS_ERR ( hw - > spi_clk ) ) {
ret = PTR_ERR ( hw - > spi_clk ) ;
goto err_out ;
}
2014-03-22 10:57:35 +04:00
hw - > syscon = syscon_regmap_lookup_by_pdevname ( " syscon.3 " ) ;
if ( IS_ERR ( hw - > syscon ) ) {
ret = PTR_ERR ( hw - > syscon ) ;
goto err_out ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
hw - > syncio = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( hw - > syncio ) ) {
ret = PTR_ERR ( hw - > syncio ) ;
goto err_out ;
}
2012-11-07 21:30:29 +04:00
/* Disable extended mode due hardware problems */
2014-03-22 10:57:35 +04:00
regmap_update_bits ( hw - > syscon , SYSCON_OFFSET , SYSCON3_ADCCON , 0 ) ;
2012-11-07 21:30:29 +04:00
/* Clear possible pending interrupt */
2014-03-22 10:57:35 +04:00
readl ( hw - > syncio ) ;
2012-11-07 21:30:29 +04:00
2014-03-22 10:57:35 +04:00
ret = devm_request_irq ( & pdev - > dev , irq , spi_clps711x_isr , 0 ,
2014-02-18 13:15:54 +04:00
dev_name ( & pdev - > dev ) , master ) ;
2014-03-22 10:57:35 +04:00
if ( ret )
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 " ,
2014-03-26 19:44:18 +04:00
master - > max_speed_hz ) ;
2012-11-07 21:30:29 +04:00
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 ,
} ,
. 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 ) ;