Skip to content

Commit 18ace3a

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 18ace3a

File tree

2 files changed

+213
-38
lines changed

2 files changed

+213
-38
lines changed

drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c

Lines changed: 213 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,182 @@ static inline int lpspi_validate_xfer_args(const struct spi_config *spi_cfg)
120122
return 0;
121123
}
122124

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

135313
/* fast path to avoid reconfigure */
136314
/* TODO: S32K3 errata ERR050456 requiring module reset before every transfer,
@@ -145,10 +323,8 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf
145323
return ret;
146324
}
147325

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

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

169345
data->ctx.config = spi_cfg;
170346

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

196-
if (IS_ENABLED(CONFIG_DEBUG)) {
197-
base->CR |= LPSPI_CR_DBGEN_MASK;
349+
ret = clock_control_get_rate(config->clock_dev, config->clock_subsys, &clock_freq);
350+
if (ret) {
351+
return ret;
198352
}
199353

354+
if (SPI_OP_MODE_GET(spi_cfg->operation) == SPI_OP_MODE_MASTER) {
355+
uint32_t ccr = 0;
356+
357+
/* sckdiv algorithm must run *before* delays are set in order to know prescaler */
358+
ccr |= lpspi_set_sckdiv(spi_cfg->frequency, clock_freq, &prescaler);
359+
ccr |= lpspi_set_delays(dev, clock_freq / TWO_EXP(prescaler));
360+
361+
/* note that not all bits of the register are readable on some platform,
362+
* that's why we update it on one write
363+
*/
364+
base->CCR = ccr;
365+
}
366+
367+
base->CR |= LPSPI_CR_MEN_MASK;
368+
369+
base->TCR = LPSPI_TCR_CPOL(!!(spi_cfg->operation & SPI_MODE_CPOL)) |
370+
LPSPI_TCR_CPHA(!!(spi_cfg->operation & SPI_MODE_CPHA)) |
371+
LPSPI_TCR_LSBF(!!(spi_cfg->operation & SPI_TRANSFER_LSB)) |
372+
LPSPI_TCR_FRAMESZ(word_size - 1) |
373+
LPSPI_TCR_PRESCALE(prescaler) | LPSPI_TCR_PCS(spi_cfg->slave);
374+
200375
return lpspi_wait_tx_fifo_empty(dev);
201376
}
202377

@@ -239,7 +414,10 @@ int spi_nxp_init_common(const struct device *dev)
239414
return err;
240415
}
241416

242-
LPSPI_Reset(base);
417+
/* Full software reset */
418+
base->CR |= LPSPI_CR_RST_MASK;
419+
base->CR |= LPSPI_CR_RRF_MASK | LPSPI_CR_RTF_MASK;
420+
base->CR = 0x00U;
243421

244422
config->irq_config_func(dev);
245423

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)