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