2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2008-02-06 12:38:15 +03:00
/*
* SH SCI SPI interface
*
* Copyright ( c ) 2008 Magnus Damm
*
* Based on S3C24XX GPIO based SPI driver , which is :
* Copyright ( c ) 2006 Ben Dooks
* Copyright ( c ) 2006 Simtec Electronics
*/
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/spinlock.h>
# include <linux/platform_device.h>
# include <linux/spi/spi.h>
# include <linux/spi/spi_bitbang.h>
2011-07-03 23:44:29 +04:00
# include <linux/module.h>
2008-02-06 12:38:15 +03:00
# include <asm/spi.h>
# include <asm/io.h>
struct sh_sci_spi {
struct spi_bitbang bitbang ;
void __iomem * membase ;
unsigned char val ;
struct sh_spi_info * info ;
struct platform_device * dev ;
} ;
# define SCSPTR(sp) (sp->membase + 0x1c)
# define PIN_SCK (1 << 2)
# define PIN_TXD (1 << 0)
# define PIN_RXD PIN_TXD
# define PIN_INIT ((1 << 1) | (1 << 3) | PIN_SCK | PIN_TXD)
static inline void setbits ( struct sh_sci_spi * sp , int bits , int on )
{
/*
* We are the only user of SCSPTR so no locking is required .
* Reading bit 2 and 0 in SCSPTR gives pin state as input .
* Writing the same bits sets the output value .
* This makes regular read - modify - write difficult so we
* use sp - > val to keep track of the latest register value .
*/
if ( on )
sp - > val | = bits ;
else
sp - > val & = ~ bits ;
iowrite8 ( sp - > val , SCSPTR ( sp ) ) ;
}
static inline void setsck ( struct spi_device * dev , int on )
{
setbits ( spi_master_get_devdata ( dev - > master ) , PIN_SCK , on ) ;
}
static inline void setmosi ( struct spi_device * dev , int on )
{
setbits ( spi_master_get_devdata ( dev - > master ) , PIN_TXD , on ) ;
}
static inline u32 getmiso ( struct spi_device * dev )
{
struct sh_sci_spi * sp = spi_master_get_devdata ( dev - > master ) ;
return ( ioread8 ( SCSPTR ( sp ) ) & PIN_RXD ) ? 1 : 0 ;
}
# define spidelay(x) ndelay(x)
2011-06-06 11:16:30 +04:00
# include "spi-bitbang-txrx.h"
2008-02-06 12:38:15 +03:00
static u32 sh_sci_spi_txrx_mode0 ( struct spi_device * spi ,
2018-07-28 11:19:13 +03:00
unsigned nsecs , u32 word , u8 bits ,
unsigned flags )
2008-02-06 12:38:15 +03:00
{
2018-07-28 11:19:13 +03:00
return bitbang_txrx_be_cpha0 ( spi , nsecs , 0 , flags , word , bits ) ;
2008-02-06 12:38:15 +03:00
}
static u32 sh_sci_spi_txrx_mode1 ( struct spi_device * spi ,
2018-07-28 11:19:13 +03:00
unsigned nsecs , u32 word , u8 bits ,
unsigned flags )
2008-02-06 12:38:15 +03:00
{
2018-07-28 11:19:13 +03:00
return bitbang_txrx_be_cpha1 ( spi , nsecs , 0 , flags , word , bits ) ;
2008-02-06 12:38:15 +03:00
}
static u32 sh_sci_spi_txrx_mode2 ( struct spi_device * spi ,
2018-07-28 11:19:13 +03:00
unsigned nsecs , u32 word , u8 bits ,
unsigned flags )
2008-02-06 12:38:15 +03:00
{
2018-07-28 11:19:13 +03:00
return bitbang_txrx_be_cpha0 ( spi , nsecs , 1 , flags , word , bits ) ;
2008-02-06 12:38:15 +03:00
}
static u32 sh_sci_spi_txrx_mode3 ( struct spi_device * spi ,
2018-07-28 11:19:13 +03:00
unsigned nsecs , u32 word , u8 bits ,
unsigned flags )
2008-02-06 12:38:15 +03:00
{
2018-07-28 11:19:13 +03:00
return bitbang_txrx_be_cpha1 ( spi , nsecs , 1 , flags , word , bits ) ;
2008-02-06 12:38:15 +03:00
}
static void sh_sci_spi_chipselect ( struct spi_device * dev , int value )
{
struct sh_sci_spi * sp = spi_master_get_devdata ( dev - > master ) ;
2014-03-13 06:32:37 +04:00
if ( sp - > info - > chip_select )
2008-02-06 12:38:15 +03:00
( sp - > info - > chip_select ) ( sp - > info , dev - > chip_select , value ) ;
}
static int sh_sci_spi_probe ( struct platform_device * dev )
{
struct resource * r ;
struct spi_master * master ;
struct sh_sci_spi * sp ;
int ret ;
master = spi_alloc_master ( & dev - > dev , sizeof ( struct sh_sci_spi ) ) ;
if ( master = = NULL ) {
dev_err ( & dev - > dev , " failed to allocate spi master \n " ) ;
ret = - ENOMEM ;
goto err0 ;
}
sp = spi_master_get_devdata ( master ) ;
platform_set_drvdata ( dev , sp ) ;
2013-07-30 11:58:59 +04:00
sp - > info = dev_get_platdata ( & dev - > dev ) ;
2014-03-13 06:32:37 +04:00
if ( ! sp - > info ) {
dev_err ( & dev - > dev , " platform data is missing \n " ) ;
ret = - ENOENT ;
goto err1 ;
}
2008-02-06 12:38:15 +03:00
/* setup spi bitbang adaptor */
2013-09-10 11:43:41 +04:00
sp - > bitbang . master = master ;
2008-02-06 12:38:15 +03:00
sp - > bitbang . master - > bus_num = sp - > info - > bus_num ;
sp - > bitbang . master - > num_chipselect = sp - > info - > num_chipselect ;
sp - > bitbang . chipselect = sh_sci_spi_chipselect ;
sp - > bitbang . txrx_word [ SPI_MODE_0 ] = sh_sci_spi_txrx_mode0 ;
sp - > bitbang . txrx_word [ SPI_MODE_1 ] = sh_sci_spi_txrx_mode1 ;
sp - > bitbang . txrx_word [ SPI_MODE_2 ] = sh_sci_spi_txrx_mode2 ;
sp - > bitbang . txrx_word [ SPI_MODE_3 ] = sh_sci_spi_txrx_mode3 ;
r = platform_get_resource ( dev , IORESOURCE_MEM , 0 ) ;
if ( r = = NULL ) {
ret = - ENOENT ;
goto err1 ;
}
2009-12-15 01:40:05 +03:00
sp - > membase = ioremap ( r - > start , resource_size ( r ) ) ;
2008-02-06 12:38:15 +03:00
if ( ! sp - > membase ) {
ret = - ENXIO ;
goto err1 ;
}
sp - > val = ioread8 ( SCSPTR ( sp ) ) ;
setbits ( sp , PIN_INIT , 1 ) ;
ret = spi_bitbang_start ( & sp - > bitbang ) ;
if ( ! ret )
return 0 ;
setbits ( sp , PIN_INIT , 0 ) ;
iounmap ( sp - > membase ) ;
err1 :
spi_master_put ( sp - > bitbang . master ) ;
err0 :
return ret ;
}
static int sh_sci_spi_remove ( struct platform_device * dev )
{
struct sh_sci_spi * sp = platform_get_drvdata ( dev ) ;
spi_bitbang_stop ( & sp - > bitbang ) ;
2014-06-16 18:39:29 +04:00
setbits ( sp , PIN_INIT , 0 ) ;
iounmap ( sp - > membase ) ;
2008-02-06 12:38:15 +03:00
spi_master_put ( sp - > bitbang . master ) ;
return 0 ;
}
static struct platform_driver sh_sci_spi_drv = {
. probe = sh_sci_spi_probe ,
. remove = sh_sci_spi_remove ,
. driver = {
. name = " spi_sh_sci " ,
} ,
} ;
2011-10-05 21:29:49 +04:00
module_platform_driver ( sh_sci_spi_drv ) ;
2008-02-06 12:38:15 +03:00
MODULE_DESCRIPTION ( " SH SCI SPI Driver " ) ;
MODULE_AUTHOR ( " Magnus Damm <damm@opensource.se> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-04-11 08:29:20 +04:00
MODULE_ALIAS ( " platform:spi_sh_sci " ) ;