1.1 --- a/pkg/devices/lib/spi/src/gpio.cc Mon Nov 13 01:20:09 2023 +0100
1.2 +++ b/pkg/devices/lib/spi/src/gpio.cc Tue Nov 14 00:02:42 2023 +0100
1.3 @@ -19,7 +19,9 @@
1.4 * Boston, MA 02110-1301, USA
1.5 */
1.6
1.7 +#include <l4/devices/byteorder.h>
1.8 #include <l4/devices/spi-gpio.h>
1.9 +#include <stdlib.h> /* NOTE: required by calloc/free */
1.10 #include <time.h>
1.11
1.12
1.13 @@ -58,18 +60,19 @@
1.14
1.15 uint32_t Spi_gpio::send(uint32_t bytes, const uint8_t data[])
1.16 {
1.17 - return send_dc(bytes, data, NULL);
1.18 + return send_dc(bytes, data, NULL, 8, false);
1.19 }
1.20
1.21 -/* Send a byte sequence with accompanying data/command indicators. */
1.22 +/* Send a byte sequence with accompanying data/command indicators. Bytes are
1.23 + grouped into character units, with each unit holding a value with the given
1.24 + byte ordering. */
1.25
1.26 uint32_t Spi_gpio::send_dc(uint32_t bytes, const uint8_t data[],
1.27 - const int dc[])
1.28 + const int dc[], uint8_t char_size, bool big_endian)
1.29 {
1.30 struct timespec ts;
1.31 - uint8_t mask;
1.32 - int bit;
1.33 - uint32_t byte;
1.34 + uint32_t offset, char_unit;
1.35 + uint8_t char_unit_size = ((char_size ? char_size - 1 : 0) / 8) + 1;
1.36
1.37 if (_frequency)
1.38 {
1.39 @@ -83,27 +86,34 @@
1.40 _clock_device->set(_clock_pin, 1);
1.41 _data_device->set(_data_pin, 0);
1.42
1.43 + if ((_control_device != NULL) && (_control_pin >= 0) && (dc != NULL))
1.44 + _control_device->set(_control_pin, dc[0] ? 1 : 0);
1.45 +
1.46 /* Enter the transmission state. */
1.47
1.48 _enable_device->set(_enable_pin, 0);
1.49
1.50 /* Clock data using the clock and data outputs. */
1.51
1.52 - for (byte = 0; byte < bytes; byte++)
1.53 + for (offset = 0, char_unit = 0; offset < bytes; offset += char_unit_size, char_unit++)
1.54 {
1.55 - mask = 0x80;
1.56 + uint32_t mask, value = get_stored_value(&data[offset], char_unit_size, big_endian);
1.57 +
1.58 + /* Set the control level, if appropriate. */
1.59
1.60 - for (bit = 0; bit < 8; bit++)
1.61 + if ((_control_device != NULL) && (_control_pin >= 0) && (dc != NULL))
1.62 + _control_device->set(_control_pin, dc[char_unit] ? 1 : 0);
1.63 +
1.64 + mask = 1 << (char_size - 1);
1.65 +
1.66 + for (uint8_t bit = 0; bit < char_size; bit++)
1.67 {
1.68 /* NOTE: Data presented on falling clock level and sampled on rising clock
1.69 level. This is SPI mode 3, or 0 given that the enable level is
1.70 driven low immediately before the first bit is presented. */
1.71
1.72 _clock_device->set(_clock_pin, 0);
1.73 - _data_device->set(_data_pin, data[byte] & mask ? 1 : 0);
1.74 -
1.75 - if ((_control_device != NULL) && (_control_pin >= 0) && (dc != NULL))
1.76 - _control_device->set(_control_pin, dc[byte] ? 1 : 0);
1.77 + _data_device->set(_data_pin, value & mask ? 1 : 0);
1.78
1.79 if (_frequency)
1.80 nanosleep(&ts, NULL);
1.81 @@ -122,45 +132,44 @@
1.82 return bytes;
1.83 }
1.84
1.85 -/* Send a number of units, each holding a value in a big endian arrangement
1.86 - limited to the given character size, with any bit immediately above the
1.87 - character portion of the unit indicating the data/command level. */
1.88 +/* Send a number of units, each holding a value limited to the given character
1.89 + size, with any bit immediately above the character portion of the unit
1.90 + indicating the data/command level. */
1.91
1.92 uint32_t Spi_gpio::send_units(uint32_t bytes, const uint8_t data[],
1.93 - uint8_t unit_size, uint8_t char_size)
1.94 + uint8_t unit_size, uint8_t char_size,
1.95 + bool big_endian)
1.96 {
1.97 - /* Obtain the data/command values for each unit. */
1.98 + uint32_t char_units = bytes / unit_size;
1.99 + uint8_t char_unit_size = ((char_size ? char_size - 1 : 0) / 8) + 1;
1.100
1.101 - uint32_t chars = bytes / unit_size;
1.102 - int dc[chars];
1.103 -
1.104 + /* Obtain the data/command values for each unit. */
1.105 /* Obtain the actual character bytes to be sent. */
1.106
1.107 - int char_unit_size = ((char_size ? char_size - 1 : 0) / 8) + 1;
1.108 - uint8_t char_data[char_unit_size * chars];
1.109 + /* NOTE: Work around stack allocation limitations in L4Re. We would want to
1.110 + use a static allocation as follows:
1.111 +
1.112 + int dc[char_units];
1.113 + uint8_t char_data[char_unit_size * char_units];
1.114 + */
1.115 +
1.116 + int *dc = (int *) calloc(char_units, sizeof(int));
1.117 + uint8_t *char_data = (uint8_t *) calloc(char_units, char_unit_size);
1.118
1.119 /* Traverse the byte sequence, extracting data/command bits for each unit. */
1.120
1.121 - for (uint32_t offset = 0, unit = 0, char_offset = 0; unit < chars;
1.122 - offset += unit_size, unit++, char_offset += char_unit_size)
1.123 + for (uint32_t offset = 0, char_unit = 0, char_offset = 0; char_unit < char_units;
1.124 + offset += unit_size, char_unit++, char_offset += char_unit_size)
1.125 {
1.126 /* Obtain the unit value. */
1.127
1.128 - uint32_t value = 0;
1.129 -
1.130 - for (uint8_t byte = 0; byte < unit_size; byte++)
1.131 - value = (value << 8) | data[offset + byte];
1.132 + uint32_t value = get_stored_value(&data[offset], unit_size, big_endian);
1.133
1.134 /* The unit size must be greater than the character size for data/command
1.135 bits to be present. */
1.136
1.137 - if (unit_size * 8 <= char_size)
1.138 - dc[unit] = 0;
1.139 -
1.140 - /* Extract the data/command level. */
1.141 -
1.142 - else
1.143 - dc[unit] = value & (1 << char_size) ? 1 : 0;
1.144 + if (unit_size * 8 > char_size)
1.145 + dc[char_unit] = value & (1 << char_size) ? 1 : 0;
1.146
1.147 /* Obtain the actual character data from the input data, discarding the most
1.148 significant bytes beyond the character and masking out any remaining
1.149 @@ -168,14 +177,38 @@
1.150
1.151 value = value & ((1 << char_size) - 1);
1.152
1.153 - for (uint8_t byte = char_unit_size; byte != 0; byte--)
1.154 - {
1.155 - char_data[char_offset + byte - 1] = value & 0xff;
1.156 - value >>= 8;
1.157 - }
1.158 + set_stored_value(value, &char_data[char_offset], char_unit_size, big_endian);
1.159 }
1.160
1.161 - return send_dc(chars, char_data, dc);
1.162 + /* Discard the data/command values if they will not be used. */
1.163 +
1.164 + uint32_t result = send_dc(char_units * char_unit_size, char_data,
1.165 + (unit_size * 8 <= char_size) ? NULL : dc,
1.166 + char_size, big_endian);
1.167 +
1.168 + /* NOTE: Working around allocation limitations with dc... */
1.169 +
1.170 + free(dc);
1.171 + free(char_data);
1.172 + return result;
1.173 +}
1.174 +
1.175 +/* Perform a transfer, ignoring any DMA-specific parameters. */
1.176 +
1.177 +uint32_t Spi_gpio::transfer(l4_addr_t vaddr,
1.178 + l4re_dma_space_dma_addr_t paddr,
1.179 + uint32_t count, uint8_t unit_size,
1.180 + uint8_t char_size,
1.181 + l4_addr_t desc_vaddr,
1.182 + l4re_dma_space_dma_addr_t desc_paddr)
1.183 +{
1.184 + (void) paddr; (void) desc_vaddr; (void) desc_paddr;
1.185 +
1.186 + /* Assume little endian data is being transferred, being the native data
1.187 + representation. */
1.188 +
1.189 + return send_units(count, (const uint8_t *) vaddr, unit_size, char_size,
1.190 + false);
1.191 }
1.192
1.193
1.194 @@ -201,13 +234,34 @@
1.195 }
1.196
1.197 uint32_t spi_gpio_send_dc(void *channel, uint32_t bytes, const uint8_t data[],
1.198 - const int dc[])
1.199 + const int dc[], uint8_t char_size, int big_endian)
1.200 {
1.201 - return static_cast<Spi_gpio *>(channel)->send_dc(bytes, data, dc);
1.202 + return static_cast<Spi_gpio *>(channel)->send_dc(bytes, data, dc, char_size,
1.203 + big_endian);
1.204 }
1.205
1.206 uint32_t spi_gpio_send_units(void *channel, uint32_t bytes, const uint8_t data[],
1.207 - uint8_t unit_size, uint8_t char_size)
1.208 + uint8_t unit_size, uint8_t char_size, int big_endian)
1.209 {
1.210 - return static_cast<Spi_gpio *>(channel)->send_units(bytes, data, unit_size, char_size);
1.211 + return static_cast<Spi_gpio *>(channel)->send_units(bytes, data, unit_size,
1.212 + char_size, big_endian);
1.213 }
1.214 +
1.215 +uint32_t spi_gpio_transfer(void *channel, l4_addr_t vaddr,
1.216 + l4re_dma_space_dma_addr_t paddr,
1.217 + uint32_t count, uint8_t unit_size,
1.218 + uint8_t char_size)
1.219 +{
1.220 + return static_cast<Spi_gpio *>(channel)->transfer(vaddr, paddr, count,
1.221 + unit_size, char_size);
1.222 +}
1.223 +
1.224 +uint32_t spi_gpio_transfer_descriptor(void *channel, l4_addr_t vaddr,
1.225 + l4re_dma_space_dma_addr_t paddr,
1.226 + uint32_t count, uint8_t unit_size,
1.227 + uint8_t char_size, l4_addr_t desc_vaddr,
1.228 + l4re_dma_space_dma_addr_t desc_paddr)
1.229 +{
1.230 + return static_cast<Spi_gpio *>(channel)->transfer(vaddr, paddr, count,
1.231 + unit_size, char_size, desc_vaddr, desc_paddr);
1.232 +}