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 uint32_t frequency) 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 _frequency(frequency) 38 { 39 _clock_device->setup(_clock_pin, Hw::Gpio_chip::Output, 1); 40 _data_device->setup(_data_pin, Hw::Gpio_chip::Output, 0); 41 _enable_device->setup(_enable_pin, Hw::Gpio_chip::Output, 1); 42 } 43 44 /* Send a SPI command. */ 45 46 void Spi_gpio::send(int bytes, const uint8_t data[]) 47 { 48 struct timespec ts; 49 uint8_t mask; 50 int bit, byte; 51 52 if (_frequency) 53 { 54 ts.tv_sec = 0; 55 ts.tv_nsec = 1000000000 / _frequency; 56 } 57 58 /* Initialise pin levels. */ 59 60 _enable_device->set(_enable_pin, 1); 61 _clock_device->set(_clock_pin, 1); 62 _data_device->set(_data_pin, 0); 63 64 /* Enter the transmission state. */ 65 66 _enable_device->set(_enable_pin, 0); 67 68 /* Clock data using the clock and data outputs. */ 69 70 for (byte = 0; byte < bytes; byte++) 71 { 72 mask = 0x80; 73 74 for (bit = 0; bit < 8; bit++) 75 { 76 /* NOTE: Data presented on falling clock level and sampled on rising clock 77 level. This is SPI mode 3, or 0 given that the enable level is 78 driven low immediately before the first bit is presented. */ 79 80 _clock_device->set(_clock_pin, 0); 81 _data_device->set(_data_pin, data[byte] & mask ? 1 : 0); 82 83 if (_frequency) 84 nanosleep(&ts, NULL); 85 86 _clock_device->set(_clock_pin, 1); 87 88 if (_frequency) 89 nanosleep(&ts, NULL); 90 91 mask >>= 1; 92 } 93 } 94 95 _enable_device->set(_enable_pin, 1); 96 } 97 98 99 100 /* C language interface. */ 101 102 void *spi_gpio_get_channel(void *clock_chip, int clock_pin, 103 void *data_chip, int data_pin, 104 void *enable_chip, int enable_pin, 105 uint32_t frequency) 106 { 107 return (void *) new Spi_gpio(reinterpret_cast<Hw::Gpio_chip *>(clock_chip), clock_pin, 108 reinterpret_cast<Hw::Gpio_chip *>(data_chip), data_pin, 109 reinterpret_cast<Hw::Gpio_chip *>(enable_chip), enable_pin, 110 frequency); 111 } 112 113 void spi_gpio_send(void *channel, int bytes, const uint8_t data[]) 114 { 115 static_cast<Spi_gpio *>(channel)->send(bytes, data); 116 }