2014-08-17 20:33:38 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/bcma/bcma.h>
# include <linux/spi/spi.h>
# include "spi-bcm53xx.h"
# define BCM53XXSPI_MAX_SPI_BAUD 13500000 /* 216 MHz? */
2016-04-18 15:39:30 +03:00
# define BCM53XXSPI_FLASH_WINDOW SZ_32M
2014-08-17 20:33:38 +04:00
/* The longest observed required wait was 19 ms */
# define BCM53XXSPI_SPE_TIMEOUT_MS 80
struct bcm53xxspi {
struct bcma_device * core ;
struct spi_master * master ;
2016-04-18 15:39:30 +03:00
void __iomem * mmio_base ;
2014-08-17 20:33:38 +04:00
size_t read_offset ;
2016-04-18 15:39:30 +03:00
bool bspi ; /* Boot SPI mode with memory mapping */
2014-08-17 20:33:38 +04:00
} ;
static inline u32 bcm53xxspi_read ( struct bcm53xxspi * b53spi , u16 offset )
{
return bcma_read32 ( b53spi - > core , offset ) ;
}
static inline void bcm53xxspi_write ( struct bcm53xxspi * b53spi , u16 offset ,
u32 value )
{
bcma_write32 ( b53spi - > core , offset , value ) ;
}
2016-04-18 15:39:30 +03:00
static void bcm53xxspi_disable_bspi ( struct bcm53xxspi * b53spi )
{
struct device * dev = & b53spi - > core - > dev ;
unsigned long deadline ;
u32 tmp ;
if ( ! b53spi - > bspi )
return ;
tmp = bcm53xxspi_read ( b53spi , B53SPI_BSPI_MAST_N_BOOT_CTRL ) ;
if ( tmp & 0x1 )
return ;
deadline = jiffies + usecs_to_jiffies ( 200 ) ;
do {
tmp = bcm53xxspi_read ( b53spi , B53SPI_BSPI_BUSY_STATUS ) ;
if ( ! ( tmp & 0x1 ) ) {
bcm53xxspi_write ( b53spi , B53SPI_BSPI_MAST_N_BOOT_CTRL ,
0x1 ) ;
ndelay ( 200 ) ;
b53spi - > bspi = false ;
return ;
}
udelay ( 1 ) ;
} while ( ! time_after_eq ( jiffies , deadline ) ) ;
dev_warn ( dev , " Timeout disabling BSPI \n " ) ;
}
static void bcm53xxspi_enable_bspi ( struct bcm53xxspi * b53spi )
{
u32 tmp ;
if ( b53spi - > bspi )
return ;
tmp = bcm53xxspi_read ( b53spi , B53SPI_BSPI_MAST_N_BOOT_CTRL ) ;
if ( ! ( tmp & 0x1 ) )
return ;
bcm53xxspi_write ( b53spi , B53SPI_BSPI_MAST_N_BOOT_CTRL , 0x0 ) ;
b53spi - > bspi = true ;
}
2014-08-17 20:33:38 +04:00
static inline unsigned int bcm53xxspi_calc_timeout ( size_t len )
{
/* Do some magic calculation based on length and buad. Add 10% and 1. */
return ( len * 9000 / BCM53XXSPI_MAX_SPI_BAUD * 110 / 100 ) + 1 ;
}
static int bcm53xxspi_wait ( struct bcm53xxspi * b53spi , unsigned int timeout_ms )
{
unsigned long deadline ;
u32 tmp ;
/* SPE bit has to be 0 before we read MSPI STATUS */
2015-02-08 18:11:05 +03:00
deadline = jiffies + msecs_to_jiffies ( BCM53XXSPI_SPE_TIMEOUT_MS ) ;
2014-08-17 20:33:38 +04:00
do {
tmp = bcm53xxspi_read ( b53spi , B53SPI_MSPI_SPCR2 ) ;
if ( ! ( tmp & B53SPI_MSPI_SPCR2_SPE ) )
break ;
udelay ( 5 ) ;
} while ( ! time_after_eq ( jiffies , deadline ) ) ;
if ( tmp & B53SPI_MSPI_SPCR2_SPE )
goto spi_timeout ;
/* Check status */
2015-02-08 18:11:05 +03:00
deadline = jiffies + msecs_to_jiffies ( timeout_ms ) ;
2014-08-17 20:33:38 +04:00
do {
tmp = bcm53xxspi_read ( b53spi , B53SPI_MSPI_MSPI_STATUS ) ;
if ( tmp & B53SPI_MSPI_MSPI_STATUS_SPIF ) {
bcm53xxspi_write ( b53spi , B53SPI_MSPI_MSPI_STATUS , 0 ) ;
return 0 ;
}
cpu_relax ( ) ;
udelay ( 100 ) ;
} while ( ! time_after_eq ( jiffies , deadline ) ) ;
spi_timeout :
bcm53xxspi_write ( b53spi , B53SPI_MSPI_MSPI_STATUS , 0 ) ;
pr_err ( " Timeout waiting for SPI to be ready! \n " ) ;
return - EBUSY ;
}
static void bcm53xxspi_buf_write ( struct bcm53xxspi * b53spi , u8 * w_buf ,
size_t len , bool cont )
{
u32 tmp ;
int i ;
for ( i = 0 ; i < len ; i + + ) {
/* Transmit Register File MSB */
bcm53xxspi_write ( b53spi , B53SPI_MSPI_TXRAM + 4 * ( i * 2 ) ,
( unsigned int ) w_buf [ i ] ) ;
}
for ( i = 0 ; i < len ; i + + ) {
tmp = B53SPI_CDRAM_CONT | B53SPI_CDRAM_PCS_DISABLE_ALL |
B53SPI_CDRAM_PCS_DSCK ;
if ( ! cont & & i = = len - 1 )
tmp & = ~ B53SPI_CDRAM_CONT ;
tmp & = ~ 0x1 ;
/* Command Register File */
bcm53xxspi_write ( b53spi , B53SPI_MSPI_CDRAM + 4 * i , tmp ) ;
}
/* Set queue pointers */
bcm53xxspi_write ( b53spi , B53SPI_MSPI_NEWQP , 0 ) ;
bcm53xxspi_write ( b53spi , B53SPI_MSPI_ENDQP , len - 1 ) ;
if ( cont )
bcm53xxspi_write ( b53spi , B53SPI_MSPI_WRITE_LOCK , 1 ) ;
/* Start SPI transfer */
tmp = bcm53xxspi_read ( b53spi , B53SPI_MSPI_SPCR2 ) ;
tmp | = B53SPI_MSPI_SPCR2_SPE ;
if ( cont )
tmp | = B53SPI_MSPI_SPCR2_CONT_AFTER_CMD ;
bcm53xxspi_write ( b53spi , B53SPI_MSPI_SPCR2 , tmp ) ;
/* Wait for SPI to finish */
bcm53xxspi_wait ( b53spi , bcm53xxspi_calc_timeout ( len ) ) ;
if ( ! cont )
bcm53xxspi_write ( b53spi , B53SPI_MSPI_WRITE_LOCK , 0 ) ;
b53spi - > read_offset = len ;
}
static void bcm53xxspi_buf_read ( struct bcm53xxspi * b53spi , u8 * r_buf ,
size_t len , bool cont )
{
u32 tmp ;
int i ;
for ( i = 0 ; i < b53spi - > read_offset + len ; i + + ) {
tmp = B53SPI_CDRAM_CONT | B53SPI_CDRAM_PCS_DISABLE_ALL |
B53SPI_CDRAM_PCS_DSCK ;
if ( ! cont & & i = = b53spi - > read_offset + len - 1 )
tmp & = ~ B53SPI_CDRAM_CONT ;
tmp & = ~ 0x1 ;
/* Command Register File */
bcm53xxspi_write ( b53spi , B53SPI_MSPI_CDRAM + 4 * i , tmp ) ;
}
/* Set queue pointers */
bcm53xxspi_write ( b53spi , B53SPI_MSPI_NEWQP , 0 ) ;
bcm53xxspi_write ( b53spi , B53SPI_MSPI_ENDQP ,
b53spi - > read_offset + len - 1 ) ;
if ( cont )
bcm53xxspi_write ( b53spi , B53SPI_MSPI_WRITE_LOCK , 1 ) ;
/* Start SPI transfer */
tmp = bcm53xxspi_read ( b53spi , B53SPI_MSPI_SPCR2 ) ;
tmp | = B53SPI_MSPI_SPCR2_SPE ;
if ( cont )
tmp | = B53SPI_MSPI_SPCR2_CONT_AFTER_CMD ;
bcm53xxspi_write ( b53spi , B53SPI_MSPI_SPCR2 , tmp ) ;
/* Wait for SPI to finish */
bcm53xxspi_wait ( b53spi , bcm53xxspi_calc_timeout ( len ) ) ;
if ( ! cont )
bcm53xxspi_write ( b53spi , B53SPI_MSPI_WRITE_LOCK , 0 ) ;
for ( i = 0 ; i < len ; + + i ) {
int offset = b53spi - > read_offset + i ;
/* Data stored in the transmit register file LSB */
r_buf [ i ] = ( u8 ) bcm53xxspi_read ( b53spi , B53SPI_MSPI_RXRAM + 4 * ( 1 + offset * 2 ) ) ;
}
b53spi - > read_offset = 0 ;
}
static int bcm53xxspi_transfer_one ( struct spi_master * master ,
struct spi_device * spi ,
struct spi_transfer * t )
{
struct bcm53xxspi * b53spi = spi_master_get_devdata ( master ) ;
u8 * buf ;
size_t left ;
2016-04-18 15:39:30 +03:00
bcm53xxspi_disable_bspi ( b53spi ) ;
2014-08-17 20:33:38 +04:00
if ( t - > tx_buf ) {
buf = ( u8 * ) t - > tx_buf ;
left = t - > len ;
while ( left ) {
size_t to_write = min_t ( size_t , 16 , left ) ;
bool cont = left - to_write > 0 ;
bcm53xxspi_buf_write ( b53spi , buf , to_write , cont ) ;
left - = to_write ;
buf + = to_write ;
}
}
if ( t - > rx_buf ) {
buf = ( u8 * ) t - > rx_buf ;
left = t - > len ;
while ( left ) {
size_t to_read = min_t ( size_t , 16 - b53spi - > read_offset ,
left ) ;
bool cont = left - to_read > 0 ;
bcm53xxspi_buf_read ( b53spi , buf , to_read , cont ) ;
left - = to_read ;
buf + = to_read ;
}
}
return 0 ;
}
2016-04-18 15:39:30 +03:00
static int bcm53xxspi_flash_read ( struct spi_device * spi ,
struct spi_flash_read_message * msg )
{
struct bcm53xxspi * b53spi = spi_master_get_devdata ( spi - > master ) ;
int ret = 0 ;
if ( msg - > from + msg - > len > BCM53XXSPI_FLASH_WINDOW )
return - EINVAL ;
bcm53xxspi_enable_bspi ( b53spi ) ;
memcpy_fromio ( msg - > buf , b53spi - > mmio_base + msg - > from , msg - > len ) ;
msg - > retlen = msg - > len ;
return ret ;
}
2014-08-17 20:33:38 +04:00
/**************************************************
* BCMA
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static const struct bcma_device_id bcm53xxspi_bcma_tbl [ ] = {
BCMA_CORE ( BCMA_MANUF_BCM , BCMA_CORE_NS_QSPI , BCMA_ANY_REV , BCMA_ANY_CLASS ) ,
2015-02-10 23:55:03 +03:00
{ } ,
2014-08-17 20:33:38 +04:00
} ;
MODULE_DEVICE_TABLE ( bcma , bcm53xxspi_bcma_tbl ) ;
static int bcm53xxspi_bcma_probe ( struct bcma_device * core )
{
2016-04-18 15:39:30 +03:00
struct device * dev = & core - > dev ;
2014-08-17 20:33:38 +04:00
struct bcm53xxspi * b53spi ;
struct spi_master * master ;
int err ;
if ( core - > bus - > drv_cc . core - > id . rev ! = 42 ) {
pr_err ( " SPI on SoC with unsupported ChipCommon rev \n " ) ;
return - ENOTSUPP ;
}
2016-04-18 15:39:30 +03:00
master = spi_alloc_master ( dev , sizeof ( * b53spi ) ) ;
2014-08-17 20:33:38 +04:00
if ( ! master )
return - ENOMEM ;
b53spi = spi_master_get_devdata ( master ) ;
b53spi - > master = master ;
b53spi - > core = core ;
2016-04-18 15:39:30 +03:00
if ( core - > addr_s [ 0 ] )
b53spi - > mmio_base = devm_ioremap ( dev , core - > addr_s [ 0 ] ,
BCM53XXSPI_FLASH_WINDOW ) ;
b53spi - > bspi = true ;
bcm53xxspi_disable_bspi ( b53spi ) ;
2016-12-29 19:27:55 +03:00
master - > dev . of_node = dev - > of_node ;
2014-08-17 20:33:38 +04:00
master - > transfer_one = bcm53xxspi_transfer_one ;
2016-04-18 15:39:30 +03:00
if ( b53spi - > mmio_base )
master - > spi_flash_read = bcm53xxspi_flash_read ;
2014-08-17 20:33:38 +04:00
bcma_set_drvdata ( core , b53spi ) ;
2016-04-18 15:39:30 +03:00
err = devm_spi_register_master ( dev , master ) ;
2014-08-17 20:33:38 +04:00
if ( err ) {
spi_master_put ( master ) ;
bcma_set_drvdata ( core , NULL ) ;
2015-08-20 20:58:38 +03:00
return err ;
2014-08-17 20:33:38 +04:00
}
2015-08-20 20:58:38 +03:00
return 0 ;
2014-08-17 20:33:38 +04:00
}
static struct bcma_driver bcm53xxspi_bcma_driver = {
. name = KBUILD_MODNAME ,
. id_table = bcm53xxspi_bcma_tbl ,
. probe = bcm53xxspi_bcma_probe ,
} ;
/**************************************************
* Init & exit
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int __init bcm53xxspi_module_init ( void )
{
int err = 0 ;
err = bcma_driver_register ( & bcm53xxspi_bcma_driver ) ;
if ( err )
pr_err ( " Failed to register bcma driver: %d \n " , err ) ;
return err ;
}
static void __exit bcm53xxspi_module_exit ( void )
{
bcma_driver_unregister ( & bcm53xxspi_bcma_driver ) ;
}
module_init ( bcm53xxspi_module_init ) ;
module_exit ( bcm53xxspi_module_exit ) ;
2014-09-26 10:19:22 +04:00
MODULE_DESCRIPTION ( " Broadcom BCM53xx SPI Controller driver " ) ;
MODULE_AUTHOR ( " Rafał Miłecki <zajec5@gmail.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;