Skip to content

Commit 253d691

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 b72ef52 commit 253d691

File tree

2 files changed

+177
-36
lines changed

2 files changed

+177
-36
lines changed

drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c

Lines changed: 177 additions & 33 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;
@@ -116,6 +118,156 @@ static inline int lpspi_validate_xfer_args(const struct spi_config *spi_cfg)
116118
return 0;
117119
}
118120

121+
static uint8_t lpspi_calc_delay(uint32_t desired_delay_ns, uint32_t best_delay_ns,
122+
uint32_t prescaled_clock, uint32_t additional_scaler)
123+
{
124+
uint64_t real_delay = NSEC_PER_SEC / prescaled_clock;
125+
uint8_t best_scaler = 0, scaler = 0;
126+
uint32_t diff, min_diff = 0xFFFFFFFF;
127+
128+
while (scaler < 256 && min_diff != 0) {
129+
real_delay *= ((uint64_t)scaler + 1 + (uint64_t)additional_scaler);
130+
131+
/* Delay must not be less than desired */
132+
if (real_delay >= desired_delay_ns) {
133+
diff = (uint32_t)(real_delay - (uint64_t)desired_delay_ns);
134+
if (min_diff > diff) {
135+
/* a better match found */
136+
min_diff = diff;
137+
best_scaler = scaler;
138+
}
139+
}
140+
141+
scaler++;
142+
}
143+
144+
return best_scaler;
145+
}
146+
147+
static inline void lpspi_set_delays(const struct device *dev, uint32_t prescaled_clock)
148+
{
149+
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
150+
uint64_t minimum_delay_ns = NSEC_PER_SEC / prescaled_clock;
151+
const struct lpspi_config *config = dev->config;
152+
uint32_t ccr = base->CCR;
153+
154+
ccr &= ~(LPSPI_CCR_PCSSCK_MASK | LPSPI_CCR_SCKPCS_MASK | LPSPI_CCR_DBT_MASK);
155+
156+
if (config->pcs_sck_delay <= minimum_delay_ns) {
157+
ccr |= LPSPI_CCR_PCSSCK(lpspi_calc_delay(config->pcs_sck_delay,
158+
minimum_delay_ns * 256,
159+
prescaled_clock, 0));
160+
}
161+
162+
if (config->sck_pcs_delay <= minimum_delay_ns) {
163+
ccr |= LPSPI_CCR_SCKPCS(lpspi_calc_delay(config->sck_pcs_delay,
164+
minimum_delay_ns * 256,
165+
prescaled_clock, 0));
166+
}
167+
168+
if (config->transfer_delay <= (2 * minimum_delay_ns)) {
169+
ccr |= LPSPI_CCR_DBT(lpspi_calc_delay(config->transfer_delay,
170+
minimum_delay_ns * 257,
171+
prescaled_clock, 1));
172+
}
173+
174+
base->CCR = ccr;
175+
}
176+
177+
178+
/* This function configures the clock control register (CCR) for the desired frequency
179+
* It does a binary search for the optimal CCR divider and TCR prescaler.
180+
* The return value is the actual best frequency found, and the prescale_value parameter
181+
* is changed to the best value of the prescaler, for use in setting the TCR outside this function.
182+
*/
183+
static inline uint32_t lpspi_set_sckdiv(LPSPI_Type *base, uint32_t desired_freq,
184+
uint32_t clock_freq, uint8_t *prescale_value)
185+
{
186+
uint8_t best_prescaler = 0, best_div = 0;
187+
uint32_t best_freq = 0;
188+
189+
for (uint8_t prescaler = 0U; prescaler < 8U; prescaler++) {
190+
uint8_t high = 255, low = 0, test_div = 255;
191+
uint32_t real_freq = 0;
192+
193+
/* maximum freq won't get better than what we got with previous prescaler */
194+
if (clock_freq / (TWO_EXP(prescaler) * 2) < best_freq) {
195+
goto done;
196+
}
197+
198+
while (test_div > 0) {
199+
test_div = low + (high - low) / 2;
200+
real_freq = (clock_freq / (TWO_EXP(prescaler) * (test_div + 2)));
201+
202+
/* ensure that we do not exceed desired freq */
203+
if (real_freq > desired_freq) {
204+
low = test_div + 1;
205+
continue;
206+
} else {
207+
high = test_div - 1;
208+
}
209+
210+
/* check if we are closer to the desired */
211+
if (real_freq >= best_freq) {
212+
best_prescaler = prescaler;
213+
best_div = test_div;
214+
best_freq = real_freq;
215+
}
216+
217+
/* if our best found is a match, we're done */
218+
if (best_freq == desired_freq) {
219+
goto done;
220+
}
221+
}
222+
}
223+
224+
done:
225+
uint32_t ccr_val = base->CCR & ~LPSPI_CCR_SCKDIV_MASK;
226+
227+
ccr_val |= LPSPI_CCR_SCKDIV(best_div);
228+
229+
base->CCR = ccr_val;
230+
231+
*prescale_value = best_prescaler;
232+
233+
return best_freq;
234+
}
235+
236+
/* This function configures everything except the TCR and the clock scaler */
237+
static void lpspi_basic_config(const struct device *dev, const struct spi_config *spi_cfg)
238+
{
239+
const struct lpspi_config *config = dev->config;
240+
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
241+
uint32_t pcs_control_bit = 1 << (LPSPI_CFGR1_PCSPOL_SHIFT + spi_cfg->slave);
242+
uint32_t cfgr1_val = 0;
243+
244+
if (spi_cfg->operation & SPI_CS_ACTIVE_HIGH) {
245+
cfgr1_val |= pcs_control_bit;
246+
} else {
247+
cfgr1_val &= ~pcs_control_bit;
248+
}
249+
250+
if (SPI_OP_MODE_GET(spi_cfg->operation) == SPI_OP_MODE_MASTER) {
251+
cfgr1_val |= LPSPI_CFGR1_MASTER_MASK;
252+
}
253+
254+
if (config->tristate_output) {
255+
cfgr1_val |= LPSPI_CFGR1_OUTCFG_MASK;
256+
}
257+
258+
cfgr1_val |= config->data_pin_config << LPSPI_CFGR1_PINCFG_SHIFT;
259+
260+
base->CFGR1 = cfgr1_val;
261+
262+
if (IS_ENABLED(CONFIG_DEBUG)) {
263+
/* DEBUG mode makes it so the lpspi does not keep
264+
* running while debugger has halted the chip.
265+
* This makes debugging spi transfers easier.
266+
*/
267+
base->CR |= LPSPI_CR_DBGEN_MASK;
268+
}
269+
}
270+
119271
int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cfg)
120272
{
121273
const struct lpspi_config *config = dev->config;
@@ -124,8 +276,8 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf
124276
bool already_configured = spi_context_configured(ctx, spi_cfg);
125277
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
126278
uint32_t word_size = SPI_WORD_SIZE_GET(spi_cfg->operation);
127-
lpspi_master_config_t master_config;
128279
uint32_t clock_freq;
280+
uint8_t prescaler;
129281
int ret;
130282

131283
/* fast path to avoid reconfigure */
@@ -141,10 +293,8 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf
141293
return ret;
142294
}
143295

