blob: a071b16e5340750a6c740c98b2343728c9210090 [file] [log] [blame]
Mikhail Siomin8dbfdd62024-03-11 23:45:36 +03001/*
2 * Copyright (c) 2024, STRIM, ALC
Anke Xiao04ae22e2024-08-12 16:11:44 +08003 * Copyright 2024 NXP
Mikhail Siomin8dbfdd62024-03-11 23:45:36 +03004 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8#define DT_DRV_COMPAT nxp_flexio_spi
9
10#include <errno.h>
11#include <zephyr/drivers/spi.h>
Luis Ubieda64a038a2024-08-16 01:09:59 -040012#include <zephyr/drivers/spi/rtio.h>
Mikhail Siomin8dbfdd62024-03-11 23:45:36 +030013#include <zephyr/drivers/clock_control.h>
14#include <fsl_flexio_spi.h>
15#include <zephyr/logging/log.h>
16#include <zephyr/drivers/pinctrl.h>
17#include <zephyr/drivers/misc/nxp_flexio/nxp_flexio.h>
18
19LOG_MODULE_REGISTER(spi_mcux_flexio_spi, CONFIG_SPI_LOG_LEVEL);
20
21#include "spi_context.h"
22
23
24struct spi_mcux_flexio_config {
25 FLEXIO_SPI_Type *flexio_spi;
26 const struct device *flexio_dev;
27 const struct pinctrl_dev_config *pincfg;
28 const struct nxp_flexio_child *child;
29};
30
31struct spi_mcux_flexio_data {
32 const struct device *dev;
33 flexio_spi_master_handle_t handle;
34 struct spi_context ctx;
35 size_t transfer_len;
36 uint8_t transfer_flags;
37};
38
39
40static void spi_mcux_transfer_next_packet(const struct device *dev)
41{
42 const struct spi_mcux_flexio_config *config = dev->config;
43 struct spi_mcux_flexio_data *data = dev->data;
44 struct spi_context *ctx = &data->ctx;
45 flexio_spi_transfer_t transfer;
46 status_t status;
47
48 if ((ctx->tx_len == 0) && (ctx->rx_len == 0)) {
49 /* nothing left to rx or tx, we're done! */
50 spi_context_cs_control(&data->ctx, false);
51 spi_context_complete(&data->ctx, dev, 0);
52 return;
53 }
54
55 transfer.flags = kFLEXIO_SPI_csContinuous | data->transfer_flags;
56
57 if (ctx->tx_len == 0) {
58 /* rx only, nothing to tx */
59 transfer.txData = NULL;
60 transfer.rxData = ctx->rx_buf;
61 transfer.dataSize = ctx->rx_len;
62 } else if (ctx->rx_len == 0) {
63 /* tx only, nothing to rx */
64 transfer.txData = (uint8_t *) ctx->tx_buf;
65 transfer.rxData = NULL;
66 transfer.dataSize = ctx->tx_len;
67 } else if (ctx->tx_len == ctx->rx_len) {
68 /* rx and tx are the same length */
69 transfer.txData = (uint8_t *) ctx->tx_buf;
70 transfer.rxData = ctx->rx_buf;
71 transfer.dataSize = ctx->tx_len;
72 } else if (ctx->tx_len > ctx->rx_len) {
73 /* Break up the tx into multiple transfers so we don't have to
74 * rx into a longer intermediate buffer. Leave chip select
75 * active between transfers.
76 */
77 transfer.txData = (uint8_t *) ctx->tx_buf;
78 transfer.rxData = ctx->rx_buf;
79 transfer.dataSize = ctx->rx_len;
80 } else {
81 /* Break up the rx into multiple transfers so we don't have to
82 * tx from a longer intermediate buffer. Leave chip select
83 * active between transfers.
84 */
85 transfer.txData = (uint8_t *) ctx->tx_buf;
86 transfer.rxData = ctx->rx_buf;
87 transfer.dataSize = ctx->tx_len;
88 }
89
90 data->transfer_len = transfer.dataSize;
91
92 status = FLEXIO_SPI_MasterTransferNonBlocking(config->flexio_spi, &data->handle,
93 &transfer);
94 if (status != kStatus_Success) {
95 LOG_ERR("Transfer could not start");
96 }
97}
98
99static int spi_mcux_flexio_isr(void *user_data)
100{
101 const struct device *dev = (const struct device *)user_data;
102 const struct spi_mcux_flexio_config *config = dev->config;
103 struct spi_mcux_flexio_data *data = dev->data;
104
Anke Xiao04ae22e2024-08-12 16:11:44 +0800105#if defined(CONFIG_SOC_SERIES_KE1XZ)
106 /* Wait until data transfer complete. */
107 WAIT_FOR((0U == (FLEXIO_SPI_GetStatusFlags(config->flexio_spi)
108 & (uint32_t)kFLEXIO_SPI_TxBufferEmptyFlag)), 100, NULL);
109#endif
Mikhail Siomin8dbfdd62024-03-11 23:45:36 +0300110 FLEXIO_SPI_MasterTransferHandleIRQ(config->flexio_spi, &data->handle);
111
112 return 0;
113}
114
115static void spi_mcux_master_transfer_callback(FLEXIO_SPI_Type *flexio_spi,
116 flexio_spi_master_handle_t *handle, status_t status, void *userData)
117{
118 struct spi_mcux_flexio_data *data = userData;
119
120 spi_context_update_tx(&data->ctx, 1, data->transfer_len);
121 spi_context_update_rx(&data->ctx, 1, data->transfer_len);
122
123 spi_mcux_transfer_next_packet(data->dev);
124}
125
126static void spi_flexio_master_init(FLEXIO_SPI_Type *base, flexio_spi_master_config_t *masterConfig,
127 uint8_t pol, uint32_t srcClock_Hz)
128{
129 assert(base != NULL);
130 assert(masterConfig != NULL);
131
132 flexio_shifter_config_t shifterConfig;
133 flexio_timer_config_t timerConfig;
134 uint32_t ctrlReg = 0;
135 uint16_t timerDiv = 0;
136 uint16_t timerCmp = 0;
137
138 /* Clear the shifterConfig & timerConfig struct. */
139 (void)memset(&shifterConfig, 0, sizeof(shifterConfig));
140 (void)memset(&timerConfig, 0, sizeof(timerConfig));
141
142 /* Configure FLEXIO SPI Master */
143 ctrlReg = base->flexioBase->CTRL;
144 ctrlReg &= ~(FLEXIO_CTRL_DOZEN_MASK | FLEXIO_CTRL_DBGE_MASK |
145 FLEXIO_CTRL_FASTACC_MASK | FLEXIO_CTRL_FLEXEN_MASK);
146 ctrlReg |= (FLEXIO_CTRL_DBGE(masterConfig->enableInDebug) |
147 FLEXIO_CTRL_FASTACC(masterConfig->enableFastAccess) |
148 FLEXIO_CTRL_FLEXEN(masterConfig->enableMaster));
149 if (!masterConfig->enableInDoze) {
150 ctrlReg |= FLEXIO_CTRL_DOZEN_MASK;
151 }
152
153 base->flexioBase->CTRL = ctrlReg;
154
155 /* Do hardware configuration. */
156 /* 1. Configure the shifter 0 for tx. */
157 shifterConfig.timerSelect = base->timerIndex[0];
158 shifterConfig.pinConfig = kFLEXIO_PinConfigOutput;
159 shifterConfig.pinSelect = base->SDOPinIndex;
160 shifterConfig.pinPolarity = kFLEXIO_PinActiveHigh;
161 shifterConfig.shifterMode = kFLEXIO_ShifterModeTransmit;
162 shifterConfig.inputSource = kFLEXIO_ShifterInputFromPin;
163 if (masterConfig->phase == kFLEXIO_SPI_ClockPhaseFirstEdge) {
164 shifterConfig.timerPolarity = kFLEXIO_ShifterTimerPolarityOnNegitive;
165 shifterConfig.shifterStop = kFLEXIO_ShifterStopBitDisable;
166 shifterConfig.shifterStart = kFLEXIO_ShifterStartBitDisabledLoadDataOnEnable;
167 } else {
168 shifterConfig.timerPolarity = kFLEXIO_ShifterTimerPolarityOnPositive;
169 shifterConfig.shifterStop = kFLEXIO_ShifterStopBitLow;
170 shifterConfig.shifterStart = kFLEXIO_ShifterStartBitDisabledLoadDataOnShift;
171 }
172
173 FLEXIO_SetShifterConfig(base->flexioBase, base->shifterIndex[0], &shifterConfig);
174
175 /* 2. Configure the shifter 1 for rx. */
176 shifterConfig.timerSelect = base->timerIndex[0];
177 shifterConfig.pinConfig = kFLEXIO_PinConfigOutputDisabled;
178 shifterConfig.pinSelect = base->SDIPinIndex;
179 shifterConfig.pinPolarity = kFLEXIO_PinActiveHigh;
180 shifterConfig.shifterMode = kFLEXIO_ShifterModeReceive;
181 shifterConfig.inputSource = kFLEXIO_ShifterInputFromPin;
182 shifterConfig.shifterStop = kFLEXIO_ShifterStopBitDisable;
183 shifterConfig.shifterStart = kFLEXIO_ShifterStartBitDisabledLoadDataOnEnable;
184 if (masterConfig->phase == kFLEXIO_SPI_ClockPhaseFirstEdge) {
185 shifterConfig.timerPolarity = kFLEXIO_ShifterTimerPolarityOnPositive;
186 } else {
187 shifterConfig.timerPolarity = kFLEXIO_ShifterTimerPolarityOnNegitive;
188 }
189
190 FLEXIO_SetShifterConfig(base->flexioBase, base->shifterIndex[1], &shifterConfig);
191
192 /*3. Configure the timer 0 for SCK. */
193 timerConfig.triggerSelect = FLEXIO_TIMER_TRIGGER_SEL_SHIFTnSTAT(base->shifterIndex[0]);
194 timerConfig.triggerPolarity = kFLEXIO_TimerTriggerPolarityActiveLow;
195 timerConfig.triggerSource = kFLEXIO_TimerTriggerSourceInternal;
196 timerConfig.pinConfig = kFLEXIO_PinConfigOutput;
197 timerConfig.pinSelect = base->SCKPinIndex;
198 timerConfig.pinPolarity = pol ? kFLEXIO_PinActiveLow : kFLEXIO_PinActiveHigh;
199 timerConfig.timerMode = kFLEXIO_TimerModeDual8BitBaudBit;
200 timerConfig.timerOutput = kFLEXIO_TimerOutputZeroNotAffectedByReset;
201 timerConfig.timerDecrement = kFLEXIO_TimerDecSrcOnFlexIOClockShiftTimerOutput;
202 timerConfig.timerReset = kFLEXIO_TimerResetNever;
203 timerConfig.timerDisable = kFLEXIO_TimerDisableOnTimerCompare;
204 timerConfig.timerEnable = kFLEXIO_TimerEnableOnTriggerHigh;
205 timerConfig.timerStop = kFLEXIO_TimerStopBitEnableOnTimerDisable;
206 timerConfig.timerStart = kFLEXIO_TimerStartBitEnabled;
207 /* Low 8-bits are used to configure baudrate. */
208 timerDiv = (uint16_t)(srcClock_Hz / masterConfig->baudRate_Bps);
209 timerDiv = timerDiv / 2U - 1U;
210 /* High 8-bits are used to configure shift clock edges(transfer width). */
211 timerCmp = ((uint16_t)masterConfig->dataMode * 2U - 1U) << 8U;
212 timerCmp |= timerDiv;
213
214 timerConfig.timerCompare = timerCmp;
215
216 FLEXIO_SetTimerConfig(base->flexioBase, base->timerIndex[0], &timerConfig);
217}
218
219static int spi_mcux_flexio_configure(const struct device *dev,
220 const struct spi_config *spi_cfg)
221{
222 const struct spi_mcux_flexio_config *config = dev->config;
223 struct spi_mcux_flexio_data *data = dev->data;
224
225 flexio_spi_master_config_t master_config;
226 uint32_t clock_freq;
227 uint32_t word_size;
228
229 if (spi_context_configured(&data->ctx, spi_cfg)) {
230 /* This configuration is already in use */
231 return 0;
232 }
233
234 if (spi_cfg->operation & SPI_HALF_DUPLEX) {
235 LOG_ERR("Half-duplex not supported");
236 return -ENOTSUP;
237 }
238
239 if (SPI_OP_MODE_GET(spi_cfg->operation) != SPI_OP_MODE_MASTER) {
240 LOG_ERR("Mode Slave not supported");
241 return -ENOTSUP;
242 }
243
244 FLEXIO_SPI_MasterGetDefaultConfig(&master_config);
245
246 word_size = SPI_WORD_SIZE_GET(spi_cfg->operation);
247 if ((word_size != 8) && (word_size != 16) && (word_size != 32)) {
248 LOG_ERR("Word size %d must be 8, 16 or 32", word_size);
249 return -EINVAL;
250 }
251 master_config.dataMode = word_size;
252
253 if (spi_cfg->operation & SPI_TRANSFER_LSB) {
254 if (word_size == 8) {
255 data->transfer_flags = kFLEXIO_SPI_8bitLsb;
256 } else if (word_size == 16) {
257 data->transfer_flags = kFLEXIO_SPI_16bitLsb;
258 } else {
259 data->transfer_flags = kFLEXIO_SPI_32bitLsb;
260 }
261 } else {
262 if (word_size == 8) {
263 data->transfer_flags = kFLEXIO_SPI_8bitMsb;
264 } else if (word_size == 16) {
265 data->transfer_flags = kFLEXIO_SPI_16bitMsb;
266 } else {
267 data->transfer_flags = kFLEXIO_SPI_32bitMsb;
268 }
269 }
270
271 if (nxp_flexio_get_rate(config->flexio_dev, &clock_freq)) {
272 return -EINVAL;
273 }
274
275 master_config.phase =
276 (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPHA)
277 ? kFLEXIO_SPI_ClockPhaseSecondEdge
278 : kFLEXIO_SPI_ClockPhaseFirstEdge;
279
280 master_config.baudRate_Bps = spi_cfg->frequency;
281 spi_flexio_master_init(config->flexio_spi, &master_config,
282 (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPOL), clock_freq);
283
284 FLEXIO_SPI_MasterTransferCreateHandle(config->flexio_spi, &data->handle,
285 spi_mcux_master_transfer_callback,
286 data);
287 /* No SetDummyData() for FlexIO_SPI */
288
289 data->ctx.config = spi_cfg;
290
291 return 0;
292}
293
294
295static int transceive(const struct device *dev,
296 const struct spi_config *spi_cfg,
297 const struct spi_buf_set *tx_bufs,
298 const struct spi_buf_set *rx_bufs,
299 bool asynchronous,
300 spi_callback_t cb,
301 void *userdata)
302{
303 const struct spi_mcux_flexio_config *config = dev->config;
304 struct spi_mcux_flexio_data *data = dev->data;
305 int ret;
306
307 spi_context_lock(&data->ctx, asynchronous, cb, userdata, spi_cfg);
308
309 nxp_flexio_lock(config->flexio_dev);
310 ret = spi_mcux_flexio_configure(dev, spi_cfg);
311 nxp_flexio_unlock(config->flexio_dev);
312 if (ret) {
313 goto out;
314 }
315
316 spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1);
317
318 spi_context_cs_control(&data->ctx, true);
319
320 nxp_flexio_lock(config->flexio_dev);
321 nxp_flexio_irq_disable(config->flexio_dev);
322
323 spi_mcux_transfer_next_packet(dev);
324
325 nxp_flexio_irq_enable(config->flexio_dev);
326 nxp_flexio_unlock(config->flexio_dev);
327
328 ret = spi_context_wait_for_completion(&data->ctx);
329out:
330 spi_context_release(&data->ctx, ret);
331
332 return ret;
333}
334
335static int spi_mcux_transceive(const struct device *dev,
336 const struct spi_config *spi_cfg,
337 const struct spi_buf_set *tx_bufs,
338 const struct spi_buf_set *rx_bufs)
339{
340 return transceive(dev, spi_cfg, tx_bufs, rx_bufs, false, NULL, NULL);
341}
342
343#ifdef CONFIG_SPI_ASYNC
344static int spi_mcux_transceive_async(const struct device *dev,
345 const struct spi_config *spi_cfg,
346 const struct spi_buf_set *tx_bufs,
347 const struct spi_buf_set *rx_bufs,
348 spi_callback_t cb,
349 void *userdata)
350{
351 return transceive(dev, spi_cfg, tx_bufs, rx_bufs, true, cb, userdata);
352}
353#endif /* CONFIG_SPI_ASYNC */
354
355static int spi_mcux_release(const struct device *dev,
356 const struct spi_config *spi_cfg)
357{
358 struct spi_mcux_flexio_data *data = dev->data;
359
360 spi_context_unlock_unconditionally(&data->ctx);
361
362 return 0;
363}
364
365static int spi_mcux_init(const struct device *dev)
366{
367 const struct spi_mcux_flexio_config *config = dev->config;
368 struct spi_mcux_flexio_data *data = dev->data;
369 int err;
370
371 err = nxp_flexio_child_attach(config->flexio_dev, config->child);
372 if (err < 0) {
373 return err;
374 }
375
376 err = spi_context_cs_configure_all(&data->ctx);
377 if (err < 0) {
378 return err;
379 }
380
381 spi_context_unlock_unconditionally(&data->ctx);
382
383 data->dev = dev;
384
385 /* TODO: DMA */
386
387 err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
388 if (err) {
389 return err;
390 }
391
392 spi_context_unlock_unconditionally(&data->ctx);
393
394 return 0;
395}
396
397static const struct spi_driver_api spi_mcux_driver_api = {
398 .transceive = spi_mcux_transceive,
399#ifdef CONFIG_SPI_ASYNC
400 .transceive_async = spi_mcux_transceive_async,
401#endif
Luis Ubieda64a038a2024-08-16 01:09:59 -0400402#ifdef CONFIG_SPI_RTIO
403 .iodev_submit = spi_rtio_iodev_default_submit,
404#endif
Mikhail Siomin8dbfdd62024-03-11 23:45:36 +0300405 .release = spi_mcux_release,
406};
407
408#define SPI_MCUX_FLEXIO_SPI_INIT(n) \
409 PINCTRL_DT_INST_DEFINE(n); \
410 \
411 static FLEXIO_SPI_Type flexio_spi_##n = { \
412 .flexioBase = (FLEXIO_Type *)DT_REG_ADDR(DT_INST_PARENT(n)), \
413 .SDOPinIndex = DT_INST_PROP(n, sdo_pin), \
414 .SDIPinIndex = DT_INST_PROP(n, sdi_pin), \
415 .SCKPinIndex = DT_INST_PROP(n, sck_pin), \
416 }; \
417 \
418 static const struct nxp_flexio_child nxp_flexio_spi_child_##n = { \
419 .isr = spi_mcux_flexio_isr, \
420 .user_data = (void *)DEVICE_DT_INST_GET(n), \
421 .res = { \
422 .shifter_index = flexio_spi_##n.shifterIndex, \
423 .shifter_count = ARRAY_SIZE(flexio_spi_##n.shifterIndex), \
424 .timer_index = flexio_spi_##n.timerIndex, \
425 .timer_count = ARRAY_SIZE(flexio_spi_##n.timerIndex) \
426 } \
427 }; \
428 \
429 static const struct spi_mcux_flexio_config spi_mcux_flexio_config_##n = { \
430 .flexio_spi = &flexio_spi_##n, \
431 .flexio_dev = DEVICE_DT_GET(DT_INST_PARENT(n)), \
432 .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
433 .child = &nxp_flexio_spi_child_##n, \
434 }; \
435 \
436 static struct spi_mcux_flexio_data spi_mcux_flexio_data_##n = { \
437 SPI_CONTEXT_INIT_LOCK(spi_mcux_flexio_data_##n, ctx), \
438 SPI_CONTEXT_INIT_SYNC(spi_mcux_flexio_data_##n, ctx), \
439 SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx) \
440 }; \
441 \
Pisit Sawangvonganandcd0a272024-07-06 18:56:05 +0700442 DEVICE_DT_INST_DEFINE(n, spi_mcux_init, NULL, \
Mikhail Siomin8dbfdd62024-03-11 23:45:36 +0300443 &spi_mcux_flexio_data_##n, \
444 &spi_mcux_flexio_config_##n, POST_KERNEL, \
445 CONFIG_SPI_INIT_PRIORITY, \
446 &spi_mcux_driver_api); \
447
448DT_INST_FOREACH_STATUS_OKAY(SPI_MCUX_FLEXIO_SPI_INIT)