Skip to content

Commit 0cc535e

Browse files
decsnykartben
authored andcommitted
spi_nxp_lpspi: Optimize TX fill for less interrupt
Optimize the TX fill algorithm to have less interrupts by filling the TX fifo as much as possible during each interrupt handle. Before, the algorithm was just a very simple, fill the TX fifo with as much from only the current buffer as possible, then send it and wait for the next interrupt. Now the algorithm is to fill the TX fifo as much as possible, even if it means reading from multiple buffers during the interrupt. This has the advantage from master mode of having less interrupts. And it is very important for slave mode because the slave mode does not control the pacing of the transfer and so therefore should fill as much as possible whenever possible in order not to miss a deadline. Signed-off-by: Declan Snyder <[email protected]>
1 parent e454b0d commit 0cc535e

File tree

1 file changed

+73
-30
lines changed

1 file changed

+73
-30
lines changed

drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi.c

Lines changed: 73 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -96,68 +96,105 @@ static inline void lpspi_handle_rx_irq(const struct device *dev)
9696
}
9797
}
9898

99-
static inline uint32_t lpspi_next_tx_word(const struct device *dev, int offset)
99+
/* constructs the next word from the buffer */
100+
static inline uint32_t lpspi_next_tx_word(const struct device *dev, const uint8_t *buf,
101+
int offset, size_t max_bytes)
100102
{
101-
struct lpspi_data *data = dev->data;
102-
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
103-
struct spi_context *ctx = &data->ctx;
104-
const uint8_t *byte = ctx->tx_buf + offset;
105-
uint32_t num_bytes = MIN(lpspi_data->word_size_bytes, ctx->tx_len);
103+
const uint8_t *byte = buf + offset;
106104
uint32_t next_word = 0;
107105

108-
for (uint8_t i = 0; i < num_bytes; i++) {
106+
for (uint8_t i = 0; i < max_bytes; i++) {
109107
next_word |= byte[i] << (BITS_PER_BYTE * i);
110108
}
111109

112110
return next_word;
113111
}
114112

115-
static inline void lpspi_fill_tx_fifo(const struct device *dev)
113+
/* fills the TX fifo with specified amount of data from the specified buffer */
114+
static inline void lpspi_fill_tx_fifo(const struct device *dev, const uint8_t *buf,
115+
size_t buf_len, size_t fill_len)
116116
{
117117
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
118118
struct lpspi_data *data = dev->data;
119119
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
120-
size_t bytes_in_xfer = lpspi_data->fill_len * lpspi_data->word_size_bytes;
121-
size_t offset;
122-
123-
for (offset = 0; offset < bytes_in_xfer; offset += lpspi_data->word_size_bytes) {
124-
base->TDR = lpspi_next_tx_word(dev, offset);
120+
uint8_t word_size = lpspi_data->word_size_bytes;
121+
size_t buf_remaining_bytes = buf_len * word_size;
122+
size_t offset = 0;
123+
uint32_t next_word;
124+
uint32_t next_word_bytes;
125+
126+
for (int word_count = 0; word_count < fill_len; word_count++) {
127+
next_word_bytes = MIN(word_size, buf_remaining_bytes);
128+
next_word = lpspi_next_tx_word(dev, buf, offset, next_word_bytes);
129+
base->TDR = next_word;
130+
offset += word_size;
131+
buf_remaining_bytes -= word_size;
125132
}
126133

127134
LOG_DBG("Filled TX FIFO to %d words (%d bytes)", lpspi_data->fill_len, offset);
128135
}
129136

130-
static void lpspi_fill_tx_fifo_nop(const struct device *dev)
137+
/* just fills TX fifo with the specified amount of NOPS */
138+
static void lpspi_fill_tx_fifo_nop(const struct device *dev, size_t fill_len)
131139
{
132140
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
133-
struct lpspi_data *data = dev->data;
134-
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
135141

136-
for (int i = 0; i < lpspi_data->fill_len; i++) {
142+
for (int i = 0; i < fill_len; i++) {
137143
base->TDR = 0;
138144
}
139145

140-
LOG_DBG("Filled TX fifo with %d NOPs", lpspi_data->fill_len);
146+
LOG_DBG("Filled TX fifo with %d NOPs", fill_len);
141147
}
142148

149+
/* handles refilling the TX fifo from empty */
143150
static void lpspi_next_tx_fill(const struct device *dev)
144151
{
145152
const struct lpspi_config *config = dev->config;
146153
struct lpspi_data *data = dev->data;
147154
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
148155
struct spi_context *ctx = &data->ctx;
149-
size_t max_chunk;
156+
size_t fill_len;
157+
size_t actual_filled = 0;
150158

151159
/* Convert bytes to words for this xfer */
152-
max_chunk = DIV_ROUND_UP(ctx->tx_len, lpspi_data->word_size_bytes);
153-
max_chunk = MIN(max_chunk, config->tx_fifo_size);
154-
lpspi_data->fill_len = max_chunk;
155-
156-
if (spi_context_tx_buf_on(ctx)) {
157-
lpspi_fill_tx_fifo(dev);
158-
} else {
159-
lpspi_fill_tx_fifo_nop(dev);
160+
fill_len = DIV_ROUND_UP(spi_context_tx_len_left(ctx), lpspi_data->word_size_bytes);
161+
fill_len = MIN(fill_len, config->tx_fifo_size);
162+
163+
const struct spi_buf *current_buf = ctx->current_tx;
164+
const uint8_t *cur_buf_pos = ctx->tx_buf;
165+
size_t cur_buf_len_left = ctx->tx_len;
166+
size_t bufs_left = ctx->tx_count;
167+
168+
while (fill_len > 0) {
169+
size_t next_buf_fill = MIN(cur_buf_len_left, fill_len);
170+
171+
if (cur_buf_pos == NULL) {
172+
lpspi_fill_tx_fifo_nop(dev, next_buf_fill);
173+
} else {
174+
lpspi_fill_tx_fifo(dev, cur_buf_pos,
175+
current_buf->len, next_buf_fill);
176+
}
177+
178+
fill_len -= next_buf_fill;
179+
cur_buf_pos += next_buf_fill;
180+
181+
/* in the case where we just filled as much as we could from the current buffer,
182+
* this logic while wrong should have no effect, since fill_len will be 0,
183+
* so I choose not to make the code extra complex
184+
*/
185+
bufs_left--;
186+
if (bufs_left > 0) {
187+
current_buf += 1;
188+
cur_buf_len_left = current_buf->len;
189+
cur_buf_pos = current_buf->buf;
190+
} else {
191+
fill_len = 0;
192+
}
193+
194+
actual_filled += next_buf_fill;
160195
}
196+
197+
lpspi_data->fill_len = actual_filled;
161198
}
162199

163200
static inline void lpspi_handle_tx_irq(const struct device *dev)
@@ -167,7 +204,12 @@ static inline void lpspi_handle_tx_irq(const struct device *dev)
167204
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
168205
struct spi_context *ctx = &data->ctx;
169206

170-
spi_context_update_tx(ctx, lpspi_data->word_size_bytes, lpspi_data->fill_len);
207+
while (spi_context_tx_on(ctx) && lpspi_data->fill_len > 0) {
208+
size_t this_buf_words_sent = MIN(lpspi_data->fill_len, ctx->tx_len);
209+
210+
spi_context_update_tx(ctx, lpspi_data->word_size_bytes, this_buf_words_sent);
211+
lpspi_data->fill_len -= this_buf_words_sent;
212+
}
171213

172214
base->SR = LPSPI_SR_TDF_MASK;
173215

@@ -206,10 +248,11 @@ static void lpspi_isr(const struct device *dev)
206248
size_t max_fill = MIN(expected_rx_left, config->rx_fifo_size);
207249
size_t tx_current_fifo_len = tx_fifo_cur_len(base);
208250

209-
lpspi_data->fill_len = tx_current_fifo_len < ctx->rx_len ?
251+
size_t fill_len = tx_current_fifo_len < ctx->rx_len ?
210252
max_fill - tx_current_fifo_len : 0;
211253

212-
lpspi_fill_tx_fifo_nop(dev);
254+
lpspi_fill_tx_fifo_nop(dev, fill_len);
255+
lpspi_data->fill_len = fill_len;
213256
}
214257

215258
if (spi_context_rx_len_left(ctx) == 1) {

0 commit comments

Comments
 (0)