Skip to content

Commit 4759f46

Browse files
committed
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 0197baf commit 4759f46

File tree

2 files changed

+200
-38
lines changed

2 files changed

+200
-38
lines changed

drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c

Lines changed: 200 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,169 @@ 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+
/* what the min_cycles parameter is about is
128+
* that PCSSCK and SCKPSC are +1 cycles of the
129+
* programmed value,
130+
* while DBT is +2 cycles of the programmed value.
131+
* Minimum value of min_cycles should be 1.
132+
*/
133+
uint32_t min_cycles)
134+
{
135+
/* the scaler value is how many cycles of the prescaled clock to delay for.
136+
* We most likely want to treat the requested delay as a minimum, since it
137+
* probably has to do with setup and hold times of some spi device.
138+
*/
139+
uint16_t max_cycles = 255 + min_cycles;
140+
uint64_t delay_cycles;
141+
142+
delay_cycles = (uint64_t)prescaled_clock * desired_delay_ns;
143+
delay_cycles = DIV_ROUND_UP(delay_cycles, NSEC_PER_SEC);
144+
delay_cycles = CLAMP(delay_cycles, min_cycles, max_cycles);
145+
146+
return delay_cycles - min_cycles;
147+
}
148+
149+
/* returns CCR mask of the bits 8-31 */
150+
static inline uint32_t lpspi_set_delays(const struct device *dev, uint32_t prescaled_clock)
151+
{
152+
const struct lpspi_config *config = dev->config;
153+
154+
return LPSPI_CCR_PCSSCK(lpspi_calc_delay_scaler(config->pcs_sck_delay,
155+
prescaled_clock, 1)) |
156+
LPSPI_CCR_SCKPCS(lpspi_calc_delay_scaler(config->sck_pcs_delay,
157+
prescaled_clock, 1)) |
158+
LPSPI_CCR_DBT(lpspi_calc_delay_scaler(config->transfer_delay,
159+
prescaled_clock, 2));
160+
}
161+
162+
/* This is the equation for the sck frequency given a div and prescaler. */
163+
static uint32_t lpspi_calc_sck_freq(uint32_t src_clk_hz, uint16_t sckdiv, uint8_t prescaler)
164+
{
165+
return (uint32_t)(src_clk_hz / (TWO_EXP(prescaler) * (sckdiv + 2)));
166+
}
167+
168+
/* This function returns the best div for a prescaler, within the frequency bound
169+
* of the min and max params, given a src clk freq. It tries to get as close to
170+
* the max freq as possible without going over, with min freq as the value to beat.
171+
*/
172+
static inline uint8_t lpspi_find_best_div_for_prescaler(uint32_t src_clk_hz,
173+
uint8_t prescaler,
174+
uint32_t min_freq,
175+
uint32_t max_freq)
176+
{
177+
uint8_t high = 255, low = 0, try = 255, best = 255;
178+
uint32_t new_freq = min_freq;
179+
180+
do {
181+
try = low + (high - low) / 2;
182+
new_freq = lpspi_calc_sck_freq(src_clk_hz, try, prescaler);
183+
184+
/* ensure that we do not exceed desired freq */
185+
if (new_freq > max_freq) {
186+
low = MIN(try + 1, high);
187+
continue;
188+
} else {
189+
high = MAX(try - 1, low);
190+
}
191+
192+
/* check if we are closer to the desired, update bests if needed */
193+
if (new_freq >= min_freq) {
194+
best = try;
195+
min_freq = new_freq;
196+
}
197+
198+
/* if our best found is an exact match, we're done early */
199+
if (min_freq == max_freq) {
200+
break;
201+
}
202+
} while (low < high);
203+
204+
if (low == high) {
205+
new_freq = lpspi_calc_sck_freq(src_clk_hz, high, prescaler);
206+
if (new_freq > min_freq && new_freq <= max_freq) {
207+
best = high;
208+
}
209+
}
210+
211+
return best;
212+
}
213+
214+
/* This function configures the clock control register (CCR) for the desired frequency
215+
* It does a binary search for the optimal CCR divider and TCR prescaler.
216+
* The prescale_value parameter is changed to the best value of the prescaler,
217+
* for use in setting the TCR outside this function.
218+
* The return value is the mask of the CCR (bits 0-7) required to set SCKDIV for best result.
219+
*/
220+
static inline uint32_t lpspi_set_sckdiv(uint32_t desired_freq,
221+
uint32_t clock_freq, uint8_t *prescale_value)
222+
{
223+
uint8_t best_prescaler = 0, best_div = 0;
224+
uint32_t best_freq = 0;
225+
226+
for (int8_t prescaler = 7U; prescaler >= 0; prescaler--) {
227+
/* if maximum freq (div = 0) won't get better than what we got with
228+
* previous prescaler, then we can fast path exit this loop.
229+
*/
230+
if (lpspi_calc_sck_freq(clock_freq, 0, prescaler) < best_freq) {
231+
break;
232+
}
233+
234+
/* the algorithm approaches the desired freq from below intentionally,
235+
* therefore the min is our previous best and the max is the desired.
236+
*/
237+
uint8_t new_div = lpspi_find_best_div_for_prescaler(clock_freq, prescaler,
238+
best_freq, desired_freq);
239+
uint32_t new_freq = lpspi_calc_sck_freq(clock_freq, new_div, prescaler);
240+
241+
if (new_freq >= best_freq && new_freq <= desired_freq) {
242+
best_div = new_div;
243+
best_freq = new_freq;
244+
best_prescaler = prescaler;
245+
}
246+
}
247+
248+
*prescale_value = best_prescaler;
249+
250+
return LPSPI_CCR_SCKDIV(best_div);
251+
}
252+
253+
/* This function configures everything except the TCR and the clock scaler */
254+
static void lpspi_basic_config(const struct device *dev, const struct spi_config *spi_cfg)
255+
{
256+
const struct lpspi_config *config = dev->config;
257+
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
258+
uint32_t pcs_control_bit = 1 << (LPSPI_CFGR1_PCSPOL_SHIFT + spi_cfg->slave);
259+
uint32_t cfgr1_val = 0;
260+
261+
if (spi_cfg->operation & SPI_CS_ACTIVE_HIGH) {
262+
cfgr1_val |= pcs_control_bit;
263+
} else {
264+
cfgr1_val &= ~pcs_control_bit;
265+
}
266+
267+
if (SPI_OP_MODE_GET(spi_cfg->operation) == SPI_OP_MODE_MASTER) {
268+
cfgr1_val |= LPSPI_CFGR1_MASTER_MASK;
269+
}
270+
271+
if (config->tristate_output) {
272+
cfgr1_val |= LPSPI_CFGR1_OUTCFG_MASK;
273+
}
274+
275+
cfgr1_val |= config->data_pin_config << LPSPI_CFGR1_PINCFG_SHIFT;
276+
277+
base->CFGR1 = cfgr1_val;
278+
279+
if (IS_ENABLED(CONFIG_DEBUG)) {
280+
/* DEBUG mode makes it so the lpspi does not keep
281+
* running while debugger has halted the chip.
282+
* This makes debugging spi transfers easier.
283+
*/
284+
base->CR |= LPSPI_CR_DBGEN_MASK;
285+
}
286+
}
287+
123288
int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cfg)
124289
{
125290
const struct lpspi_config *config = dev->config;
@@ -128,9 +293,9 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf
128293
bool already_configured = spi_context_configured(ctx, spi_cfg);
129294
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
130295
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;
296+
uint32_t clock_freq = 0;
297+
uint8_t prescaler = 0;
298+
int ret = 0;
134299

135300
/* fast path to avoid reconfigure */
136301
/* TODO: S32K3 errata ERR050456 requiring module reset before every transfer,
@@ -145,10 +310,8 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf
145310
return ret;
146311
}
147312

148-
ret = clock_control_get_rate(config->clock_dev, config->clock_subsys, &clock_freq);
149-
if (ret) {
150-
return ret;
151-
}
313+
/* For the purpose of configuring the LPSPI, 8 is the minimum frame size for the hardware */
314+
word_size = word_size < 8 ? 8 : word_size;
152315

153316
/* specific driver implementation should set up watermarks and interrupts.
154317
* we reset them here to avoid any unexpected events during configuring.
@@ -168,35 +331,34 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf
168331

169332
data->ctx.config = spi_cfg;
170333

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);
334+
lpspi_basic_config(dev, spi_cfg);
195335

196-
if (IS_ENABLED(CONFIG_DEBUG)) {
197-
base->CR |= LPSPI_CR_DBGEN_MASK;
336+
ret = clock_control_get_rate(config->clock_dev, config->clock_subsys, &clock_freq);
337+
if (ret) {
338+
return ret;
339+
}
340+
341+
if (SPI_OP_MODE_GET(spi_cfg->operation) == SPI_OP_MODE_MASTER) {
342+
uint32_t ccr = 0;
343+
344+
/* sckdiv algorithm must run *before* delays are set in order to know prescaler */
345+
ccr |= lpspi_set_sckdiv(spi_cfg->frequency, clock_freq, &prescaler);
346+
ccr |= lpspi_set_delays(dev, clock_freq / TWO_EXP(prescaler));
347+
348+
/* note that not all bits of the register are readable on some platform,
349+
* that's why we update it on one write
350+
*/
351+
base->CCR = ccr;
198352
}
199353

354+
base->CR |= LPSPI_CR_MEN_MASK;
355+
356+
base->TCR = LPSPI_TCR_CPOL(!!(spi_cfg->operation & SPI_MODE_CPOL)) |
357+
LPSPI_TCR_CPHA(!!(spi_cfg->operation & SPI_MODE_CPHA)) |
358+
LPSPI_TCR_LSBF(!!(spi_cfg->operation & SPI_TRANSFER_LSB)) |
359+
LPSPI_TCR_FRAMESZ(word_size - 1) |
360+
LPSPI_TCR_PRESCALE(prescaler) | LPSPI_TCR_PCS(spi_cfg->slave);
361+
200362
return lpspi_wait_tx_fifo_empty(dev);
201363
}
202364

@@ -239,7 +401,10 @@ int spi_nxp_init_common(const struct device *dev)
239401
return err;
240402
}
241403

242-
LPSPI_Reset(base);
404+
/* Full software reset */
405+
base->CR |= LPSPI_CR_RST_MASK;
406+
base->CR |= LPSPI_CR_RRF_MASK | LPSPI_CR_RTF_MASK;
407+
base->CR = 0x00U;
243408

244409
config->irq_config_func(dev);
245410

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)