Skip to content

Commit 7c9a718

Browse files
authored
Merge pull request #9469 from kjbracey-arm/spi_muxing
SPI upgrade - per-peripheral mutex and GPIO-based SSEL
2 parents 10f2c05 + b12be6b commit 7c9a718

File tree

6 files changed

+254
-76
lines changed

6 files changed

+254
-76
lines changed

components/storage/blockdevice/COMPONENT_SPIF/SPIFBlockDevice.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#ifndef MBED_SPIF_BLOCK_DEVICE_H
1717
#define MBED_SPIF_BLOCK_DEVICE_H
1818

19+
#include "platform/SingletonPtr.h"
1920
#include "SPI.h"
2021
#include "DigitalOut.h"
2122
#include "BlockDevice.h"

drivers/SPI.cpp

Lines changed: 132 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -25,31 +25,96 @@
2525

2626
namespace mbed {
2727

28-
#if DEVICE_SPI_ASYNCH && TRANSACTION_QUEUE_SIZE_SPI
29-
CircularBuffer<Transaction<SPI>, TRANSACTION_QUEUE_SIZE_SPI> SPI::_transaction_buffer;
30-
#endif
28+
SPI::spi_peripheral_s SPI::_peripherals[SPI_PERIPHERALS_USED];
29+
int SPI::_peripherals_used;
3130

3231
SPI::SPI(PinName mosi, PinName miso, PinName sclk, PinName ssel) :
33-
_spi(),
3432
#if DEVICE_SPI_ASYNCH
3533
_irq(this),
36-
_usage(DMA_USAGE_NEVER),
37-
_deep_sleep_locked(false),
3834
#endif
39-
_bits(8),
40-
_mode(0),
41-
_hz(1000000),
42-
_write_fill(SPI_FILL_CHAR)
35+
_mosi(mosi),
36+
_miso(miso),
37+
_sclk(sclk),
38+
_hw_ssel(ssel),
39+
_sw_ssel(NC)
40+
{
41+
_do_construct();
42+
}
43+
44+
SPI::SPI(PinName mosi, PinName miso, PinName sclk, PinName ssel, use_gpio_ssel_t) :
45+
#if DEVICE_SPI_ASYNCH
46+
_irq(this),
47+
#endif
48+
_mosi(mosi),
49+
_miso(miso),
50+
_sclk(sclk),
51+
_hw_ssel(NC),
52+
_sw_ssel(ssel, 1)
53+
{
54+
_do_construct();
55+
}
56+
57+
void SPI::_do_construct()
4358
{
4459
// No lock needed in the constructor
45-
spi_init(&_spi, mosi, miso, sclk, ssel);
60+
#if DEVICE_SPI_ASYNCH
61+
_usage = DMA_USAGE_NEVER;
62+
_deep_sleep_locked = false;
63+
#endif
64+
_select_count = 0;
65+
_bits = 8;
66+
_mode = 0;
67+
_hz = 1000000;
68+
_write_fill = SPI_FILL_CHAR;
69+
70+
// Need backwards compatibility with HALs not providing API
71+
#ifdef DEVICE_SPI_COUNT
72+
SPIName name = spi_get_peripheral_name(_mosi, _miso, _sclk);
73+
#else
74+
SPIName name = GlobalSPI;
75+
#endif
76+
77+
core_util_critical_section_enter();
78+
// lookup in a critical section if we already have it else initialize it
79+
80+
_peripheral = SPI::_lookup(name);
81+
if (!_peripheral) {
82+
_peripheral = SPI::_alloc();
83+
_peripheral->name = name;
84+
}
85+
core_util_critical_section_exit();
86+
// we don't need to _acquire at this stage.
87+
// this will be done anyway before any operation.
4688
}
4789

4890
SPI::~SPI()
4991
{
50-
if (_owner == this) {
51-
_owner = NULL;
92+
lock();
93+
/* Make sure a stale pointer isn't left in peripheral's owner field */
94+
if (_peripheral->owner == this) {
95+
_peripheral->owner = NULL;
96+
}
97+
unlock();
98+
}
99+
100+
SPI::spi_peripheral_s *SPI::_lookup(SPI::SPIName name)
101+
{
102+
SPI::spi_peripheral_s *result = NULL;
103+
core_util_critical_section_enter();
104+
for (int idx = 0; idx < _peripherals_used; idx++) {
105+
if (_peripherals[idx].name == name) {
106+
result = &_peripherals[idx];
107+
break;
108+
}
52109
}
110+
core_util_critical_section_exit();
111+
return result;
112+
}
113+
114+
SPI::spi_peripheral_s *SPI::_alloc()
115+
{
116+
MBED_ASSERT(_peripherals_used < SPI_PERIPHERALS_USED);
117+
return &_peripherals[_peripherals_used++];
53118
}
54119

55120
void SPI::format(int bits, int mode)
@@ -60,8 +125,8 @@ void SPI::format(int bits, int mode)
60125
// If changing format while you are the owner then just
61126
// update format, but if owner is changed then even frequency should be
62127
// updated which is done by acquire.
63-
if (_owner == this) {
64-
spi_format(&_spi, _bits, _mode, 0);
128+
if (_peripheral->owner == this) {
129+
spi_format(&_peripheral->spi, _bits, _mode, 0);
65130
} else {
66131
_acquire();
67132
}
@@ -75,69 +140,78 @@ void SPI::frequency(int hz)
75140
// If changing format while you are the owner then just
76141
// update frequency, but if owner is changed then even frequency should be
77142
// updated which is done by acquire.
78-
if (_owner == this) {
79-
spi_frequency(&_spi, _hz);
143+
if (_peripheral->owner == this) {
144+
spi_frequency(&_peripheral->spi, _hz);
80145
} else {
81146
_acquire();
82147
}
83148
unlock();
84149
}
85150

86-
SPI *SPI::_owner = NULL;
87-
SingletonPtr<PlatformMutex> SPI::_mutex;
88-
89-
// ignore the fact there are multiple physical spis, and always update if it wasn't us last
90-
void SPI::aquire()
91-
{
92-
lock();
93-
if (_owner != this) {
94-
spi_format(&_spi, _bits, _mode, 0);
95-
spi_frequency(&_spi, _hz);
96-
_owner = this;
97-
}
98-
unlock();
99-
}
100-
101151
// Note: Private function with no locking
102152
void SPI::_acquire()
103153
{
104-
if (_owner != this) {
105-
spi_format(&_spi, _bits, _mode, 0);
106-
spi_frequency(&_spi, _hz);
107-
_owner = this;
154+
if (_peripheral->owner != this) {
155+
spi_init(&_peripheral->spi, _mosi, _miso, _sclk, _hw_ssel);
156+
spi_format(&_peripheral->spi, _bits, _mode, 0);
157+
spi_frequency(&_peripheral->spi, _hz);
158+
_peripheral->owner = this;
108159
}
109160
}
110161

111162
int SPI::write(int value)
112163
{
113-
lock();
114-
_acquire();
115-
int ret = spi_master_write(&_spi, value);
116-
unlock();
164+
select();
165+
int ret = spi_master_write(&_peripheral->spi, value);
166+
deselect();
117167
return ret;
118168
}
119169

120170
int SPI::write(const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length)
121171
{
122-
lock();
123-
_acquire();
124-
int ret = spi_master_block_write(&_spi, tx_buffer, tx_length, rx_buffer, rx_length, _write_fill);
125-
unlock();
172+
select();
173+
int ret = spi_master_block_write(&_peripheral->spi, tx_buffer, tx_length, rx_buffer, rx_length, _write_fill);
174+
deselect();
126175
return ret;
127176
}
128177

178+
void SPI::_set_ssel(int val)
179+
{
180+
if (_sw_ssel.is_connected()) {
181+
_sw_ssel = val;
182+
}
183+
}
184+
129185
void SPI::lock()
130186
{
131-
_mutex->lock();
187+
_peripheral->mutex->lock();
188+
}
189+
190+
void SPI::select()
191+
{
192+
lock();
193+
if (_select_count++ == 0) {
194+
_acquire();
195+
_set_ssel(0);
196+
}
132197
}
133198

134199
void SPI::unlock()
135200
{
136-
_mutex->unlock();
201+
_peripheral->mutex->unlock();
202+
}
203+
204+
void SPI::deselect()
205+
{
206+
if (--_select_count == 0) {
207+
_set_ssel(1);
208+
}
209+
unlock();
137210
}
138211

139212
void SPI::set_default_write_value(char data)
140213
{
214+
// this does not actually need to lock the peripheral.
141215
lock();
142216
_write_fill = data;
143217
unlock();
@@ -147,7 +221,7 @@ void SPI::set_default_write_value(char data)
147221

148222
int SPI::transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_length, unsigned char bit_width, const event_callback_t &callback, int event)
149223
{
150-
if (spi_active(&_spi)) {
224+
if (spi_active(&_peripheral->spi)) {
151225
return queue_transfer(tx_buffer, tx_length, rx_buffer, rx_length, bit_width, callback, event);
152226
}
153227
start_transfer(tx_buffer, tx_length, rx_buffer, rx_length, bit_width, callback, event);
@@ -156,7 +230,7 @@ int SPI::transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_
156230

157231
void SPI::abort_transfer()
158232
{
159-
spi_abort_asynch(&_spi);
233+
spi_abort_asynch(&_peripheral->spi);
160234
unlock_deep_sleep();
161235
#if TRANSACTION_QUEUE_SIZE_SPI
162236
dequeue_transaction();
@@ -167,7 +241,7 @@ void SPI::abort_transfer()
167241
void SPI::clear_transfer_buffer()
168242
{
169243
#if TRANSACTION_QUEUE_SIZE_SPI
170-
_transaction_buffer.reset();
244+
_peripheral->transaction_buffer->reset();
171245
#endif
172246
}
173247

@@ -179,7 +253,7 @@ void SPI::abort_all_transfers()
179253

180254
int SPI::set_dma_usage(DMAUsage usage)
181255
{
182-
if (spi_active(&_spi)) {
256+
if (spi_active(&_peripheral->spi)) {
183257
return -1;
184258
}
185259
_usage = usage;
@@ -199,12 +273,12 @@ int SPI::queue_transfer(const void *tx_buffer, int tx_length, void *rx_buffer, i
199273
t.callback = callback;
200274
t.width = bit_width;
201275
Transaction<SPI> transaction(this, t);
202-
if (_transaction_buffer.full()) {
276+
if (_peripheral->transaction_buffer->full()) {
203277
return -1; // the buffer is full
204278
} else {
205279
core_util_critical_section_enter();
206-
_transaction_buffer.push(transaction);
207-
if (!spi_active(&_spi)) {
280+
_peripheral->transaction_buffer->push(transaction);
281+
if (!spi_active(&_peripheral->spi)) {
208282
dequeue_transaction();
209283
}
210284
core_util_critical_section_exit();
@@ -219,9 +293,10 @@ void SPI::start_transfer(const void *tx_buffer, int tx_length, void *rx_buffer,
219293
{
220294
lock_deep_sleep();
221295
_acquire();
296+
_set_ssel(0);
222297
_callback = callback;
223298
_irq.callback(&SPI::irq_handler_asynch);
224-
spi_master_transfer(&_spi, tx_buffer, tx_length, rx_buffer, rx_length, bit_width, _irq.entry(), event, _usage);
299+
spi_master_transfer(&_peripheral->spi, tx_buffer, tx_length, rx_buffer, rx_length, bit_width, _irq.entry(), event, _usage);
225300
}
226301

227302
void SPI::lock_deep_sleep()
@@ -250,7 +325,7 @@ void SPI::start_transaction(transaction_t *data)
250325
void SPI::dequeue_transaction()
251326
{
252327
Transaction<SPI> t;
253-
if (_transaction_buffer.pop(t)) {
328+
if (_peripheral->transaction_buffer->pop(t)) {
254329
SPI *obj = t.get_object();
255330
transaction_t *data = t.get_transaction();
256331
obj->start_transaction(data);
@@ -261,8 +336,9 @@ void SPI::dequeue_transaction()
261336

262337
void SPI::irq_handler_asynch(void)
263338
{
264-
int event = spi_irq_handler_asynch(&_spi);
339+
int event = spi_irq_handler_asynch(&_peripheral->spi);
265340
if (_callback && (event & SPI_EVENT_ALL)) {
341+
_set_ssel(1);
266342
unlock_deep_sleep();
267343
_callback.call(event & SPI_EVENT_ALL);
268344
}

0 commit comments

Comments
 (0)