1.1 --- a/pkg/devices/Control Sat Oct 14 22:02:07 2023 +0200 1.2 +++ b/pkg/devices/Control Sun Oct 15 22:24:25 2023 +0200 1.3 @@ -39,5 +39,6 @@ 1.4 provides: libdrivers-panel-loader 1.5 provides: libdrivers-panel-qi_lb60 1.6 provides: libdrivers-pwm 1.7 +provides: libdrivers-spi 1.8 requires: libc libc_be_l4re libdl l4re_c libio libipc 1.9 Maintainer: paul@boddie.org.uk
2.1 --- a/pkg/devices/lib/Makefile Sat Oct 14 22:02:07 2023 +0200 2.2 +++ b/pkg/devices/lib/Makefile Sun Oct 15 22:24:25 2023 +0200 2.3 @@ -1,7 +1,7 @@ 2.4 PKGDIR ?= .. 2.5 L4DIR ?= $(PKGDIR)/../.. 2.6 2.7 -TARGET := common cpm dma gpio hdmi i2c keypad lcd panel pwm 2.8 +TARGET := common cpm dma gpio hdmi i2c keypad lcd panel pwm spi 2.9 2.10 include $(L4DIR)/mk/subdir.mk 2.11 2.12 @@ -14,3 +14,4 @@ 2.13 lcd: common 2.14 panel: lcd 2.15 pwm: common 2.16 +spi: common
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/pkg/devices/lib/spi/Makefile Sun Oct 15 22:24:25 2023 +0200 3.3 @@ -0,0 +1,8 @@ 3.4 +PKGDIR ?= ../.. 3.5 +L4DIR ?= $(PKGDIR)/../.. 3.6 + 3.7 +TARGET := include src 3.8 + 3.9 +include $(L4DIR)/mk/subdir.mk 3.10 + 3.11 +src: include
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/pkg/devices/lib/spi/include/Makefile Sun Oct 15 22:24:25 2023 +0200 4.3 @@ -0,0 +1,4 @@ 4.4 +PKGDIR = ../../.. 4.5 +L4DIR ?= $(PKGDIR)/../.. 4.6 + 4.7 +include $(L4DIR)/mk/include.mk
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/pkg/devices/lib/spi/include/spi-gpio.h Sun Oct 15 22:24:25 2023 +0200 5.3 @@ -0,0 +1,69 @@ 5.4 +/* 5.5 + * Perform SPI communication using GPIO operations. 5.6 + * 5.7 + * Copyright (C) 2018, 2020, 2023 Paul Boddie <paul@boddie.org.uk> 5.8 + * 5.9 + * This program is free software; you can redistribute it and/or 5.10 + * modify it under the terms of the GNU General Public License as 5.11 + * published by the Free Software Foundation; either version 2 of 5.12 + * the License, or (at your option) any later version. 5.13 + * 5.14 + * This program is distributed in the hope that it will be useful, 5.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 5.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 5.17 + * GNU General Public License for more details. 5.18 + * 5.19 + * You should have received a copy of the GNU General Public License 5.20 + * along with this program; if not, write to the Free Software 5.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, 5.22 + * Boston, MA 02110-1301, USA 5.23 + */ 5.24 + 5.25 +#include <l4/devices/gpio.h> 5.26 +#include <stdint.h> 5.27 + 5.28 +#pragma once 5.29 + 5.30 + 5.31 + 5.32 +#ifdef __cplusplus 5.33 + 5.34 +/* SPI peripheral device. */ 5.35 + 5.36 +class Spi_gpio 5.37 +{ 5.38 + Hw::Gpio_chip *_clock_device; 5.39 + int _clock_pin; 5.40 + Hw::Gpio_chip *_data_device; 5.41 + int _data_pin; 5.42 + Hw::Gpio_chip *_enable_device; 5.43 + int _enable_pin; 5.44 + uint32_t _frequency; 5.45 + 5.46 +public: 5.47 + /* Associate the device with a particular memory region. */ 5.48 + 5.49 + explicit Spi_gpio(Hw::Gpio_chip *clock_device, int clock_pin, 5.50 + Hw::Gpio_chip *data_device, int data_pin, 5.51 + Hw::Gpio_chip *enable_device, int enable_pin, 5.52 + uint32_t frequency = 0); 5.53 + 5.54 + void send(int bytes, uint8_t data[]); 5.55 +}; 5.56 + 5.57 +#endif /* __cplusplus */ 5.58 + 5.59 + 5.60 + 5.61 +/* C language interface. */ 5.62 + 5.63 +EXTERN_C_BEGIN 5.64 + 5.65 +void *spi_gpio_get_channel(void *clock_chip, int clock_pin, 5.66 + void *data_chip, int data_pin, 5.67 + void *enable_chip, int enable_pin, 5.68 + uint32_t frequency); 5.69 + 5.70 +void spi_gpio_send(void *channel, int bytes, uint8_t data[]); 5.71 + 5.72 +EXTERN_C_END
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/pkg/devices/lib/spi/src/Makefile Sun Oct 15 22:24:25 2023 +0200 6.3 @@ -0,0 +1,13 @@ 6.4 +PKGDIR ?= ../../.. 6.5 +L4DIR ?= $(PKGDIR)/../.. 6.6 + 6.7 +TARGET = libspi.o.a libspi.o.so 6.8 +PC_FILENAME := libdrivers-spi 6.9 + 6.10 +SRC_CC := gpio.cc 6.11 + 6.12 +PRIVATE_INCDIR += $(PKGDIR)/lib/spi/include 6.13 + 6.14 +REQUIRES_LIBS := l4re_c l4re_c-util libdrivers-common libdrivers-gpio 6.15 + 6.16 +include $(L4DIR)/mk/lib.mk
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/pkg/devices/lib/spi/src/gpio.cc Sun Oct 15 22:24:25 2023 +0200 7.3 @@ -0,0 +1,116 @@ 7.4 +/* 7.5 + * Perform SPI communication using GPIO operations. 7.6 + * 7.7 + * Copyright (C) 2018, 2020, 2023 Paul Boddie <paul@boddie.org.uk> 7.8 + * 7.9 + * This program is free software; you can redistribute it and/or 7.10 + * modify it under the terms of the GNU General Public License as 7.11 + * published by the Free Software Foundation; either version 2 of 7.12 + * the License, or (at your option) any later version. 7.13 + * 7.14 + * This program is distributed in the hope that it will be useful, 7.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 7.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 7.17 + * GNU General Public License for more details. 7.18 + * 7.19 + * You should have received a copy of the GNU General Public License 7.20 + * along with this program; if not, write to the Free Software 7.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, 7.22 + * Boston, MA 02110-1301, USA 7.23 + */ 7.24 + 7.25 +#include <l4/devices/spi-gpio.h> 7.26 +#include <time.h> 7.27 + 7.28 + 7.29 + 7.30 +Spi_gpio::Spi_gpio(Hw::Gpio_chip *clock_device, int clock_pin, 7.31 + Hw::Gpio_chip *data_device, int data_pin, 7.32 + Hw::Gpio_chip *enable_device, int enable_pin, 7.33 + uint32_t frequency) 7.34 +: _clock_device(clock_device), 7.35 + _clock_pin(clock_pin), 7.36 + _data_device(data_device), 7.37 + _data_pin(data_pin), 7.38 + _enable_device(enable_device), 7.39 + _enable_pin(enable_pin), 7.40 + _frequency(frequency) 7.41 +{ 7.42 + _clock_device->setup(_clock_pin, Hw::Gpio_chip::Output, 1); 7.43 + _data_device->setup(_data_pin, Hw::Gpio_chip::Output, 0); 7.44 + _enable_device->setup(_enable_pin, Hw::Gpio_chip::Output, 1); 7.45 +} 7.46 + 7.47 +/* Send a SPI command. */ 7.48 + 7.49 +void Spi_gpio::send(int bytes, uint8_t data[]) 7.50 +{ 7.51 + struct timespec ts; 7.52 + uint8_t mask; 7.53 + int bit, byte; 7.54 + 7.55 + if (_frequency) 7.56 + { 7.57 + ts.tv_sec = 0; 7.58 + ts.tv_nsec = 1000000000 / _frequency; 7.59 + } 7.60 + 7.61 + /* Initialise pin levels. */ 7.62 + 7.63 + _enable_device->set(_enable_pin, 1); 7.64 + _clock_device->set(_clock_pin, 1); 7.65 + _data_device->set(_data_pin, 0); 7.66 + 7.67 + /* Enter the transmission state. */ 7.68 + 7.69 + _enable_device->set(_enable_pin, 0); 7.70 + 7.71 + /* Clock data using the clock and data outputs. */ 7.72 + 7.73 + for (byte = 0; byte < bytes; byte++) 7.74 + { 7.75 + mask = 0x80; 7.76 + 7.77 + for (bit = 0; bit < 8; bit++) 7.78 + { 7.79 + /* NOTE: Data presented on falling clock level and sampled on rising clock 7.80 + level. This is SPI mode 3, or 0 given that the enable level is 7.81 + driven low immediately before the first bit is presented. */ 7.82 + 7.83 + _clock_device->set(_clock_pin, 0); 7.84 + _data_device->set(_data_pin, data[byte] & mask ? 1 : 0); 7.85 + 7.86 + if (_frequency) 7.87 + nanosleep(&ts, NULL); 7.88 + 7.89 + _clock_device->set(_clock_pin, 1); 7.90 + 7.91 + if (_frequency) 7.92 + nanosleep(&ts, NULL); 7.93 + 7.94 + mask >>= 1; 7.95 + } 7.96 + } 7.97 + 7.98 + _enable_device->set(_enable_pin, 1); 7.99 +} 7.100 + 7.101 + 7.102 + 7.103 +/* C language interface. */ 7.104 + 7.105 +void *spi_gpio_get_channel(void *clock_chip, int clock_pin, 7.106 + void *data_chip, int data_pin, 7.107 + void *enable_chip, int enable_pin, 7.108 + uint32_t frequency) 7.109 +{ 7.110 + return (void *) new Spi_gpio(reinterpret_cast<Hw::Gpio_chip *>(clock_chip), clock_pin, 7.111 + reinterpret_cast<Hw::Gpio_chip *>(data_chip), data_pin, 7.112 + reinterpret_cast<Hw::Gpio_chip *>(enable_chip), enable_pin, 7.113 + frequency); 7.114 +} 7.115 + 7.116 +void spi_gpio_send(void *channel, int bytes, uint8_t data[]) 7.117 +{ 7.118 + static_cast<Spi_gpio *>(channel)->send(bytes, data); 7.119 +}
8.1 --- a/pkg/devices/spi/src/jz4740/Makefile Sat Oct 14 22:02:07 2023 +0200 8.2 +++ b/pkg/devices/spi/src/jz4740/Makefile Sun Oct 15 22:24:25 2023 +0200 8.3 @@ -27,7 +27,7 @@ 8.4 8.5 SRC_CC = $(SERVER_INTERFACES_SRC_CC) $(PLAIN_SRC_CC) 8.6 8.7 -REQUIRES_LIBS = l4re_c l4re_c-util libdevice-util libdrivers-gpio libipc 8.8 +REQUIRES_LIBS = l4re_c l4re_c-util libdevice-util libdrivers-gpio libdrivers-spi libipc 8.9 8.10 PRIVATE_INCDIR = $(PKGDIR)/spi/include $(PKGDIR)/util/include \ 8.11 $(IDL_BUILD_DIR) $(IDL_EXPORT_DIR)
9.1 --- a/pkg/devices/spi/src/jz4740/spi-jz4740.cc Sat Oct 14 22:02:07 2023 +0200 9.2 +++ b/pkg/devices/spi/src/jz4740/spi-jz4740.cc Sun Oct 15 22:24:25 2023 +0200 9.3 @@ -20,6 +20,7 @@ 9.4 */ 9.5 9.6 #include <l4/devices/gpio-jz4740.h> 9.7 +#include <l4/devices/spi-gpio.h> 9.8 #include <l4/devices/memory.h> 9.9 9.10 #include <l4/re/env.h> 9.11 @@ -54,20 +55,13 @@ 9.12 9.13 class SPI_server : public SPI 9.14 { 9.15 - Gpio_jz4740_chip *_clock_device = 0, *_data_device = 0, *_enable_device = 0; 9.16 - int _clock_pin, _data_pin, _enable_pin; 9.17 + Spi_gpio *_spi; 9.18 9.19 public: 9.20 /* Associate the device with a particular memory region. */ 9.21 9.22 - explicit SPI_server(Gpio_jz4740_chip *clock_device, 9.23 - Gpio_jz4740_chip *data_device, 9.24 - Gpio_jz4740_chip *enable_device, 9.25 - int clock_pin, int data_pin, int enable_pin) 9.26 - : _clock_device(clock_device), 9.27 - _data_device(data_device), 9.28 - _enable_device(enable_device), 9.29 - _clock_pin(clock_pin), _data_pin(data_pin), _enable_pin(enable_pin) 9.30 + explicit SPI_server(Spi_gpio *spi) 9.31 + : _spi(spi) 9.32 { 9.33 } 9.34 9.35 @@ -75,31 +69,15 @@ 9.36 9.37 long send(int bits, int data) 9.38 { 9.39 - uint32_t mask = 1 << (bits - 1); 9.40 - int bit; 9.41 - 9.42 - /* Initialise pin levels. */ 9.43 + int bytes = (bits + 7) / 8; 9.44 + uint8_t buffer[bytes]; 9.45 9.46 - _enable_device->set(_enable_pin, 1); 9.47 - _clock_device->set(_clock_pin, 1); 9.48 - _data_device->set(_data_pin, 0); 9.49 - 9.50 - /* Enter the transmission state. */ 9.51 + /* Convert the data into a sequence of bytes. */ 9.52 9.53 - _enable_device->set(_enable_pin, 0); 9.54 - 9.55 - /* Clock data using the clock and data outputs. */ 9.56 - 9.57 - for (bit = 0; bit < bits; bit++) 9.58 - { 9.59 - _clock_device->set(_clock_pin, 0); 9.60 - _data_device->set(_data_pin, data & mask ? 1 : 0); 9.61 - _clock_device->set(_clock_pin, 1); 9.62 - mask >>= 1; 9.63 - } 9.64 + for (int byte = bytes; byte > 0; byte--) 9.65 + buffer[byte - 1] = (data >> ((byte - 1) * 8)) & 0xff; 9.66 9.67 - _enable_device->set(_enable_pin, 1); 9.68 - 9.69 + _spi->send(bytes, buffer); 9.70 return L4_EOK; 9.71 } 9.72 }; 9.73 @@ -182,10 +160,14 @@ 9.74 9.75 gpio_port_enable.setup(enable_pin, Hw::Gpio_chip::Output, 0); 9.76 9.77 + /* Create an object for SPI communication. */ 9.78 + 9.79 + Spi_gpio spi(&gpio_port_clock, clock_pin, &gpio_port_data, data_pin, 9.80 + &gpio_port_enable, enable_pin); 9.81 + 9.82 /* Initialise and register a new server object. */ 9.83 9.84 - SPI_server obj(&gpio_port_clock, &gpio_port_data, &gpio_port_enable, 9.85 - clock_pin, data_pin, enable_pin); 9.86 + SPI_server obj(&spi); 9.87 9.88 /* Bind and start the IPC server loop. */ 9.89