Browse Source

spi_nxp_lpspi: Remove call to MasterInit

For optimization purpose, remove calls to SDK. Since we know exactly
what we want, this results in smaller code size.

Also, this code calculates the SCK parameters more efficiently than the
SDK driver did it by using a binary (instead of linear) search.

Lastly, remove call to LPSPI_Reset in the init call and replace with
native driver code, and remove inclusion of SDK header.

Signed-off-by: Declan Snyder <declan.snyder@nxp.com>
pull/89798/head
Declan Snyder 2 months ago committed by Anas Nashif
parent
commit
2aad9ebb78
  1. 208
      drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c
  2. 3
      modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake

208
drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c

@ -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);

3
modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake

@ -24,12 +24,9 @@ if(CONFIG_NXP_LP_FLEXCOMM) @@ -24,12 +24,9 @@ if(CONFIG_NXP_LP_FLEXCOMM)
set_variable_ifdef(CONFIG_I2C_MCUX_LPI2C CONFIG_MCUX_COMPONENT_driver.lpflexcomm_lpi2c)
set_variable_ifdef(CONFIG_UART_MCUX_LPUART CONFIG_MCUX_COMPONENT_driver.lpflexcomm)
set_variable_ifdef(CONFIG_UART_MCUX_LPUART CONFIG_MCUX_COMPONENT_driver.lpflexcomm_lpuart)
set_variable_ifdef(CONFIG_SPI_MCUX_LPSPI CONFIG_MCUX_COMPONENT_driver.lpflexcomm)
set_variable_ifdef(CONFIG_SPI_MCUX_LPSPI CONFIG_MCUX_COMPONENT_driver.lpflexcomm_lpspi)
else()
set_variable_ifdef(CONFIG_I2C_MCUX_LPI2C CONFIG_MCUX_COMPONENT_driver.lpi2c)
set_variable_ifdef(CONFIG_UART_MCUX_LPUART CONFIG_MCUX_COMPONENT_driver.lpuart)
set_variable_ifdef(CONFIG_SPI_MCUX_LPSPI CONFIG_MCUX_COMPONENT_driver.lpspi)
endif()
set_variable_ifdef(CONFIG_DMA_MCUX_LPC CONFIG_MCUX_COMPONENT_driver.lpc_dma)

Loading…
Cancel
Save