1 /* 2 * Export JZ4740 GPIO pins as a SPI server. 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/gpio-jz4740.h> 23 #include <l4/devices/spi-gpio.h> 24 #include <l4/devices/memory.h> 25 26 #include <l4/re/env.h> 27 28 #include <errno.h> 29 #include <stdint.h> 30 #include <stdlib.h> 31 32 #include <ipc/server.h> 33 34 #include "spi_server.h" 35 36 37 38 /* Virtual addresses for the GPIO register block. */ 39 40 static l4_addr_t gpio_virt_base = 0, gpio_virt_base_end = 0; 41 42 43 44 static int setup_memory(void) 45 { 46 if (get_memory("jz4740-gpio", &gpio_virt_base, &gpio_virt_base_end)) 47 return 1; 48 49 return 0; 50 } 51 52 53 54 /* SPI peripheral device. */ 55 56 class SPI_server : public SPI 57 { 58 Spi_gpio *_spi; 59 60 public: 61 /* Associate the device with a particular memory region. */ 62 63 explicit SPI_server(Spi_gpio *spi) 64 : _spi(spi) 65 { 66 } 67 68 /* Send a SPI command. */ 69 70 long send(int bits, int data) 71 { 72 uint32_t bytes = (bits + 7) / 8; 73 uint8_t buffer[bytes]; 74 75 /* Convert the data into a sequence of bytes. */ 76 77 for (int byte = bytes; byte > 0; byte--) 78 buffer[byte - 1] = (data >> ((byte - 1) * 8)) & 0xff; 79 80 _spi->send(bytes, buffer); 81 return L4_EOK; 82 } 83 }; 84 85 86 87 /* 88 Parse a string of the form "<port><pin>" where <port> is a single character 89 whose position in the ports string indicates the port number, and where <pin> is 90 either a base 10 number, a base 8 number preceded by "0", or a base 16 number 91 preceded by "0x" or "0X", defined by the strtol library function. 92 */ 93 94 static int parse_pin(const char *s, const char *ports, int *port, int *pin) 95 { 96 int i = 0; 97 98 if (!s || !(*s)) return 0; 99 100 /* Parse prefix in character range. */ 101 102 while (*ports) 103 { 104 if (s[0] == *ports) 105 { 106 *port = i; 107 108 /* Parse pin number. */ 109 110 *pin = (int) strtol(s+1, NULL, 0); 111 return !errno; 112 } 113 ports++; i++; 114 } 115 116 return 0; 117 } 118 119 /* Arguments: <SPI clock pin> <SPI data pin> <SPI enable pin> */ 120 121 int main(int argc, char *argv[]) 122 { 123 int clock_port, clock_pin, data_port, data_pin, enable_port, enable_pin; 124 125 if (argc < 4) 126 return 1; 127 128 /* Interpret the pin details. */ 129 130 if (!parse_pin(argv[1], "ABCD", &clock_port, &clock_pin)) 131 return 1; 132 if (!parse_pin(argv[2], "ABCD", &data_port, &data_pin)) 133 return 1; 134 if (!parse_pin(argv[3], "ABCD", &enable_port, &enable_pin)) 135 return 1; 136 137 /* Obtain access to peripheral memory. */ 138 139 if (setup_memory()) 140 return 1; 141 142 /* Configure the clock pin. */ 143 144 Gpio_jz4740_chip gpio_port_clock(gpio_virt_base + clock_port * 0x100, 145 gpio_virt_base + (clock_port + 1) * 0x100, 32); 146 147 gpio_port_clock.setup(clock_pin, Hw::Gpio_chip::Output, 0); 148 149 /* Configure the data pin. */ 150 151 Gpio_jz4740_chip gpio_port_data(gpio_virt_base + data_port * 0x100, 152 gpio_virt_base + (data_port + 1) * 0x100, 32); 153 154 gpio_port_data.setup(data_pin, Hw::Gpio_chip::Output, 0); 155 156 /* Configure the enable pin. */ 157 158 Gpio_jz4740_chip gpio_port_enable(gpio_virt_base + enable_port * 0x100, 159 gpio_virt_base + (enable_port + 1) * 0x100, 32); 160 161 gpio_port_enable.setup(enable_pin, Hw::Gpio_chip::Output, 0); 162 163 /* Create an object for SPI communication omitting any specific frequency. */ 164 165 Spi_gpio spi(&gpio_port_clock, clock_pin, &gpio_port_data, data_pin, 166 &gpio_port_enable, enable_pin); 167 168 /* Initialise and register a new server object. */ 169 170 SPI_server obj(&spi); 171 172 /* Bind and start the IPC server loop. */ 173 174 if (ipc_server_loop_for(SPI, &obj, "spi")) 175 return 1; 176 177 return 0; 178 }