144-
ret = clock_control_get_rate(config->clock_dev, config->clock_subsys, &clock_freq);
145-
if (ret) {
146-
return ret;
147-
}
296+
/* For the purpose of configuring the LPSPI, 8 is the minimum frame size for the hardware */
297+
word_size = word_size < 8 ? 8 : word_size;
148298

149299
/* specific driver implementation should set up watermarks and interrupts.
150300
* we reset them here to avoid any unexpected events during configuring.
@@ -164,35 +314,26 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf
164314

165315
data->ctx.config = spi_cfg;
166316

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

192-
if (IS_ENABLED(CONFIG_DEBUG)) {
193-
base->CR |= LPSPI_CR_DBGEN_MASK;
319+
ret = clock_control_get_rate(config->clock_dev, config->clock_subsys, &clock_freq);
320+
if (ret) {
321+
return ret;
194322
}
195323

324+
lpspi_set_sckdiv(base, spi_cfg->frequency, clock_freq, &prescaler);
325+
lpspi_set_delays(dev, clock_freq / TWO_EXP(prescaler));
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(SPI_WORD_SIZE_GET(spi_cfg->operation)) |
333+
LPSPI_TCR_PRESCALE(prescaler) | LPSPI_TCR_PCS(spi_cfg->slave);
334+
335+
lpspi_wait_tx_fifo_empty(dev);
336+
196337
return 0;
197338
}
198339

@@ -235,7 +376,10 @@ int spi_nxp_init_common(const struct device *dev)
235376
return err;
236377
}
237378

238-
LPSPI_Reset(base);
379+
/* Full software reset */
380+
base->CR |= LPSPI_CR_RST_MASK;
381+
base->CR |= LPSPI_CR_RRF_MASK | LPSPI_CR_RTF_MASK;
382+
base->CR = 0x00U;
239383

240384
config->irq_config_func(dev);
241385

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)