1 /* 2 * Perform SPI communication using GPIO operations. 3 * 4 * Copyright (C) 2018, 2020, 2023 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/devices/spi-gpio.h> 23 #include <time.h> 24 25 26 27 Spi_gpio::Spi_gpio(Hw::Gpio_chip *clock_device, int clock_pin, 28 Hw::Gpio_chip *data_device, int data_pin, 29 Hw::Gpio_chip *enable_device, int enable_pin, 30 Hw::Gpio_chip *control_device, int control_pin) 31 : _clock_device(clock_device), 32 _clock_pin(clock_pin), 33 _data_device(data_device), 34 _data_pin(data_pin), 35 _enable_device(enable_device), 36 _enable_pin(enable_pin), 37 _control_device(control_device), 38 _control_pin(control_pin) 39 { 40 _clock_device->setup(_clock_pin, Hw::Gpio_chip::Output, 1); 41 _data_device->setup(_data_pin, Hw::Gpio_chip::Output, 0); 42 _enable_device->setup(_enable_pin, Hw::Gpio_chip::Output, 1); 43 } 44 45 Spi_gpio::Spi_gpio(uint64_t frequency, 46 Hw::Gpio_chip *clock_device, int clock_pin, 47 Hw::Gpio_chip *data_device, int data_pin, 48 Hw::Gpio_chip *enable_device, int enable_pin, 49 Hw::Gpio_chip *control_device, int control_pin) 50 : Spi_gpio(clock_device, clock_pin, data_device, data_pin, enable_device, 51 enable_pin, control_device, control_pin) 52 { 53 _frequency = frequency; 54 } 55 56 /* Send a byte sequence. */ 57 58 uint32_t Spi_gpio::send(uint32_t bytes, const uint8_t data[]) 59 { 60 return send_dc(bytes, data, NULL); 61 } 62 63 /* Send a byte sequence with accompanying data/command indicators. */ 64 65 uint32_t Spi_gpio::send_dc(uint32_t bytes, const uint8_t data[], 66 const int dc[]) 67 { 68 struct timespec ts; 69 uint8_t mask; 70 int bit; 71 uint32_t byte; 72 73 if (_frequency) 74 { 75 ts.tv_sec = 0; 76 ts.tv_nsec = 1000000000 / _frequency; 77 } 78 79 /* Initialise pin levels. */ 80 81 _enable_device->set(_enable_pin, 1); 82 _clock_device->set(_clock_pin, 1); 83 _data_device->set(_data_pin, 0); 84 85 /* Enter the transmission state. */ 86 87 _enable_device->set(_enable_pin, 0); 88 89 /* Clock data using the clock and data outputs. */ 90 91 for (byte = 0; byte < bytes; byte++) 92 { 93 mask = 0x80; 94 95 for (bit = 0; bit < 8; bit++) 96 { 97 /* NOTE: Data presented on falling clock level and sampled on rising clock 98 level. This is SPI mode 3, or 0 given that the enable level is 99 driven low immediately before the first bit is presented. */ 100 101 _clock_device->set(_clock_pin, 0); 102 _data_device->set(_data_pin, data[byte] & mask ? 1 : 0); 103 104 if ((_control_device != NULL) && (dc != NULL)) 105 _control_device->set(_control_pin, dc[byte] ? 1 : 0); 106 107 if (_frequency) 108 nanosleep(&ts, NULL); 109 110 _clock_device->set(_clock_pin, 1); 111 112 if (_frequency) 113 nanosleep(&ts, NULL); 114 115 mask >>= 1; 116 } 117 } 118 119 _enable_device->set(_enable_pin, 1); 120 121 return bytes; 122 } 123 124 /* Send a number of units, each holding a value in a big endian arrangement 125 limited to the given character size, with any bit immediately above the 126 character portion of the unit indicating the data/command level. */ 127 128 uint32_t Spi_gpio::send_units(uint32_t bytes, const uint8_t data[], 129 uint8_t unit_size, uint8_t char_size) 130 { 131 uint32_t count = bytes / unit_size; 132 int dc[count]; 133 134 /* Traverse the byte sequence, extracting data/command bits for each unit. */ 135 136 for (uint32_t offset = 0, unit = 0; offset < bytes; offset += unit_size, unit++) 137 { 138 /* The unit size must be greater than the character size for data/command 139 bits to be present. */ 140 141 if (unit_size * 8 <= char_size) 142 dc[unit] = 0; 143 else 144 { 145 /* Obtain the unit value. */ 146 147 uint32_t value = 0; 148 149 for (uint8_t byte = 0; byte < unit_size; byte++) 150 value = (value << 8) | data[offset + byte]; 151 152 /* Extract the data/command level. */ 153 154 dc[unit] = value & (1 << char_size) ? 1 : 0; 155 } 156 } 157 158 return send_dc(bytes, data, dc); 159 } 160 161 162 163 /* C language interface. */ 164 165 void *spi_gpio_get_channel(uint64_t frequency, 166 void *clock_chip, int clock_pin, 167 void *data_chip, int data_pin, 168 void *enable_chip, int enable_pin, 169 void *control_chip, int control_pin) 170 { 171 return (void *) new Spi_gpio(frequency, 172 reinterpret_cast<Hw::Gpio_chip *>(clock_chip), clock_pin, 173 reinterpret_cast<Hw::Gpio_chip *>(data_chip), data_pin, 174 reinterpret_cast<Hw::Gpio_chip *>(enable_chip), enable_pin, 175 reinterpret_cast<Hw::Gpio_chip *>(control_chip), control_pin); 176 } 177 178 uint32_t spi_gpio_send(void *channel, uint32_t bytes, const uint8_t data[]) 179 { 180 return static_cast<Spi_gpio *>(channel)->send(bytes, data); 181 } 182 183 uint32_t spi_gpio_send_dc(void *channel, uint32_t bytes, const uint8_t data[], 184 const int dc[]) 185 { 186 return static_cast<Spi_gpio *>(channel)->send_dc(bytes, data, dc); 187 } 188 189 uint32_t spi_gpio_send_units(void *channel, uint32_t bytes, const uint8_t data[], 190 uint8_t unit_size, uint8_t char_size) 191 { 192 return static_cast<Spi_gpio *>(channel)->send_units(bytes, data, unit_size, char_size); 193 }