Skip to content

Commit 5e059b7

Browse files
committed
SPI upgrade - per-peripheral mutex and GPIO-based SSEL
This commit takes some of the work done on the SPI class from #8445, and refines it, to provide the per-peripheral mutex functionality. This also implements GPIO-based SSEL, which exposes a new select()/deselect() API for users to group transfers, and should work on every platform (unlike the HAL-based SSEL). This requires users to use a new constructor to avoid backwards compatibility issues. To activate the per-peripheral mutex, the HAL must define SPI_COUNT and provide spi_get_peripheral_name(). (In #8445 this is a reworked spi_get_module, but the name is changed here to avoid a collision with existing HALs - this commit is designed to work without wider HAL changes). Fixes: #9149
1 parent 857cd9f commit 5e059b7

File tree

4 files changed

+245
-76
lines changed

4 files changed

+245
-76
lines changed

drivers/SPI.cpp

Lines changed: 133 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -23,33 +23,99 @@
2323

2424
#if DEVICE_SPI
2525

26+
/* Backwards compatibility with HALs that don't provide this */
27+
MBED_WEAK SPIName spi_get_peripheral_name(PinName /*mosi*/, PinName /*miso*/, PinName /*mclk*/)
28+
{
29+
return (SPIName)1;
30+
}
31+
2632
namespace mbed {
2733

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

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

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

55121
void SPI::format(int bits, int mode)
@@ -60,8 +126,8 @@ void SPI::format(int bits, int mode)
60126
// If changing format while you are the owner then just
61127
// update format, but if owner is changed then even frequency should be
62128
// updated which is done by acquire.
63-
if (_owner == this) {
64-
spi_format(&_spi, _bits, _mode, 0);
129+
if (_peripheral->owner == this) {
130+
spi_format(&_peripheral->spi, _bits, _mode, 0);
65131
} else {
66132
_acquire();
67133
}
@@ -75,69 +141,78 @@ void SPI::frequency(int hz)
75141
// If changing format while you are the owner then just
76142
// update frequency, but if owner is changed then even frequency should be
77143
// updated which is done by acquire.
78-
if (_owner == this) {
79-
spi_frequency(&_spi, _hz);
144+
if (_peripheral->owner == this) {
145+
spi_frequency(&_peripheral->spi, _hz);
80146
} else {
81147
_acquire();
82148
}
83149
unlock();
84150
}
85151

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-
101152
// Note: Private function with no locking
102153
void SPI::_acquire()
103154
{
104-
if (_owner != this) {
105-
spi_format(&_spi, _bits, _mode, 0);
106-
spi_frequency(&_spi, _hz);
107-
_owner = this;
155+
if (_peripheral->owner != this) {
156+
spi_init(&_peripheral->spi, _mosi, _miso, _sclk, _hw_ssel);
157+
spi_format(&_peripheral->spi, _bits, _mode, 0);
158+
spi_frequency(&_peripheral->spi, _hz);
159+
_peripheral->owner = this;
108160
}
109161
}
110162

111163
int SPI::write(int value)
112164
{
113-
lock();
114-
_acquire();
115-
int ret = spi_master_write(&_spi, value);
116-
unlock();
165+
select();
166+
int ret = spi_master_write(&_peripheral->spi, value);
167+
deselect();
117168
return ret;
118169
}
119170

120171
int SPI::write(const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length)
121172
{
122-
lock();
123-
_acquire();
124-
int ret = spi_master_block_write(&_spi, tx_buffer, tx_length, rx_buffer, rx_length, _write_fill);
125-
unlock();
173+
select();
174+
int ret = spi_master_block_write(&_peripheral->spi, tx_buffer, tx_length, rx_buffer, rx_length, _write_fill);
175+
deselect();
126176
return ret;
127177
}
128178

179+
void SPI::_set_ssel(int val)
180+
{
181+
if (_sw_ssel.is_connected()) {
182+
_sw_ssel = val;
183+
}
184+
}
185+
129186
void SPI::lock()
130187
{
131-
_mutex->lock();
188+
_peripheral->mutex->lock();
189+
}
190+
191+
void SPI::select()
192+
{
193+
lock();
194+
if (_select_count++ == 0) {
195+
_acquire();
196+
_set_ssel(0);
197+
}
132198
}
133199

134200
void SPI::unlock()
135201
{
136-
_mutex->unlock();
202+
_peripheral->mutex->unlock();
203+
}
204+
205+
void SPI::deselect()
206+
{
207+
if (--_select_count == 0) {
208+
_set_ssel(1);
209+
}
210+
unlock();
137211
}
138212

139213
void SPI::set_default_write_value(char data)
140214
{
215+
// this does not actually need to lock the peripheral.
141216
lock();
142217
_write_fill = data;
143218
unlock();
@@ -147,7 +222,7 @@ void SPI::set_default_write_value(char data)
147222

148223
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)
149224
{
150-
if (spi_active(&_spi)) {
225+
if (spi_active(&_peripheral->spi)) {
151226
return queue_transfer(tx_buffer, tx_length, rx_buffer, rx_length, bit_width, callback, event);
152227
}
153228
start_transfer(tx_buffer, tx_length, rx_buffer, rx_length, bit_width, callback, event);
@@ -156,7 +231,7 @@ int SPI::transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_
156231

157232
void SPI::abort_transfer()
158233
{
159-
spi_abort_asynch(&_spi);
234+
spi_abort_asynch(&_peripheral->spi);
160235
unlock_deep_sleep();
161236
#if TRANSACTION_QUEUE_SIZE_SPI
162237
dequeue_transaction();
@@ -167,7 +242,7 @@ void SPI::abort_transfer()
167242
void SPI::clear_transfer_buffer()
168243
{
169244
#if TRANSACTION_QUEUE_SIZE_SPI
170-
_transaction_buffer.reset();
245+
_peripheral->transaction_buffer->reset();
171246
#endif
172247
}
173248

@@ -179,7 +254,7 @@ void SPI::abort_all_transfers()
179254

180255
int SPI::set_dma_usage(DMAUsage usage)
181256
{
182-
if (spi_active(&_spi)) {
257+
if (spi_active(&_peripheral->spi)) {
183258
return -1;
184259
}
185260
_usage = usage;
@@ -199,12 +274,12 @@ int SPI::queue_transfer(const void *tx_buffer, int tx_length, void *rx_buffer, i
199274
t.callback = callback;
200275
t.width = bit_width;
201276
Transaction<SPI> transaction(this, t);
202-
if (_transaction_buffer.full()) {
277+
if (_peripheral->transaction_buffer->full()) {
203278
return -1; // the buffer is full
204279
} else {
205280
core_util_critical_section_enter();
206-
_transaction_buffer.push(transaction);
207-
if (!spi_active(&_spi)) {
281+
_peripheral->transaction_buffer->push(transaction);
282+
if (!spi_active(&_peripheral->spi)) {
208283
dequeue_transaction();
209284
}
210285
core_util_critical_section_exit();
@@ -219,9 +294,10 @@ void SPI::start_transfer(const void *tx_buffer, int tx_length, void *rx_buffer,
219294
{
220295
lock_deep_sleep();
221296
_acquire();
297+
_set_ssel(0);
222298
_callback = callback;
223299
_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);
300+
spi_master_transfer(&_peripheral->spi, tx_buffer, tx_length, rx_buffer, rx_length, bit_width, _irq.entry(), event, _usage);
225301
}
226302

227303
void SPI::lock_deep_sleep()
@@ -250,7 +326,7 @@ void SPI::start_transaction(transaction_t *data)
250326
void SPI::dequeue_transaction()
251327
{
252328
Transaction<SPI> t;
253-
if (_transaction_buffer.pop(t)) {
329+
if (_peripheral->transaction_buffer->pop(t)) {
254330
SPI *obj = t.get_object();
255331
transaction_t *data = t.get_transaction();
256332
obj->start_transaction(data);
@@ -261,8 +337,9 @@ void SPI::dequeue_transaction()
261337

262338
void SPI::irq_handler_asynch(void)
263339
{
264-
int event = spi_irq_handler_asynch(&_spi);
340+
int event = spi_irq_handler_asynch(&_peripheral->spi);
265341
if (_callback && (event & SPI_EVENT_ALL)) {
342+
_set_ssel(1);
266343
unlock_deep_sleep();
267344
_callback.call(event & SPI_EVENT_ALL);
268345
}

0 commit comments

Comments
 (0)