2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2012-02-01 14:14:09 +04:00
/*
* Broadcom BCM63xx SPI controller support
*
2012-04-20 17:37:33 +04:00
* Copyright ( C ) 2009 - 2012 Florian Fainelli < florian @ openwrt . org >
2012-02-01 14:14:09 +04:00
* Copyright ( C ) 2010 Tanguy Bouzeloc < tanguy . bouzeloc @ efixo . com >
*/
# include <linux/kernel.h>
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/spi/spi.h>
# include <linux/completion.h>
# include <linux/err.h>
2012-04-20 17:37:33 +04:00
# include <linux/pm_runtime.h>
2017-02-21 13:58:22 +03:00
# include <linux/of.h>
2020-06-16 20:32:34 +03:00
# include <linux/reset.h>
2012-02-01 14:14:09 +04:00
2015-10-12 13:24:23 +03:00
/* BCM 6338/6348 SPI core */
# define SPI_6348_RSET_SIZE 64
# define SPI_6348_CMD 0x00 /* 16-bits register */
# define SPI_6348_INT_STATUS 0x02
# define SPI_6348_INT_MASK_ST 0x03
# define SPI_6348_INT_MASK 0x04
# define SPI_6348_ST 0x05
# define SPI_6348_CLK_CFG 0x06
# define SPI_6348_FILL_BYTE 0x07
# define SPI_6348_MSG_TAIL 0x09
# define SPI_6348_RX_TAIL 0x0b
# define SPI_6348_MSG_CTL 0x40 /* 8-bits register */
# define SPI_6348_MSG_CTL_WIDTH 8
# define SPI_6348_MSG_DATA 0x41
# define SPI_6348_MSG_DATA_SIZE 0x3f
# define SPI_6348_RX_DATA 0x80
# define SPI_6348_RX_DATA_SIZE 0x3f
/* BCM 3368/6358/6262/6368 SPI core */
# define SPI_6358_RSET_SIZE 1804
# define SPI_6358_MSG_CTL 0x00 /* 16-bits register */
# define SPI_6358_MSG_CTL_WIDTH 16
# define SPI_6358_MSG_DATA 0x02
# define SPI_6358_MSG_DATA_SIZE 0x21e
# define SPI_6358_RX_DATA 0x400
# define SPI_6358_RX_DATA_SIZE 0x220
# define SPI_6358_CMD 0x700 /* 16-bits register */
# define SPI_6358_INT_STATUS 0x702
# define SPI_6358_INT_MASK_ST 0x703
# define SPI_6358_INT_MASK 0x704
# define SPI_6358_ST 0x705
# define SPI_6358_CLK_CFG 0x706
# define SPI_6358_FILL_BYTE 0x707
# define SPI_6358_MSG_TAIL 0x709
# define SPI_6358_RX_TAIL 0x70B
/* Shared SPI definitions */
/* Message configuration */
# define SPI_FD_RW 0x00
# define SPI_HD_W 0x01
# define SPI_HD_R 0x02
# define SPI_BYTE_CNT_SHIFT 0
# define SPI_6348_MSG_TYPE_SHIFT 6
# define SPI_6358_MSG_TYPE_SHIFT 14
/* Command */
# define SPI_CMD_NOOP 0x00
# define SPI_CMD_SOFT_RESET 0x01
# define SPI_CMD_HARD_RESET 0x02
# define SPI_CMD_START_IMMEDIATE 0x03
# define SPI_CMD_COMMAND_SHIFT 0
# define SPI_CMD_COMMAND_MASK 0x000f
# define SPI_CMD_DEVICE_ID_SHIFT 4
# define SPI_CMD_PREPEND_BYTE_CNT_SHIFT 8
# define SPI_CMD_ONE_BYTE_SHIFT 11
# define SPI_CMD_ONE_WIRE_SHIFT 12
# define SPI_DEV_ID_0 0
# define SPI_DEV_ID_1 1
# define SPI_DEV_ID_2 2
# define SPI_DEV_ID_3 3
/* Interrupt mask */
# define SPI_INTR_CMD_DONE 0x01
# define SPI_INTR_RX_OVERFLOW 0x02
# define SPI_INTR_TX_UNDERFLOW 0x04
# define SPI_INTR_TX_OVERFLOW 0x08
# define SPI_INTR_RX_UNDERFLOW 0x10
# define SPI_INTR_CLEAR_ALL 0x1f
/* Status */
# define SPI_RX_EMPTY 0x02
# define SPI_CMD_BUSY 0x04
# define SPI_SERIAL_BUSY 0x08
/* Clock configuration */
# define SPI_CLK_20MHZ 0x00
# define SPI_CLK_0_391MHZ 0x01
# define SPI_CLK_0_781MHZ 0x02 /* default */
# define SPI_CLK_1_563MHZ 0x03
# define SPI_CLK_3_125MHZ 0x04
# define SPI_CLK_6_250MHZ 0x05
# define SPI_CLK_12_50MHZ 0x06
# define SPI_CLK_MASK 0x07
# define SPI_SSOFFTIME_MASK 0x38
# define SPI_SSOFFTIME_SHIFT 3
# define SPI_BYTE_SWAP 0x80
enum bcm63xx_regs_spi {
SPI_CMD ,
SPI_INT_STATUS ,
SPI_INT_MASK_ST ,
SPI_INT_MASK ,
SPI_ST ,
SPI_CLK_CFG ,
SPI_FILL_BYTE ,
SPI_MSG_TAIL ,
SPI_RX_TAIL ,
SPI_MSG_CTL ,
SPI_MSG_DATA ,
SPI_RX_DATA ,
SPI_MSG_TYPE_SHIFT ,
SPI_MSG_CTL_WIDTH ,
SPI_MSG_DATA_SIZE ,
} ;
2012-02-01 14:14:09 +04:00
2013-02-03 18:15:13 +04:00
# define BCM63XX_SPI_MAX_PREPEND 15
2015-09-10 17:11:40 +03:00
# define BCM63XX_SPI_MAX_CS 8
2015-09-10 17:11:41 +03:00
# define BCM63XX_SPI_BUS_NUM 0
2015-09-10 17:11:40 +03:00
2012-02-01 14:14:09 +04:00
struct bcm63xx_spi {
struct completion done ;
void __iomem * regs ;
int irq ;
/* Platform data */
2015-10-12 13:24:23 +03:00
const unsigned long * reg_offsets ;
2017-06-19 21:17:48 +03:00
unsigned int fifo_size ;
2012-06-18 14:07:51 +04:00
unsigned int msg_type_shift ;
unsigned int msg_ctl_width ;
2012-02-01 14:14:09 +04:00
/* data iomem */
u8 __iomem * tx_io ;
const u8 __iomem * rx_io ;
struct clk * clk ;
struct platform_device * pdev ;
} ;
static inline u8 bcm_spi_readb ( struct bcm63xx_spi * bs ,
2015-10-12 13:24:23 +03:00
unsigned int offset )
2012-02-01 14:14:09 +04:00
{
2015-10-12 13:24:23 +03:00
return readb ( bs - > regs + bs - > reg_offsets [ offset ] ) ;
2012-02-01 14:14:09 +04:00
}
static inline u16 bcm_spi_readw ( struct bcm63xx_spi * bs ,
unsigned int offset )
{
2015-10-12 13:24:21 +03:00
# ifdef CONFIG_CPU_BIG_ENDIAN
2015-10-12 13:24:23 +03:00
return ioread16be ( bs - > regs + bs - > reg_offsets [ offset ] ) ;
2015-09-10 17:11:42 +03:00
# else
2015-10-12 13:24:23 +03:00
return readw ( bs - > regs + bs - > reg_offsets [ offset ] ) ;
2015-09-10 17:11:42 +03:00
# endif
2012-02-01 14:14:09 +04:00
}
static inline void bcm_spi_writeb ( struct bcm63xx_spi * bs ,
u8 value , unsigned int offset )
{
2015-10-12 13:24:23 +03:00
writeb ( value , bs - > regs + bs - > reg_offsets [ offset ] ) ;
2012-02-01 14:14:09 +04:00
}
static inline void bcm_spi_writew ( struct bcm63xx_spi * bs ,
u16 value , unsigned int offset )
{
2015-10-12 13:24:21 +03:00
# ifdef CONFIG_CPU_BIG_ENDIAN
2015-10-12 13:24:23 +03:00
iowrite16be ( value , bs - > regs + bs - > reg_offsets [ offset ] ) ;
2015-09-10 17:11:42 +03:00
# else
2015-10-12 13:24:23 +03:00
writew ( value , bs - > regs + bs - > reg_offsets [ offset ] ) ;
2015-09-10 17:11:42 +03:00
# endif
2012-02-01 14:14:09 +04:00
}
2017-06-19 21:17:48 +03:00
static const unsigned int bcm63xx_spi_freq_table [ SPI_CLK_MASK ] [ 2 ] = {
2012-02-01 14:14:09 +04:00
{ 20000000 , SPI_CLK_20MHZ } ,
{ 12500000 , SPI_CLK_12_50MHZ } ,
{ 6250000 , SPI_CLK_6_250MHZ } ,
{ 3125000 , SPI_CLK_3_125MHZ } ,
{ 1563000 , SPI_CLK_1_563MHZ } ,
{ 781000 , SPI_CLK_0_781MHZ } ,
{ 391000 , SPI_CLK_0_391MHZ }
} ;
2012-04-20 17:37:33 +04:00
static void bcm63xx_spi_setup_transfer ( struct spi_device * spi ,
struct spi_transfer * t )
{
struct bcm63xx_spi * bs = spi_master_get_devdata ( spi - > master ) ;
u8 clk_cfg , reg ;
int i ;
2015-11-09 12:28:50 +03:00
/* Default to lowest clock configuration */
clk_cfg = SPI_CLK_0_391MHZ ;
2012-02-01 14:14:09 +04:00
/* Find the closest clock configuration */
for ( i = 0 ; i < SPI_CLK_MASK ; i + + ) {
2013-03-12 03:13:46 +04:00
if ( t - > speed_hz > = bcm63xx_spi_freq_table [ i ] [ 0 ] ) {
2012-02-01 14:14:09 +04:00
clk_cfg = bcm63xx_spi_freq_table [ i ] [ 1 ] ;
break ;
}
}
/* clear existing clock configuration bits of the register */
reg = bcm_spi_readb ( bs , SPI_CLK_CFG ) ;
reg & = ~ SPI_CLK_MASK ;
reg | = clk_cfg ;
bcm_spi_writeb ( bs , reg , SPI_CLK_CFG ) ;
dev_dbg ( & spi - > dev , " Setting clock register to %02x (hz %d) \n " ,
2013-03-12 03:13:46 +04:00
clk_cfg , t - > speed_hz ) ;
2012-02-01 14:14:09 +04:00
}
/* the spi->mode bits understood by this driver: */
# define MODEBITS (SPI_CPOL | SPI_CPHA)
2013-02-03 18:15:13 +04:00
static int bcm63xx_txrx_bufs ( struct spi_device * spi , struct spi_transfer * first ,
unsigned int num_transfers )
2012-02-01 14:14:09 +04:00
{
struct bcm63xx_spi * bs = spi_master_get_devdata ( spi - > master ) ;
u16 msg_ctl ;
u16 cmd ;
2013-02-03 18:15:13 +04:00
unsigned int i , timeout = 0 , prepend_len = 0 , len = 0 ;
struct spi_transfer * t = first ;
bool do_rx = false ;
bool do_tx = false ;
2012-02-01 14:14:09 +04:00
2012-04-20 17:37:33 +04:00
/* Disable the CMD_DONE interrupt */
bcm_spi_writeb ( bs , 0 , SPI_INT_MASK ) ;
2012-02-01 14:14:09 +04:00
dev_dbg ( & spi - > dev , " txrx: tx %p, rx %p, len %d \n " ,
t - > tx_buf , t - > rx_buf , t - > len ) ;
2013-02-03 18:15:13 +04:00
if ( num_transfers > 1 & & t - > tx_buf & & t - > len < = BCM63XX_SPI_MAX_PREPEND )
prepend_len = t - > len ;
/* prepare the buffer */
for ( i = 0 ; i < num_transfers ; i + + ) {
if ( t - > tx_buf ) {
do_tx = true ;
memcpy_toio ( bs - > tx_io + len , t - > tx_buf , t - > len ) ;
/* don't prepend more than one tx */
if ( t ! = first )
prepend_len = 0 ;
}
if ( t - > rx_buf ) {
do_rx = true ;
/* prepend is half-duplex write only */
if ( t = = first )
prepend_len = 0 ;
}
len + = t - > len ;
t = list_entry ( t - > transfer_list . next , struct spi_transfer ,
transfer_list ) ;
}
2014-02-09 07:06:04 +04:00
reinit_completion ( & bs - > done ) ;
2012-02-01 14:14:09 +04:00
/* Fill in the Message control register */
2013-02-03 18:15:13 +04:00
msg_ctl = ( len < < SPI_BYTE_CNT_SHIFT ) ;
2012-02-01 14:14:09 +04:00
2013-02-03 18:15:13 +04:00
if ( do_rx & & do_tx & & prepend_len = = 0 )
2012-06-18 14:07:51 +04:00
msg_ctl | = ( SPI_FD_RW < < bs - > msg_type_shift ) ;
2013-02-03 18:15:13 +04:00
else if ( do_rx )
2012-06-18 14:07:51 +04:00
msg_ctl | = ( SPI_HD_R < < bs - > msg_type_shift ) ;
2013-02-03 18:15:13 +04:00
else if ( do_tx )
2012-06-18 14:07:51 +04:00
msg_ctl | = ( SPI_HD_W < < bs - > msg_type_shift ) ;
switch ( bs - > msg_ctl_width ) {
case 8 :
bcm_spi_writeb ( bs , msg_ctl , SPI_MSG_CTL ) ;
break ;
case 16 :
bcm_spi_writew ( bs , msg_ctl , SPI_MSG_CTL ) ;
break ;
}
2012-02-01 14:14:09 +04:00
/* Issue the transfer */
cmd = SPI_CMD_START_IMMEDIATE ;
2013-02-03 18:15:13 +04:00
cmd | = ( prepend_len < < SPI_CMD_PREPEND_BYTE_CNT_SHIFT ) ;
2012-02-01 14:14:09 +04:00
cmd | = ( spi - > chip_select < < SPI_CMD_DEVICE_ID_SHIFT ) ;
bcm_spi_writew ( bs , cmd , SPI_CMD ) ;
2012-04-20 17:37:33 +04:00
/* Enable the CMD_DONE interrupt */
bcm_spi_writeb ( bs , SPI_INTR_CMD_DONE , SPI_INT_MASK ) ;
2012-02-01 14:14:09 +04:00
2013-02-03 18:15:12 +04:00
timeout = wait_for_completion_timeout ( & bs - > done , HZ ) ;
if ( ! timeout )
return - ETIMEDOUT ;
2013-12-18 00:42:08 +04:00
if ( ! do_rx )
2013-02-03 18:15:13 +04:00
return 0 ;
len = 0 ;
t = first ;
2013-02-03 18:15:12 +04:00
/* Read out all the data */
2013-02-03 18:15:13 +04:00
for ( i = 0 ; i < num_transfers ; i + + ) {
if ( t - > rx_buf )
memcpy_fromio ( t - > rx_buf , bs - > rx_io + len , t - > len ) ;
if ( t ! = first | | prepend_len = = 0 )
len + = t - > len ;
t = list_entry ( t - > transfer_list . next , struct spi_transfer ,
transfer_list ) ;
}
2013-02-03 18:15:12 +04:00
return 0 ;
2012-02-01 14:14:09 +04:00
}
2012-04-20 17:37:33 +04:00
static int bcm63xx_spi_transfer_one ( struct spi_master * master ,
struct spi_message * m )
{
struct bcm63xx_spi * bs = spi_master_get_devdata ( master ) ;
2013-02-03 18:15:13 +04:00
struct spi_transfer * t , * first = NULL ;
2012-04-20 17:37:33 +04:00
struct spi_device * spi = m - > spi ;
int status = 0 ;
2013-02-03 18:15:13 +04:00
unsigned int n_transfers = 0 , total_len = 0 ;
bool can_use_prepend = false ;
/*
* This SPI controller does not support keeping CS active after a
* transfer .
* Work around this by merging as many transfers we can into one big
* full - duplex transfers .
*/
2012-02-01 14:14:09 +04:00
list_for_each_entry ( t , & m - > transfers , transfer_list ) {
2013-02-03 18:15:13 +04:00
if ( ! first )
first = t ;
n_transfers + + ;
total_len + = t - > len ;
if ( n_transfers = = 2 & & ! first - > rx_buf & & ! t - > tx_buf & &
first - > len < = BCM63XX_SPI_MAX_PREPEND )
can_use_prepend = true ;
else if ( can_use_prepend & & t - > tx_buf )
can_use_prepend = false ;
2013-02-03 18:15:12 +04:00
/* we can only transfer one fifo worth of data */
2013-02-03 18:15:13 +04:00
if ( ( can_use_prepend & &
total_len > ( bs - > fifo_size + BCM63XX_SPI_MAX_PREPEND ) ) | |
( ! can_use_prepend & & total_len > bs - > fifo_size ) ) {
2013-02-03 18:15:12 +04:00
dev_err ( & spi - > dev , " unable to do transfers larger than FIFO size (%i > %i) \n " ,
2013-02-03 18:15:13 +04:00
total_len , bs - > fifo_size ) ;
2013-02-03 18:15:12 +04:00
status = - EINVAL ;
goto exit ;
}
2012-04-20 17:37:33 +04:00
2013-02-03 18:15:13 +04:00
/* all combined transfers have to have the same speed */
if ( t - > speed_hz ! = first - > speed_hz ) {
dev_err ( & spi - > dev , " unable to change speed between transfers \n " ) ;
2013-02-03 18:15:12 +04:00
status = - EINVAL ;
goto exit ;
}
2012-04-20 17:37:33 +04:00
2013-02-03 18:15:13 +04:00
/* CS will be deasserted directly after transfer */
2021-03-08 17:54:54 +03:00
if ( t - > delay . value ) {
2013-02-03 18:15:13 +04:00
dev_err ( & spi - > dev , " unable to keep CS asserted after transfer \n " ) ;
2013-02-03 18:15:12 +04:00
status = - EINVAL ;
goto exit ;
}
2012-04-20 17:37:33 +04:00
2013-02-03 18:15:13 +04:00
if ( t - > cs_change | |
list_is_last ( & t - > transfer_list , & m - > transfers ) ) {
/* configure adapter for a new transfer */
bcm63xx_spi_setup_transfer ( spi , first ) ;
2012-04-20 17:37:33 +04:00
2013-02-03 18:15:13 +04:00
/* send the data */
status = bcm63xx_txrx_bufs ( spi , first , n_transfers ) ;
if ( status )
goto exit ;
m - > actual_length + = total_len ;
2012-02-01 14:14:09 +04:00
2013-02-03 18:15:13 +04:00
first = NULL ;
n_transfers = 0 ;
total_len = 0 ;
can_use_prepend = false ;
}
2012-04-20 17:37:33 +04:00
}
exit :
m - > status = status ;
spi_finalize_current_message ( master ) ;
2012-02-01 14:14:09 +04:00
2012-04-20 17:37:33 +04:00
return 0 ;
2012-02-01 14:14:09 +04:00
}
/* This driver supports single master mode only. Hence
* CMD_DONE is the only interrupt we care about
*/
static irqreturn_t bcm63xx_spi_interrupt ( int irq , void * dev_id )
{
struct spi_master * master = ( struct spi_master * ) dev_id ;
struct bcm63xx_spi * bs = spi_master_get_devdata ( master ) ;
u8 intr ;
/* Read interupts and clear them immediately */
intr = bcm_spi_readb ( bs , SPI_INT_STATUS ) ;
bcm_spi_writeb ( bs , SPI_INTR_CLEAR_ALL , SPI_INT_STATUS ) ;
bcm_spi_writeb ( bs , 0 , SPI_INT_MASK ) ;
2012-04-20 17:37:33 +04:00
/* A transfer completed */
if ( intr & SPI_INTR_CMD_DONE )
complete ( & bs - > done ) ;
2012-02-01 14:14:09 +04:00
return IRQ_HANDLED ;
}
2017-02-22 02:30:40 +03:00
static size_t bcm63xx_spi_max_length ( struct spi_device * spi )
2017-02-20 13:50:09 +03:00
{
struct bcm63xx_spi * bs = spi_master_get_devdata ( spi - > master ) ;
return bs - > fifo_size ;
}
2015-10-12 13:24:23 +03:00
static const unsigned long bcm6348_spi_reg_offsets [ ] = {
[ SPI_CMD ] = SPI_6348_CMD ,
[ SPI_INT_STATUS ] = SPI_6348_INT_STATUS ,
[ SPI_INT_MASK_ST ] = SPI_6348_INT_MASK_ST ,
[ SPI_INT_MASK ] = SPI_6348_INT_MASK ,
[ SPI_ST ] = SPI_6348_ST ,
[ SPI_CLK_CFG ] = SPI_6348_CLK_CFG ,
[ SPI_FILL_BYTE ] = SPI_6348_FILL_BYTE ,
[ SPI_MSG_TAIL ] = SPI_6348_MSG_TAIL ,
[ SPI_RX_TAIL ] = SPI_6348_RX_TAIL ,
[ SPI_MSG_CTL ] = SPI_6348_MSG_CTL ,
[ SPI_MSG_DATA ] = SPI_6348_MSG_DATA ,
[ SPI_RX_DATA ] = SPI_6348_RX_DATA ,
[ SPI_MSG_TYPE_SHIFT ] = SPI_6348_MSG_TYPE_SHIFT ,
[ SPI_MSG_CTL_WIDTH ] = SPI_6348_MSG_CTL_WIDTH ,
[ SPI_MSG_DATA_SIZE ] = SPI_6348_MSG_DATA_SIZE ,
} ;
static const unsigned long bcm6358_spi_reg_offsets [ ] = {
[ SPI_CMD ] = SPI_6358_CMD ,
[ SPI_INT_STATUS ] = SPI_6358_INT_STATUS ,
[ SPI_INT_MASK_ST ] = SPI_6358_INT_MASK_ST ,
[ SPI_INT_MASK ] = SPI_6358_INT_MASK ,
[ SPI_ST ] = SPI_6358_ST ,
[ SPI_CLK_CFG ] = SPI_6358_CLK_CFG ,
[ SPI_FILL_BYTE ] = SPI_6358_FILL_BYTE ,
[ SPI_MSG_TAIL ] = SPI_6358_MSG_TAIL ,
[ SPI_RX_TAIL ] = SPI_6358_RX_TAIL ,
[ SPI_MSG_CTL ] = SPI_6358_MSG_CTL ,
[ SPI_MSG_DATA ] = SPI_6358_MSG_DATA ,
[ SPI_RX_DATA ] = SPI_6358_RX_DATA ,
[ SPI_MSG_TYPE_SHIFT ] = SPI_6358_MSG_TYPE_SHIFT ,
[ SPI_MSG_CTL_WIDTH ] = SPI_6358_MSG_CTL_WIDTH ,
[ SPI_MSG_DATA_SIZE ] = SPI_6358_MSG_DATA_SIZE ,
} ;
static const struct platform_device_id bcm63xx_spi_dev_match [ ] = {
{
. name = " bcm6348-spi " ,
. driver_data = ( unsigned long ) bcm6348_spi_reg_offsets ,
} ,
{
. name = " bcm6358-spi " ,
. driver_data = ( unsigned long ) bcm6358_spi_reg_offsets ,
} ,
{
} ,
} ;
2012-02-01 14:14:09 +04:00
2017-02-21 13:58:22 +03:00
static const struct of_device_id bcm63xx_spi_of_match [ ] = {
{ . compatible = " brcm,bcm6348-spi " , . data = & bcm6348_spi_reg_offsets } ,
{ . compatible = " brcm,bcm6358-spi " , . data = & bcm6358_spi_reg_offsets } ,
{ } ,
} ;
2012-12-07 20:57:14 +04:00
static int bcm63xx_spi_probe ( struct platform_device * pdev )
2012-02-01 14:14:09 +04:00
{
struct resource * r ;
2015-10-12 13:24:23 +03:00
const unsigned long * bcm63xx_spireg ;
2012-02-01 14:14:09 +04:00
struct device * dev = & pdev - > dev ;
2017-02-21 13:58:22 +03:00
int irq , bus_num ;
2012-02-01 14:14:09 +04:00
struct spi_master * master ;
struct clk * clk ;
struct bcm63xx_spi * bs ;
int ret ;
2017-02-21 13:58:22 +03:00
u32 num_cs = BCM63XX_SPI_MAX_CS ;
2020-06-16 20:32:34 +03:00
struct reset_control * reset ;
2012-02-01 14:14:09 +04:00
2017-02-21 13:58:22 +03:00
if ( dev - > of_node ) {
const struct of_device_id * match ;
2015-10-12 13:24:23 +03:00
2017-02-21 13:58:22 +03:00
match = of_match_node ( bcm63xx_spi_of_match , dev - > of_node ) ;
if ( ! match )
return - EINVAL ;
bcm63xx_spireg = match - > data ;
of_property_read_u32 ( dev - > of_node , " num-cs " , & num_cs ) ;
if ( num_cs > BCM63XX_SPI_MAX_CS ) {
dev_warn ( dev , " unsupported number of cs (%i), reducing to 8 \n " ,
num_cs ) ;
num_cs = BCM63XX_SPI_MAX_CS ;
}
bus_num = - 1 ;
} else if ( pdev - > id_entry - > driver_data ) {
const struct platform_device_id * match = pdev - > id_entry ;
bcm63xx_spireg = ( const unsigned long * ) match - > driver_data ;
bus_num = BCM63XX_SPI_BUS_NUM ;
} else {
return - EINVAL ;
}
2015-10-12 13:24:23 +03:00
2012-02-01 14:14:09 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
2019-07-30 21:15:41 +03:00
if ( irq < 0 )
2017-08-08 07:52:34 +03:00
return irq ;
2012-02-01 14:14:09 +04:00
2013-12-09 14:20:15 +04:00
clk = devm_clk_get ( dev , " spi " ) ;
2012-02-01 14:14:09 +04:00
if ( IS_ERR ( clk ) ) {
dev_err ( dev , " no clock for device \n " ) ;
2013-12-09 14:20:15 +04:00
return PTR_ERR ( clk ) ;
2012-02-01 14:14:09 +04:00
}
2020-06-16 20:32:34 +03:00
reset = devm_reset_control_get_optional_exclusive ( dev , NULL ) ;
if ( IS_ERR ( reset ) )
return PTR_ERR ( reset ) ;
2012-02-01 14:14:09 +04:00
master = spi_alloc_master ( dev , sizeof ( * bs ) ) ;
if ( ! master ) {
dev_err ( dev , " out of memory \n " ) ;
2013-12-09 14:20:15 +04:00
return - ENOMEM ;
2012-02-01 14:14:09 +04:00
}
bs = spi_master_get_devdata ( master ) ;
2014-02-09 07:06:04 +04:00
init_completion ( & bs - > done ) ;
2012-02-01 14:14:09 +04:00
platform_set_drvdata ( pdev , master ) ;
bs - > pdev = pdev ;
spi/spi-{bcm63xx.c,bfin-v3.c}: simplify use of devm_ioremap_resource
Remove unneeded error handling on the result of a call to
platform_get_resource when the value is passed to devm_ioremap_resource.
Move the call to platform_get_resource adjacent to the call to
devm_ioremap_resource to make the connection between them more clear.
A simplified version of the semantic patch that makes this change is as
follows: (http://coccinelle.lip6.fr/)
// <smpl>
@@
expression pdev,res,n,e,e1;
expression ret != 0;
identifier l;
@@
- res = platform_get_resource(pdev, IORESOURCE_MEM, n);
... when != res
- if (res == NULL) { ... \(goto l;\|return ret;\) }
... when != res
+ res = platform_get_resource(pdev, IORESOURCE_MEM, n);
e = devm_ioremap_resource(e1, res);
// </smpl>
Signed-off-by: Julia Lawall <Julia.Lawall@lip6.fr>
Signed-off-by: Mark Brown <broonie@linaro.org>
2013-08-14 13:11:09 +04:00
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-03-12 03:13:47 +04:00
bs - > regs = devm_ioremap_resource ( & pdev - > dev , r ) ;
if ( IS_ERR ( bs - > regs ) ) {
ret = PTR_ERR ( bs - > regs ) ;
2012-02-01 14:14:09 +04:00
goto out_err ;
}
bs - > irq = irq ;
bs - > clk = clk ;
2015-10-12 13:24:23 +03:00
bs - > reg_offsets = bcm63xx_spireg ;
bs - > fifo_size = bs - > reg_offsets [ SPI_MSG_DATA_SIZE ] ;
2012-02-01 14:14:09 +04:00
ret = devm_request_irq ( & pdev - > dev , irq , bcm63xx_spi_interrupt , 0 ,
pdev - > name , master ) ;
if ( ret ) {
dev_err ( dev , " unable to request irq \n " ) ;
goto out_err ;
}
2017-02-21 13:58:22 +03:00
master - > dev . of_node = dev - > of_node ;
master - > bus_num = bus_num ;
master - > num_chipselect = num_cs ;
2012-04-20 17:37:33 +04:00
master - > transfer_one_message = bcm63xx_spi_transfer_one ;
2012-04-20 17:37:35 +04:00
master - > mode_bits = MODEBITS ;
2013-05-22 06:36:35 +04:00
master - > bits_per_word_mask = SPI_BPW_MASK ( 8 ) ;
2017-02-20 13:50:09 +03:00
master - > max_transfer_size = bcm63xx_spi_max_length ;
master - > max_message_size = bcm63xx_spi_max_length ;
2013-07-28 18:34:06 +04:00
master - > auto_runtime_pm = true ;
2015-10-12 13:24:23 +03:00
bs - > msg_type_shift = bs - > reg_offsets [ SPI_MSG_TYPE_SHIFT ] ;
bs - > msg_ctl_width = bs - > reg_offsets [ SPI_MSG_CTL_WIDTH ] ;
bs - > tx_io = ( u8 * ) ( bs - > regs + bs - > reg_offsets [ SPI_MSG_DATA ] ) ;
bs - > rx_io = ( const u8 * ) ( bs - > regs + bs - > reg_offsets [ SPI_RX_DATA ] ) ;
2012-06-18 14:07:51 +04:00
2012-02-01 14:14:09 +04:00
/* Initialize hardware */
2013-12-18 00:42:09 +04:00
ret = clk_prepare_enable ( bs - > clk ) ;
if ( ret )
goto out_err ;
2020-06-16 20:32:34 +03:00
ret = reset_control_reset ( reset ) ;
if ( ret ) {
dev_err ( dev , " unable to reset device: %d \n " , ret ) ;
goto out_clk_disable ;
}
2012-02-01 14:14:09 +04:00
bcm_spi_writeb ( bs , SPI_INTR_CLEAR_ALL , SPI_INT_STATUS ) ;
2021-02-23 18:18:50 +03:00
pm_runtime_enable ( & pdev - > dev ) ;
2012-02-01 14:14:09 +04:00
/* register and we are done */
2013-09-24 08:24:57 +04:00
ret = devm_spi_register_master ( dev , master ) ;
2012-02-01 14:14:09 +04:00
if ( ret ) {
dev_err ( dev , " spi register failed \n " ) ;
2021-02-23 18:18:50 +03:00
goto out_pm_disable ;
2012-02-01 14:14:09 +04:00
}
2015-11-12 19:44:34 +03:00
dev_info ( dev , " at %pr (irq %d, FIFOs size %d) \n " ,
r , irq , bs - > fifo_size ) ;
2012-02-01 14:14:09 +04:00
return 0 ;
2021-02-23 18:18:50 +03:00
out_pm_disable :
pm_runtime_disable ( & pdev - > dev ) ;
2012-02-01 14:14:09 +04:00
out_clk_disable :
2013-03-12 03:13:38 +04:00
clk_disable_unprepare ( clk ) ;
2012-02-01 14:14:09 +04:00
out_err :
spi_master_put ( master ) ;
return ret ;
}
2012-12-07 20:57:14 +04:00
static int bcm63xx_spi_remove ( struct platform_device * pdev )
2012-02-01 14:14:09 +04:00
{
2013-11-15 11:50:59 +04:00
struct spi_master * master = platform_get_drvdata ( pdev ) ;
2012-02-01 14:14:09 +04:00
struct bcm63xx_spi * bs = spi_master_get_devdata ( master ) ;
/* reset spi block */
bcm_spi_writeb ( bs , 0 , SPI_INT_MASK ) ;
/* HW shutdown */
2013-03-12 03:13:38 +04:00
clk_disable_unprepare ( bs - > clk ) ;
2012-02-01 14:14:09 +04:00
return 0 ;
}
2013-12-18 00:42:10 +04:00
# ifdef CONFIG_PM_SLEEP
2012-02-01 14:14:09 +04:00
static int bcm63xx_spi_suspend ( struct device * dev )
{
2013-08-09 11:35:16 +04:00
struct spi_master * master = dev_get_drvdata ( dev ) ;
2012-02-01 14:14:09 +04:00
struct bcm63xx_spi * bs = spi_master_get_devdata ( master ) ;
2012-10-03 13:56:54 +04:00
spi_master_suspend ( master ) ;
2013-03-12 03:13:38 +04:00
clk_disable_unprepare ( bs - > clk ) ;
2012-02-01 14:14:09 +04:00
return 0 ;
}
static int bcm63xx_spi_resume ( struct device * dev )
{
2013-08-09 11:35:16 +04:00
struct spi_master * master = dev_get_drvdata ( dev ) ;
2012-02-01 14:14:09 +04:00
struct bcm63xx_spi * bs = spi_master_get_devdata ( master ) ;
2013-12-18 00:42:09 +04:00
int ret ;
2012-02-01 14:14:09 +04:00
2013-12-18 00:42:09 +04:00
ret = clk_prepare_enable ( bs - > clk ) ;
if ( ret )
return ret ;
2012-02-01 14:14:09 +04:00
2012-10-03 13:56:54 +04:00
spi_master_resume ( master ) ;
2012-02-01 14:14:09 +04:00
return 0 ;
}
2013-12-18 00:42:10 +04:00
# endif
2012-02-01 14:14:09 +04:00
static const struct dev_pm_ops bcm63xx_spi_pm_ops = {
2013-12-18 00:42:10 +04:00
SET_SYSTEM_SLEEP_PM_OPS ( bcm63xx_spi_suspend , bcm63xx_spi_resume )
2012-02-01 14:14:09 +04:00
} ;
static struct platform_driver bcm63xx_spi_driver = {
. driver = {
. name = " bcm63xx-spi " ,
2013-12-18 00:42:10 +04:00
. pm = & bcm63xx_spi_pm_ops ,
2017-02-21 13:58:22 +03:00
. of_match_table = bcm63xx_spi_of_match ,
2012-02-01 14:14:09 +04:00
} ,
2015-10-12 13:24:23 +03:00
. id_table = bcm63xx_spi_dev_match ,
2012-02-01 14:14:09 +04:00
. probe = bcm63xx_spi_probe ,
2012-12-07 20:57:14 +04:00
. remove = bcm63xx_spi_remove ,
2012-02-01 14:14:09 +04:00
} ;
module_platform_driver ( bcm63xx_spi_driver ) ;
MODULE_ALIAS ( " platform:bcm63xx_spi " ) ;
MODULE_AUTHOR ( " Florian Fainelli <florian@openwrt.org> " ) ;
MODULE_AUTHOR ( " Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com> " ) ;
MODULE_DESCRIPTION ( " Broadcom BCM63xx SPI Controller driver " ) ;
MODULE_LICENSE ( " GPL " ) ;