1 /* 2 * Perform SPI communication using a suitable abstraction augmented with 3 * explicit manipulation of a control signal. 4 * 5 * Copyright (C) 2023 Paul Boddie <paul@boddie.org.uk> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA 21 */ 22 23 #include <l4/devices/spi-hybrid.h> 24 25 26 27 Spi_hybrid::Spi_hybrid(Spi_channel_base *channel, 28 Hw::Gpio_chip *control_device, int control_pin, 29 int control_alt_func) 30 : _channel(channel), 31 _control_device(control_device), 32 _control_pin(control_pin), 33 _control_alt_func(control_alt_func) 34 { 35 /* Provide this control interface to the channel. */ 36 37 if (_control_alt_func < 0) 38 _channel->set_control(this); 39 } 40 41 Spi_channel_base *Spi_hybrid::get_channel() 42 { 43 return _channel; 44 } 45 46 void Spi_hybrid::acquire_control(bool asserted) 47 { 48 _control_device->setup(_control_pin, Hw::Gpio_chip::Output, asserted ? 1 : 0); 49 } 50 51 void Spi_hybrid::release_control() 52 { 53 if (_control_alt_func >= 0) 54 _control_device->config_pad(_control_pin, Hw::Gpio_chip::Function_alt, _control_alt_func); 55 else 56 _control_device->setup(_control_pin, Hw::Gpio_chip::Output, 0); 57 } 58 59 /* Send a byte sequence. */ 60 61 uint32_t Spi_hybrid::send(uint32_t bytes, const uint8_t data[]) 62 { 63 return _channel->send(bytes, data); 64 } 65 66 /* Send a byte sequence with control information. */ 67 68 uint32_t Spi_hybrid::send_dc(uint32_t bytes, const uint8_t data[], 69 const int dc[], uint8_t char_size, bool big_endian) 70 { 71 return _channel->send_dc(bytes, data, dc, char_size, big_endian); 72 } 73 74 /* Send a sequence of units having the given character size. */ 75 76 uint32_t Spi_hybrid::send_units(uint32_t bytes, const uint8_t data[], 77 uint8_t unit_size, uint8_t char_size, 78 bool big_endian) 79 { 80 return _channel->send_units(bytes, data, unit_size, char_size, big_endian); 81 } 82 83 /* Transfer a sequence of units using DMA if available. */ 84 85 uint32_t Spi_hybrid::transfer(l4_addr_t vaddr, 86 l4re_dma_space_dma_addr_t paddr, 87 uint32_t count, uint8_t unit_size, 88 uint8_t char_size, 89 l4_addr_t desc_vaddr, 90 l4re_dma_space_dma_addr_t desc_paddr) 91 { 92 return _channel->transfer(vaddr, paddr, count, unit_size, char_size, 93 desc_vaddr, desc_paddr); 94 } 95 96 97 98 /* C language interface. */ 99 100 void *spi_hybrid_get_channel(void *channel, void *control_chip, int control_pin, 101 int control_alt_func) 102 { 103 return (void *) new Spi_hybrid(reinterpret_cast<Spi_channel_base *>(channel), 104 reinterpret_cast<Hw::Gpio_chip *>(control_chip), 105 control_pin, control_alt_func); 106 } 107 108 void *spi_hybrid_get_raw_channel(void *channel) 109 { 110 return static_cast<Spi_hybrid *>(channel)->get_channel(); 111 } 112 113 void spi_hybrid_acquire_control(void *channel, int level) 114 { 115 static_cast<Spi_hybrid *>(channel)->acquire_control(level); 116 } 117 118 void spi_hybrid_release_control(void *channel) 119 { 120 static_cast<Spi_hybrid *>(channel)->release_control(); 121 } 122 123 uint32_t spi_hybrid_send(void *channel, uint32_t bytes, const uint8_t data[]) 124 { 125 return static_cast<Spi_hybrid *>(channel)->send(bytes, data); 126 } 127 128 uint32_t spi_hybrid_send_dc(void *channel, uint32_t bytes, const uint8_t data[], 129 const int dc[], uint8_t char_size, int big_endian) 130 { 131 return static_cast<Spi_hybrid *>(channel)->send_dc(bytes, data, dc, char_size, 132 big_endian); 133 } 134 135 uint32_t spi_hybrid_send_units(void *channel, uint32_t bytes, const uint8_t data[], 136 uint8_t unit_size, uint8_t char_size, 137 int big_endian) 138 { 139 return static_cast<Spi_hybrid *>(channel)->send_units(bytes, data, unit_size, 140 char_size, big_endian); 141 } 142 143 uint32_t spi_hybrid_transfer(void *channel, l4_addr_t vaddr, 144 l4re_dma_space_dma_addr_t paddr, 145 uint32_t count, uint8_t unit_size, 146 uint8_t char_size) 147 { 148 return static_cast<Spi_hybrid *>(channel)->transfer(vaddr, paddr, count, unit_size, char_size); 149 } 150 151 uint32_t spi_hybrid_transfer_descriptor(void *channel, l4_addr_t vaddr, 152 l4re_dma_space_dma_addr_t paddr, 153 uint32_t count, uint8_t unit_size, 154 uint8_t char_size, l4_addr_t desc_vaddr, 155 l4re_dma_space_dma_addr_t desc_paddr) 156 { 157 return static_cast<Spi_hybrid *>(channel)->transfer(vaddr, paddr, count, 158 unit_size, char_size, desc_vaddr, desc_paddr); 159 }