2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2011-02-14 05:10:43 +03:00
/*
* Altera SPI driver
*
* Copyright ( C ) 2008 Thomas Chou < thomas @ wytron . com . tw >
*
* Based on spi_s3c24xx . c , which is :
* Copyright ( c ) 2006 Ben Dooks
* Copyright ( c ) 2006 Simtec Electronics
* Ben Dooks < ben @ simtec . co . uk >
*/
# include <linux/interrupt.h>
# include <linux/errno.h>
2011-07-03 23:44:29 +04:00
# include <linux/module.h>
2011-02-14 05:10:43 +03:00
# include <linux/platform_device.h>
2020-06-11 06:25:07 +03:00
# include <linux/spi/altera.h>
2011-02-14 05:10:43 +03:00
# include <linux/spi/spi.h>
# include <linux/io.h>
# include <linux/of.h>
# define DRV_NAME "spi_altera"
# define ALTERA_SPI_RXDATA 0
# define ALTERA_SPI_TXDATA 4
# define ALTERA_SPI_STATUS 8
# define ALTERA_SPI_CONTROL 12
# define ALTERA_SPI_SLAVE_SEL 20
# define ALTERA_SPI_STATUS_ROE_MSK 0x8
# define ALTERA_SPI_STATUS_TOE_MSK 0x10
# define ALTERA_SPI_STATUS_TMT_MSK 0x20
# define ALTERA_SPI_STATUS_TRDY_MSK 0x40
# define ALTERA_SPI_STATUS_RRDY_MSK 0x80
# define ALTERA_SPI_STATUS_E_MSK 0x100
# define ALTERA_SPI_CONTROL_IROE_MSK 0x8
# define ALTERA_SPI_CONTROL_ITOE_MSK 0x10
# define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40
# define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80
# define ALTERA_SPI_CONTROL_IE_MSK 0x100
# define ALTERA_SPI_CONTROL_SSO_MSK 0x400
2020-06-11 06:25:07 +03:00
# define ALTERA_SPI_MAX_CS 32
2011-02-14 05:10:43 +03:00
struct altera_spi {
int irq ;
int len ;
int count ;
int bytes_per_word ;
unsigned long imr ;
/* data buffers */
const unsigned char * tx ;
unsigned char * rx ;
2020-06-19 04:43:39 +03:00
struct regmap * regmap ;
struct device * dev ;
} ;
static const struct regmap_config spi_altera_config = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. fast_io = true ,
2011-02-14 05:10:43 +03:00
} ;
2020-06-19 04:43:39 +03:00
static int altr_spi_writel ( struct altera_spi * hw , unsigned int reg ,
unsigned int val )
{
int ret ;
ret = regmap_write ( hw - > regmap , reg , val ) ;
if ( ret )
dev_err ( hw - > dev , " fail to write reg 0x%x val 0x%x: %d \n " ,
reg , val , ret ) ;
return ret ;
}
static int altr_spi_readl ( struct altera_spi * hw , unsigned int reg ,
unsigned int * val )
{
int ret ;
ret = regmap_read ( hw - > regmap , reg , val ) ;
if ( ret )
dev_err ( hw - > dev , " fail to read reg 0x%x: %d \n " , reg , ret ) ;
return ret ;
}
2011-02-14 05:10:43 +03:00
static inline struct altera_spi * altera_spi_to_hw ( struct spi_device * sdev )
{
return spi_master_get_devdata ( sdev - > master ) ;
}
2017-08-16 12:33:11 +03:00
static void altera_spi_set_cs ( struct spi_device * spi , bool is_high )
2011-02-14 05:10:43 +03:00
{
struct altera_spi * hw = altera_spi_to_hw ( spi ) ;
2017-08-16 12:33:11 +03:00
if ( is_high ) {
hw - > imr & = ~ ALTERA_SPI_CONTROL_SSO_MSK ;
2020-06-19 04:43:39 +03:00
altr_spi_writel ( hw , ALTERA_SPI_CONTROL , hw - > imr ) ;
altr_spi_writel ( hw , ALTERA_SPI_SLAVE_SEL , 0 ) ;
2011-02-14 05:10:43 +03:00
} else {
2020-06-19 04:43:39 +03:00
altr_spi_writel ( hw , ALTERA_SPI_SLAVE_SEL ,
BIT ( spi - > chip_select ) ) ;
2017-08-16 12:33:11 +03:00
hw - > imr | = ALTERA_SPI_CONTROL_SSO_MSK ;
2020-06-19 04:43:39 +03:00
altr_spi_writel ( hw , ALTERA_SPI_CONTROL , hw - > imr ) ;
2011-02-14 05:10:43 +03:00
}
}
2017-08-16 12:33:12 +03:00
static void altera_spi_tx_word ( struct altera_spi * hw )
2011-02-14 05:10:43 +03:00
{
2017-08-16 12:33:12 +03:00
unsigned int txd = 0 ;
2011-02-14 05:10:43 +03:00
if ( hw - > tx ) {
switch ( hw - > bytes_per_word ) {
case 1 :
2017-08-16 12:33:12 +03:00
txd = hw - > tx [ hw - > count ] ;
break ;
2011-02-14 05:10:43 +03:00
case 2 :
2017-08-16 12:33:12 +03:00
txd = ( hw - > tx [ hw - > count * 2 ]
| ( hw - > tx [ hw - > count * 2 + 1 ] < < 8 ) ) ;
break ;
2020-06-11 06:25:06 +03:00
case 4 :
txd = ( hw - > tx [ hw - > count * 4 ]
| ( hw - > tx [ hw - > count * 4 + 1 ] < < 8 )
| ( hw - > tx [ hw - > count * 4 + 2 ] < < 16 )
| ( hw - > tx [ hw - > count * 4 + 3 ] < < 24 ) ) ;
break ;
2011-02-14 05:10:43 +03:00
}
}
2017-08-16 12:33:12 +03:00
2020-06-19 04:43:39 +03:00
altr_spi_writel ( hw , ALTERA_SPI_TXDATA , txd ) ;
2017-08-16 12:33:12 +03:00
}
static void altera_spi_rx_word ( struct altera_spi * hw )
{
unsigned int rxd ;
2020-06-19 04:43:39 +03:00
altr_spi_readl ( hw , ALTERA_SPI_RXDATA , & rxd ) ;
2017-08-16 12:33:12 +03:00
if ( hw - > rx ) {
switch ( hw - > bytes_per_word ) {
case 1 :
hw - > rx [ hw - > count ] = rxd ;
break ;
case 2 :
hw - > rx [ hw - > count * 2 ] = rxd ;
hw - > rx [ hw - > count * 2 + 1 ] = rxd > > 8 ;
break ;
2020-06-11 06:25:06 +03:00
case 4 :
hw - > rx [ hw - > count * 4 ] = rxd ;
hw - > rx [ hw - > count * 4 + 1 ] = rxd > > 8 ;
hw - > rx [ hw - > count * 4 + 2 ] = rxd > > 16 ;
hw - > rx [ hw - > count * 4 + 3 ] = rxd > > 24 ;
break ;
2017-08-16 12:33:12 +03:00
}
}
hw - > count + + ;
2011-02-14 05:10:43 +03:00
}
2017-08-16 12:33:11 +03:00
static int altera_spi_txrx ( struct spi_master * master ,
struct spi_device * spi , struct spi_transfer * t )
2011-02-14 05:10:43 +03:00
{
2017-08-16 12:33:11 +03:00
struct altera_spi * hw = spi_master_get_devdata ( master ) ;
2020-06-19 04:43:39 +03:00
u32 val ;
2011-02-14 05:10:43 +03:00
hw - > tx = t - > tx_buf ;
hw - > rx = t - > rx_buf ;
hw - > count = 0 ;
2013-08-29 19:41:20 +04:00
hw - > bytes_per_word = DIV_ROUND_UP ( t - > bits_per_word , 8 ) ;
2011-02-14 05:10:43 +03:00
hw - > len = t - > len / hw - > bytes_per_word ;
if ( hw - > irq > = 0 ) {
/* enable receive interrupt */
hw - > imr | = ALTERA_SPI_CONTROL_IRRDY_MSK ;
2020-06-19 04:43:39 +03:00
altr_spi_writel ( hw , ALTERA_SPI_CONTROL , hw - > imr ) ;
2011-02-14 05:10:43 +03:00
/* send the first byte */
2017-08-16 12:33:12 +03:00
altera_spi_tx_word ( hw ) ;
2011-02-14 05:10:43 +03:00
} else {
2013-08-15 10:18:46 +04:00
while ( hw - > count < hw - > len ) {
2017-08-16 12:33:12 +03:00
altera_spi_tx_word ( hw ) ;
2013-08-15 10:18:46 +04:00
2020-06-19 04:43:39 +03:00
for ( ; ; ) {
altr_spi_readl ( hw , ALTERA_SPI_STATUS , & val ) ;
if ( val & ALTERA_SPI_STATUS_RRDY_MSK )
break ;
2011-02-14 05:10:43 +03:00
cpu_relax ( ) ;
2020-06-19 04:43:39 +03:00
}
2011-02-14 05:10:43 +03:00
2017-08-16 12:33:12 +03:00
altera_spi_rx_word ( hw ) ;
2011-02-14 05:10:43 +03:00
}
2017-08-16 12:33:11 +03:00
spi_finalize_current_transfer ( master ) ;
2011-02-14 05:10:43 +03:00
}
2017-08-16 12:33:11 +03:00
return t - > len ;
2011-02-14 05:10:43 +03:00
}
static irqreturn_t altera_spi_irq ( int irq , void * dev )
{
2017-08-16 12:33:11 +03:00
struct spi_master * master = dev ;
struct altera_spi * hw = spi_master_get_devdata ( master ) ;
2011-02-14 05:10:43 +03:00
2017-08-16 12:33:12 +03:00
altera_spi_rx_word ( hw ) ;
2011-02-14 05:10:43 +03:00
2017-08-16 12:33:11 +03:00
if ( hw - > count < hw - > len ) {
2017-08-16 12:33:12 +03:00
altera_spi_tx_word ( hw ) ;
2017-08-16 12:33:11 +03:00
} else {
/* disable receive interrupt */
hw - > imr & = ~ ALTERA_SPI_CONTROL_IRRDY_MSK ;
2020-06-19 04:43:39 +03:00
altr_spi_writel ( hw , ALTERA_SPI_CONTROL , hw - > imr ) ;
2017-08-16 12:33:11 +03:00
spi_finalize_current_transfer ( master ) ;
}
2011-02-14 05:10:43 +03:00
return IRQ_HANDLED ;
}
2012-12-07 20:57:14 +04:00
static int altera_spi_probe ( struct platform_device * pdev )
2011-02-14 05:10:43 +03:00
{
2020-06-11 06:25:07 +03:00
struct altera_spi_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
2011-02-14 05:10:43 +03:00
struct altera_spi * hw ;
struct spi_master * master ;
2020-06-19 04:43:39 +03:00
void __iomem * res ;
2011-02-14 05:10:43 +03:00
int err = - ENODEV ;
2020-06-19 04:43:39 +03:00
u32 val ;
2020-06-11 06:25:08 +03:00
u16 i ;
2011-02-14 05:10:43 +03:00
master = spi_alloc_master ( & pdev - > dev , sizeof ( struct altera_spi ) ) ;
if ( ! master )
return err ;
/* setup the master state. */
master - > bus_num = pdev - > id ;
2020-06-11 06:25:07 +03:00
if ( pdata ) {
if ( pdata - > num_chipselect > ALTERA_SPI_MAX_CS ) {
dev_err ( & pdev - > dev ,
" Invalid number of chipselect: %hu \n " ,
pdata - > num_chipselect ) ;
return - EINVAL ;
}
master - > num_chipselect = pdata - > num_chipselect ;
master - > mode_bits = pdata - > mode_bits ;
master - > bits_per_word_mask = pdata - > bits_per_word_mask ;
} else {
master - > num_chipselect = 16 ;
master - > mode_bits = SPI_CS_HIGH ;
master - > bits_per_word_mask = SPI_BPW_RANGE_MASK ( 1 , 16 ) ;
}
2014-03-21 07:21:58 +04:00
master - > dev . of_node = pdev - > dev . of_node ;
2017-08-16 12:33:11 +03:00
master - > transfer_one = altera_spi_txrx ;
master - > set_cs = altera_spi_set_cs ;
2011-02-14 05:10:43 +03:00
hw = spi_master_get_devdata ( master ) ;
2020-06-19 04:43:39 +03:00
hw - > dev = & pdev - > dev ;
2011-02-14 05:10:43 +03:00
/* find and map our resources */
2020-06-19 04:43:39 +03:00
res = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( res ) ) {
err = PTR_ERR ( res ) ;
2013-08-24 21:13:15 +04:00
goto exit ;
}
2020-06-19 04:43:39 +03:00
hw - > regmap = devm_regmap_init_mmio ( & pdev - > dev , res ,
& spi_altera_config ) ;
if ( IS_ERR ( hw - > regmap ) ) {
dev_err ( & pdev - > dev , " regmap mmio init failed \n " ) ;
err = PTR_ERR ( hw - > regmap ) ;
goto exit ;
}
2011-02-14 05:10:43 +03:00
/* program defaults into the registers */
hw - > imr = 0 ; /* disable spi interrupts */
2020-06-19 04:43:39 +03:00
altr_spi_writel ( hw , ALTERA_SPI_CONTROL , hw - > imr ) ;
altr_spi_writel ( hw , ALTERA_SPI_STATUS , 0 ) ; /* clear status reg */
altr_spi_readl ( hw , ALTERA_SPI_STATUS , & val ) ;
if ( val & ALTERA_SPI_STATUS_RRDY_MSK )
altr_spi_readl ( hw , ALTERA_SPI_RXDATA , & val ) ; /* flush rxdata */
2011-02-14 05:10:43 +03:00
/* irq is optional */
hw - > irq = platform_get_irq ( pdev , 0 ) ;
if ( hw - > irq > = 0 ) {
err = devm_request_irq ( & pdev - > dev , hw - > irq , altera_spi_irq , 0 ,
2017-08-16 12:33:11 +03:00
pdev - > name , master ) ;
2011-02-14 05:10:43 +03:00
if ( err )
goto exit ;
}
2017-08-16 12:33:11 +03:00
err = devm_spi_register_master ( & pdev - > dev , master ) ;
2011-02-14 05:10:43 +03:00
if ( err )
goto exit ;
2020-06-11 06:25:08 +03:00
if ( pdata ) {
for ( i = 0 ; i < pdata - > num_devices ; i + + ) {
if ( ! spi_new_device ( master , pdata - > devices + i ) )
dev_warn ( & pdev - > dev ,
" unable to create SPI device: %s \n " ,
pdata - > devices [ i ] . modalias ) ;
}
}
2020-06-19 04:43:39 +03:00
dev_info ( & pdev - > dev , " base %p, irq %d \n " , res , hw - > irq ) ;
2011-02-14 05:10:43 +03:00
return 0 ;
exit :
spi_master_put ( master ) ;
return err ;
}
# ifdef CONFIG_OF
static const struct of_device_id altera_spi_match [ ] = {
{ . compatible = " ALTR,spi-1.0 " , } ,
2013-08-15 00:25:19 +04:00
{ . compatible = " altr,spi-1.0 " , } ,
2011-02-14 05:10:43 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , altera_spi_match ) ;
# endif /* CONFIG_OF */
static struct platform_driver altera_spi_driver = {
. probe = altera_spi_probe ,
. driver = {
. name = DRV_NAME ,
. pm = NULL ,
2012-08-15 11:30:28 +04:00
. of_match_table = of_match_ptr ( altera_spi_match ) ,
2011-02-14 05:10:43 +03:00
} ,
} ;
2011-10-05 21:29:49 +04:00
module_platform_driver ( altera_spi_driver ) ;
2011-02-14 05:10:43 +03:00
MODULE_DESCRIPTION ( " Altera SPI driver " ) ;
MODULE_AUTHOR ( " Thomas Chou <thomas@wytron.com.tw> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform: " DRV_NAME ) ;