@ -15,7 +15,9 @@
@@ -15,7 +15,9 @@
LOG_MODULE_REGISTER ( spi_lpspi , CONFIG_SPI_LOG_LEVEL ) ;
# include "spi_nxp_lpspi_priv.h"
# include <fsl_lpspi.h>
/* simple macro for readability of the equations used in the clock configuring */
# define TWO_EXP(power) BIT(power)
# if defined(LPSPI_RSTS) || defined(LPSPI_CLOCKS)
static LPSPI_Type * const lpspi_bases [ ] = LPSPI_BASE_PTRS ;
@ -120,6 +122,142 @@ static inline int lpspi_validate_xfer_args(const struct spi_config *spi_cfg)
@@ -120,6 +122,142 @@ static inline int lpspi_validate_xfer_args(const struct spi_config *spi_cfg)
return 0 ;
}
static uint8_t lpspi_calc_delay_scaler ( uint32_t desired_delay_ns ,
uint32_t prescaled_clock ,
uint32_t min_cycles )
{
uint64_t delay_cycles ;
/* calculates the number of functional clock cycles needed to achieve delay */
delay_cycles = ( uint64_t ) prescaled_clock * desired_delay_ns ;
delay_cycles = DIV_ROUND_UP ( delay_cycles , NSEC_PER_SEC ) ;
/* what the min_cycles parameter is about is that
* PCSSCK and SCKPSC are + 1 cycles of the programmed value ,
* while DBT is + 2 cycles of the programmed value .
* So this calculates the value to program to the register .
*/
delay_cycles - = min_cycles ;
/* Don't overflow */
delay_cycles = MIN ( delay_cycles , UINT8_MAX ) ;
return ( uint8_t ) delay_cycles ;
}
/* returns CCR mask of the bits 8-31 */
static inline uint32_t lpspi_set_delays ( const struct device * dev , uint32_t prescaled_clock )
{
const struct lpspi_config * config = dev - > config ;
return LPSPI_CCR_PCSSCK ( lpspi_calc_delay_scaler ( config - > pcs_sck_delay ,
prescaled_clock , 1 ) ) |
LPSPI_CCR_SCKPCS ( lpspi_calc_delay_scaler ( config - > sck_pcs_delay ,
prescaled_clock , 1 ) ) |
LPSPI_CCR_DBT ( lpspi_calc_delay_scaler ( config - > transfer_delay ,
prescaled_clock , 2 ) ) ;
}
/* This is the equation for the sck frequency given a div and prescaler. */
static uint32_t lpspi_calc_sck_freq ( uint32_t src_clk_hz , uint16_t sckdiv , uint8_t prescaler )
{
return ( uint32_t ) ( src_clk_hz / ( TWO_EXP ( prescaler ) * ( sckdiv + 2 ) ) ) ;
}
static inline uint8_t lpspi_calc_best_div_for_prescaler ( uint32_t src_clk_hz ,
uint8_t prescaler ,
uint32_t req_freq )
{
uint64_t prescaled_req_freq = TWO_EXP ( prescaler ) * req_freq ;
uint64_t ratio ;
if ( prescaled_req_freq = = 0 ) {
ratio = UINT8_MAX + 2 ;
} else {
ratio = DIV_ROUND_UP ( src_clk_hz , prescaled_req_freq ) ;
}
ratio = MAX ( ratio , 2 ) ;
ratio - = 2 ;
ratio = MIN ( ratio , UINT8_MAX ) ;
return ( uint8_t ) ratio ;
}
/* This function configures the clock control register (CCR) for the desired frequency
* It does a binary search for the optimal CCR divider and TCR prescaler .
* The prescale_value parameter is changed to the best value of the prescaler ,
* for use in setting the TCR outside this function .
* The return value is the mask of the CCR ( bits 0 - 7 ) required to set SCKDIV for best result .
*/
static inline uint32_t lpspi_set_sckdiv ( uint32_t desired_freq ,
uint32_t clock_freq , uint8_t * prescale_value )
{
uint8_t best_prescaler = 0 , best_div = 0 ;
uint32_t best_freq = 0 ;
for ( int8_t prescaler = 7U ; prescaler > = 0 ; prescaler - - ) {
/* if maximum freq (div = 0) won't get better than what we got with
* previous prescaler , then we can fast path exit this loop .
*/
if ( lpspi_calc_sck_freq ( clock_freq , 0 , prescaler ) < best_freq ) {
break ;
}
/* the algorithm approaches the desired freq from below intentionally,
* therefore the min is our previous best and the max is the desired .
*/
uint8_t new_div = lpspi_calc_best_div_for_prescaler ( clock_freq , prescaler ,
desired_freq ) ;
uint32_t new_freq = lpspi_calc_sck_freq ( clock_freq , new_div , prescaler ) ;
if ( new_freq > = best_freq & & new_freq < = desired_freq ) {
best_div = new_div ;
best_freq = new_freq ;
best_prescaler = prescaler ;
}
}
* prescale_value = best_prescaler ;
return LPSPI_CCR_SCKDIV ( best_div ) ;
}
/* This function configures everything except the TCR and the clock scaler */
static void lpspi_basic_config ( const struct device * dev , const struct spi_config * spi_cfg )
{
const struct lpspi_config * config = dev - > config ;
LPSPI_Type * base = ( LPSPI_Type * ) DEVICE_MMIO_NAMED_GET ( dev , reg_base ) ;
uint32_t pcs_control_bit = 1 < < ( LPSPI_CFGR1_PCSPOL_SHIFT + spi_cfg - > slave ) ;
uint32_t cfgr1_val = 0 ;
if ( spi_cfg - > operation & SPI_CS_ACTIVE_HIGH ) {
cfgr1_val | = pcs_control_bit ;
} else {
cfgr1_val & = ~ pcs_control_bit ;
}
if ( SPI_OP_MODE_GET ( spi_cfg - > operation ) = = SPI_OP_MODE_MASTER ) {
cfgr1_val | = LPSPI_CFGR1_MASTER_MASK ;
}
if ( config - > tristate_output ) {
cfgr1_val | = LPSPI_CFGR1_OUTCFG_MASK ;
}
cfgr1_val | = config - > data_pin_config < < LPSPI_CFGR1_PINCFG_SHIFT ;
base - > CFGR1 = cfgr1_val ;
if ( IS_ENABLED ( CONFIG_DEBUG ) ) {
/* DEBUG mode makes it so the lpspi does not keep
* running while debugger has halted the chip .
* This makes debugging spi transfers easier .
*/
base - > CR | = LPSPI_CR_DBGEN_MASK ;
}
}
int spi_mcux_configure ( const struct device * dev , const struct spi_config * spi_cfg )
{
const struct lpspi_config * config = dev - > config ;
@ -128,9 +266,9 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf
@@ -128,9 +266,9 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf
bool already_configured = spi_context_configured ( ctx , spi_cfg ) ;
LPSPI_Type * base = ( LPSPI_Type * ) DEVICE_MMIO_NAMED_GET ( dev , reg_base ) ;
uint32_t word_size = SPI_WORD_SIZE_GET ( spi_cfg - > operation ) ;
lpspi_master_config_t master_config ;
uint32_t clock_freq ;
int ret ;
uint32_t clock_freq = 0 ;
uint8_t prescaler = 0 ;
int ret = 0 ;
/* fast path to avoid reconfigure */
/* TODO: S32K3 errata ERR050456 requiring module reset before every transfer,
@ -145,10 +283,8 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf
@@ -145,10 +283,8 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf
return ret ;
}
ret = clock_control_get_rate ( config - > clock_dev , config - > clock_subsys , & clock_freq ) ;
if ( ret ) {
return ret ;
}
/* For the purpose of configuring the LPSPI, 8 is the minimum frame size for the hardware */
word_size = MAX ( word_size , 8 ) ;
/* specific driver implementation should set up watermarks and interrupts.
* we reset them here to avoid any unexpected events during configuring .
@ -168,35 +304,34 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf
@@ -168,35 +304,34 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf
data - > ctx . config = spi_cfg ;
LPSPI_MasterGetDefaultConfig ( & master_config ) ;
master_config . bitsPerFrame = word_size < 8 ? 8 : word_size ; /* minimum FRAMSZ is 8 */
master_config . cpol = ( SPI_MODE_GET ( spi_cfg - > operation ) & SPI_MODE_CPOL )
? kLPSPI_ClockPolarityActiveLow
: kLPSPI_ClockPolarityActiveHigh ;
master_config . cpha = ( SPI_MODE_GET ( spi_cfg - > operation ) & SPI_MODE_CPHA )
? kLPSPI_ClockPhaseSecondEdge
: kLPSPI_ClockPhaseFirstEdge ;
master_config . direction =
( spi_cfg - > operation & SPI_TRANSFER_LSB ) ? kLPSPI_LsbFirst : kLPSPI_MsbFirst ;
master_config . baudRate = spi_cfg - > frequency ;
master_config . pcsToSckDelayInNanoSec = config - > pcs_sck_delay ;
master_config . lastSckToPcsDelayInNanoSec = config - > sck_pcs_delay ;
master_config . betweenTransferDelayInNanoSec = config - > transfer_delay ;
master_config . whichPcs = spi_cfg - > slave + kLPSPI_Pcs0 ;
master_config . pcsActiveHighOrLow = ( spi_cfg - > operation & SPI_CS_ACTIVE_HIGH )
? kLPSPI_PcsActiveHigh : kLPSPI_PcsActiveLow ;
master_config . pinCfg = config - > data_pin_config ;
master_config . dataOutConfig = config - > tristate_output ? kLpspiDataOutTristate :
kLpspiDataOutRetained ;
LPSPI_MasterInit ( base , & master_config , clock_freq ) ;
LPSPI_SetDummyData ( base , 0 ) ;
lpspi_basic_config ( dev , spi_cfg ) ;
if ( IS_ENABLED ( CONFIG_DEBUG ) ) {
base - > CR | = LPSPI_CR_DBGEN_MASK ;
ret = clock_control_get_rate ( config - > clock_dev , config - > clock_subsys , & clock_freq ) ;
if ( ret ) {
return ret ;
}
if ( SPI_OP_MODE_GET ( spi_cfg - > operation ) = = SPI_OP_MODE_MASTER ) {
uint32_t ccr = 0 ;
/* sckdiv algorithm must run *before* delays are set in order to know prescaler */
ccr | = lpspi_set_sckdiv ( spi_cfg - > frequency , clock_freq , & prescaler ) ;
ccr | = lpspi_set_delays ( dev , clock_freq / TWO_EXP ( prescaler ) ) ;
/* note that not all bits of the register are readable on some platform,
* that ' s why we update it on one write
*/
base - > CCR = ccr ;
}
base - > CR | = LPSPI_CR_MEN_MASK ;
base - > TCR = LPSPI_TCR_CPOL ( ! ! ( spi_cfg - > operation & SPI_MODE_CPOL ) ) |
LPSPI_TCR_CPHA ( ! ! ( spi_cfg - > operation & SPI_MODE_CPHA ) ) |
LPSPI_TCR_LSBF ( ! ! ( spi_cfg - > operation & SPI_TRANSFER_LSB ) ) |
LPSPI_TCR_FRAMESZ ( word_size - 1 ) |
LPSPI_TCR_PRESCALE ( prescaler ) | LPSPI_TCR_PCS ( spi_cfg - > slave ) ;
return lpspi_wait_tx_fifo_empty ( dev ) ;
}
@ -239,7 +374,10 @@ int spi_nxp_init_common(const struct device *dev)
@@ -239,7 +374,10 @@ int spi_nxp_init_common(const struct device *dev)
return err ;
}
LPSPI_Reset ( base ) ;
/* Full software reset */
base - > CR | = LPSPI_CR_RST_MASK ;
base - > CR | = LPSPI_CR_RRF_MASK | LPSPI_CR_RTF_MASK ;
base - > CR = 0x00U ;
config - > irq_config_func ( dev ) ;