Skip to content

Commit d8e8a72

Browse files
committed
spi: sam0: fix txrx, NULL buffers, and add a stub async method.
This patches fixes a few bugs with the SAM0 driver: - txrx was trasnmitting too many bytes - adds support for NULL buffers to the fast paths - fixes a NULL dereference on the rx buffer slow path The tests under tests/driver/spi/spi_loopback now pass. Signed-off-by: Michael Hope <[email protected]>
1 parent 578e6fd commit d8e8a72

File tree

1 file changed

+81
-55
lines changed

1 file changed

+81
-55
lines changed

drivers/spi/spi_sam0.c

+81-55
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ static void spi_sam0_shift_master(SercomSpi *regs, struct spi_sam0_data *data)
128128
u8_t tx;
129129
u8_t rx;
130130

131-
if (spi_context_tx_on(&data->ctx)) {
131+
if (spi_context_tx_buf_on(&data->ctx)) {
132132
tx = *(u8_t *)(data->ctx.tx_buf);
133133
} else {
134134
tx = 0;
@@ -145,10 +145,10 @@ static void spi_sam0_shift_master(SercomSpi *regs, struct spi_sam0_data *data)
145145

146146
rx = regs->DATA.reg;
147147

148-
if (spi_context_rx_on(&data->ctx)) {
148+
if (spi_context_rx_buf_on(&data->ctx)) {
149149
*data->ctx.rx_buf = rx;
150-
spi_context_update_rx(&data->ctx, 1, 1);
151150
}
151+
spi_context_update_rx(&data->ctx, 1, 1);
152152
}
153153

154154
/* Fast path that transmits a buf */
@@ -173,103 +173,109 @@ static void spi_sam0_fast_tx(SercomSpi *regs, const struct spi_buf *tx_buf)
173173
/* Fast path that reads into a buf */
174174
static void spi_sam0_fast_rx(SercomSpi *regs, struct spi_buf *rx_buf)
175175
{
176-
u8_t *p = rx_buf->buf;
177-
size_t len = rx_buf->len;
178-
179-
while (regs->INTFLAG.bit.RXC) {
180-
(void)regs->DATA.reg;
181-
}
176+
u8_t *rx = rx_buf->buf;
177+
int len = rx_buf->len;
182178

183179
if (len <= 0) {
184180
return;
185181
}
186182

187-
/*
188-
* The code below interleaves the transmit of the next byte
189-
* with the receive of the next. The code is equivalent to:
190-
*
191-
* Transmit byte 0
192-
* Loop:
193-
* - Transmit byte n+1
194-
* - Receive byte n
195-
*/
183+
/* See the comment in spi_sam0_fast_txrx re: interleaving. */
196184

197-
/* Load the first outgoing byte */
185+
/* Ensure transmit is idle */
198186
while (!regs->INTFLAG.bit.DRE) {
199187
}
200188

189+
/* Flush the receive buffer */
190+
while (regs->INTFLAG.bit.RXC) {
191+
(void)regs->DATA.reg;
192+
}
193+
194+
/* Write the first byte */
201195
regs->DATA.reg = 0;
196+
len--;
202197

203198
while (len) {
204-
if (len != 0) {
205-
while (!regs->INTFLAG.bit.DRE) {
206-
}
207-
208-
regs->DATA.reg = 0;
209-
}
210-
211-
/*
212-
* Decrement len while waiting for the transfer to
213-
* complete.
214-
*/
199+
/* Load byte N+1 into the transmit register */
200+
regs->DATA.reg = 0;
215201
len--;
216202

203+
/* Read byte N+0 from the receive register */
217204
while (!regs->INTFLAG.bit.RXC) {
218205
}
219206

220-
*p++ = regs->DATA.reg;
207+
*rx++ = regs->DATA.reg;
221208
}
222209

223-
/* Note that all transmits are complete and the RX buf is empty */
210+
/* Read the final incoming byte */
211+
while (!regs->INTFLAG.bit.RXC) {
212+
}
213+
214+
*rx = regs->DATA.reg;
224215
}
225216

226217
/* Fast path that writes and reads bufs of the same length */
227218
static void spi_sam0_fast_txrx(SercomSpi *regs, const struct spi_buf *tx_buf,
228219
struct spi_buf *rx_buf)
229220
{
230-
const u8_t *psrc = tx_buf->buf;
231-
u8_t *p = rx_buf->buf;
221+
const u8_t *tx = tx_buf->buf;
222+
const u8_t *txend = tx_buf->buf + tx_buf->len;
223+
u8_t *rx = rx_buf->buf;
232224
size_t len = rx_buf->len;
233225

234-
while (regs->INTFLAG.bit.RXC) {
235-
(void)regs->DATA.reg;
236-
}
237-
238226
if (len <= 0) {
239227
return;
240228
}
241229

242-
/* See the comment in spi_sam0_fast_rx re: interleaving. */
230+
/*
231+
* The code below interleaves the transmit writes with the
232+
* receive reads to keep the bus fully utilised. The code is
233+
* equivalent to:
234+
*
235+
* Transmit byte 0
236+
* Loop:
237+
* - Transmit byte n+1
238+
* - Receive byte n
239+
* Receive the final byte
240+
*/
243241

244-
/* Load the first outgoing byte */
242+
/* Ensure transmit is idle */
245243
while (!regs->INTFLAG.bit.DRE) {
246244
}
247245

248-
regs->DATA.reg = *psrc++;
249-
250-
while (len) {
251-
if (len != 0) {
252-
while (!regs->INTFLAG.bit.DRE) {
253-
}
246+
/* Flush the receive buffer */
247+
while (regs->INTFLAG.bit.RXC) {
248+
(void)regs->DATA.reg;
249+
}
254250

255-
regs->DATA.reg = *psrc++;
256-
}
251+
/* Write the first byte */
252+
regs->DATA.reg = *tx++;
257253

258-
len--;
254+
while (tx != txend) {
255+
/* Load byte N+1 into the transmit register. TX is
256+
* single buffered and we have at most one byte in
257+
* flight so skip the DRE check.
258+
*/
259+
regs->DATA.reg = *tx++;
259260

261+
/* Read byte N+0 from the receive register */
260262
while (!regs->INTFLAG.bit.RXC) {
261263
}
262264

263-
*p++ = regs->DATA.reg;
265+
*rx++ = regs->DATA.reg;
266+
}
267+
268+
/* Read the final incoming byte */
269+
while (!regs->INTFLAG.bit.RXC) {
264270
}
265271

266-
/* Note that all transmits are complete and the RX buf is empty */
272+
*rx = regs->DATA.reg;
267273
}
268274

269275
/* Finish any ongoing writes and drop any remaining read data */
270276
static void spi_sam0_finish(SercomSpi *regs)
271277
{
272-
while (!regs->INTFLAG.bit.TXC) {
278+
while (!regs->INTFLAG.bit.DRE) {
273279
}
274280

275281
while (regs->INTFLAG.bit.RXC) {
@@ -287,7 +293,13 @@ static void spi_sam0_fast_transceive(struct spi_config *config,
287293
SercomSpi *regs = cfg->regs;
288294

289295
while (tx_count != 0 && rx_count != 0) {
290-
spi_sam0_fast_txrx(regs, tx_bufs, rx_bufs);
296+
if (tx_bufs->buf == NULL) {
297+
spi_sam0_fast_rx(regs, rx_bufs);
298+
} else if (rx_bufs->buf == NULL) {
299+
spi_sam0_fast_tx(regs, tx_bufs);
300+
} else {
301+
spi_sam0_fast_txrx(regs, tx_bufs, rx_bufs);
302+
}
291303
tx_bufs++;
292304
tx_count--;
293305
rx_bufs++;
@@ -350,10 +362,10 @@ static int spi_sam0_transceive(struct spi_config *config,
350362
spi_context_cs_configure(&data->ctx);
351363
spi_context_cs_control(&data->ctx, true);
352364

353-
/* This driver special case for the common send only, receive
365+
/* This driver special cases the common send only, receive
354366
* only, and transmit then receive operations. This special
355367
* casing is 4x faster than the spi_context() routines
356-
* and also allows the transmit and receive to be interleaved.
368+
* and allows the transmit and receive to be interleaved.
357369
*/
358370
if (spi_sam0_is_regular(tx_bufs, tx_count, rx_bufs, rx_count)) {
359371
spi_sam0_fast_transceive(config, tx_bufs, tx_count, rx_bufs,
@@ -374,6 +386,17 @@ static int spi_sam0_transceive(struct spi_config *config,
374386
return err;
375387
}
376388

389+
#ifdef CONFIG_POLL
390+
static int spi_sam0_transceive_async(struct spi_config *config,
391+
const struct spi_buf *tx_bufs,
392+
size_t tx_count, struct spi_buf *rx_bufs,
393+
size_t rx_count,
394+
struct k_poll_signal *async)
395+
{
396+
return -ENOTSUP;
397+
}
398+
#endif
399+
377400
static int spi_sam0_release(struct spi_config *config)
378401
{
379402
struct spi_sam0_data *data = config->dev->driver_data;
@@ -416,6 +439,9 @@ static int spi_sam0_init(struct device *dev)
416439

417440
static const struct spi_driver_api spi_sam0_driver_api = {
418441
.transceive = spi_sam0_transceive,
442+
#ifdef CONFIG_POLL
443+
.transceive_async = spi_sam0_transceive_async,
444+
#endif
419445
.release = spi_sam0_release,
420446
};
421447

0 commit comments

Comments
 (0)