2019-05-30 02:57:25 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-12-09 23:21:30 +03:00
/*
* Copyright ( c ) 2008 - 2014 STMicroelectronics Limited
*
* Author : Angus Clark < Angus . Clark @ st . com >
* Patrice Chotard < patrice . chotard @ st . com >
* Lee Jones < lee . jones @ linaro . org >
*
* SPI master mode controller driver , used in STMicroelectronics devices .
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/pinctrl/consumer.h>
# include <linux/platform_device.h>
# include <linux/of.h>
# include <linux/of_irq.h>
# include <linux/pm_runtime.h>
# include <linux/spi/spi.h>
# include <linux/spi/spi_bitbang.h>
/* SSC registers */
# define SSC_BRG 0x000
# define SSC_TBUF 0x004
# define SSC_RBUF 0x008
# define SSC_CTL 0x00C
# define SSC_IEN 0x010
# define SSC_I2C 0x018
/* SSC Control */
# define SSC_CTL_DATA_WIDTH_9 0x8
# define SSC_CTL_DATA_WIDTH_MSK 0xf
# define SSC_CTL_BM 0xf
# define SSC_CTL_HB BIT(4)
# define SSC_CTL_PH BIT(5)
# define SSC_CTL_PO BIT(6)
# define SSC_CTL_SR BIT(7)
# define SSC_CTL_MS BIT(8)
# define SSC_CTL_EN BIT(9)
# define SSC_CTL_LPB BIT(10)
# define SSC_CTL_EN_TX_FIFO BIT(11)
# define SSC_CTL_EN_RX_FIFO BIT(12)
# define SSC_CTL_EN_CLST_RX BIT(13)
/* SSC Interrupt Enable */
# define SSC_IEN_TEEN BIT(2)
# define FIFO_SIZE 8
struct spi_st {
/* SSC SPI Controller */
void __iomem * base ;
struct clk * clk ;
struct device * dev ;
/* SSC SPI current transaction */
const u8 * tx_ptr ;
u8 * rx_ptr ;
u16 bytes_per_word ;
unsigned int words_remaining ;
unsigned int baud ;
struct completion done ;
} ;
/* Load the TX FIFO */
static void ssc_write_tx_fifo ( struct spi_st * spi_st )
{
unsigned int count , i ;
uint32_t word = 0 ;
if ( spi_st - > words_remaining > FIFO_SIZE )
count = FIFO_SIZE ;
else
count = spi_st - > words_remaining ;
for ( i = 0 ; i < count ; i + + ) {
if ( spi_st - > tx_ptr ) {
if ( spi_st - > bytes_per_word = = 1 ) {
word = * spi_st - > tx_ptr + + ;
} else {
word = * spi_st - > tx_ptr + + ;
word = * spi_st - > tx_ptr + + | ( word < < 8 ) ;
}
}
writel_relaxed ( word , spi_st - > base + SSC_TBUF ) ;
}
}
/* Read the RX FIFO */
static void ssc_read_rx_fifo ( struct spi_st * spi_st )
{
unsigned int count , i ;
uint32_t word = 0 ;
if ( spi_st - > words_remaining > FIFO_SIZE )
count = FIFO_SIZE ;
else
count = spi_st - > words_remaining ;
for ( i = 0 ; i < count ; i + + ) {
word = readl_relaxed ( spi_st - > base + SSC_RBUF ) ;
if ( spi_st - > rx_ptr ) {
if ( spi_st - > bytes_per_word = = 1 ) {
* spi_st - > rx_ptr + + = ( uint8_t ) word ;
} else {
* spi_st - > rx_ptr + + = ( word > > 8 ) ;
* spi_st - > rx_ptr + + = word & 0xff ;
}
}
}
spi_st - > words_remaining - = count ;
}
static int spi_st_transfer_one ( struct spi_master * master ,
struct spi_device * spi , struct spi_transfer * t )
{
struct spi_st * spi_st = spi_master_get_devdata ( master ) ;
uint32_t ctl = 0 ;
/* Setup transfer */
spi_st - > tx_ptr = t - > tx_buf ;
spi_st - > rx_ptr = t - > rx_buf ;
if ( spi - > bits_per_word > 8 ) {
/*
* Anything greater than 8 bits - per - word requires 2
* bytes - per - word in the RX / TX buffers
*/
spi_st - > bytes_per_word = 2 ;
spi_st - > words_remaining = t - > len / 2 ;
} else if ( spi - > bits_per_word = = 8 & & ! ( t - > len & 0x1 ) ) {
/*
* If transfer is even - length , and 8 bits - per - word , then
* implement as half - length 16 bits - per - word transfer
*/
spi_st - > bytes_per_word = 2 ;
spi_st - > words_remaining = t - > len / 2 ;
/* Set SSC_CTL to 16 bits-per-word */
ctl = readl_relaxed ( spi_st - > base + SSC_CTL ) ;
writel_relaxed ( ( ctl | 0xf ) , spi_st - > base + SSC_CTL ) ;
readl_relaxed ( spi_st - > base + SSC_RBUF ) ;
} else {
spi_st - > bytes_per_word = 1 ;
spi_st - > words_remaining = t - > len ;
}
reinit_completion ( & spi_st - > done ) ;
/* Start transfer by writing to the TX FIFO */
ssc_write_tx_fifo ( spi_st ) ;
writel_relaxed ( SSC_IEN_TEEN , spi_st - > base + SSC_IEN ) ;
/* Wait for transfer to complete */
wait_for_completion ( & spi_st - > done ) ;
/* Restore SSC_CTL if necessary */
if ( ctl )
writel_relaxed ( ctl , spi_st - > base + SSC_CTL ) ;
spi_finalize_current_transfer ( spi - > master ) ;
return t - > len ;
}
/* the spi->mode bits understood by this driver: */
# define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_LOOP | SPI_CS_HIGH)
static int spi_st_setup ( struct spi_device * spi )
{
struct spi_st * spi_st = spi_master_get_devdata ( spi - > master ) ;
u32 spi_st_clk , sscbrg , var ;
u32 hz = spi - > max_speed_hz ;
if ( ! hz ) {
dev_err ( & spi - > dev , " max_speed_hz unspecified \n " ) ;
return - EINVAL ;
}
2023-03-10 20:32:03 +03:00
if ( ! spi_get_csgpiod ( spi , 0 ) ) {
2022-02-01 04:26:54 +03:00
dev_err ( & spi - > dev , " no valid gpio assigned \n " ) ;
2014-12-09 23:21:30 +03:00
return - EINVAL ;
}
spi_st_clk = clk_get_rate ( spi_st - > clk ) ;
/* Set SSC_BRF */
sscbrg = spi_st_clk / ( 2 * hz ) ;
if ( sscbrg < 0x07 | | sscbrg > BIT ( 16 ) ) {
dev_err ( & spi - > dev ,
" baudrate %d outside valid range %d \n " , sscbrg , hz ) ;
2022-02-01 04:26:54 +03:00
return - EINVAL ;
2014-12-09 23:21:30 +03:00
}
spi_st - > baud = spi_st_clk / ( 2 * sscbrg ) ;
if ( sscbrg = = BIT ( 16 ) ) /* 16-bit counter wraps */
sscbrg = 0x0 ;
writel_relaxed ( sscbrg , spi_st - > base + SSC_BRG ) ;
dev_dbg ( & spi - > dev ,
" setting baudrate:target= %u hz, actual= %u hz, sscbrg= %u \n " ,
hz , spi_st - > baud , sscbrg ) ;
2017-05-25 07:05:05 +03:00
/* Set SSC_CTL and enable SSC */
var = readl_relaxed ( spi_st - > base + SSC_CTL ) ;
var | = SSC_CTL_MS ;
2014-12-09 23:21:30 +03:00
2017-05-25 07:05:05 +03:00
if ( spi - > mode & SPI_CPOL )
2014-12-09 23:21:30 +03:00
var | = SSC_CTL_PO ;
2017-05-25 07:05:05 +03:00
else
2014-12-09 23:21:30 +03:00
var & = ~ SSC_CTL_PO ;
2017-05-25 07:05:05 +03:00
if ( spi - > mode & SPI_CPHA )
2014-12-09 23:21:30 +03:00
var | = SSC_CTL_PH ;
2017-05-25 07:05:05 +03:00
else
2014-12-09 23:21:30 +03:00
var & = ~ SSC_CTL_PH ;
2017-05-25 07:05:05 +03:00
if ( ( spi - > mode & SPI_LSB_FIRST ) = = 0 )
2014-12-09 23:21:30 +03:00
var | = SSC_CTL_HB ;
2017-05-25 07:05:05 +03:00
else
2014-12-09 23:21:30 +03:00
var & = ~ SSC_CTL_HB ;
2017-05-25 07:05:05 +03:00
if ( spi - > mode & SPI_LOOP )
2014-12-09 23:21:30 +03:00
var | = SSC_CTL_LPB ;
2017-05-25 07:05:05 +03:00
else
2014-12-09 23:21:30 +03:00
var & = ~ SSC_CTL_LPB ;
2017-05-25 07:05:05 +03:00
var & = ~ SSC_CTL_DATA_WIDTH_MSK ;
var | = ( spi - > bits_per_word - 1 ) ;
2014-12-09 23:21:30 +03:00
2017-05-25 07:05:05 +03:00
var | = SSC_CTL_EN_TX_FIFO | SSC_CTL_EN_RX_FIFO ;
var | = SSC_CTL_EN ;
2014-12-09 23:21:30 +03:00
2017-05-25 07:05:05 +03:00
writel_relaxed ( var , spi_st - > base + SSC_CTL ) ;
2014-12-09 23:21:30 +03:00
2017-05-25 07:05:05 +03:00
/* Clear the status register */
readl_relaxed ( spi_st - > base + SSC_RBUF ) ;
2014-12-09 23:21:30 +03:00
2017-05-25 07:05:05 +03:00
return 0 ;
2014-12-09 23:21:30 +03:00
}
/* Interrupt fired when TX shift register becomes empty */
static irqreturn_t spi_st_irq ( int irq , void * dev_id )
{
struct spi_st * spi_st = ( struct spi_st * ) dev_id ;
/* Read RX FIFO */
ssc_read_rx_fifo ( spi_st ) ;
/* Fill TX FIFO */
if ( spi_st - > words_remaining ) {
ssc_write_tx_fifo ( spi_st ) ;
} else {
/* TX/RX complete */
writel_relaxed ( 0x0 , spi_st - > base + SSC_IEN ) ;
/*
* read SSC_IEN to ensure that this bit is set
* before re - enabling interrupt
*/
readl ( spi_st - > base + SSC_IEN ) ;
complete ( & spi_st - > done ) ;
}
return IRQ_HANDLED ;
}
static int spi_st_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct spi_master * master ;
struct spi_st * spi_st ;
int irq , ret = 0 ;
u32 var ;
master = spi_alloc_master ( & pdev - > dev , sizeof ( * spi_st ) ) ;
if ( ! master )
return - ENOMEM ;
master - > dev . of_node = np ;
master - > mode_bits = MODEBITS ;
master - > setup = spi_st_setup ;
master - > transfer_one = spi_st_transfer_one ;
master - > bits_per_word_mask = SPI_BPW_MASK ( 8 ) | SPI_BPW_MASK ( 16 ) ;
master - > auto_runtime_pm = true ;
master - > bus_num = pdev - > id ;
2022-02-01 04:26:54 +03:00
master - > use_gpio_descriptors = true ;
2014-12-09 23:21:30 +03:00
spi_st = spi_master_get_devdata ( master ) ;
spi_st - > clk = devm_clk_get ( & pdev - > dev , " ssc " ) ;
if ( IS_ERR ( spi_st - > clk ) ) {
dev_err ( & pdev - > dev , " Unable to request clock \n " ) ;
2016-04-29 08:39:38 +03:00
ret = PTR_ERR ( spi_st - > clk ) ;
goto put_master ;
2014-12-09 23:21:30 +03:00
}
2016-06-07 14:19:00 +03:00
ret = clk_prepare_enable ( spi_st - > clk ) ;
2014-12-09 23:21:30 +03:00
if ( ret )
2016-04-29 08:39:38 +03:00
goto put_master ;
2014-12-09 23:21:30 +03:00
init_completion ( & spi_st - > done ) ;
/* Get resources */
2019-09-04 16:59:10 +03:00
spi_st - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2014-12-09 23:21:30 +03:00
if ( IS_ERR ( spi_st - > base ) ) {
ret = PTR_ERR ( spi_st - > base ) ;
goto clk_disable ;
}
/* Disable I2C and Reset SSC */
writel_relaxed ( 0x0 , spi_st - > base + SSC_I2C ) ;
var = readw_relaxed ( spi_st - > base + SSC_CTL ) ;
var | = SSC_CTL_SR ;
writel_relaxed ( var , spi_st - > base + SSC_CTL ) ;
udelay ( 1 ) ;
var = readl_relaxed ( spi_st - > base + SSC_CTL ) ;
var & = ~ SSC_CTL_SR ;
writel_relaxed ( var , spi_st - > base + SSC_CTL ) ;
/* Set SSC into slave mode before reconfiguring PIO pins */
var = readl_relaxed ( spi_st - > base + SSC_CTL ) ;
var & = ~ SSC_CTL_MS ;
writel_relaxed ( var , spi_st - > base + SSC_CTL ) ;
irq = irq_of_parse_and_map ( np , 0 ) ;
if ( ! irq ) {
dev_err ( & pdev - > dev , " IRQ missing or invalid \n " ) ;
ret = - EINVAL ;
goto clk_disable ;
}
ret = devm_request_irq ( & pdev - > dev , irq , spi_st_irq , 0 ,
pdev - > name , spi_st ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to request irq %d \n " , irq ) ;
goto clk_disable ;
}
/* by default the device is on */
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
platform_set_drvdata ( pdev , master ) ;
ret = devm_spi_register_master ( & pdev - > dev , master ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to register master \n " ) ;
2020-11-09 01:41:00 +03:00
goto rpm_disable ;
2014-12-09 23:21:30 +03:00
}
return 0 ;
2020-11-09 01:41:00 +03:00
rpm_disable :
2019-11-18 05:48:48 +03:00
pm_runtime_disable ( & pdev - > dev ) ;
2020-11-09 01:41:00 +03:00
clk_disable :
2016-06-07 14:19:00 +03:00
clk_disable_unprepare ( spi_st - > clk ) ;
2016-04-29 08:39:38 +03:00
put_master :
spi_master_put ( master ) ;
2014-12-09 23:21:30 +03:00
return ret ;
}
2023-03-03 20:20:25 +03:00
static void spi_st_remove ( struct platform_device * pdev )
2014-12-09 23:21:30 +03:00
{
struct spi_master * master = platform_get_drvdata ( pdev ) ;
struct spi_st * spi_st = spi_master_get_devdata ( master ) ;
2019-11-18 05:48:48 +03:00
pm_runtime_disable ( & pdev - > dev ) ;
2016-06-07 14:19:00 +03:00
clk_disable_unprepare ( spi_st - > clk ) ;
2014-12-09 23:21:30 +03:00
pinctrl_pm_select_sleep_state ( & pdev - > dev ) ;
}
# ifdef CONFIG_PM
static int spi_st_runtime_suspend ( struct device * dev )
{
struct spi_master * master = dev_get_drvdata ( dev ) ;
struct spi_st * spi_st = spi_master_get_devdata ( master ) ;
writel_relaxed ( 0 , spi_st - > base + SSC_IEN ) ;
pinctrl_pm_select_sleep_state ( dev ) ;
2016-06-07 14:19:00 +03:00
clk_disable_unprepare ( spi_st - > clk ) ;
2014-12-09 23:21:30 +03:00
return 0 ;
}
static int spi_st_runtime_resume ( struct device * dev )
{
struct spi_master * master = dev_get_drvdata ( dev ) ;
struct spi_st * spi_st = spi_master_get_devdata ( master ) ;
int ret ;
2016-06-07 14:19:00 +03:00
ret = clk_prepare_enable ( spi_st - > clk ) ;
2014-12-09 23:21:30 +03:00
pinctrl_pm_select_default_state ( dev ) ;
return ret ;
}
# endif
# ifdef CONFIG_PM_SLEEP
static int spi_st_suspend ( struct device * dev )
{
struct spi_master * master = dev_get_drvdata ( dev ) ;
int ret ;
ret = spi_master_suspend ( master ) ;
if ( ret )
return ret ;
return pm_runtime_force_suspend ( dev ) ;
}
static int spi_st_resume ( struct device * dev )
{
struct spi_master * master = dev_get_drvdata ( dev ) ;
int ret ;
ret = spi_master_resume ( master ) ;
if ( ret )
return ret ;
return pm_runtime_force_resume ( dev ) ;
}
# endif
static const struct dev_pm_ops spi_st_pm = {
SET_SYSTEM_SLEEP_PM_OPS ( spi_st_suspend , spi_st_resume )
SET_RUNTIME_PM_OPS ( spi_st_runtime_suspend , spi_st_runtime_resume , NULL )
} ;
2015-03-16 22:20:31 +03:00
static const struct of_device_id stm_spi_match [ ] = {
2014-12-09 23:21:30 +03:00
{ . compatible = " st,comms-ssc4-spi " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , stm_spi_match ) ;
static struct platform_driver spi_st_driver = {
. driver = {
. name = " spi-st " ,
. pm = & spi_st_pm ,
. of_match_table = of_match_ptr ( stm_spi_match ) ,
} ,
. probe = spi_st_probe ,
2023-03-03 20:20:25 +03:00
. remove_new = spi_st_remove ,
2014-12-09 23:21:30 +03:00
} ;
module_platform_driver ( spi_st_driver ) ;
MODULE_AUTHOR ( " Patrice Chotard <patrice.chotard@st.com> " ) ;
MODULE_DESCRIPTION ( " STM SSC SPI driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;