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/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-19 04:43:39 +03:00
static int altr_spi_writel ( struct altera_spi * hw , unsigned int reg ,
unsigned int val )
{
int ret ;
2020-06-19 04:43:40 +03:00
ret = regmap_write ( hw - > regmap , hw - > regoff + reg , val ) ;
2020-06-19 04:43:39 +03:00
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 ;
2020-06-19 04:43:40 +03:00
ret = regmap_read ( hw - > regmap , hw - > regoff + reg , val ) ;
2020-06-19 04:43:39 +03:00
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 ) ;
2013-08-15 10:18:46 +04:00
2020-12-29 08:27:41 +03:00
return 1 ;
}
2020-06-19 04:43:39 +03:00
2020-12-29 08:27:41 +03:00
while ( hw - > count < hw - > len ) {
altera_spi_tx_word ( hw ) ;
2011-02-14 05:10:43 +03:00
2020-12-29 08:27:41 +03:00
for ( ; ; ) {
altr_spi_readl ( hw , ALTERA_SPI_STATUS , & val ) ;
if ( val & ALTERA_SPI_STATUS_RRDY_MSK )
break ;
cpu_relax ( ) ;
2011-02-14 05:10:43 +03:00
}
2020-12-29 08:27:41 +03:00
altera_spi_rx_word ( hw ) ;
2011-02-14 05:10:43 +03:00
}
2020-12-29 08:27:41 +03:00
spi_finalize_current_transfer ( master ) ;
2011-02-14 05:10:43 +03:00
2020-12-29 08:27:41 +03:00
return 0 ;
2011-02-14 05:10:43 +03:00
}
2021-04-16 19:57:19 +03:00
irqreturn_t altera_spi_irq ( int irq , void * dev )
2011-02-14 05:10:43 +03:00
{
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 ;
}
2021-04-16 19:57:19 +03:00
EXPORT_SYMBOL_GPL ( altera_spi_irq ) ;
2011-02-14 05:10:43 +03:00
2021-04-16 19:57:19 +03:00
void altera_spi_init_master ( struct spi_master * master )
2011-02-14 05:10:43 +03:00
{
2021-04-16 19:57:19 +03:00
struct altera_spi * hw = spi_master_get_devdata ( master ) ;
2020-06-19 04:43:39 +03:00
u32 val ;
2020-06-11 06:25:07 +03:00
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
/* 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
}
2021-04-16 19:57:19 +03:00
EXPORT_SYMBOL_GPL ( altera_spi_init_master ) ;
2011-02-14 05:10:43 +03:00
MODULE_LICENSE ( " GPL " ) ;