2023-08-04 11:46:01 +01:00
// SPDX-License-Identifier: GPL-2.0
//
// CS42L43 SPI Controller Driver
//
// Copyright (C) 2022-2023 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
# include <linux/bits.h>
# include <linux/bitfield.h>
# include <linux/device.h>
# include <linux/errno.h>
# include <linux/mfd/cs42l43.h>
# include <linux/mfd/cs42l43-regs.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/regmap.h>
# include <linux/spi/spi.h>
# include <linux/units.h>
# define CS42L43_FIFO_SIZE 16
# define CS42L43_SPI_ROOT_HZ (40 * HZ_PER_MHZ)
# define CS42L43_SPI_MAX_LENGTH 65532
enum cs42l43_spi_cmd {
CS42L43_WRITE ,
CS42L43_READ
} ;
struct cs42l43_spi {
struct device * dev ;
struct regmap * regmap ;
struct spi_controller * ctlr ;
} ;
static const unsigned int cs42l43_clock_divs [ ] = {
2 , 2 , 4 , 6 , 8 , 10 , 12 , 14 , 16 , 18 , 20 , 22 , 24 , 26 , 28 , 30
} ;
static int cs42l43_spi_tx ( struct regmap * regmap , const u8 * buf , unsigned int len )
{
const u8 * end = buf + len ;
u32 val = 0 ;
int ret ;
while ( buf < end ) {
const u8 * block = min ( buf + CS42L43_FIFO_SIZE , end ) ;
while ( buf < block ) {
const u8 * word = min ( buf + sizeof ( u32 ) , block ) ;
int pad = ( buf + sizeof ( u32 ) ) - word ;
while ( buf < word ) {
val > > = BITS_PER_BYTE ;
val | = FIELD_PREP ( GENMASK ( 31 , 24 ) , * buf ) ;
buf + + ;
}
val > > = pad * BITS_PER_BYTE ;
regmap_write ( regmap , CS42L43_TX_DATA , val ) ;
}
regmap_write ( regmap , CS42L43_TRAN_CONFIG8 , CS42L43_SPI_TX_DONE_MASK ) ;
ret = regmap_read_poll_timeout ( regmap , CS42L43_TRAN_STATUS1 ,
val , ( val & CS42L43_SPI_TX_REQUEST_MASK ) ,
1000 , 5000 ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static int cs42l43_spi_rx ( struct regmap * regmap , u8 * buf , unsigned int len )
{
u8 * end = buf + len ;
u32 val ;
int ret ;
while ( buf < end ) {
u8 * block = min ( buf + CS42L43_FIFO_SIZE , end ) ;
ret = regmap_read_poll_timeout ( regmap , CS42L43_TRAN_STATUS1 ,
val , ( val & CS42L43_SPI_RX_REQUEST_MASK ) ,
1000 , 5000 ) ;
if ( ret )
return ret ;
while ( buf < block ) {
u8 * word = min ( buf + sizeof ( u32 ) , block ) ;
ret = regmap_read ( regmap , CS42L43_RX_DATA , & val ) ;
if ( ret )
return ret ;
while ( buf < word ) {
* buf = FIELD_GET ( GENMASK ( 7 , 0 ) , val ) ;
val > > = BITS_PER_BYTE ;
buf + + ;
}
}
regmap_write ( regmap , CS42L43_TRAN_CONFIG8 , CS42L43_SPI_RX_DONE_MASK ) ;
}
return 0 ;
}
static int cs42l43_transfer_one ( struct spi_controller * ctlr , struct spi_device * spi ,
struct spi_transfer * tfr )
{
struct cs42l43_spi * priv = spi_controller_get_devdata ( spi - > controller ) ;
int i , ret = - EINVAL ;
for ( i = 0 ; i < ARRAY_SIZE ( cs42l43_clock_divs ) ; i + + ) {
if ( CS42L43_SPI_ROOT_HZ / cs42l43_clock_divs [ i ] < = tfr - > speed_hz )
break ;
}
if ( i = = ARRAY_SIZE ( cs42l43_clock_divs ) )
return - EINVAL ;
regmap_write ( priv - > regmap , CS42L43_SPI_CLK_CONFIG1 , i ) ;
if ( tfr - > tx_buf ) {
regmap_write ( priv - > regmap , CS42L43_TRAN_CONFIG3 , CS42L43_WRITE ) ;
regmap_write ( priv - > regmap , CS42L43_TRAN_CONFIG4 , tfr - > len - 1 ) ;
} else if ( tfr - > rx_buf ) {
regmap_write ( priv - > regmap , CS42L43_TRAN_CONFIG3 , CS42L43_READ ) ;
regmap_write ( priv - > regmap , CS42L43_TRAN_CONFIG5 , tfr - > len - 1 ) ;
}
regmap_write ( priv - > regmap , CS42L43_TRAN_CONFIG1 , CS42L43_SPI_START_MASK ) ;
if ( tfr - > tx_buf )
ret = cs42l43_spi_tx ( priv - > regmap , ( const u8 * ) tfr - > tx_buf , tfr - > len ) ;
else if ( tfr - > rx_buf )
ret = cs42l43_spi_rx ( priv - > regmap , ( u8 * ) tfr - > rx_buf , tfr - > len ) ;
return ret ;
}
static void cs42l43_set_cs ( struct spi_device * spi , bool is_high )
{
struct cs42l43_spi * priv = spi_controller_get_devdata ( spi - > controller ) ;
2024-03-06 16:10:04 +00:00
regmap_write ( priv - > regmap , CS42L43_SPI_CONFIG2 , ! is_high ) ;
2023-08-04 11:46:01 +01:00
}
static int cs42l43_prepare_message ( struct spi_controller * ctlr , struct spi_message * msg )
{
struct cs42l43_spi * priv = spi_controller_get_devdata ( ctlr ) ;
struct spi_device * spi = msg - > spi ;
unsigned int spi_config1 = 0 ;
/* select another internal CS, which doesn't exist, so CS 0 is not used */
if ( spi_get_csgpiod ( spi , 0 ) )
spi_config1 | = 1 < < CS42L43_SPI_SS_SEL_SHIFT ;
if ( spi - > mode & SPI_CPOL )
spi_config1 | = CS42L43_SPI_CPOL_MASK ;
if ( spi - > mode & SPI_CPHA )
spi_config1 | = CS42L43_SPI_CPHA_MASK ;
if ( spi - > mode & SPI_3WIRE )
spi_config1 | = CS42L43_SPI_THREE_WIRE_MASK ;
regmap_write ( priv - > regmap , CS42L43_SPI_CONFIG1 , spi_config1 ) ;
return 0 ;
}
static int cs42l43_prepare_transfer_hardware ( struct spi_controller * ctlr )
{
struct cs42l43_spi * priv = spi_controller_get_devdata ( ctlr ) ;
int ret ;
ret = regmap_write ( priv - > regmap , CS42L43_BLOCK_EN2 , CS42L43_SPI_MSTR_EN_MASK ) ;
if ( ret )
dev_err ( priv - > dev , " Failed to enable SPI controller: %d \n " , ret ) ;
return ret ;
}
static int cs42l43_unprepare_transfer_hardware ( struct spi_controller * ctlr )
{
struct cs42l43_spi * priv = spi_controller_get_devdata ( ctlr ) ;
int ret ;
ret = regmap_write ( priv - > regmap , CS42L43_BLOCK_EN2 , 0 ) ;
if ( ret )
dev_err ( priv - > dev , " Failed to disable SPI controller: %d \n " , ret ) ;
return ret ;
}
static size_t cs42l43_spi_max_length ( struct spi_device * spi )
{
return CS42L43_SPI_MAX_LENGTH ;
}
static int cs42l43_spi_probe ( struct platform_device * pdev )
{
struct cs42l43 * cs42l43 = dev_get_drvdata ( pdev - > dev . parent ) ;
struct cs42l43_spi * priv ;
struct fwnode_handle * fwnode = dev_fwnode ( cs42l43 - > dev ) ;
int ret ;
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2023-11-28 17:30:30 +08:00
priv - > ctlr = devm_spi_alloc_host ( & pdev - > dev , sizeof ( * priv - > ctlr ) ) ;
2023-08-04 11:46:01 +01:00
if ( ! priv - > ctlr )
return - ENOMEM ;
spi_controller_set_devdata ( priv - > ctlr , priv ) ;
priv - > dev = & pdev - > dev ;
priv - > regmap = cs42l43 - > regmap ;
priv - > ctlr - > prepare_message = cs42l43_prepare_message ;
priv - > ctlr - > prepare_transfer_hardware = cs42l43_prepare_transfer_hardware ;
priv - > ctlr - > unprepare_transfer_hardware = cs42l43_unprepare_transfer_hardware ;
priv - > ctlr - > transfer_one = cs42l43_transfer_one ;
priv - > ctlr - > set_cs = cs42l43_set_cs ;
priv - > ctlr - > max_transfer_size = cs42l43_spi_max_length ;
if ( is_of_node ( fwnode ) )
fwnode = fwnode_get_named_child_node ( fwnode , " spi " ) ;
device_set_node ( & priv - > ctlr - > dev , fwnode ) ;
priv - > ctlr - > mode_bits = SPI_3WIRE | SPI_MODE_X_MASK ;
priv - > ctlr - > flags = SPI_CONTROLLER_HALF_DUPLEX ;
priv - > ctlr - > bits_per_word_mask = SPI_BPW_MASK ( 8 ) | SPI_BPW_MASK ( 16 ) |
SPI_BPW_MASK ( 32 ) ;
priv - > ctlr - > min_speed_hz = CS42L43_SPI_ROOT_HZ /
cs42l43_clock_divs [ ARRAY_SIZE ( cs42l43_clock_divs ) - 1 ] ;
priv - > ctlr - > max_speed_hz = CS42L43_SPI_ROOT_HZ / cs42l43_clock_divs [ 0 ] ;
priv - > ctlr - > use_gpio_descriptors = true ;
priv - > ctlr - > auto_runtime_pm = true ;
2024-01-24 17:41:01 +00:00
ret = devm_pm_runtime_enable ( priv - > dev ) ;
if ( ret )
return ret ;
2023-08-04 11:46:01 +01:00
pm_runtime_idle ( priv - > dev ) ;
regmap_write ( priv - > regmap , CS42L43_TRAN_CONFIG6 , CS42L43_FIFO_SIZE - 1 ) ;
regmap_write ( priv - > regmap , CS42L43_TRAN_CONFIG7 , CS42L43_FIFO_SIZE - 1 ) ;
// Disable Watchdog timer and enable stall
regmap_write ( priv - > regmap , CS42L43_SPI_CONFIG3 , 0 ) ;
regmap_write ( priv - > regmap , CS42L43_SPI_CONFIG4 , CS42L43_SPI_STALL_ENA_MASK ) ;
ret = devm_spi_register_controller ( priv - > dev , priv - > ctlr ) ;
if ( ret ) {
dev_err ( priv - > dev , " Failed to register SPI controller: %d \n " , ret ) ;
}
return ret ;
}
static const struct platform_device_id cs42l43_spi_id_table [ ] = {
{ " cs42l43-spi " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( platform , cs42l43_spi_id_table ) ;
static struct platform_driver cs42l43_spi_driver = {
. driver = {
. name = " cs42l43-spi " ,
} ,
. probe = cs42l43_spi_probe ,
. id_table = cs42l43_spi_id_table ,
} ;
module_platform_driver ( cs42l43_spi_driver ) ;
MODULE_DESCRIPTION ( " CS42L43 SPI Driver " ) ;
MODULE_AUTHOR ( " Lucas Tanure <tanureal@opensource.cirrus.com> " ) ;
MODULE_AUTHOR ( " Maciej Strozek <mstrozek@opensource.cirrus.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;