2012-08-22 23:25:07 +04:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Copyright ( C ) 2011 , 2012 Cavium , Inc .
*/
# include <linux/spi/spi.h>
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/io.h>
2016-07-23 13:42:53 +03:00
# include "spi-cavium.h"
2012-08-22 23:25:07 +04:00
static void octeon_spi_wait_ready ( struct octeon_spi * p )
{
union cvmx_mpi_sts mpi_sts ;
unsigned int loops = 0 ;
do {
if ( loops + + )
__delay ( 500 ) ;
2016-07-23 13:42:52 +03:00
mpi_sts . u64 = readq ( p - > register_base + OCTEON_SPI_STS ( p ) ) ;
2012-08-22 23:25:07 +04:00
} while ( mpi_sts . s . busy ) ;
}
static int octeon_spi_do_transfer ( struct octeon_spi * p ,
struct spi_message * msg ,
struct spi_transfer * xfer ,
bool last_xfer )
{
2014-02-19 13:30:52 +04:00
struct spi_device * spi = msg - > spi ;
2012-08-22 23:25:07 +04:00
union cvmx_mpi_cfg mpi_cfg ;
union cvmx_mpi_tx mpi_tx ;
unsigned int clkdiv ;
int mode ;
bool cpha , cpol ;
const u8 * tx_buf ;
u8 * rx_buf ;
int len ;
int i ;
2014-02-19 13:30:52 +04:00
mode = spi - > mode ;
2012-08-22 23:25:07 +04:00
cpha = mode & SPI_CPHA ;
cpol = mode & SPI_CPOL ;
2016-07-23 13:42:51 +03:00
clkdiv = p - > sys_freq / ( 2 * xfer - > speed_hz ) ;
2012-08-22 23:25:07 +04:00
mpi_cfg . u64 = 0 ;
mpi_cfg . s . clkdiv = clkdiv ;
mpi_cfg . s . cshi = ( mode & SPI_CS_HIGH ) ? 1 : 0 ;
mpi_cfg . s . lsbfirst = ( mode & SPI_LSB_FIRST ) ? 1 : 0 ;
mpi_cfg . s . wireor = ( mode & SPI_3WIRE ) ? 1 : 0 ;
mpi_cfg . s . idlelo = cpha ! = cpol ;
mpi_cfg . s . cslate = cpha ? 1 : 0 ;
mpi_cfg . s . enable = 1 ;
2023-03-10 20:32:03 +03:00
if ( spi_get_chipselect ( spi , 0 ) < 4 )
p - > cs_enax | = 1ull < < ( 12 + spi_get_chipselect ( spi , 0 ) ) ;
2012-08-22 23:25:07 +04:00
mpi_cfg . u64 | = p - > cs_enax ;
if ( mpi_cfg . u64 ! = p - > last_cfg ) {
p - > last_cfg = mpi_cfg . u64 ;
2016-07-23 13:42:52 +03:00
writeq ( mpi_cfg . u64 , p - > register_base + OCTEON_SPI_CFG ( p ) ) ;
2012-08-22 23:25:07 +04:00
}
tx_buf = xfer - > tx_buf ;
rx_buf = xfer - > rx_buf ;
len = xfer - > len ;
while ( len > OCTEON_SPI_MAX_BYTES ) {
for ( i = 0 ; i < OCTEON_SPI_MAX_BYTES ; i + + ) {
u8 d ;
if ( tx_buf )
d = * tx_buf + + ;
else
d = 0 ;
2016-07-23 13:42:52 +03:00
writeq ( d , p - > register_base + OCTEON_SPI_DAT0 ( p ) + ( 8 * i ) ) ;
2012-08-22 23:25:07 +04:00
}
mpi_tx . u64 = 0 ;
2023-03-10 20:32:03 +03:00
mpi_tx . s . csid = spi_get_chipselect ( spi , 0 ) ;
2012-08-22 23:25:07 +04:00
mpi_tx . s . leavecs = 1 ;
mpi_tx . s . txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0 ;
mpi_tx . s . totnum = OCTEON_SPI_MAX_BYTES ;
2016-07-23 13:42:52 +03:00
writeq ( mpi_tx . u64 , p - > register_base + OCTEON_SPI_TX ( p ) ) ;
2012-08-22 23:25:07 +04:00
octeon_spi_wait_ready ( p ) ;
if ( rx_buf )
for ( i = 0 ; i < OCTEON_SPI_MAX_BYTES ; i + + ) {
2016-07-23 13:42:52 +03:00
u64 v = readq ( p - > register_base + OCTEON_SPI_DAT0 ( p ) + ( 8 * i ) ) ;
2012-08-22 23:25:07 +04:00
* rx_buf + + = ( u8 ) v ;
}
len - = OCTEON_SPI_MAX_BYTES ;
}
for ( i = 0 ; i < len ; i + + ) {
u8 d ;
if ( tx_buf )
d = * tx_buf + + ;
else
d = 0 ;
2016-07-23 13:42:52 +03:00
writeq ( d , p - > register_base + OCTEON_SPI_DAT0 ( p ) + ( 8 * i ) ) ;
2012-08-22 23:25:07 +04:00
}
mpi_tx . u64 = 0 ;
2023-03-10 20:32:03 +03:00
mpi_tx . s . csid = spi_get_chipselect ( spi , 0 ) ;
2012-08-22 23:25:07 +04:00
if ( last_xfer )
mpi_tx . s . leavecs = xfer - > cs_change ;
else
mpi_tx . s . leavecs = ! xfer - > cs_change ;
mpi_tx . s . txnum = tx_buf ? len : 0 ;
mpi_tx . s . totnum = len ;
2016-07-23 13:42:52 +03:00
writeq ( mpi_tx . u64 , p - > register_base + OCTEON_SPI_TX ( p ) ) ;
2012-08-22 23:25:07 +04:00
octeon_spi_wait_ready ( p ) ;
if ( rx_buf )
for ( i = 0 ; i < len ; i + + ) {
2016-07-23 13:42:52 +03:00
u64 v = readq ( p - > register_base + OCTEON_SPI_DAT0 ( p ) + ( 8 * i ) ) ;
2012-08-22 23:25:07 +04:00
* rx_buf + + = ( u8 ) v ;
}
2019-09-26 13:51:37 +03:00
spi_transfer_delay_exec ( xfer ) ;
2012-08-22 23:25:07 +04:00
return xfer - > len ;
}
2016-07-23 13:42:54 +03:00
int octeon_spi_transfer_one_message ( struct spi_master * master ,
struct spi_message * msg )
2012-08-22 23:25:07 +04:00
{
struct octeon_spi * p = spi_master_get_devdata ( master ) ;
unsigned int total_len = 0 ;
int status = 0 ;
struct spi_transfer * xfer ;
list_for_each_entry ( xfer , & msg - > transfers , transfer_list ) {
2014-01-15 09:22:17 +04:00
bool last_xfer = list_is_last ( & xfer - > transfer_list ,
& msg - > transfers ) ;
2012-08-22 23:25:07 +04:00
int r = octeon_spi_do_transfer ( p , msg , xfer , last_xfer ) ;
if ( r < 0 ) {
status = r ;
goto err ;
}
total_len + = r ;
}
err :
msg - > status = status ;
msg - > actual_length = total_len ;
spi_finalize_current_message ( master ) ;
return status ;
}