Skip to content

Commit 2aad9eb

Browse files
decsnynashif
authored andcommitted
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>
1 parent c0e3ec7 commit 2aad9eb

File tree

2 files changed

+173
-38
lines changed

2 files changed

+173
-38
lines changed

drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c

Lines changed: 173 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
LOG_MODULE_REGISTER(spi_lpspi, CONFIG_SPI_LOG_LEVEL);
1616

1717
#include "spi_nxp_lpspi_priv.h"
18-
#include <fsl_lpspi.h>
18+
19+
/* simple macro for readability of the equations used in the clock configuring */
20+
#define TWO_EXP(power) BIT(power)
1921

2022
#if defined(LPSPI_RSTS) || defined(LPSPI_CLOCKS)
2123
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)
120122
return 0;
121123
}
122124

125+
static uint8_t lpspi_calc_delay_scaler(uint32_t desired_delay_ns,
126+
uint32_t prescaled_clock,
127+
uint32_t min_cycles)
128+
{
129+
uint64_t delay_cycles;
130+
131+
/* calculates the number of functional clock cycles needed to achieve delay */
132+
delay_cycles = (uint64_t)prescaled_clock * desired_delay_ns;
133+
delay_cycles = DIV_ROUND_UP(delay_cycles, NSEC_PER_SEC);
134+
135+
/* what the min_cycles parameter is about is that
136+
* PCSSCK and SCKPSC are +1 cycles of the programmed value,
137+
* while DBT is +2 cycles of the programmed value.
138+
* So this calculates the value to program to the register.
139+
*/
140+
delay_cycles -= min_cycles;
141+
142+
/* Don't overflow */
143+
delay_cycles = MIN(delay_cycles, UINT8_MAX);
144+
145+
return (uint8_t)delay_cycles;
146+
}
147+
148+
/* returns CCR mask of the bits 8-31 */
149+
static inline uint32_t lpspi_set_delays(const struct device *dev, uint32_t prescaled_clock)
150+
{
151+
const struct lpspi_config *config = dev->config;
152+
153+
return LPSPI_CCR_PCSSCK(lpspi_calc_delay_scaler(config->pcs_sck_delay,
154+
prescaled_clock, 1)) |
155+
LPSPI_CCR_SCKPCS(lpspi_calc_delay_scaler(config->sck_pcs_delay,
156+
prescaled_clock, 1)) |
157+
LPSPI_CCR_DBT(lpspi_calc_delay_scaler(config->transfer_delay,
158+
prescaled_clock, 2));
159+
}
160+
161+
/* This is the equation for the sck frequency given a div and prescaler. */
162+
static uint32_t lpspi_calc_sck_freq(uint32_t src_clk_hz, uint16_t sckdiv, uint8_t prescaler)
163+
{
164+
return (uint32_t)(src_clk_hz / (TWO_EXP(prescaler) * (sckdiv + 2)));
165+
}
166+
167+
static inline uint8_t lpspi_calc_best_div_for_prescaler(uint32_t src_clk_hz,
168+
uint8_t prescaler,
169+
uint32_t req_freq)
170+
{
171+
uint64_t prescaled_req_freq = TWO_EXP(prescaler) * req_freq;
172+
uint64_t ratio;
173+
174+
if (prescaled_req_freq == 0) {
175+
ratio = UINT8_MAX + 2;
176+
} else {
177+
ratio = DIV_ROUND_UP(src_clk_hz, prescaled_req_freq);
178+
}
179+
180+
ratio = MAX(ratio, 2);
181+
ratio -= 2;
182+
ratio = MIN(ratio, UINT8_MAX);
183+
184+
return (uint8_t)ratio;
185+
}
186+
187+
/* This function configures the clock control register (CCR) for the desired frequency
188+
* It does a binary search for the optimal CCR divider and TCR prescaler.
189+
* The prescale_value parameter is changed to the best value of the prescaler,
190+
* for use in setting the TCR outside this function.
191+
* The return value is the mask of the CCR (bits 0-7) required to set SCKDIV for best result.
192+
*/
193+
static inline uint32_t lpspi_set_sckdiv(uint32_t desired_freq,
194+
uint32_t clock_freq, uint8_t *prescale_value)
195+
{
196+
uint8_t best_prescaler = 0, best_div = 0;
197+
uint32_t best_freq = 0;
198+
199+
for (int8_t prescaler = 7U; prescaler >= 0; prescaler--) {
200+
/* if maximum freq (div = 0) won't get better than what we got with
201+
* previous prescaler, then we can fast path exit this loop.
202+
*/
203+
if (lpspi_calc_sck_freq(clock_freq, 0, prescaler) < best_freq) {
204+
break;
205+
}
206+
207+
/* the algorithm approaches the desired freq from below intentionally,
208+
* therefore the min is our previous best and the max is the desired.
209+
*/
210+
uint8_t new_div = lpspi_calc_best_div_for_prescaler(clock_freq, prescaler,
211+
desired_freq);
212+
uint32_t new_freq = lpspi_calc_sck_freq(clock_freq, new_div, prescaler);
213+
214+
if (new_freq >= best_freq && new_freq <= desired_freq) {
215+
best_div = new_div;
216+
best_freq = new_freq;
217+
best_prescaler = prescaler;
218+
}
219+
}
220+
221+
*prescale_value = best_prescaler;
222+
223+
return LPSPI_CCR_SCKDIV(best_div);
224+
}
225+
226+
/* This function configures everything except the TCR and the clock scaler */
227+
static void lpspi_basic_config(const struct device *dev, const struct spi_config *spi_cfg)
228+
{
229+
const struct lpspi_config *config = dev->config;
230+
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
231+
uint32_t pcs_control_bit = 1 << (LPSPI_CFGR1_PCSPOL_SHIFT + spi_cfg->slave);
232+
uint32_t cfgr1_val = 0;
233+
234+
if (spi_cfg->operation & SPI_CS_ACTIVE_HIGH) {
235+
cfgr1_val |= pcs_control_bit;
236+
} else {
237+
cfgr1_val &= ~pcs_control_bit;
238+
}
239+
240+
if (SPI_OP_MODE_GET(spi_cfg->operation) == SPI_OP_MODE_MASTER) {
241+
cfgr1_val |= LPSPI_CFGR1_MASTER_MASK;
242+
}
243+
244+
if (config->tristate_output) {
245+
cfgr1_val |= LPSPI_CFGR1_OUTCFG_MASK;
246+
}
247+
248+
cfgr1_val |= config->data_pin_config << LPSPI_CFGR1_PINCFG_SHIFT;
249+
250+
base->CFGR1 = cfgr1_val;
251+
252+
if (IS_ENABLED(CONFIG_DEBUG)) {
253+
/* DEBUG mode makes it so the lpspi does not keep
254+
* running while debugger has halted the chip.
255+
* This makes debugging spi transfers easier.
256+
*/
257+
base->CR |= LPSPI_CR_DBGEN_MASK;
258+
}
259+
}
260+
123261
int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cfg)
124262
{
125263
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
128266
bool already_configured = spi_context_configured(ctx, spi_cfg);
129267
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
130268
uint32_t word_size = SPI_WORD_SIZE_GET(spi_cfg->operation);
131-
lpspi_master_config_t master_config;
132-
uint32_t clock_freq;
133-
int ret;
269+
uint32_t clock_freq = 0;
270+
uint8_t prescaler = 0;
271+
int ret = 0;
134272

135273
/* fast path to avoid reconfigure */
136274
/* 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
145283
return ret;
146284
}
147285

148-
ret = clock_control_get_rate(config->clock_dev, config->clock_subsys, &clock_freq);
149-
if (ret) {
150-
return ret;
151-
}
286+
/* For the purpose of configuring the LPSPI, 8 is the minimum frame size for the hardware */
287+
word_size = MAX(word_size, 8);
152288

153289
/* specific driver implementation should set up watermarks and interrupts.
154290
* 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
168304

169305
data->ctx.config = spi_cfg;
170306

171-
LPSPI_MasterGetDefaultConfig(&master_config);
172-
173-
master_config.bitsPerFrame = word_size < 8 ? 8 : word_size; /* minimum FRAMSZ is 8 */
174-
master_config.cpol = (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPOL)
175-
? kLPSPI_ClockPolarityActiveLow
176-
: kLPSPI_ClockPolarityActiveHigh;
177-
master_config.cpha = (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPHA)
178-
? kLPSPI_ClockPhaseSecondEdge
179-
: kLPSPI_ClockPhaseFirstEdge;
180-
master_config.direction =
181-
(spi_cfg->operation & SPI_TRANSFER_LSB) ? kLPSPI_LsbFirst : kLPSPI_MsbFirst;
182-
master_config.baudRate = spi_cfg->frequency;
183-
master_config.pcsToSckDelayInNanoSec = config->pcs_sck_delay;
184-
master_config.lastSckToPcsDelayInNanoSec = config->sck_pcs_delay;
185-
master_config.betweenTransferDelayInNanoSec = config->transfer_delay;
186-
master_config.whichPcs = spi_cfg->slave + kLPSPI_Pcs0;
187-
master_config.pcsActiveHighOrLow = (spi_cfg->operation & SPI_CS_ACTIVE_HIGH)
188-
? kLPSPI_PcsActiveHigh : kLPSPI_PcsActiveLow;
189-
master_config.pinCfg = config->data_pin_config;
190-
master_config.dataOutConfig = config->tristate_output ? kLpspiDataOutTristate :
191-
kLpspiDataOutRetained;
192-
193-
LPSPI_MasterInit(base, &master_config, clock_freq);
194-
LPSPI_SetDummyData(base, 0);
307+
lpspi_basic_config(dev, spi_cfg);
195308

196-
if (IS_ENABLED(CONFIG_DEBUG)) {
197-
base->CR |= LPSPI_CR_DBGEN_MASK;
309+
ret = clock_control_get_rate(config->clock_dev, config->clock_subsys, &clock_freq);
310+
if (ret) {
311+
return ret;
198312
}
199313

314+
if (SPI_OP_MODE_GET(spi_cfg->operation) == SPI_OP_MODE_MASTER) {
315+
uint32_t ccr = 0;
316+
317+
/* sckdiv algorithm must run *before* delays are set in order to know prescaler */
318+
ccr |= lpspi_set_sckdiv(spi_cfg->frequency, clock_freq, &prescaler);
319+
ccr |= lpspi_set_delays(dev, clock_freq / TWO_EXP(prescaler));
320+
321+
/* note that not all bits of the register are readable on some platform,
322+
* that's why we update it on one write
323+
*/
324+
base->CCR = ccr;
325+
}
326+
327+
base->CR |= LPSPI_CR_MEN_MASK;
328+
329+
base->TCR = LPSPI_TCR_CPOL(!!(spi_cfg->operation & SPI_MODE_CPOL)) |
330+
LPSPI_TCR_CPHA(!!(spi_cfg->operation & SPI_MODE_CPHA)) |
331+
LPSPI_TCR_LSBF(!!(spi_cfg->operation & SPI_TRANSFER_LSB)) |
332+
LPSPI_TCR_FRAMESZ(word_size - 1) |
333+
LPSPI_TCR_PRESCALE(prescaler) | LPSPI_TCR_PCS(spi_cfg->slave);
334+
200335
return lpspi_wait_tx_fifo_empty(dev);
201336
}
202337

@@ -239,7 +374,10 @@ int spi_nxp_init_common(const struct device *dev)
239374
return err;
240375
}
241376

