2018-11-12 18:42:32 +02:00
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018 Nuvoton Technology corporation.
# include <linux/kernel.h>
# include <linux/bitfield.h>
# include <linux/bitops.h>
# include <linux/clk.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/spi/spi.h>
2020-01-15 18:23:01 +02:00
# include <linux/reset.h>
2018-11-12 18:42:32 +02:00
# include <asm/unaligned.h>
# include <linux/regmap.h>
# include <linux/mfd/syscon.h>
struct npcm_pspi {
struct completion xfer_done ;
2020-01-15 18:23:01 +02:00
struct reset_control * reset ;
2023-08-23 11:29:59 +08:00
struct spi_controller * host ;
2018-11-12 18:42:32 +02:00
unsigned int tx_bytes ;
unsigned int rx_bytes ;
void __iomem * base ;
bool is_save_param ;
u8 bits_per_word ;
const u8 * tx_buf ;
struct clk * clk ;
u32 speed_hz ;
u8 * rx_buf ;
u16 mode ;
u32 id ;
} ;
# define DRIVER_NAME "npcm-pspi"
# define NPCM_PSPI_DATA 0x00
# define NPCM_PSPI_CTL1 0x02
# define NPCM_PSPI_STAT 0x04
/* definitions for control and status register */
# define NPCM_PSPI_CTL1_SPIEN BIT(0)
# define NPCM_PSPI_CTL1_MOD BIT(2)
# define NPCM_PSPI_CTL1_EIR BIT(5)
# define NPCM_PSPI_CTL1_EIW BIT(6)
# define NPCM_PSPI_CTL1_SCM BIT(7)
# define NPCM_PSPI_CTL1_SCIDL BIT(8)
# define NPCM_PSPI_CTL1_SCDV6_0 GENMASK(15, 9)
# define NPCM_PSPI_STAT_BSY BIT(0)
# define NPCM_PSPI_STAT_RBF BIT(1)
/* general definitions */
# define NPCM_PSPI_TIMEOUT_MS 2000
# define NPCM_PSPI_MAX_CLK_DIVIDER 256
# define NPCM_PSPI_MIN_CLK_DIVIDER 4
# define NPCM_PSPI_DEFAULT_CLK 25000000
static inline unsigned int bytes_per_word ( unsigned int bits )
{
return bits < = 8 ? 1 : 2 ;
}
static inline void npcm_pspi_irq_enable ( struct npcm_pspi * priv , u16 mask )
{
u16 val ;
val = ioread16 ( priv - > base + NPCM_PSPI_CTL1 ) ;
val | = mask ;
iowrite16 ( val , priv - > base + NPCM_PSPI_CTL1 ) ;
}
static inline void npcm_pspi_irq_disable ( struct npcm_pspi * priv , u16 mask )
{
u16 val ;
val = ioread16 ( priv - > base + NPCM_PSPI_CTL1 ) ;
val & = ~ mask ;
iowrite16 ( val , priv - > base + NPCM_PSPI_CTL1 ) ;
}
static inline void npcm_pspi_enable ( struct npcm_pspi * priv )
{
u16 val ;
val = ioread16 ( priv - > base + NPCM_PSPI_CTL1 ) ;
val | = NPCM_PSPI_CTL1_SPIEN ;
iowrite16 ( val , priv - > base + NPCM_PSPI_CTL1 ) ;
}
static inline void npcm_pspi_disable ( struct npcm_pspi * priv )
{
u16 val ;
val = ioread16 ( priv - > base + NPCM_PSPI_CTL1 ) ;
val & = ~ NPCM_PSPI_CTL1_SPIEN ;
iowrite16 ( val , priv - > base + NPCM_PSPI_CTL1 ) ;
}
static void npcm_pspi_set_mode ( struct spi_device * spi )
{
2023-08-23 11:29:59 +08:00
struct npcm_pspi * priv = spi_controller_get_devdata ( spi - > controller ) ;
2018-11-12 18:42:32 +02:00
u16 regtemp ;
u16 mode_val ;
2021-05-10 16:12:13 +03:00
switch ( spi - > mode & SPI_MODE_X_MASK ) {
2018-11-12 18:42:32 +02:00
case SPI_MODE_0 :
mode_val = 0 ;
break ;
case SPI_MODE_1 :
mode_val = NPCM_PSPI_CTL1_SCIDL ;
break ;
case SPI_MODE_2 :
mode_val = NPCM_PSPI_CTL1_SCM ;
break ;
case SPI_MODE_3 :
mode_val = NPCM_PSPI_CTL1_SCIDL | NPCM_PSPI_CTL1_SCM ;
break ;
}
regtemp = ioread16 ( priv - > base + NPCM_PSPI_CTL1 ) ;
regtemp & = ~ ( NPCM_PSPI_CTL1_SCM | NPCM_PSPI_CTL1_SCIDL ) ;
iowrite16 ( regtemp | mode_val , priv - > base + NPCM_PSPI_CTL1 ) ;
}
static void npcm_pspi_set_transfer_size ( struct npcm_pspi * priv , int size )
{
u16 regtemp ;
regtemp = ioread16 ( NPCM_PSPI_CTL1 + priv - > base ) ;
switch ( size ) {
case 8 :
regtemp & = ~ NPCM_PSPI_CTL1_MOD ;
break ;
case 16 :
regtemp | = NPCM_PSPI_CTL1_MOD ;
break ;
}
iowrite16 ( regtemp , NPCM_PSPI_CTL1 + priv - > base ) ;
}
static void npcm_pspi_set_baudrate ( struct npcm_pspi * priv , unsigned int speed )
{
u32 ckdiv ;
u16 regtemp ;
/* the supported rates are numbers from 4 to 256. */
ckdiv = DIV_ROUND_CLOSEST ( clk_get_rate ( priv - > clk ) , ( 2 * speed ) ) - 1 ;
regtemp = ioread16 ( NPCM_PSPI_CTL1 + priv - > base ) ;
regtemp & = ~ NPCM_PSPI_CTL1_SCDV6_0 ;
iowrite16 ( regtemp | ( ckdiv < < 9 ) , NPCM_PSPI_CTL1 + priv - > base ) ;
}
static void npcm_pspi_setup_transfer ( struct spi_device * spi ,
struct spi_transfer * t )
{
2023-08-23 11:29:59 +08:00
struct npcm_pspi * priv = spi_controller_get_devdata ( spi - > controller ) ;
2018-11-12 18:42:32 +02:00
priv - > tx_buf = t - > tx_buf ;
priv - > rx_buf = t - > rx_buf ;
priv - > tx_bytes = t - > len ;
priv - > rx_bytes = t - > len ;
if ( ! priv - > is_save_param | | priv - > mode ! = spi - > mode ) {
npcm_pspi_set_mode ( spi ) ;
priv - > mode = spi - > mode ;
}
2020-01-15 18:22:59 +02:00
/*
* If transfer is even length , and 8 bits per word transfer ,
* then implement 16 bits - per - word transfer .
*/
if ( priv - > bits_per_word = = 8 & & ! ( t - > len & 0x1 ) )
t - > bits_per_word = 16 ;
2018-11-12 18:42:32 +02:00
if ( ! priv - > is_save_param | | priv - > bits_per_word ! = t - > bits_per_word ) {
npcm_pspi_set_transfer_size ( priv , t - > bits_per_word ) ;
priv - > bits_per_word = t - > bits_per_word ;
}
if ( ! priv - > is_save_param | | priv - > speed_hz ! = t - > speed_hz ) {
npcm_pspi_set_baudrate ( priv , t - > speed_hz ) ;
priv - > speed_hz = t - > speed_hz ;
}
if ( ! priv - > is_save_param )
priv - > is_save_param = true ;
}
static void npcm_pspi_send ( struct npcm_pspi * priv )
{
int wsize ;
2020-01-15 18:22:58 +02:00
u16 val ;
2018-11-12 18:42:32 +02:00
wsize = min ( bytes_per_word ( priv - > bits_per_word ) , priv - > tx_bytes ) ;
priv - > tx_bytes - = wsize ;
2018-12-04 15:40:35 +02:00
if ( ! priv - > tx_buf )
return ;
2018-11-12 18:42:32 +02:00
2018-12-04 15:40:35 +02:00
switch ( wsize ) {
case 1 :
2020-01-15 18:22:58 +02:00
val = * priv - > tx_buf + + ;
iowrite8 ( val , NPCM_PSPI_DATA + priv - > base ) ;
2018-12-04 15:40:35 +02:00
break ;
case 2 :
2020-01-15 18:22:58 +02:00
val = * priv - > tx_buf + + ;
val = * priv - > tx_buf + + | ( val < < 8 ) ;
iowrite16 ( val , NPCM_PSPI_DATA + priv - > base ) ;
2018-12-04 15:40:35 +02:00
break ;
default :
WARN_ON_ONCE ( 1 ) ;
return ;
2018-11-12 18:42:32 +02:00
}
}
static void npcm_pspi_recv ( struct npcm_pspi * priv )
{
int rsize ;
u16 val ;
rsize = min ( bytes_per_word ( priv - > bits_per_word ) , priv - > rx_bytes ) ;
priv - > rx_bytes - = rsize ;
2018-11-16 19:55:04 -08:00
if ( ! priv - > rx_buf )
return ;
switch ( rsize ) {
case 1 :
2020-01-15 18:22:58 +02:00
* priv - > rx_buf + + = ioread8 ( priv - > base + NPCM_PSPI_DATA ) ;
2018-11-16 19:55:04 -08:00
break ;
case 2 :
val = ioread16 ( priv - > base + NPCM_PSPI_DATA ) ;
2020-01-15 18:22:58 +02:00
* priv - > rx_buf + + = ( val > > 8 ) ;
* priv - > rx_buf + + = val & 0xff ;
2018-11-16 19:55:04 -08:00
break ;
default :
WARN_ON_ONCE ( 1 ) ;
return ;
2018-11-12 18:42:32 +02:00
}
}
2023-08-23 11:29:59 +08:00
static int npcm_pspi_transfer_one ( struct spi_controller * host ,
2018-11-12 18:42:32 +02:00
struct spi_device * spi ,
struct spi_transfer * t )
{
2023-08-23 11:29:59 +08:00
struct npcm_pspi * priv = spi_controller_get_devdata ( host ) ;
2018-11-12 18:42:32 +02:00
int status ;
npcm_pspi_setup_transfer ( spi , t ) ;
reinit_completion ( & priv - > xfer_done ) ;
npcm_pspi_enable ( priv ) ;
status = wait_for_completion_timeout ( & priv - > xfer_done ,
msecs_to_jiffies
( NPCM_PSPI_TIMEOUT_MS ) ) ;
if ( status = = 0 ) {
npcm_pspi_disable ( priv ) ;
return - ETIMEDOUT ;
}
return 0 ;
}
2023-08-23 11:29:59 +08:00
static int npcm_pspi_prepare_transfer_hardware ( struct spi_controller * host )
2018-11-12 18:42:32 +02:00
{
2023-08-23 11:29:59 +08:00
struct npcm_pspi * priv = spi_controller_get_devdata ( host ) ;
2018-11-12 18:42:32 +02:00
npcm_pspi_irq_enable ( priv , NPCM_PSPI_CTL1_EIR | NPCM_PSPI_CTL1_EIW ) ;
return 0 ;
}
2023-08-23 11:29:59 +08:00
static int npcm_pspi_unprepare_transfer_hardware ( struct spi_controller * host )
2018-11-12 18:42:32 +02:00
{
2023-08-23 11:29:59 +08:00
struct npcm_pspi * priv = spi_controller_get_devdata ( host ) ;
2018-11-12 18:42:32 +02:00
npcm_pspi_irq_disable ( priv , NPCM_PSPI_CTL1_EIR | NPCM_PSPI_CTL1_EIW ) ;
return 0 ;
}
static void npcm_pspi_reset_hw ( struct npcm_pspi * priv )
{
2020-01-15 18:23:01 +02:00
reset_control_assert ( priv - > reset ) ;
udelay ( 5 ) ;
reset_control_deassert ( priv - > reset ) ;
2018-11-12 18:42:32 +02:00
}
static irqreturn_t npcm_pspi_handler ( int irq , void * dev_id )
{
struct npcm_pspi * priv = dev_id ;
u8 stat ;
stat = ioread8 ( priv - > base + NPCM_PSPI_STAT ) ;
if ( ! priv - > tx_buf & & ! priv - > rx_buf )
return IRQ_NONE ;
if ( priv - > tx_buf ) {
if ( stat & NPCM_PSPI_STAT_RBF ) {
2019-10-09 08:37:17 +08:00
ioread8 ( NPCM_PSPI_DATA + priv - > base ) ;
2018-11-12 18:42:32 +02:00
if ( priv - > tx_bytes = = 0 ) {
npcm_pspi_disable ( priv ) ;
complete ( & priv - > xfer_done ) ;
return IRQ_HANDLED ;
}
}
if ( ( stat & NPCM_PSPI_STAT_BSY ) = = 0 )
if ( priv - > tx_bytes )
npcm_pspi_send ( priv ) ;
}
if ( priv - > rx_buf ) {
if ( stat & NPCM_PSPI_STAT_RBF ) {
if ( ! priv - > rx_bytes )
return IRQ_NONE ;
npcm_pspi_recv ( priv ) ;
if ( ! priv - > rx_bytes ) {
npcm_pspi_disable ( priv ) ;
complete ( & priv - > xfer_done ) ;
return IRQ_HANDLED ;
}
}
if ( ( ( stat & NPCM_PSPI_STAT_BSY ) = = 0 ) & & ! priv - > tx_buf )
iowrite8 ( 0x0 , NPCM_PSPI_DATA + priv - > base ) ;
}
return IRQ_HANDLED ;
}
static int npcm_pspi_probe ( struct platform_device * pdev )
{
struct npcm_pspi * priv ;
2023-08-23 11:29:59 +08:00
struct spi_controller * host ;
2018-11-12 18:42:32 +02:00
unsigned long clk_hz ;
int irq ;
int ret ;
2023-08-23 11:29:59 +08:00
host = spi_alloc_host ( & pdev - > dev , sizeof ( * priv ) ) ;
if ( ! host )
2018-11-12 18:42:32 +02:00
return - ENOMEM ;
2023-08-23 11:29:59 +08:00
platform_set_drvdata ( pdev , host ) ;
2018-11-12 18:42:32 +02:00
2023-08-23 11:29:59 +08:00
priv = spi_controller_get_devdata ( host ) ;
priv - > host = host ;
2018-11-12 18:42:32 +02:00
priv - > is_save_param = false ;
2019-09-04 21:59:01 +08:00
priv - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2018-11-12 18:42:32 +02:00
if ( IS_ERR ( priv - > base ) ) {
ret = PTR_ERR ( priv - > base ) ;
2023-08-23 11:29:59 +08:00
goto out_host_put ;
2018-11-12 18:42:32 +02:00
}
priv - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( priv - > clk ) ) {
dev_err ( & pdev - > dev , " failed to get clock \n " ) ;
ret = PTR_ERR ( priv - > clk ) ;
2023-08-23 11:29:59 +08:00
goto out_host_put ;
2018-11-12 18:42:32 +02:00
}
ret = clk_prepare_enable ( priv - > clk ) ;
if ( ret )
2023-08-23 11:29:59 +08:00
goto out_host_put ;
2018-11-12 18:42:32 +02:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
ret = irq ;
goto out_disable_clk ;
}
2020-01-15 18:23:01 +02:00
priv - > reset = devm_reset_control_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( priv - > reset ) ) {
ret = PTR_ERR ( priv - > reset ) ;
goto out_disable_clk ;
2018-11-12 18:42:32 +02:00
}
/* reset SPI-HW block */
npcm_pspi_reset_hw ( priv ) ;
ret = devm_request_irq ( & pdev - > dev , irq , npcm_pspi_handler , 0 ,
" npcm-pspi " , priv ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to request IRQ \n " ) ;
goto out_disable_clk ;
}
init_completion ( & priv - > xfer_done ) ;
clk_hz = clk_get_rate ( priv - > clk ) ;
2023-08-23 11:29:59 +08:00
host - > max_speed_hz = DIV_ROUND_UP ( clk_hz , NPCM_PSPI_MIN_CLK_DIVIDER ) ;
host - > min_speed_hz = DIV_ROUND_UP ( clk_hz , NPCM_PSPI_MAX_CLK_DIVIDER ) ;
host - > mode_bits = SPI_CPHA | SPI_CPOL ;
host - > dev . of_node = pdev - > dev . of_node ;
host - > bus_num = - 1 ;
host - > bits_per_word_mask = SPI_BPW_MASK ( 8 ) | SPI_BPW_MASK ( 16 ) ;
host - > transfer_one = npcm_pspi_transfer_one ;
host - > prepare_transfer_hardware =
2018-11-12 18:42:32 +02:00
npcm_pspi_prepare_transfer_hardware ;
2023-08-23 11:29:59 +08:00
host - > unprepare_transfer_hardware =
2018-11-12 18:42:32 +02:00
npcm_pspi_unprepare_transfer_hardware ;
2023-08-23 11:29:59 +08:00
host - > use_gpio_descriptors = true ;
2018-11-12 18:42:32 +02:00
/* set to default clock rate */
npcm_pspi_set_baudrate ( priv , NPCM_PSPI_DEFAULT_CLK ) ;
2023-08-23 11:29:59 +08:00
ret = devm_spi_register_controller ( & pdev - > dev , host ) ;
2018-11-12 18:42:32 +02:00
if ( ret )
goto out_disable_clk ;
2023-08-23 11:29:59 +08:00
pr_info ( " NPCM Peripheral SPI %d probed \n " , host - > bus_num ) ;
2018-11-12 18:42:32 +02:00
return 0 ;
out_disable_clk :
clk_disable_unprepare ( priv - > clk ) ;
2023-08-23 11:29:59 +08:00
out_host_put :
spi_controller_put ( host ) ;
2018-11-12 18:42:32 +02:00
return ret ;
}
2023-03-03 18:20:00 +01:00
static void npcm_pspi_remove ( struct platform_device * pdev )
2018-11-12 18:42:32 +02:00
{
2023-08-23 11:29:59 +08:00
struct spi_controller * host = platform_get_drvdata ( pdev ) ;
struct npcm_pspi * priv = spi_controller_get_devdata ( host ) ;
2018-11-12 18:42:32 +02:00
npcm_pspi_reset_hw ( priv ) ;
clk_disable_unprepare ( priv - > clk ) ;
}
static const struct of_device_id npcm_pspi_match [ ] = {
{ . compatible = " nuvoton,npcm750-pspi " , . data = NULL } ,
2022-07-22 14:41:36 +03:00
{ . compatible = " nuvoton,npcm845-pspi " , . data = NULL } ,
2018-11-12 18:42:32 +02:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , npcm_pspi_match ) ;
static struct platform_driver npcm_pspi_driver = {
. driver = {
. name = DRIVER_NAME ,
. of_match_table = npcm_pspi_match ,
} ,
. probe = npcm_pspi_probe ,
2023-03-03 18:20:00 +01:00
. remove_new = npcm_pspi_remove ,
2018-11-12 18:42:32 +02:00
} ;
module_platform_driver ( npcm_pspi_driver ) ;
MODULE_DESCRIPTION ( " NPCM peripheral SPI Controller driver " ) ;
MODULE_AUTHOR ( " Tomer Maimon <tomer.maimon@nuvoton.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;