242-
LPSPI_Reset(base);
377+
/* Full software reset */
378+
base->CR |= LPSPI_CR_RST_MASK;
379+
base->CR |= LPSPI_CR_RRF_MASK | LPSPI_CR_RTF_MASK;
380+
base->CR = 0x00U;
243381

244382
config->irq_config_func(dev);
245383

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,9 @@ if(CONFIG_NXP_LP_FLEXCOMM)
2424
set_variable_ifdef(CONFIG_I2C_MCUX_LPI2C CONFIG_MCUX_COMPONENT_driver.lpflexcomm_lpi2c)
2525
set_variable_ifdef(CONFIG_UART_MCUX_LPUART CONFIG_MCUX_COMPONENT_driver.lpflexcomm)
2626
set_variable_ifdef(CONFIG_UART_MCUX_LPUART CONFIG_MCUX_COMPONENT_driver.lpflexcomm_lpuart)
27-
set_variable_ifdef(CONFIG_SPI_MCUX_LPSPI CONFIG_MCUX_COMPONENT_driver.lpflexcomm)
28-
set_variable_ifdef(CONFIG_SPI_MCUX_LPSPI CONFIG_MCUX_COMPONENT_driver.lpflexcomm_lpspi)
2927
else()
3028
set_variable_ifdef(CONFIG_I2C_MCUX_LPI2C CONFIG_MCUX_COMPONENT_driver.lpi2c)
3129
set_variable_ifdef(CONFIG_UART_MCUX_LPUART CONFIG_MCUX_COMPONENT_driver.lpuart)
32-
set_variable_ifdef(CONFIG_SPI_MCUX_LPSPI CONFIG_MCUX_COMPONENT_driver.lpspi)
3330
endif()
3431

3532
set_variable_ifdef(CONFIG_DMA_MCUX_LPC CONFIG_MCUX_COMPONENT_driver.lpc_dma)

0 commit comments

Comments
 (0)