# HG changeset patch # User Paul Boddie # Date 1698166326 -7200 # Node ID 934192f4945707ab31efe7e90c0f0d1d95eac68e # Parent 40a23a1e92f80b96bdfaa74cc16e6b01142cff9a# Parent ba9d8393e52269d4dcb2d21444a8ae48ec51533b Merged X1600 branches. diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/Control --- a/pkg/devices/Control Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/Control Tue Oct 24 18:52:06 2023 +0200 @@ -21,6 +21,7 @@ provides: libdevice-lcd provides: libdevice-lcd-jz4740 provides: libdevice-util +provides: libdrivers-aic provides: libdrivers-common provides: libdrivers-cpm provides: libdrivers-dma @@ -39,5 +40,6 @@ provides: libdrivers-panel-loader provides: libdrivers-panel-qi_lb60 provides: libdrivers-pwm +provides: libdrivers-spi requires: libc libc_be_l4re libdl l4re_c libio libipc Maintainer: paul@boddie.org.uk diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/include/clocks.h --- a/pkg/devices/include/clocks.h Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/include/clocks.h Tue Oct 24 18:52:06 2023 +0200 @@ -27,6 +27,7 @@ enum Clock_identifiers { + Clock_audio, Clock_aic_bitclk, Clock_aic_pclk, Clock_can0, @@ -44,9 +45,12 @@ Clock_i2c, Clock_i2c0, Clock_i2c1, - Clock_i2s, + Clock_i2s0, Clock_i2s0_rx, Clock_i2s0_tx, + Clock_i2s1, + Clock_i2s1_rx, + Clock_i2s1_tx, Clock_kbc, Clock_lcd, Clock_lcd_pixel, diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lcd/src/jz4740/lcd-jz4740-device.cc --- a/pkg/devices/lcd/src/jz4740/lcd-jz4740-device.cc Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lcd/src/jz4740/lcd-jz4740-device.cc Tue Oct 24 18:52:06 2023 +0200 @@ -19,6 +19,7 @@ * Boston, MA 02110-1301, USA */ +#include #include #include #include @@ -26,13 +27,8 @@ #include "lcd-jz4740-device.h" #include -#include -#include #include -#include -#include #include -#include #include #include @@ -124,100 +120,35 @@ int Lcd_jz4740_device::_setup_memory() { - // Framebuffer and descriptor sizes. - - unsigned long fb_size, desc_size; - - // Size of physically contiguous regions to be allocated. - - l4_size_t fb_size_out, desc_size_out; - - // Memory allocation capabilities. - - l4_cap_idx_t descmem, dma, vbus; - // Test for existing setup. if (fb_vaddr) return 0; - // Create the DMA space. - - dma = ipc_cap_alloc(); - - if (l4_is_invalid_cap(dma)) - return 1; - - vbus = l4re_env_get_cap("vbus"); - - if (l4_is_invalid_cap(vbus)) - return 1; - - if (l4_error(l4_factory_create(l4re_env()->mem_alloc, L4RE_PROTO_DMA_SPACE, dma))) - return 1; - - l4vbus_device_handle_t device = L4VBUS_NULL; - l4vbus_resource_t dma_resource; - - if (!find_resource(&device, &dma_resource, L4VBUS_RESOURCE_DMA_DOMAIN)) - return 1; - - if (l4vbus_assign_dma_domain(vbus, dma_resource.start, - L4VBUS_DMAD_BIND | L4VBUS_DMAD_L4RE_DMA_SPACE, - dma)) - return 1; - // Obtain the memory requirements. Lcd_jz4740_chip *chip = static_cast(_chip); - fb_size = chip->get_screen_size(); - desc_size = chip->get_descriptors_size(); + // Framebuffer and descriptor sizes. + + unsigned long fb_size = chip->get_screen_size(), + desc_size = chip->get_descriptors_size(); // Allocate memory for the framebuffer at 2**6 == 64 byte == 16 word alignment, // also for the descriptors. // NOTE: A 64 word burst mode is available on the JZ4780 that would // NOTE: necessitate 64 word alignment for the framebuffer. - const l4_size_t alloc_flags = L4RE_MA_CONTINUOUS | L4RE_MA_PINNED; - const l4re_rm_flags_t attach_flags = L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW; - - // Map the allocated memory, obtaining virtual addresses. - - fb_vaddr = 0; - desc_vaddr = 0; + long err = get_dma_region(fb_size, 8, &fb_vaddr, &fb_paddr, &_fbmem); - if (ipc_new_dataspace(fb_size, alloc_flags, 8, &_fbmem)) - return 1; - - if (ipc_new_dataspace(desc_size, alloc_flags, 6, &descmem)) - return 1; - - if (ipc_attach_dataspace_align(_fbmem, fb_size, attach_flags, 8, (void **) &fb_vaddr)) - return 1; - - if (ipc_attach_dataspace_align(descmem, desc_size, attach_flags, 6, (void **) &desc_vaddr)) + if (err) return 1; - // Obtain physical addresses for the framebuffer and descriptors. - - fb_paddr = 0; - desc_paddr = 0; - - fb_size_out = fb_size; - desc_size_out = desc_size; + l4_cap_idx_t descmem; - if (l4re_dma_space_map(dma, _fbmem | L4_CAP_FPAGE_RW, 0, &fb_size_out, 0, - L4RE_DMA_SPACE_TO_DEVICE, &fb_paddr)) - return 1; + err = get_dma_region(desc_size, 6, &desc_vaddr, &desc_paddr, &descmem); - if (l4re_dma_space_map(dma, descmem | L4_CAP_FPAGE_RW, 0, &desc_size_out, 0, - L4RE_DMA_SPACE_TO_DEVICE, &desc_paddr)) - return 1; - - // Test the mapped region sizes. - - if ((fb_size_out != fb_size) || (desc_size_out != desc_size)) + if (err) return 1; return 0; diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/Makefile --- a/pkg/devices/lib/Makefile Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/Makefile Tue Oct 24 18:52:06 2023 +0200 @@ -1,10 +1,11 @@ PKGDIR ?= .. L4DIR ?= $(PKGDIR)/../.. -TARGET := common cpm dma gpio hdmi i2c keypad lcd panel pwm +TARGET := aic common cpm dma gpio hdmi i2c keypad lcd panel pwm spi include $(L4DIR)/mk/subdir.mk +aic: common cpm: common dma: common gpio: common @@ -14,3 +15,4 @@ lcd: common panel: lcd pwm: common +spi: common diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/aic/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/aic/Makefile Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,8 @@ +PKGDIR ?= ../.. +L4DIR ?= $(PKGDIR)/../.. + +TARGET := include src + +include $(L4DIR)/mk/subdir.mk + +src: include diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/aic/include/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/aic/include/Makefile Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,4 @@ +PKGDIR = ../../.. +L4DIR ?= $(PKGDIR)/../.. + +include $(L4DIR)/mk/include.mk diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/aic/include/aic-x1600.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/aic/include/aic-x1600.h Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,106 @@ +/* + * AIC support for the X1600. + * + * Copyright (C) 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include +#include + + + +#ifdef __cplusplus + +#include +#include +#include + +// AIC channel. + +class Aic_x1600_channel +{ +private: + l4_addr_t _aic_start; + Hw::Register_block<32> _regs; + enum Clock_identifiers _clock_rx, _clock_tx; + Cpm_x1600_chip *_cpm; + Dma_x1600_channel *_dma; + + // Buffer management. + + unsigned int _size = 0; + l4_addr_t _vaddr = 0; + l4re_dma_space_dma_addr_t _paddr = 0; + l4_cap_idx_t _mem = L4_INVALID_CAP; + +public: + Aic_x1600_channel(l4_addr_t aic_start, l4_addr_t start, + enum Clock_identifiers clock_rx, + enum Clock_identifiers clock_tx, + Cpm_x1600_chip *cpm, + Dma_x1600_channel *dma); + + long get_buffer(uint32_t count, l4_addr_t *addr); + unsigned int transfer(uint32_t count, uint32_t sample_rate, uint8_t sample_size); + +private: + void disable(); + void enable(); + + void config(); + uint32_t encode_input_size(uint8_t sample_size); + uint32_t encode_output_size(uint8_t sample_size); + void set_divider(uint32_t divider, uint32_t mask, uint32_t limit, int bit); + void set_recv_frequency(uint32_t sample_rate); + void set_trans_frequency(uint32_t sample_rate); +}; + +// AIC device control. + +class Aic_x1600_chip +{ +private: + l4_addr_t _aic_start, _start, _end; + Cpm_x1600_chip *_cpm; + +public: + Aic_x1600_chip(l4_addr_t aic_start, l4_addr_t start, l4_addr_t end, Cpm_x1600_chip *cpm); + + Aic_x1600_channel *get_channel(uint8_t channel, Dma_x1600_channel *dma); +}; + +#endif /* __cplusplus */ + + + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *x1600_aic_init(l4_addr_t aic_start, l4_addr_t start, l4_addr_t end, void *cpm); + +void *x1600_aic_get_channel(void *aic, uint8_t channel, void *dma); + +long x1600_aic_get_buffer(void *channel, uint32_t count, l4_addr_t *addr); + +unsigned int x1600_aic_transfer(void *channel, uint32_t count, uint32_t sample_rate, uint8_t sample_size); + +EXTERN_C_END diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/aic/src/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/aic/src/Makefile Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,13 @@ +PKGDIR ?= ../../.. +L4DIR ?= $(PKGDIR)/../.. + +TARGET = libaic.o.a libaic.o.so +PC_FILENAME := libdrivers-aic + +SRC_CC := x1600.cc + +PRIVATE_INCDIR += $(PKGDIR)/lib/aic/include + +REQUIRES_LIBS := l4re_c l4re_c-util libdrivers-common libdrivers-cpm libdrivers-gpio + +include $(L4DIR)/mk/lib.mk diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/aic/src/x1600.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/aic/src/x1600.cc Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,466 @@ +/* + * AIC support for the X1600. + * + * Copyright (C) 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include +#include + +#include +#include + +#include + +enum Regs +{ + Aic_config = 0x000, // AICFR + Aic_control = 0x004, // AICCR + Aic_i2s_msb_control = 0x010, // I2SCR + Aic_fifo_status = 0x014, // AICSR + Aic_i2s_msb_status = 0x01c, // I2SSR + Aic_i2s_msb_divider = 0x030, // I2SDIV + Aic_fifo_data = 0x034, // AICDR + Aic_loop_data = 0x038, // AICLR + Aic_tfifo_loop = 0x03c, // AICTFLR +}; + +enum Aic_config_bits : unsigned +{ + Aic_enable_big_endian = 0x1000, // MSB + Aic_independent_clock = 0x0100, // DMODE + Aic_shared_clock = 0x0000, // DMODE + Aic_last_sample_uflow = 0x0040, // LSMP + Aic_select_i2s_msb = 0x0010, // AUSEL + Aic_reset = 0x0008, // RST + Aic_tmaster = 0x0004, // TMASTER + Aic_rmaster = 0x0002, // RMASTER + Aic_enable = 0x0001, // ENB + + Aic_rfifo_thold_bit = 24, // RFTH + Aic_tfifo_thold_bit = 16, // TFTH +}; + +enum Aic_fifo_limits : unsigned +{ + Aic_rfifo_max = 15, + Aic_tfifo_max = 31, +}; + +enum Aic_control_bits : unsigned +{ + Aic_enable_tfifo_loop_overrun = 0x80000000, // ETFLOR + Aic_enable_tfifo_loop_request = 0x40000000, // ETFLS + Aic_packed_data = 0x10000000, // PACK16 + + Aic_channel_number_mask = 0x07000000, // CHANNEL + Aic_channel_mono = 0x00000000, + Aic_channel_stereo = 0x01000000, + + Aic_enable_tfifo_loop = 0x00800000, // ETFL + Aic_enable_tfifo_loop_dma = 0x00400000, // TLDMS + + Aic_output_size_mask = 0x00380000, // OSS + Aic_output_size_8bit = 0x00000000, // 0 + Aic_output_size_16bit = 0x00080000, // 1 + Aic_output_size_18bit = 0x00100000, // 2 + Aic_output_size_20bit = 0x00180000, // 3 + Aic_output_size_24bit = 0x00200000, // 4 + + Aic_input_size_mask = 0x00070000, // ISS + Aic_input_size_8bit = 0x00000000, // 0 + Aic_input_size_16bit = 0x00010000, // 1 + Aic_input_size_18bit = 0x00020000, // 2 + Aic_input_size_20bit = 0x00030000, // 3 + Aic_input_size_24bit = 0x00040000, // 4 + + Aic_enable_recv_dma = 0x00008000, // RDMS + Aic_enable_trans_dma = 0x00004000, // TDMS + + Aic_mono_control_mask = 0x00003000, // MONOCTR + Aic_mono_control_both = 0x00000000, // 0 + Aic_mono_control_right = 0x00001000, // 1 + Aic_mono_control_left = 0x00002000, // 2 + + Aic_trans_byte_swap = 0x00000400, // ENDSW + Aic_tfifo_flush = 0x00000100, // TFLUSH + Aic_rfifo_flush = 0x00000080, // RFLUSH + Aic_enable_rfifo_overrun = 0x00000040, // EROR + Aic_enable_tfifo_underrun = 0x00000020, // ETUR + Aic_enable_rfifo_request = 0x00000010, // ERFS + Aic_enable_tfifo_request = 0x00000008, // ETFS + Aic_enable_loopback = 0x00000004, // ENLBF + Aic_enable_playback = 0x00000002, // ERPL + Aic_enable_record = 0x00000001, // EREC +}; + +enum Aic_i2s_msb_control_bits : unsigned +{ + Aic_i2s_msb_right_first = 0x20000, // RFIRST + Aic_i2s_msb_switch_lr = 0x10000, // SWLH + Aic_select_msb_justified = 0x00001, // AMSL + Aic_select_i2s = 0x00000, // AMSL +}; + +enum Aic_fifo_status_bits : unsigned +{ + Aic_rfifo_overrun = 0x40, // ROR + Aic_tfifo_underrun = 0x20, // TUR + Aic_rfifo_request = 0x10, // RFS + Aic_tfifo_request = 0x08, // TFS + Aic_tfifo_loop_overrun = 0x04, // TFLOR + Aic_tfifo_loop_request = 0x02, // TFLS + + Aic_rfifo_level_bit = 24, // RFL + Aic_tfifo_loop_level_bit = 15, // TFLL + Aic_tfifo_level_bit = 8, // TFL +}; + +enum Aic_i2s_msb_status_bits : unsigned +{ + Aic_trans_channel_busy = 0x20, // CHBSY + Aic_trans_busy = 0x10, // TBSY + Aic_recv_busy = 0x08, // RBSY + Aic_busy = 0x04, // BSY +}; + +enum Aic_i2s_msb_divider_bits : unsigned +{ + Aic_recv_divider_mask = 0x1ff0000, // RDIV + Aic_trans_divider_mask = 0x00001ff, // TDIV + + Aic_recv_divider_bit = 16, // RDIV + Aic_trans_divider_bit = 0, // TDIV +}; + +enum Aic_i2s_msb_divider_limits : unsigned +{ + Aic_recv_divider_limit = 0x1ff, // RDIV + Aic_trans_divider_limit = 0x1ff, // TDIV +}; + +enum Aic_fifo_data_limits : unsigned +{ + Aic_fifo_data_limit = 0xffffff, // DATA +}; + +enum Aic_loop_data_limits : unsigned +{ + Aic_loop_data_limit = 0xffffff, // DATALP +}; + +enum Aic_tfifo_loop_limits : unsigned +{ + Aic_tfifo_loop_limit = 0xf, // TFLTH +}; + + + +// Initialise a channel. + +Aic_x1600_channel::Aic_x1600_channel(l4_addr_t aic_start, l4_addr_t start, + enum Clock_identifiers clock_rx, + enum Clock_identifiers clock_tx, + Cpm_x1600_chip *cpm, + Dma_x1600_channel *dma) +: _aic_start(aic_start), _clock_rx(clock_rx), _clock_tx(clock_tx), _cpm(cpm), _dma(dma) +{ + _regs = new Hw::Mmio_register_block<32>(start); + _cpm->start_clock(clock_rx); + _cpm->start_clock(clock_tx); + config(); +} + +/* +I2S configuration. + +The system clock (SYS_CLK) for I2S in the X1600 is issued by the X1600 as the +master/system clock (MCLK) externally to the DAC/codec. + +SYS_CLK is divided to obtain BIT_CLK which is issued by the X1600 as BCLK. +This is dependent on the actual sample rate. + +LRCLK indicates the left/right channel for the issued data. + +Where clocks (MCLK, BCLK, LRCLK) are issued only by the X1600, share mode +is used. Otherwise, independent (6-line) mode is used. + +To drive the MAX9835A, no MCLK is needed. + +Initialisation involves the following: + +- Configure MCLK, BCLK, LRCLK as outputs +- Select I2S in Aic_i2s_msb_control (deselecting Aic_select_msb_justified) +- Set master mode in Aic_config (Aic_tmaster, also Aic_rmaster if independent + mode is used) +- Set the dividers in Aic_i2s_msb_divider for BCLK using the configured I2S + clock frequency +- Perform a reset by writing to Aic_reset in Aic_config, if necessary +- The MAX9835A may not need any configuration using I2C or some other bus +*/ + +void +Aic_x1600_channel::config() +{ + // NOTE: Setting transmit request threshold to 31 (15 * 2 + 1). + + printf("config = %08x\n", (uint32_t) _regs[Aic_config]); + + _regs[Aic_config] = Aic_shared_clock | Aic_select_i2s_msb | Aic_reset | + Aic_tmaster | Aic_enable | (15 << Aic_tfifo_thold_bit); + + _regs[Aic_i2s_msb_control] = Aic_select_i2s; + + printf("config = %08x\n", (uint32_t) _regs[Aic_config]); +} + +/* +Clock configuration: + +- SYS_CLK (MCLK) is Clock_i2s0_tx (or Clock_i2s0_rx) +- BIT_CLK (BCLK) is MCLK divided by the appropriate AIC/I2S divider +- BCLK should be 64 times the sample rate according to the manual + +This suggests that the X1600 always produces data for two 32-bit channels +regardless of the input data characteristics. +*/ + +void +Aic_x1600_channel::set_divider(uint32_t divider, uint32_t mask, uint32_t limit, + int bit) +{ + if (divider <= limit) + _regs[Aic_i2s_msb_divider] = (_regs[Aic_i2s_msb_divider] & ~mask) | + (divider << bit); +} + +void +Aic_x1600_channel::set_recv_frequency(uint32_t sample_rate) +{ + set_divider(_cpm->get_frequency(_clock_rx) / (sample_rate * 32 * 2), + Aic_recv_divider_mask, Aic_recv_divider_limit, Aic_recv_divider_bit); +} + +void +Aic_x1600_channel::set_trans_frequency(uint32_t sample_rate) +{ + printf("tx %d / (freq %d * %d * 2) = %d vs. %d\n", _cpm->get_frequency(_clock_tx), + sample_rate, 32, + _cpm->get_frequency(_clock_tx) / (sample_rate * 32 * 2), + Aic_trans_divider_limit); + + set_divider(_cpm->get_frequency(_clock_tx) / (sample_rate * 32 * 2), + Aic_trans_divider_mask, Aic_trans_divider_limit, Aic_trans_divider_bit); +} + +uint32_t +Aic_x1600_channel::encode_input_size(uint8_t sample_size) +{ + switch (sample_size) + { + case 16: + return Aic_input_size_16bit; + + case 18: + return Aic_input_size_18bit; + + case 20: + return Aic_input_size_20bit; + + case 24: + return Aic_input_size_24bit; + + default: + return Aic_input_size_8bit; + } +} + +uint32_t +Aic_x1600_channel::encode_output_size(uint8_t sample_size) +{ + switch (sample_size) + { + case 16: + return Aic_output_size_16bit; + + case 18: + return Aic_output_size_18bit; + + case 20: + return Aic_output_size_20bit; + + case 24: + return Aic_output_size_24bit; + + default: + return Aic_output_size_8bit; + } +} + +// Obtain a DMA-accessible buffer for sample transfers. + +long +Aic_x1600_channel::get_buffer(uint32_t count, l4_addr_t *addr) +{ + printf("get_buffer(%d)\n", count); + long err = get_dma_region(count, 8, &_vaddr, &_paddr, &_mem); + + if (err) + return err; + + // Set the region size as the requested size, not any allocated size. + + _size = count; + *addr = _vaddr; + printf("size = %d\n", _size); + return L4_EOK; +} + +// Transfer a sample using the given byte count (total sample size), sample rate +// (or frequency), sample unit size (or resolution, width). + +unsigned int +Aic_x1600_channel::transfer(uint32_t count, uint32_t sample_rate, uint8_t sample_size) +{ + printf("transfer(%d, %d, %d)\n", count, sample_rate, sample_size); + + if (count > _size) + return 0; + + /* To play a sample: + + - Configure the sample size using the Aic_output_size settings in Aic_control + - The sample size will be 16- or 24-bit for the MAX9835A, with the X1600 not + supporting 32-bit samples + - Configure the number of channels in Aic_control (Aic_channel_mono or + Aic_channel_stereo) + - For 16-bit samples, select Aic_packed_data in Aic_control if appropriate, + possibly not for the MAX983A + - If two channels are used, select Aic_i2s_msb_right_first in + Aic_i2s_msb_control if appropriate, as well as Aic_i2s_msb_switch_lr if + switching the channels + - Reconfigure the dividers if appropriate + - For DMA, set Aic_enable_trans_dma in Aic_control and the transmit FIFO + threshold value + */ + + // NOTE: Introduce sample size. + + set_trans_frequency(sample_rate); + + /* NOTE: The MAX98357A might require stereo input data, but perhaps the + peripheral can generate the appropriate LRCLK to make it accept mono + data. */ + + _regs[Aic_control] = encode_output_size(sample_size) | + Aic_channel_mono | + Aic_enable_trans_dma | + Aic_enable_playback; + + printf("control = %08x\n", (uint32_t) _regs[Aic_control]); + printf("config = %08x\n", (uint32_t) _regs[Aic_config]); + printf("divider = %d\n", (uint32_t) _regs[Aic_i2s_msb_divider]); + printf("status = %08x\n", (uint32_t) _regs[Aic_i2s_msb_status]); + printf("fifo = %08x\n", (uint32_t) _regs[Aic_fifo_status]); + + // Transfer from the allocated region to the FIFO. Use an incrementing source + // address with source width, destination width and transfer unit reflecting + // the sample size, and with transfers initiated by an empty AIC transmit + // FIFO. + + printf("transfer from %llx to %lx\n", _paddr, _aic_start + Aic_fifo_data); + + unsigned int sample_unit = (sample_size == 8) ? 1 : + (sample_size == 16) ? 2 : + 4; + unsigned int unit_count = count / sample_unit; + + unsigned int to_transfer = _dma->transfer(_paddr, + _aic_start + Aic_fifo_data, + unit_count, + true, + false, + sample_unit, + sample_unit, + sample_unit, + Dma_request_aic_out); + + printf("status = %08x\n", (uint32_t) _regs[Aic_i2s_msb_status]); + printf("fifo = %08x\n", (uint32_t) _regs[Aic_fifo_status]); + + unsigned int transferred = 0; + + if (to_transfer) + transferred = to_transfer ? (unit_count - _dma->wait()) * sample_unit : 0; + +#if 0 + if (transferred) + while (_regs[Aic_fifo_status]); + + _regs[Aic_control] = _regs[Aic_control] & ~Aic_enable_playback; +#endif + + return transferred; +} + + + +// Initialise the AIC/I2S controller. + +Aic_x1600_chip::Aic_x1600_chip(l4_addr_t aic_start, l4_addr_t start, l4_addr_t end, + Cpm_x1600_chip *cpm) +: _aic_start(aic_start), _start(start), _end(end), _cpm(cpm) +{ +} + +// Obtain a channel object. + +Aic_x1600_channel * +Aic_x1600_chip::get_channel(uint8_t channel, Dma_x1600_channel *dma) +{ + if (channel < 1) + return new Aic_x1600_channel(_aic_start, _start, Clock_i2s0_rx, Clock_i2s0_tx, _cpm, dma); + else + throw -L4_EINVAL; +} + + + +// C language interface functions. + +void *x1600_aic_init(l4_addr_t aic_start, l4_addr_t start, l4_addr_t end, void *cpm) +{ + return (void *) new Aic_x1600_chip(aic_start, start, end, static_cast(cpm)); +} + +void *x1600_aic_get_channel(void *aic, uint8_t channel, void *dma) +{ + return static_cast(aic)->get_channel(channel, + static_cast(dma)); +} + +long x1600_aic_get_buffer(void *channel, uint32_t count, l4_addr_t *addr) +{ + return static_cast(channel)->get_buffer(count, addr); +} + +unsigned int x1600_aic_transfer(void *channel, uint32_t count, uint32_t sample_rate, uint8_t sample_size) +{ + return static_cast(channel)->transfer(count, sample_rate, sample_size); +} diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/cpm/include/cpm-common.h --- a/pkg/devices/lib/cpm/include/cpm-common.h Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/cpm/include/cpm-common.h Tue Oct 24 18:52:06 2023 +0200 @@ -67,6 +67,7 @@ uint32_t mask; uint8_t bit; bool defined; + uint32_t _asserted = 0, _deasserted = 0; public: explicit Field() @@ -74,14 +75,22 @@ { } - explicit Field(uint32_t reg, uint32_t mask, uint32_t bit) + explicit Field(uint32_t reg, uint32_t mask, uint32_t bit, + bool inverted = false) : reg(reg), mask(mask), bit(bit), defined(true) { + if (inverted) + _deasserted = mask; + else + _asserted = mask; } uint32_t get_field(Cpm_regs ®s); void set_field(Cpm_regs ®s, uint32_t value); + uint32_t get_asserted() { return _asserted; } + uint32_t get_deasserted() { return _deasserted; } + bool is_defined() { return defined; } uint32_t get_limit() { return mask; } diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/cpm/src/common.cc --- a/pkg/devices/lib/cpm/src/common.cc Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/cpm/src/common.cc Tue Oct 24 18:52:06 2023 +0200 @@ -164,7 +164,7 @@ Control::have_clock(Cpm_regs ®s) { if (_gate.is_defined()) - return !_gate.get_field(regs); + return _gate.get_field(regs) == _gate.get_asserted(); else return true; } @@ -173,14 +173,14 @@ Control::start_clock(Cpm_regs ®s) { if (_gate.is_defined()) - _gate.set_field(regs, 0); + _gate.set_field(regs, _gate.get_asserted()); } void Control::stop_clock(Cpm_regs ®s) { if (_gate.is_defined()) - _gate.set_field(regs, 1); + _gate.set_field(regs, _gate.get_deasserted()); } void diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/cpm/src/x1600.cc --- a/pkg/devices/lib/cpm/src/x1600.cc Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/cpm/src/x1600.cc Tue Oct 24 18:52:06 2023 +0200 @@ -98,7 +98,8 @@ Clock_source_cdbus (Divider_cdbus, 3, 30), // CDCS Clock_source_cim (Divider_cim, 3, 30), // CIMPCS Clock_source_ddr (Divider_ddr, 3, 30), // DCS - Clock_source_i2s (Divider0_i2s0, 1, 30), // I2PCS + Clock_source_i2s0 (Divider0_i2s0, 1, 30), // I2PCS + Clock_source_i2s1 (Divider0_i2s1, 1, 30), // I2PCS Clock_source_lcd (Divider_lcd, 3, 30), // LPCS Clock_source_mac (Divider_mac, 3, 30), // MACPCS Clock_source_msc0 (Divider_msc0, 3, 30), // MPCS @@ -126,7 +127,8 @@ Clock_change_enable_ahb2 (Clock_control, 1, 20), Clock_change_enable_ddr (Divider_ddr, 1, 29), Clock_change_enable_mac (Divider_mac, 1, 29), - // Clock_change_enable_i2s (Divider0_i2s0, 1, 29), // CE_I2S may not be change enable + Clock_gate_i2s0 (Divider0_i2s0, 1, 29), // CE_I2S is gate, not change enable + Clock_gate_i2s1 (Divider0_i2s1, 1, 29), // CE_I2S is gate, not change enable Clock_change_enable_lcd (Divider_lcd, 1, 29), Clock_change_enable_msc0 (Divider_msc0, 1, 29), Clock_change_enable_msc1 (Divider_msc1, 1, 29), @@ -167,45 +169,45 @@ Clock_divider_i2s1_n_auto (Divider1_i2s1, 1, 31), // I2S_NEN Clock_divider_i2s1_d_auto (Divider1_i2s1, 1, 30), // I2S_DEN - Clock_gate_main (Clock_control, 1, 23), // GATE_SCLKA - Clock_gate_ddr (Clock_gate0, 1, 31), // DDR - Clock_gate_ahb0 (Clock_gate0, 1, 29), // AHB0 - Clock_gate_apb0 (Clock_gate0, 1, 28), // APB0 - Clock_gate_rtc (Clock_gate0, 1, 27), // RTC - Clock_gate_aes (Clock_gate0, 1, 24), // AES - Clock_gate_lcd_pixel (Clock_gate0, 1, 23), // LCD - Clock_gate_cim (Clock_gate0, 1, 22), // CIM - Clock_gate_dma (Clock_gate0, 1, 21), // PDMA - Clock_gate_ost (Clock_gate0, 1, 20), // OST - Clock_gate_ssi0 (Clock_gate0, 1, 19), // SSI0 - Clock_gate_timer (Clock_gate0, 1, 18), // TCU - Clock_gate_dtrng (Clock_gate0, 1, 17), // DTRNG - Clock_gate_uart2 (Clock_gate0, 1, 16), // UART2 - Clock_gate_uart1 (Clock_gate0, 1, 15), // UART1 - Clock_gate_uart0 (Clock_gate0, 1, 14), // UART0 - Clock_gate_sadc (Clock_gate0, 1, 13), // SADC - Clock_gate_audio (Clock_gate0, 1, 11), // AUDIO - Clock_gate_ssi_slv (Clock_gate0, 1, 10), // SSI_SLV - Clock_gate_i2c1 (Clock_gate0, 1, 8), // I2C1 - Clock_gate_i2c0 (Clock_gate0, 1, 7), // I2C0 - Clock_gate_msc1 (Clock_gate0, 1, 5), // MSC1 - Clock_gate_msc0 (Clock_gate0, 1, 4), // MSC0 - Clock_gate_otg (Clock_gate0, 1, 3), // OTG - Clock_gate_sfc (Clock_gate0, 1, 2), // SFC - Clock_gate_efuse (Clock_gate0, 1, 1), // EFUSE - Clock_gate_nemc (Clock_gate0, 1, 0), // NEMC - Clock_gate_arb (Clock_gate1, 1, 30), // ARB - Clock_gate_mipi_csi (Clock_gate1, 1, 28), // MIPI_CSI - Clock_gate_intc (Clock_gate1, 1, 26), // INTC - Clock_gate_gmac0 (Clock_gate1, 1, 23), // GMAC0 - Clock_gate_uart3 (Clock_gate1, 1, 16), // UART3 - Clock_gate_i2s0_tx (Clock_gate1, 1, 9), // I2S0_dev_tclk - Clock_gate_i2s0_rx (Clock_gate1, 1, 8), // I2S0_dev_rclk - Clock_gate_hash (Clock_gate1, 1, 6), // HASH - Clock_gate_pwm (Clock_gate1, 1, 5), // PWM - Clock_gate_cdbus (Clock_gate1, 1, 2), // CDBUS - Clock_gate_can1 (Clock_gate1, 1, 1), // CAN1 - Clock_gate_can0 (Clock_gate1, 1, 0), // CAN0 + Clock_gate_main (Clock_control, 1, 23, true), // GATE_SCLKA + Clock_gate_ddr (Clock_gate0, 1, 31, true), // DDR + Clock_gate_ahb0 (Clock_gate0, 1, 29, true), // AHB0 + Clock_gate_apb0 (Clock_gate0, 1, 28, true), // APB0 + Clock_gate_rtc (Clock_gate0, 1, 27, true), // RTC + Clock_gate_aes (Clock_gate0, 1, 24, true), // AES + Clock_gate_lcd_pixel (Clock_gate0, 1, 23, true), // LCD + Clock_gate_cim (Clock_gate0, 1, 22, true), // CIM + Clock_gate_dma (Clock_gate0, 1, 21, true), // PDMA + Clock_gate_ost (Clock_gate0, 1, 20, true), // OST + Clock_gate_ssi0 (Clock_gate0, 1, 19, true), // SSI0 + Clock_gate_timer (Clock_gate0, 1, 18, true), // TCU + Clock_gate_dtrng (Clock_gate0, 1, 17, true), // DTRNG + Clock_gate_uart2 (Clock_gate0, 1, 16, true), // UART2 + Clock_gate_uart1 (Clock_gate0, 1, 15, true), // UART1 + Clock_gate_uart0 (Clock_gate0, 1, 14, true), // UART0 + Clock_gate_sadc (Clock_gate0, 1, 13, true), // SADC + Clock_gate_audio (Clock_gate0, 1, 11, true), // AUDIO + Clock_gate_ssi_slv (Clock_gate0, 1, 10, true), // SSI_SLV + Clock_gate_i2c1 (Clock_gate0, 1, 8, true), // I2C1 + Clock_gate_i2c0 (Clock_gate0, 1, 7, true), // I2C0 + Clock_gate_msc1 (Clock_gate0, 1, 5, true), // MSC1 + Clock_gate_msc0 (Clock_gate0, 1, 4, true), // MSC0 + Clock_gate_otg (Clock_gate0, 1, 3, true), // OTG + Clock_gate_sfc (Clock_gate0, 1, 2, true), // SFC + Clock_gate_efuse (Clock_gate0, 1, 1, true), // EFUSE + Clock_gate_nemc (Clock_gate0, 1, 0, true), // NEMC + Clock_gate_arb (Clock_gate1, 1, 30, true), // ARB + Clock_gate_mipi_csi (Clock_gate1, 1, 28, true), // MIPI_CSI + Clock_gate_intc (Clock_gate1, 1, 26, true), // INTC + Clock_gate_gmac0 (Clock_gate1, 1, 23, true), // GMAC0 + Clock_gate_uart3 (Clock_gate1, 1, 16, true), // UART3 + Clock_gate_i2s0_tx (Clock_gate1, 1, 9, true), // I2S0_dev_tclk + Clock_gate_i2s0_rx (Clock_gate1, 1, 8, true), // I2S0_dev_rclk + Clock_gate_hash (Clock_gate1, 1, 6, true), // HASH + Clock_gate_pwm (Clock_gate1, 1, 5, true), // PWM + Clock_gate_cdbus (Clock_gate1, 1, 2, true), // CDBUS + Clock_gate_can1 (Clock_gate1, 1, 1, true), // CAN1 + Clock_gate_can0 (Clock_gate1, 1, 0, true), // CAN0 Pll_enable_A (Pll_control_A, 1, 0), // APLLEN Pll_enable_E (Pll_control_E, 1, 0), // EPLLEN @@ -245,10 +247,13 @@ mux_hclock0 (Clock_hclock0), mux_hclock2 (Clock_hclock2), mux_pclock (Clock_pclock), + mux_main (3, Clocks(Clock_none, Clock_external, Clock_pll_A)), mux_core (3, Clocks(Clock_none, Clock_main, Clock_pll_M)), mux_bus (4, Clocks(Clock_main, Clock_pll_M, Clock_pll_E, Clock_external)), mux_dev (3, Clocks(Clock_main, Clock_pll_M, Clock_pll_E)), - mux_i2s (2, Clocks(Clock_main, Clock_pll_E)); + mux_i2s (2, Clocks(Clock_main, Clock_pll_E)), + mux_i2s0_rx (Clock_i2s0), + mux_i2s0_tx (Clock_i2s1); @@ -261,7 +266,9 @@ // Note the use of extra parentheses due to the annoying C++ "most vexing parse" // problem. See: https://en.wikipedia.org/wiki/Most_vexing_parse -static Clock clock_dma((Source(mux_hclock2)), (Control(Clock_gate_dma))), +static Clock clock_audio((Source(mux_hclock2)), (Control(Clock_gate_audio))), + + clock_dma((Source(mux_hclock2)), (Control(Clock_gate_dma))), clock_i2c((Source(mux_pclock)), (Control(Clock_gate_i2c0))), @@ -269,7 +276,11 @@ clock_i2c1((Source(mux_pclock)), (Control(Clock_gate_i2c1))), - clock_main(Source(mux_core, Clock_source_main), Control(Clock_gate_main)), + clock_i2s0(Source(mux_i2s, Clock_source_i2s0), Control(Clock_gate_i2s0)), + + clock_i2s1(Source(mux_i2s, Clock_source_i2s1), Control(Clock_gate_i2s1)), + + clock_main(Source(mux_main, Clock_source_main), Control(Clock_gate_main)), clock_mipi_csi((Source(mux_hclock0)), Control(Clock_gate_mipi_csi)), @@ -359,13 +370,13 @@ Divider(Clock_divider_ssi)); static Clock_divided_i2s - clock_i2s0_rx(Source(mux_i2s, Clock_source_i2s), + clock_i2s0_rx(Source(mux_i2s0_rx), Control(Clock_gate_i2s0_rx), Divider_i2s(Clock_divider_i2s0_m, Clock_divider_i2s0_n, Clock_divider_i2s0_d, Clock_divider_i2s0_n_auto, Clock_divider_i2s0_d_auto)), - clock_i2s0_tx(Source(mux_i2s, Clock_source_i2s), + clock_i2s0_tx(Source(mux_i2s0_tx), Control(Clock_gate_i2s0_tx), Divider_i2s(Clock_divider_i2s1_m, Clock_divider_i2s1_n, Clock_divider_i2s1_d, Clock_divider_i2s1_n_auto, @@ -391,6 +402,7 @@ // Clock register. static Clock_base *clocks[Clock_identifier_count] = { + &clock_audio, &clock_none, // Clock_aic_bitclk &clock_none, // Clock_aic_pclk &clock_can0, @@ -408,9 +420,12 @@ &clock_i2c, &clock_i2c0, &clock_i2c1, - &clock_none, // Clock_i2s + &clock_i2s0, // supplies i2s0_rx &clock_i2s0_rx, &clock_i2s0_tx, + &clock_i2s1, // supplies i2s0_tx + &clock_none, // Clock_i2s1_rx + &clock_none, // Clock_i2s1_tx &clock_none, // Clock_kbc &clock_none, // Clock_lcd &clock_lcd_pixel, diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/dma/include/dma-jz4730.h --- a/pkg/devices/lib/dma/include/dma-jz4730.h Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/dma/include/dma-jz4730.h Tue Oct 24 18:52:06 2023 +0200 @@ -1,7 +1,7 @@ /* * DMA support for the JZ4730. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -28,7 +28,7 @@ /* Enumerated types for various transfer parameters. */ -enum Dma_jz4730_request_type : unsigned +enum Dma_jz4730_request_type { Dma_request_external = 0, Dma_request_pcmcia_out = 4, @@ -53,19 +53,19 @@ Dma_request_ost2_underflow = 28, }; -enum Dma_jz4730_ext_level : unsigned +enum Dma_jz4730_ext_level { Dma_ext_active_high = 0, Dma_ext_active_low = 1, }; -enum Dma_jz4730_ext_output_mode_cycle : unsigned +enum Dma_jz4730_ext_output_mode_cycle { Dma_ext_output_mode_read_cycle = 0, Dma_ext_output_mode_write_cycle = 1, }; -enum Dma_jz4730_ext_req_detect_mode : unsigned +enum Dma_jz4730_ext_req_detect_mode { Dma_ext_req_detect_mode_low_level = 0, Dma_ext_req_detect_mode_falling_edge = 1, @@ -73,15 +73,6 @@ Dma_ext_req_detect_mode_rising_edge = 3, }; -enum Dma_jz4730_trans_unit_size : unsigned -{ - Dma_trans_unit_size_32_bit = 0, - Dma_trans_unit_size_8_bit = 1, - Dma_trans_unit_size_16_bit = 2, - Dma_trans_unit_size_16_byte = 3, - Dma_trans_unit_size_32_byte = 4, -}; - #ifdef __cplusplus @@ -103,7 +94,7 @@ Hw::Register_block<32> _regs; Dma_jz4730_chip *_chip; uint8_t _channel; - l4_cap_idx_t _irq=L4_INVALID_CAP; + l4_cap_idx_t _irq = L4_INVALID_CAP; // External transfer properties with defaults. @@ -117,9 +108,13 @@ unsigned int transfer(uint32_t source, uint32_t destination, unsigned int count, - enum Dma_jz4730_trans_unit_size size, + bool source_increment, bool destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, enum Dma_jz4730_request_type type=Dma_request_auto); + unsigned int wait(); + // External transfer property configuration. void set_output_polarity(enum Dma_jz4730_ext_level polarity) @@ -139,15 +134,13 @@ uint32_t encode_external_transfer(enum Dma_jz4730_request_type type); - uint32_t encode_source_address_increment(enum Dma_jz4730_request_type type); - - uint32_t encode_destination_address_increment(enum Dma_jz4730_request_type type); - uint32_t encode_req_detect_int_length(uint8_t units); - uint32_t encode_source_port_width(enum Dma_jz4730_request_type type); + uint32_t encode_source_port_width(uint8_t width); - uint32_t encode_destination_port_width(enum Dma_jz4730_request_type type); + uint32_t encode_destination_port_width(uint8_t width); + + uint32_t encode_transfer_unit_size(uint8_t size); // Transaction control. @@ -209,9 +202,14 @@ void jz4730_dma_set_req_detect_mode(void *dma_channel, enum Dma_jz4730_ext_req_detect_mode mode); -unsigned int jz4730_dma_transfer(void *dma_channel, uint32_t source, - uint32_t destination, unsigned int count, - enum Dma_jz4730_trans_unit_size size, - enum Dma_jz4730_request_type type); +unsigned int jz4730_dma_transfer(void *dma_channel, + uint32_t source, uint32_t destination, + unsigned int count, + int source_increment, int destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, + enum Dma_jz4730_request_type type); + +unsigned int jz4730_dma_wait(void *dma_channel); EXTERN_C_END diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/dma/include/dma-x1600.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/dma/include/dma-x1600.h Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,180 @@ +/* + * DMA support for the X1600. + * + * Copyright (C) 2021, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include + + + +/* Enumerated types for various transfer parameters. */ + +enum Dma_x1600_request_type +{ + Dma_request_auto = 8, + Dma_request_can0_out = 10, + Dma_request_can0_in = 11, + Dma_request_can1_out = 10, + Dma_request_can1_in = 11, + Dma_request_uart3_out = 14, + Dma_request_uart3_in = 15, + Dma_request_uart2_out = 16, + Dma_request_uart2_in = 17, + Dma_request_uart1_out = 18, + Dma_request_uart1_in = 19, + Dma_request_uart0_out = 20, + Dma_request_uart0_in = 21, + Dma_request_ssi0_send_empty = 22, + Dma_request_ssi0_recv_full = 23, + Dma_request_i2c0_send_empty = 36, + Dma_request_i2c0_recv_full = 37, + Dma_request_i2c1_send_empty = 38, + Dma_request_i2c1_recv_full = 39, + Dma_request_ssi_slv_out = 42, + Dma_request_ssi_slv_in = 43, + Dma_request_msc0_send_empty = 52, + Dma_request_msc0_recv_full = 53, + Dma_request_msc1_send_empty = 54, + Dma_request_msc1_recv_full = 55, + Dma_request_sadc_in = 56, + Dma_request_aic_loop_out = 61, + Dma_request_aic_out = 62, + Dma_request_aic_in = 63, +}; + + + +#ifdef __cplusplus + +#include +#include + +// Forward declaration. + +class Dma_x1600_chip; + + + +// DMA channel. + +class Dma_x1600_channel +{ +private: + Hw::Register_block<32> _regs; + Dma_x1600_chip *_chip; + uint8_t _channel; + l4_cap_idx_t _irq = L4_INVALID_CAP; + +public: + Dma_x1600_channel(Dma_x1600_chip *chip, uint8_t channel, l4_addr_t start, l4_cap_idx_t irq); + + unsigned int transfer(uint32_t source, uint32_t destination, + unsigned int count, + bool source_increment, bool destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, + enum Dma_x1600_request_type type=Dma_request_auto); + + unsigned int wait(); + +protected: + // Transfer property configuration. + + uint32_t encode_req_detect_int_length(uint8_t units); + + uint32_t encode_source_port_width(uint8_t width); + + uint32_t encode_destination_port_width(uint8_t width); + + uint32_t encode_transfer_unit_size(uint8_t size); + + // Transaction control. + + void ack_irq(); + + bool completed(); + + bool error(); + + bool halted(); + + bool wait_for_irq(); + + bool wait_for_irq(unsigned int timeout); +}; + +// DMA device control. + +class Dma_x1600_chip +{ +private: + Hw::Register_block<32> _regs; + l4_addr_t _start, _end; + Cpm_x1600_chip *_cpm; + +public: + Dma_x1600_chip(l4_addr_t start, l4_addr_t end, Cpm_x1600_chip *cpm); + + void disable(); + + void enable(); + + Dma_x1600_channel *get_channel(uint8_t channel, l4_cap_idx_t irq); + + // Transaction control. + + void ack_irq(uint8_t channel); + + bool error(); + + bool halted(); + + bool have_interrupt(uint8_t channel); +}; + +#endif /* __cplusplus */ + + + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *x1600_dma_init(l4_addr_t start, l4_addr_t end, void *cpm); + +void x1600_dma_disable(void *dma_chip); + +void x1600_dma_enable(void *dma_chip); + +void *x1600_dma_get_channel(void *dma, uint8_t channel, l4_cap_idx_t irq); + +unsigned int x1600_dma_transfer(void *dma_channel, + uint32_t source, uint32_t destination, + unsigned int count, + int source_increment, int destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, + enum Dma_x1600_request_type type); + +unsigned int x1600_dma_wait(void *dma_channel); + +EXTERN_C_END diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/dma/src/Makefile --- a/pkg/devices/lib/dma/src/Makefile Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/dma/src/Makefile Tue Oct 24 18:52:06 2023 +0200 @@ -4,7 +4,7 @@ TARGET = libdma.o.a libdma.o.so PC_FILENAME := libdrivers-dma -SRC_CC := jz4730.cc +SRC_CC := jz4730.cc x1600.cc PRIVATE_INCDIR += $(PKGDIR)/lib/dma/include diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/dma/src/jz4730.cc --- a/pkg/devices/lib/dma/src/jz4730.cc Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/dma/src/jz4730.cc Tue Oct 24 18:52:06 2023 +0200 @@ -27,7 +27,9 @@ #include #include -#include +#include + + enum Global_regs { @@ -96,7 +98,16 @@ enum Dma_control_status_bits : unsigned { Dma_source_address_incr = 0x00800000, + Dma_source_address_no_incr = 0x00000000, Dma_dest_address_incr = 0x00400000, + Dma_dest_address_no_incr = 0x00000000, + + Dma_trans_unit_size_32_bit = 0x00000000, + Dma_trans_unit_size_8_bit = 0x00000100, + Dma_trans_unit_size_16_bit = 0x00000200, + Dma_trans_unit_size_16_byte = 0x00000300, + Dma_trans_unit_size_32_byte = 0x00000400, + Dma_address_error = 0x00000010, Dma_trans_completed = 0x00000008, Dma_trans_halted = 0x00000004, @@ -142,62 +153,6 @@ ((external ? (int) _ext_end_of_process_mode : 0) << Dma_ext_end_of_process_mode); } -// Encode the appropriate source address increment for the given request type. -// Here, memory-to-memory transfers and transfers to peripherals involve an -// incrementing source address. Transfers from peripherals involve a static -// source address. - -uint32_t -Dma_jz4730_channel::encode_source_address_increment(enum Dma_jz4730_request_type type) -{ - switch (type) - { - case Dma_request_auto: - case Dma_request_pcmcia_out: - case Dma_request_des_out: - case Dma_request_uart3_out: - case Dma_request_uart2_out: - case Dma_request_uart1_out: - case Dma_request_uart0_out: - case Dma_request_ssi_send_empty: - case Dma_request_aic_send_empty: - case Dma_request_msc_send_empty: - case Dma_request_ost2_underflow: - return Dma_source_address_incr; - - default: - return 0; - } -} - -// Encode the appropriate destination address increment for the given request -// type. Here, memory-to-memory transfers and transfers from peripherals involve -// an incrementing destination address. Transfers to peripherals involve a static -// destination address. - -uint32_t -Dma_jz4730_channel::encode_destination_address_increment(enum Dma_jz4730_request_type type) -{ - switch (type) - { - case Dma_request_auto: - case Dma_request_pcmcia_in: - case Dma_request_des_in: - case Dma_request_uart3_in: - case Dma_request_uart2_in: - case Dma_request_uart1_in: - case Dma_request_uart0_in: - case Dma_request_ssi_recv_full: - case Dma_request_aic_recv_full: - case Dma_request_msc_recv_full: - case Dma_request_ost2_underflow: - return Dma_dest_address_incr; - - default: - return 0; - } -} - // Return the closest interval length greater than or equal to the number of // units given encoded in the request detection interval length field of the // control/status register. @@ -211,26 +166,23 @@ if (!units) return 0; - for (i = 0; i < 15; i++) + for (i = 0; i <= 15; i++) { - if (lengths[i++] >= units) + if (lengths[i] >= units) break; } - return lengths[i] << Dma_req_detect_int_length; + return i << Dma_req_detect_int_length; } -// Encode the appropriate source port width for the given request type. +// Encode the appropriate source port width. uint32_t -Dma_jz4730_channel::encode_source_port_width(enum Dma_jz4730_request_type type) +Dma_jz4730_channel::encode_source_port_width(uint8_t width) { - switch (type) + switch (width) { - case Dma_request_uart3_in: - case Dma_request_uart2_in: - case Dma_request_uart1_in: - case Dma_request_uart0_in: + case 1: return Dma_port_width_8_bit << Dma_source_port_width; default: @@ -241,14 +193,11 @@ // Encode the appropriate destination port width for the given request type. uint32_t -Dma_jz4730_channel::encode_destination_port_width(enum Dma_jz4730_request_type type) +Dma_jz4730_channel::encode_destination_port_width(uint8_t width) { - switch (type) + switch (width) { - case Dma_request_uart3_out: - case Dma_request_uart2_out: - case Dma_request_uart1_out: - case Dma_request_uart0_out: + case 1: return Dma_port_width_8_bit << Dma_dest_port_width; default: @@ -256,12 +205,38 @@ } } +// Encode the transfer unit size. + +uint32_t +Dma_jz4730_channel::encode_transfer_unit_size(uint8_t size) +{ + switch (size) + { + case 1: + return Dma_trans_unit_size_8_bit; + + case 2: + return Dma_trans_unit_size_16_bit; + + case 16: + return Dma_trans_unit_size_16_byte; + + case 32: + return Dma_trans_unit_size_32_byte; + + default: + return Dma_trans_unit_size_32_bit; + } +} + // Transfer data between memory locations. unsigned int Dma_jz4730_channel::transfer(uint32_t source, uint32_t destination, unsigned int count, - enum Dma_jz4730_trans_unit_size size, + bool source_increment, bool destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, enum Dma_jz4730_request_type type) { // Ensure an absence of address error and halt conditions globally and in this channel. @@ -285,7 +260,9 @@ // Set transfer count to the number of units. - _regs[Dma_transfer_count] = count; + unsigned int units = count < Dma_transfer_count_mask ? count : Dma_transfer_count_mask; + + _regs[Dma_transfer_count] = units; // Set auto-request for memory-to-memory transfers. Otherwise, set the // indicated request type. @@ -303,19 +280,27 @@ */ _regs[Dma_control_status] = encode_external_transfer(type) | - encode_source_address_increment(type) | - encode_destination_address_increment(type) | - encode_source_port_width(type) | - encode_destination_port_width(type) | - (size << Dma_trans_unit_size) | + (source_increment ? Dma_source_address_incr : Dma_source_address_no_incr) | + (destination_increment ? Dma_dest_address_incr : Dma_dest_address_no_incr) | + encode_source_port_width(source_width) | + encode_destination_port_width(destination_width) | + encode_transfer_unit_size(transfer_unit_size) | (Dma_trans_mode_single << Dma_trans_mode) | Dma_channel_irq_enable | Dma_channel_enable; + // Return the number of units to transfer. + + return units; +} + +unsigned int +Dma_jz4730_channel::wait() +{ // An interrupt will occur upon completion, the completion flag will be set // and the transfer count will be zero. - unsigned int remaining = count; + unsigned int remaining = 0; do { @@ -334,9 +319,16 @@ } while (!error() && !halted() && !completed()); - // Return the number of transferred units. + // Reset the channel status. - return count - remaining; + _regs[Dma_control_status] = _regs[Dma_control_status] & ~(Dma_channel_enable | + Dma_trans_completed | Dma_address_error | + Dma_trans_halted); + _regs[Dma_transfer_count] = 0; + + // Return the number of remaining units. + + return remaining; } // Wait indefinitely for an interrupt request, returning true if one was delivered. @@ -424,7 +416,7 @@ while (_regs[Dma_control] & Dma_control_enable); } -// Obtain a channel object. Only one channel is supported. +// Obtain a channel object. Dma_jz4730_channel * Dma_jz4730_chip::get_channel(uint8_t channel, l4_cap_idx_t irq) @@ -487,10 +479,20 @@ static_cast(dma_channel)->set_req_detect_mode(mode); } -unsigned int jz4730_dma_transfer(void *dma_channel, uint32_t source, - uint32_t destination, unsigned int count, - enum Dma_jz4730_trans_unit_size size, +unsigned int jz4730_dma_transfer(void *dma_channel, + uint32_t source, uint32_t destination, + unsigned int count, + int source_increment, int destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, enum Dma_jz4730_request_type type) { - return static_cast(dma_channel)->transfer(source, destination, count, size, type); + return static_cast(dma_channel)->transfer(source, + destination, count, source_increment, destination_increment, source_width, + destination_width, transfer_unit_size, type); } + +unsigned int jz4730_dma_wait(void *dma_channel) +{ + return static_cast(dma_channel)->wait(); +} diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/dma/src/x1600.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/dma/src/x1600.cc Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,509 @@ +/* + * DMA support for the X1600. + * + * Copyright (C) 2021, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include + +#include +#include +#include +#include + +#include + + + +enum Global_regs +{ + Dma_control = 0x1000, // DMAC + Dma_irq_pending = 0x1004, // DIRQP +}; + +enum Channel_regs +{ + Dma_source = 0x00, // DSA + Dma_destination = 0x04, // DTA + Dma_transfer_count = 0x08, // DTC + Dma_request_source = 0x0c, // DRT + Dma_control_status = 0x10, // DCS + Dma_command = 0x14, // DCM + Dma_descriptor_address = 0x18, // DDA + Dma_stride = 0x1c, // DSD +}; + +enum Dma_control_bits : unsigned +{ + Dma_fast_msc_transfer = 0x80000000, // FMSC + Dma_fast_ssi_transfer = 0x40000000, // FSSI + Dma_fast_tssi_transfer = 0x20000000, // FTSSI + Dma_fast_uart_transfer = 0x10000000, // FUART + Dma_fast_aic_transfer = 0x08000000, // FAIC + Dma_control_trans_halted = 0x00000008, // HLT + Dma_control_address_error = 0x00000004, // AR + Dma_control_enable = 0x00000001, // DMAE +}; + +enum Dma_transfer_count_bits : unsigned +{ + Dma_transfer_count_mask = 0x00ffffff, +}; + +enum Dma_request_source_bits : unsigned +{ + Dma_request_type_mask = 0x0000001f, +}; + +enum Dma_control_status_bits : unsigned +{ + Dma_no_descriptor_transfer = 0x80000000, + Dma_8word_descriptor = 0x40000000, + Dma_copy_offset_mask = 0x0000ff00, + Dma_address_error = 0x00000010, + Dma_trans_completed = 0x00000008, + Dma_trans_halted = 0x00000004, + Dma_channel_enable = 0x00000001, + + Dma_copy_offset_shift = 8, +}; + +enum Dma_command_bits : unsigned +{ + Dma_source_address_increment = 0x800000, + Dma_source_address_no_increment = 0x000000, + Dma_destination_address_increment = 0x400000, + Dma_destination_address_no_increment = 0x000000, + + Dma_source_address_increment_wrap = 0x200000, + Dma_destination_address_increment_wrap = 0x100000, + Dma_recommended_data_unit_size_mask = 0x0f0000, + Dma_source_port_width_mask = 0x00c000, + Dma_destination_port_width_mask = 0x003000, + Dma_transfer_unit_size_mask = 0x000f00, + + Dma_trans_unit_size_32_bit = 0x000000, + Dma_trans_unit_size_8_bit = 0x000100, + Dma_trans_unit_size_16_bit = 0x000200, + Dma_trans_unit_size_16_byte = 0x000300, + Dma_trans_unit_size_32_byte = 0x000400, + Dma_trans_unit_size_64_byte = 0x000500, + Dma_trans_unit_size_128_byte = 0x000600, + Dma_trans_unit_size_autonomous = 0x000700, + Dma_trans_unit_size_external = 0x000800, + + Dma_source_address_compare_index = 0x000080, + Dma_destination_address_compare_index = 0x000040, + Dma_stride_enable = 0x000004, + Dma_transfer_irq_enable = 0x000002, + Dma_descriptor_link_enable = 0x000001, + + Dma_recommended_data_unit_size_shift = 16, + Dma_source_port_width_shift = 14, + Dma_destination_port_width_shift = 12, + Dma_transfer_unit_size_shift = 8, +}; + +enum Dma_port_width_values : unsigned +{ + Dma_port_width_32_bit = 0, + Dma_port_width_8_bit = 1, + Dma_port_width_16_bit = 2, +}; + + + +// Initialise a channel. + +Dma_x1600_channel::Dma_x1600_channel(Dma_x1600_chip *chip, uint8_t channel, + l4_addr_t start, l4_cap_idx_t irq) +: _chip(chip), _channel(channel), _irq(irq) +{ + _regs = new Hw::Mmio_register_block<32>(start); + + // Initialise the transfer count. + + _regs[Dma_transfer_count] = 0; +} + +// Return the closest interval length greater than or equal to the number of +// units given encoded in the request detection interval length field of the +// control/status register. + +uint32_t +Dma_x1600_channel::encode_req_detect_int_length(uint8_t units) +{ + static uint8_t lengths[] = {0, 1, 2, 3, 4, 8, 16, 32, 64, 128}; + int i; + + if (!units) + return 0; + + for (i = 0; i <= 9; i++) + { + if (lengths[i] >= units) + break; + } + + return i << Dma_recommended_data_unit_size_shift; +} + +// Encode the appropriate source port width for the given request type. + +uint32_t +Dma_x1600_channel::encode_source_port_width(uint8_t width) +{ + switch (width) + { + case 1: + return Dma_port_width_8_bit << Dma_source_port_width_shift; + + case 2: + return Dma_port_width_16_bit << Dma_source_port_width_shift; + + default: + return Dma_port_width_32_bit << Dma_source_port_width_shift; + } +} + +// Encode the appropriate destination port width for the given request type. + +uint32_t +Dma_x1600_channel::encode_destination_port_width(uint8_t width) +{ + switch (width) + { + case 1: + return Dma_port_width_8_bit << Dma_destination_port_width_shift; + + case 2: + return Dma_port_width_16_bit << Dma_destination_port_width_shift; + + default: + return Dma_port_width_32_bit << Dma_destination_port_width_shift; + } +} + +// Encode the transfer unit size. +// NOTE: This does not handle the external case. + +uint32_t +Dma_x1600_channel::encode_transfer_unit_size(uint8_t size) +{ + switch (size) + { + case 0: + return Dma_trans_unit_size_autonomous; + + case 1: + return Dma_trans_unit_size_8_bit; + + case 2: + return Dma_trans_unit_size_16_bit; + + case 16: + return Dma_trans_unit_size_16_byte; + + case 32: + return Dma_trans_unit_size_32_byte; + + case 64: + return Dma_trans_unit_size_64_byte; + + case 128: + return Dma_trans_unit_size_128_byte; + + default: + return Dma_trans_unit_size_32_bit; + } +} + +// Transfer data between memory locations. + +unsigned int +Dma_x1600_channel::transfer(uint32_t source, uint32_t destination, + unsigned int count, + bool source_increment, bool destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, + enum Dma_x1600_request_type type) +{ + printf("transfer:%s%s%s%s\n", error() ? " error" : "", + halted() ? " halted" : "", + completed() ? " completed" : "", + _regs[Dma_transfer_count] ? " count" : ""); + + // Ensure an absence of address error and halt conditions globally and in this channel. + + if (error() || halted()) + return 0; + + // Ensure an absence of transaction completed and zero transfer count for this channel. + + if (completed() || _regs[Dma_transfer_count]) + return 0; + + // Disable the channel. + + _regs[Dma_control_status] = _regs[Dma_control_status] & ~Dma_channel_enable; + + // Set addresses. + + _regs[Dma_source] = source; + _regs[Dma_destination] = destination; + + // Set transfer count to the number of units. + + unsigned int units = count < Dma_transfer_count_mask ? count : Dma_transfer_count_mask; + + _regs[Dma_transfer_count] = units; + + // Set auto-request for memory-to-memory transfers. Otherwise, set the + // indicated request type. + + _regs[Dma_request_source] = type; + + // For a descriptor, the actual fields would be populated instead of the + // command register, descriptor transfer would be indicated in the control/ + // status register along with the appropriate descriptor size indicator. + + /* NOTE: To be considered... + * request detection interval length (for autonomous mode) + */ + + _regs[Dma_command] = (source_increment ? Dma_source_address_increment : Dma_source_address_no_increment) | + (destination_increment ? Dma_destination_address_increment : Dma_destination_address_no_increment) | + encode_source_port_width(source_width) | + encode_destination_port_width(destination_width) | + encode_transfer_unit_size(transfer_unit_size) | + Dma_transfer_irq_enable; + + // For a descriptor, the descriptor address would be set and the doorbell + // register field for the channel set. + + // Enable the channel (and peripheral). + + _regs[Dma_control_status] = Dma_no_descriptor_transfer | + Dma_channel_enable; + + // Return the number of units to transfer. + + return units; +} + +unsigned int +Dma_x1600_channel::wait() +{ + // An interrupt will occur upon completion, the completion flag will be set + // and the transfer count will be zero. + + unsigned int remaining = 0; + + do + { + if (!wait_for_irq(1000000)) + printf("status = %x\n", (uint32_t) _regs[Dma_control_status]); + else + { + printf("status = %x\n", (uint32_t) _regs[Dma_control_status]); + remaining = _regs[Dma_transfer_count]; + ack_irq(); + break; + } + } + while (!error() && !halted() && !completed()); + + // Reset the channel status. + + _regs[Dma_control_status] = _regs[Dma_control_status] & ~(Dma_channel_enable | + Dma_trans_completed | Dma_address_error | + Dma_trans_halted); + _regs[Dma_transfer_count] = 0; + + // Return the number of remaining units. + + return remaining; +} + +// Wait indefinitely for an interrupt request, returning true if one was delivered. + +bool +Dma_x1600_channel::wait_for_irq() +{ + return !l4_error(l4_irq_receive(_irq, L4_IPC_NEVER)) && _chip->have_interrupt(_channel); +} + +// Wait up to the given timeout (in microseconds) for an interrupt request, +// returning true if one was delivered. + +bool +Dma_x1600_channel::wait_for_irq(unsigned int timeout) +{ + return !l4_error(l4_irq_receive(_irq, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4util_micros2l4to(timeout)))) && _chip->have_interrupt(_channel); +} + +// Acknowledge an interrupt condition. + +void +Dma_x1600_channel::ack_irq() +{ + _chip->ack_irq(_channel); +} + +// Return whether a transfer has completed. + +bool +Dma_x1600_channel::completed() +{ + return _regs[Dma_control_status] & Dma_trans_completed ? true : false; +} + +// Return whether an address error condition has arisen. + +bool +Dma_x1600_channel::error() +{ + return _chip->error() || (_regs[Dma_control_status] & Dma_address_error ? true : false); +} + +// Return whether a transfer has halted. + +bool +Dma_x1600_channel::halted() +{ + return _chip->halted() || (_regs[Dma_control_status] & Dma_trans_halted ? true : false); +} + + + +// Initialise the I2C controller. + +Dma_x1600_chip::Dma_x1600_chip(l4_addr_t start, l4_addr_t end, + Cpm_x1600_chip *cpm) +: _start(start), _end(end), _cpm(cpm) +{ + _regs = new Hw::Mmio_register_block<32>(start); +} + +// Enable the peripheral. + +void +Dma_x1600_chip::enable() +{ + // Make sure that the DMA clock is available. + + _cpm->start_clock(Clock_dma); + + _regs[Dma_control] = Dma_control_enable; + while (!(_regs[Dma_control] & Dma_control_enable)); +} + +// Disable the channel. + +void +Dma_x1600_chip::disable() +{ + _regs[Dma_control] = 0; + while (_regs[Dma_control] & Dma_control_enable); +} + +// Obtain a channel object. + +Dma_x1600_channel * +Dma_x1600_chip::get_channel(uint8_t channel, l4_cap_idx_t irq) +{ + if (channel < 32) + return new Dma_x1600_channel(this, channel, _start + 0x20 * channel, irq); + else + throw -L4_EINVAL; +} + +// Return whether an interrupt is pending on the given channel. + +bool +Dma_x1600_chip::have_interrupt(uint8_t channel) +{ + return _regs[Dma_irq_pending] & (1 << channel) ? true : false; +} + +// Acknowledge an interrupt condition on the given channel. + +void +Dma_x1600_chip::ack_irq(uint8_t channel) +{ + _regs[Dma_irq_pending] = _regs[Dma_irq_pending] & ~(1 << channel); +} + +// Return whether an address error condition has arisen. + +bool +Dma_x1600_chip::error() +{ + return _regs[Dma_control] & Dma_control_address_error ? true : false; +} + +// Return whether a transfer has halted. + +bool +Dma_x1600_chip::halted() +{ + return _regs[Dma_control] & Dma_control_trans_halted ? true : false; +} + + + +// C language interface functions. + +void *x1600_dma_init(l4_addr_t start, l4_addr_t end, void *cpm) +{ + return (void *) new Dma_x1600_chip(start, end, static_cast(cpm)); +} + +void x1600_dma_disable(void *dma_chip) +{ + static_cast(dma_chip)->disable(); +} + +void x1600_dma_enable(void *dma_chip) +{ + static_cast(dma_chip)->enable(); +} + +void *x1600_dma_get_channel(void *dma, uint8_t channel, l4_cap_idx_t irq) +{ + return static_cast(dma)->get_channel(channel, irq); +} + +unsigned int x1600_dma_transfer(void *dma_channel, + uint32_t source, uint32_t destination, + unsigned int count, + int source_increment, int destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, + enum Dma_x1600_request_type type) +{ + return static_cast(dma_channel)->transfer(source, + destination, count, source_increment, destination_increment, source_width, + destination_width, transfer_unit_size, type); +} + +unsigned int x1600_dma_wait(void *dma_channel) +{ + return static_cast(dma_channel)->wait(); +} diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/gpio/include/gpio-jz4730.h --- a/pkg/devices/lib/gpio/include/gpio-jz4730.h Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/include/gpio-jz4730.h Tue Oct 24 18:52:06 2023 +0200 @@ -2,7 +2,7 @@ * GPIO driver for Ingenic JZ4730. * (See below for additional copyright and licensing notices.) * - * Copyright (C) 2017, 2018 Paul Boddie + * Copyright (C) 2017, 2018, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -117,6 +117,11 @@ void _config_pad(unsigned bitmap, unsigned func, unsigned value); + // Paired register field access. + + void _get_pin_value(unsigned pin, uint32_t reg_upper, uint32_t reg_lower, + unsigned *value); + public: Gpio_jz4730_chip(l4_addr_t start, l4_addr_t end, unsigned nr_pins); @@ -136,6 +141,7 @@ void config_pull(unsigned pin, unsigned mode); void config_pad(unsigned pin, unsigned func, unsigned value); void config_get(unsigned pin, unsigned reg, unsigned *value); + void config_pad_get(unsigned pin, unsigned *func, unsigned *value); // Multiple pin configuration methods. @@ -172,6 +178,7 @@ void jz4730_gpio_config_pull(void *gpio, unsigned pin, unsigned mode); void jz4730_gpio_config_pad(void *gpio, unsigned pin, unsigned func, unsigned value); void jz4730_gpio_config_get(void *gpio, unsigned pin, unsigned reg, unsigned *value); +void jz4730_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value); void jz4730_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues); void jz4730_gpio_multi_config_pull(void *gpio, Pin_slice const *mask, unsigned mode); diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/gpio/include/gpio-jz4740.h --- a/pkg/devices/lib/gpio/include/gpio-jz4740.h Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/include/gpio-jz4740.h Tue Oct 24 18:52:06 2023 +0200 @@ -2,7 +2,7 @@ * GPIO driver for Ingenic JZ4740. * (See below for additional copyright and licensing notices.) * - * Copyright (C) 2017, 2018 Paul Boddie + * Copyright (C) 2017, 2018, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -128,6 +128,7 @@ void config_pull(unsigned pin, unsigned mode); void config_pad(unsigned pin, unsigned func, unsigned value); void config_get(unsigned pin, unsigned reg, unsigned *value); + void config_pad_get(unsigned pin, unsigned *func, unsigned *value); // Multiple pin configuration methods. @@ -164,6 +165,7 @@ void jz4740_gpio_config_pull(void *gpio, unsigned pin, unsigned mode); void jz4740_gpio_config_pad(void *gpio, unsigned pin, unsigned func, unsigned value); void jz4740_gpio_config_get(void *gpio, unsigned pin, unsigned reg, unsigned *value); +void jz4740_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value); void jz4740_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues); void jz4740_gpio_multi_config_pull(void *gpio, Pin_slice const *mask, unsigned mode); diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/gpio/include/gpio-jz4780.h --- a/pkg/devices/lib/gpio/include/gpio-jz4780.h Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/include/gpio-jz4780.h Tue Oct 24 18:52:06 2023 +0200 @@ -2,7 +2,7 @@ * GPIO driver for Ingenic JZ4780. * (See below for additional copyright and licensing notices.) * - * Copyright (C) 2017, 2018 Paul Boddie + * Copyright (C) 2017, 2018, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -118,6 +118,7 @@ void config_pull(unsigned pin, unsigned mode); void config_pad(unsigned pin, unsigned func, unsigned value); void config_get(unsigned pin, unsigned reg, unsigned *value); + void config_pad_get(unsigned pin, unsigned *func, unsigned *value); // Multiple pin configuration methods. @@ -154,6 +155,7 @@ void jz4780_gpio_config_pull(void *gpio, unsigned pin, unsigned mode); void jz4780_gpio_config_pad(void *gpio, unsigned pin, unsigned func, unsigned value); void jz4780_gpio_config_get(void *gpio, unsigned pin, unsigned reg, unsigned *value); +void jz4780_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value); void jz4780_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues); void jz4780_gpio_multi_config_pad(void *gpio, Pin_slice const *mask, unsigned func, unsigned value); diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/gpio/include/gpio-x1600.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/gpio/include/gpio-x1600.h Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,171 @@ +/* + * GPIO driver for Ingenic X1600. + * (See below for additional copyright and licensing notices.) + * + * Copyright (C) 2017, 2018, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + * + * + * Subject to other copyrights, being derived from the bcm2835.cc and + * omap.cc GPIO driver implementations. + * + * This file is part of TUD:OS and distributed under the terms of the + * GNU General Public License 2. + * Please see the COPYING-GPL-2 file for details. + */ + +#pragma once + +#include +#include +#include +#include "gpio.h" + + + +#ifdef __cplusplus + +#include + +// GPIO device control. + +class Gpio_x1600_irq_pin : public Hw::Gpio_irq_pin +{ + unsigned _pin; + Hw::Register_block<32> _regs; + + // Convenience method for obtaining the bit corresponding to a pin. + + l4_uint32_t _pin_bit(unsigned pin) + { return 1 << (pin & 31); } + + void write_reg_pin(unsigned reg); + +public: + Gpio_x1600_irq_pin(unsigned pin, Hw::Register_block<32> const ®s); + + void do_mask(); + void do_unmask(); + bool do_set_mode(unsigned mode); + int clear(); + bool enabled(); +}; + +class Gpio_x1600_chip : public Hw::Gpio_chip +{ +private: + Hw::Register_block<32> _regs; + + l4_addr_t _start, _end; + unsigned _nr_pins; + l4_uint32_t _pull_ups, _pull_downs; + + // Convenience method for obtaining the bit corresponding to a pin. + + l4_uint32_t _pin_bit(unsigned pin) + { return 1 << (pin & 31); } + + // Convenience method for obtaining the bit position of a pin. + + unsigned _pin_shift(unsigned pin) + { return pin % 32; } + + // Permit only "aligned" accesses to registers. + + unsigned _reg_offset_check(unsigned pin_offset) const + { + switch (pin_offset) + { + case 0: + return 0; + + default: + throw -L4_EINVAL; + } + } + +public: + Gpio_x1600_chip(l4_addr_t start, l4_addr_t end, + unsigned nr_pins, + l4_uint32_t pull_ups, l4_uint32_t pull_downs); + + // Obtain the number of pins. + + unsigned nr_pins() const { return _nr_pins; } + + // Unnecessary operations. + + void request(unsigned) {} + void free(unsigned) {} + + // Configuration methods. + + void setup(unsigned pin, unsigned mode, int value = 0); + void config_pull(unsigned pin, unsigned mode); + void config_pad(unsigned pin, unsigned func, unsigned value); + void config_get(unsigned pin, unsigned reg, unsigned *value); + void config_pad_get(unsigned pin, unsigned *func, unsigned *value); + + // Multiple pin configuration methods. + + void multi_setup(Pin_slice const &mask, unsigned mode, unsigned outvalues = 0); + void multi_config_pad(Pin_slice const &mask, unsigned func, unsigned value = 0); + void multi_set(Pin_slice const &mask, unsigned data); + unsigned multi_get(unsigned offset); + + // IRQ pin configuration. + + Hw::Gpio_irq_pin *get_irq(unsigned pin); + + // Pin/port data methods. + + int get(unsigned pin); + void set(unsigned pin, int value); + +private: + void config(unsigned pin, unsigned mode); +}; + +#endif /* __cplusplus */ + + + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *x1600_gpio_init(l4_addr_t start, l4_addr_t end, unsigned pins, + l4_uint32_t pull_ups, l4_uint32_t pull_downs); + +void x1600_gpio_setup(void *gpio, unsigned pin, unsigned mode, int value); +void x1600_gpio_config_pull(void *gpio, unsigned pin, unsigned mode); +void x1600_gpio_config_pad(void *gpio, unsigned pin, unsigned func, unsigned value); +void x1600_gpio_config_get(void *gpio, unsigned pin, unsigned reg, unsigned *value); +void x1600_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value); + +void x1600_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues); +void x1600_gpio_multi_config_pad(void *gpio, Pin_slice const *mask, unsigned func, unsigned value); +void x1600_gpio_multi_set(void *gpio, Pin_slice const *mask, unsigned data); +unsigned x1600_gpio_multi_get(void *gpio, unsigned offset); + +int x1600_gpio_get(void *gpio, unsigned pin); +void x1600_gpio_set(void *gpio, unsigned pin, int value); + +void *x1600_gpio_get_irq(void *gpio, unsigned pin); +bool x1600_gpio_irq_set_mode(void *gpio_irq, unsigned mode); + +EXTERN_C_END diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/gpio/include/gpio.h --- a/pkg/devices/lib/gpio/include/gpio.h Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/include/gpio.h Tue Oct 24 18:52:06 2023 +0200 @@ -2,7 +2,7 @@ * GPIO driver definitions. * (See below for additional copyright and licensing notices.) * - * Copyright (C) 2017, 2018 Paul Boddie + * Copyright (C) 2017, 2018, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -80,6 +80,7 @@ { Function_gpio, Function_alt, + Function_irq, }; virtual void request(unsigned pin) = 0; @@ -123,6 +124,14 @@ virtual void config_get(unsigned pin, unsigned func, unsigned *value) = 0; /** + * \brief Get platform specific pad configuration. + * \param pin the pin to configure. + * \param func a platform specific sub-function of a pad to be configured + * \param value a platform specific value for the given sub-function. + */ + virtual void config_pad_get(unsigned pin, unsigned *func, unsigned *value) = 0; + + /** * \brief Get the value of the given pin (generic API). * \param pin the pin to read the value from. */ @@ -210,6 +219,7 @@ { Function_gpio, Function_alt, + Function_irq, }; #endif /* __cplusplus */ diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/gpio/src/Makefile --- a/pkg/devices/lib/gpio/src/Makefile Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/src/Makefile Tue Oct 24 18:52:06 2023 +0200 @@ -4,7 +4,7 @@ TARGET = libgpio.o.a libgpio.o.so PC_FILENAME := libdrivers-gpio -SRC_CC := jz4730.cc jz4740.cc jz4780.cc +SRC_CC := jz4730.cc jz4740.cc jz4780.cc x1600.cc PRIVATE_INCDIR += $(PKGDIR)/lib/gpio/include diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/gpio/src/jz4730.cc --- a/pkg/devices/lib/gpio/src/jz4730.cc Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/src/jz4730.cc Tue Oct 24 18:52:06 2023 +0200 @@ -2,7 +2,7 @@ * GPIO driver for Ingenic JZ4730. * (See below for additional copyright and licensing notices.) * - * Copyright (C) 2017, 2018 Paul Boddie + * Copyright (C) 2017, 2018, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -46,6 +46,7 @@ Irq_detect_lower = 0x018, // PxGPDLR Irq_detect_upper = 0x01c, // PxGPDUR Irq_enable = 0x020, // PxGPIER + Irq_mask = 0x024, // PxGPIM Irq_flag = 0x028, // PxGPFR }; @@ -70,8 +71,6 @@ } } - - // IRQ control for each GPIO pin. Gpio_jz4730_irq_pin::Gpio_jz4730_irq_pin(unsigned pin, Hw::Register_block<32> const ®s) @@ -129,6 +128,9 @@ { // Set the interrupt bit in the PxGPIER register. + /* NOTE: This should use the Irq_mask/PxGPIM register, with the enable + register actually setting IRQ mode. */ + clear_reg_pin(Irq_enable); } @@ -137,6 +139,9 @@ // Set the interrupt bit in the PxGPIER register, first also clearing the // flag bit in the PxGPFR register to allow interrupts to be delivered. + /* NOTE: This should use the Irq_mask/PxGPIM register, with the enable + register actually setting IRQ mode. */ + clear_reg_pin(Irq_flag); set_reg_pin(Irq_enable); } @@ -306,19 +311,23 @@ Gpio_jz4730_chip::_config(unsigned bitmap, unsigned mode) { uint32_t upper_mask, lower_mask; - unsigned bitmap_values = bitmap; switch (mode) { case Input: case Irq: - // Clear the direction flags below. - bitmap_values = 0; - // Fall through to the next case to complete the operation. + // Clear the direction flags. + _regs[Port_direction] = _regs[Port_direction] & ~bitmap; + + // Clear the port function for the bits. + _get_bitmaps(bitmap, 3, &upper_mask, &lower_mask); + _regs[Port_function_upper] = (_regs[Port_function_upper] & ~upper_mask); + _regs[Port_function_lower] = (_regs[Port_function_lower] & ~lower_mask); + break; case Output: - // Clear the flags if set immediately above; otherwise, set them. - _regs[Port_direction] = (_regs[Port_direction] & ~bitmap) | bitmap_values; + // Set the direction flags. + _regs[Port_direction] = _regs[Port_direction] | bitmap; // Clear the port function for the bits. _get_bitmaps(bitmap, 3, &upper_mask, &lower_mask); @@ -382,12 +391,15 @@ { case Hw::Gpio_chip::Function_alt: _get_bitmaps(bitmap, value, &upper, &lower); - // Fall through to the next case to complete the operation. + _get_bitmaps(bitmap, 3, &upper_mask, &lower_mask); + _regs[Port_function_upper] = (_regs[Port_function_upper] & ~upper_mask) | upper; + _regs[Port_function_lower] = (_regs[Port_function_lower] & ~lower_mask) | lower; + break; case Hw::Gpio_chip::Function_gpio: _get_bitmaps(bitmap, 3, &upper_mask, &lower_mask); - _regs[Port_function_upper] = (_regs[Port_function_upper] & ~upper_mask) | upper; - _regs[Port_function_lower] = (_regs[Port_function_lower] & ~lower_mask) | lower; + _regs[Port_function_upper] = _regs[Port_function_upper] & ~upper_mask; + _regs[Port_function_lower] = _regs[Port_function_lower] & ~lower_mask; break; default: @@ -406,6 +418,62 @@ *value = (_regs[reg] >> _pin_shift(pin)) & 1; } +// Return function and function-specific configuration for a pin. + +void +Gpio_jz4730_chip::config_pad_get(unsigned pin, unsigned *func, unsigned *value) +{ + unsigned detect, direction, pin_function, interrupt; + + // Get the pin function using the awkward register pairing. + + _get_pin_value(pin, Port_function_upper, Port_function_lower, &pin_function); + + if (pin_function) + { + *func = Hw::Gpio_chip::Function_alt; + *value = pin_function; + return; + } + + config_get(pin, Irq_enable, &interrupt); + + if (interrupt) + { + _get_pin_value(pin, Irq_detect_upper, Irq_detect_lower, &detect); + + *func = Hw::Gpio_chip::Function_irq; + + switch (detect) + { + case 0: *value = L4_IRQ_F_LEVEL_LOW; break; + case 1: *value = L4_IRQ_F_LEVEL_HIGH; break; + case 2: *value = L4_IRQ_F_NEG_EDGE; break; + default: case 3: *value = L4_IRQ_F_POS_EDGE; break; + } + return; + } + + config_get(pin, Port_direction, &direction); + + *func = Hw::Gpio_chip::Function_gpio; + *value = direction ? Output : Input; +} + +void +Gpio_jz4730_chip::_get_pin_value(unsigned pin, uint32_t reg_upper, + uint32_t reg_lower, unsigned *value) +{ + uint8_t pin_out; + + if (pin >= _nr_pins) + throw -L4_EINVAL; + + uint32_t reg = _select_bank_for_pin(reg_upper, reg_lower, pin, &pin_out); + + *value = (_regs[reg] & (3 << (pin_out << 1))) >> (pin_out << 1); +} + // Obtain an IRQ abstraction for a pin. Hw::Gpio_irq_pin * @@ -473,6 +541,11 @@ static_cast(gpio)->config_get(pin, reg, value); } +void jz4730_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value) +{ + static_cast(gpio)->config_pad_get(pin, func, value); +} + void jz4730_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues) { static_cast(gpio)->multi_setup(*mask, mode, outvalues); diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/gpio/src/jz4740.cc --- a/pkg/devices/lib/gpio/src/jz4740.cc Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/src/jz4740.cc Tue Oct 24 18:52:06 2023 +0200 @@ -2,7 +2,7 @@ * GPIO driver for Ingenic JZ4740. * (See below for additional copyright and licensing notices.) * - * Copyright (C) 2017, 2018 Paul Boddie + * Copyright (C) 2017, 2018, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -339,6 +339,38 @@ *value = (_regs[reg] >> _pin_shift(pin)) & 1; } +// Return function and function-specific configuration for a pin. + +void +Gpio_jz4740_chip::config_pad_get(unsigned pin, unsigned *func, unsigned *value) +{ + unsigned direction, function, select, trigger; + + config_get(pin, Port_function, &function); + config_get(pin, Port_select, &select); + config_get(pin, Port_dir, &direction); + + if (function) + { + *func = Hw::Gpio_chip::Function_alt; + *value = select; + return; + } + + if (select) + { + config_get(pin, Port_trigger, &trigger); + + *func = Hw::Gpio_chip::Function_irq; + *value = (trigger ? (direction ? L4_IRQ_F_POS_EDGE : L4_IRQ_F_NEG_EDGE) + : (direction ? L4_IRQ_F_LEVEL_HIGH : L4_IRQ_F_LEVEL_LOW)); + return; + } + + *func = Hw::Gpio_chip::Function_gpio; + *value = direction ? Input : Output; +} + // Obtain an IRQ abstraction for a pin. Hw::Gpio_irq_pin * @@ -406,6 +438,11 @@ static_cast(gpio)->config_get(pin, reg, value); } +void jz4740_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value) +{ + static_cast(gpio)->config_pad_get(pin, func, value); +} + void jz4740_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues) { static_cast(gpio)->multi_setup(*mask, mode, outvalues); diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/gpio/src/jz4780.cc --- a/pkg/devices/lib/gpio/src/jz4780.cc Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/src/jz4780.cc Tue Oct 24 18:52:06 2023 +0200 @@ -2,7 +2,7 @@ * GPIO driver for Ingenic JZ4780. * (See below for additional copyright and licensing notices.) * - * Copyright (C) 2017 Paul Boddie + * Copyright (C) 2017, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -47,20 +47,20 @@ */ enum Regs -{ +{ Pin_level = 0x000, // PxPIN (read-only) - + Port_int = 0x010, // PxINT Port_int_set = 0x014, // PxINTS Port_int_clear = 0x018, // PxINTC - + Irq_mask = 0x020, // PxMSK (for PxINT == 1) Irq_mask_set = 0x024, // PxMSKS Irq_mask_clear = 0x028, // PxMSKC Port_gpio = 0x020, // PxMSK (for PxINT == 0) Port_gpio_set = 0x024, // PxMSKS Port_gpio_clear = 0x028, // PxMSKC - + Port_trigger = 0x030, // PxPAT1 (for PxINT == 1) Port_trigger_set = 0x034, // PxPAT1S Port_trigger_clear = 0x038, // PxPAT1C @@ -70,7 +70,7 @@ Port_group1 = 0x030, // PxPAT1 (for PxINT == 0, PxMSK == 0) Port_group1_set = 0x034, // PxPAT1S Port_group1_clear = 0x038, // PxPAT1C - + Port_level = 0x040, // PxPAT0 (for PxINT == 1) Port_level_set = 0x044, // PxPAT0S Port_level_clear = 0x048, // PxPAT0C @@ -80,10 +80,10 @@ Port_group0 = 0x040, // PxPAT0 (for PxINT == 0, PxMSK == 0) Port_group0_set = 0x044, // PxPAT0S Port_group0_clear = 0x048, // PxPAT0C - + Irq_flag = 0x050, // PxFLG (read-only) Irq_flag_clear = 0x058, // PxFLGC - + Pull_disable = 0x070, // PxPE Pull_disable_set = 0x074, // PxPES Pull_disable_clear = 0x078, // PxPEC @@ -317,7 +317,7 @@ if (pin >= _nr_pins) throw -L4_EINVAL; - if (value > 1) + if (value > 3) throw -L4_EINVAL; switch (func) @@ -354,6 +354,45 @@ *value = (_regs[reg] >> _pin_shift(pin)) & 1; } +// Return function and function-specific configuration for a pin. + +void +Gpio_jz4780_chip::config_pad_get(unsigned pin, unsigned *func, unsigned *value) +{ + unsigned direction, gpio, group0, group1, interrupt, level, trigger; + + config_get(pin, Port_int, &interrupt); + + if (interrupt) + { + config_get(pin, Port_trigger, &trigger); + config_get(pin, Port_level, &level); + + *func = Hw::Gpio_chip::Function_irq; + *value = (trigger ? (level ? L4_IRQ_F_POS_EDGE : L4_IRQ_F_NEG_EDGE) + : (level ? L4_IRQ_F_LEVEL_HIGH : L4_IRQ_F_LEVEL_LOW)); + return; + } + + config_get(pin, Port_gpio, &gpio); + + if (gpio) + { + config_get(pin, Port_dir, &direction); + + *func = Hw::Gpio_chip::Function_gpio; + *value = direction ? Input : Output; + return; + } + + *func = Hw::Gpio_chip::Function_alt; + + config_get(pin, Port_group0, &group0); + config_get(pin, Port_group1, &group1); + + *value = (group1 << 1) | group0; +} + // Obtain an IRQ abstraction for a pin. Hw::Gpio_irq_pin * @@ -417,6 +456,11 @@ static_cast(gpio)->config_get(pin, reg, value); } +void jz4780_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value) +{ + static_cast(gpio)->config_pad_get(pin, func, value); +} + void jz4780_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues) { static_cast(gpio)->multi_setup(*mask, mode, outvalues); diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/gpio/src/x1600.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/gpio/src/x1600.cc Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,519 @@ +/* + * GPIO driver for Ingenic X1600. + * (See below for additional copyright and licensing notices.) + * + * Copyright (C) 2017, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + * + * + * Subject to other copyrights, being derived from the bcm2835.cc and + * omap.cc GPIO driver implementations. + * + * This file is part of TUD:OS and distributed under the terms of the + * GNU General Public License 2. + * Please see the COPYING-GPL-2 file for details. + */ + +#include +#include +#include + +#include "gpio-x1600.h" + +/* +GPIO register offsets (x in A..D). + +Register summary: + +PxINT 0 (function/GPIO) 1 (interrupt) +PxMSK 0 (function) 1 (GPIO) 0 (IRQ enable)/1 (IRQ disable) +PxPAT1 0 (function 0/1) 1 (function 2/3) 0 (output) 1 (input) 0 (level trigger) 1 (edge trigger) +PxPAT0 0 (function 0) 0 (function 2) 0 (output value 0) 0 (low level) 0 (falling edge) + 1 (function 1) 1 (function 3) 1 (output value 1) 1 (high level) 1 (rising edge) +*/ + +enum Regs +{ + Pin_level = 0x000, // PxPINL (read-only) + + Port_int = 0x010, // PxINT + Port_int_set = 0x014, // PxINTS + Port_int_clear = 0x018, // PxINTC + + Irq_mask = 0x020, // PxMSK (for PxINT == 1) + Irq_mask_set = 0x024, // PxMSKS + Irq_mask_clear = 0x028, // PxMSKC + Port_gpio = 0x020, // PxMSK (for PxINT == 0) + Port_gpio_set = 0x024, // PxMSKS + Port_gpio_clear = 0x028, // PxMSKC + + Port_trigger = 0x030, // PxPAT1 (for PxINT == 1) + Port_trigger_set = 0x034, // PxPAT1S + Port_trigger_clear = 0x038, // PxPAT1C + Port_dir = 0x030, // PxPAT1 (for PxINT == 0, PxMSK == 1) + Port_dir_set = 0x034, // PxPAT1S + Port_dir_clear = 0x038, // PxPAT1C + Port_group1 = 0x030, // PxPAT1 (for PxINT == 0, PxMSK == 0) + Port_group1_set = 0x034, // PxPAT1S + Port_group1_clear = 0x038, // PxPAT1C + + Port_level = 0x040, // PxPAT0 (for PxINT == 1) + Port_level_set = 0x044, // PxPAT0S + Port_level_clear = 0x048, // PxPAT0C + Port_data = 0x040, // PxPAT0 (for PxINT == 0, PxMSK == 1, PxPAT1 == 0) + Port_data_set = 0x044, // PxPAT0S + Port_data_clear = 0x048, // PxPAT0C + Port_group0 = 0x040, // PxPAT0 (for PxINT == 0, PxMSK == 0) + Port_group0_set = 0x044, // PxPAT0S + Port_group0_clear = 0x048, // PxPAT0C + + Irq_flag = 0x050, // PxFLG (read-only) + Irq_flag_clear = 0x058, // PxFLGC + + // Only the following registers differ from the JZ4780. The dual-edge + // registers being added to the X1600, with the pull-up/down registers being + // relocated and their sense changed from disable to enable. + + Pull_edge = 0x070, // PxEDG + Pull_edge_set = 0x074, // PxEDGS + Pull_edge_clear = 0x078, // PxEDGC + + Pull_enable = 0x080, // PxPE + Pull_enable_set = 0x084, // PxPES + Pull_enable_clear = 0x088, // PxPEC + + // The shadow port Z is available at offset 0x700 and supports the INTS, INTC, + // MSKS, MSKC, PAT1S, PAT1C, PAT0S, PAT0C registers, along with the following. + + Shadow_transfer = 0x0f0, // PzGID2LD +}; + + + +// IRQ control for each GPIO pin. + +Gpio_x1600_irq_pin::Gpio_x1600_irq_pin(unsigned pin, Hw::Register_block<32> const ®s) +: _pin(pin), _regs(regs) +{} + +void +Gpio_x1600_irq_pin::write_reg_pin(unsigned reg) +{ + // Write the pin bit to the register, setting or clearing the pin + // depending on the register chosen. + + _regs[reg] = _pin_bit(_pin); +} + +void Gpio_x1600_irq_pin::do_mask() +{ + // Set the interrupt bit in the PxIM register. + + write_reg_pin(Irq_mask_set); +} + +void Gpio_x1600_irq_pin::do_unmask() +{ + // Clear the interrupt bit in the PxIM register, first also clearing the + // flag bit in the PxFLG register to allow interrupts to be delivered. + + write_reg_pin(Irq_flag_clear); + write_reg_pin(Irq_mask_clear); +} + +bool Gpio_x1600_irq_pin::do_set_mode(unsigned mode) +{ + // Standard comment found for this method: + // this operation touches multiple mmio registers and is thus + // not atomic, that's why we first mask the IRQ and if it was + // enabled we unmask it after we have changed the mode + + /* NOTE: The X1600 provides a special port Z that allows changes to be made + and then committed atomically using PzGID2LD. This is not currently + used. */ + + if (enabled()) + do_mask(); + + // Do the PxINT, PxPAT1 and PxPAT0 configuration. + + switch(mode) + { + case L4_IRQ_F_LEVEL_HIGH: + write_reg_pin(Port_int_set); + write_reg_pin(Port_trigger_clear); + write_reg_pin(Port_level_set); + break; + case L4_IRQ_F_LEVEL_LOW: + write_reg_pin(Port_int_set); + write_reg_pin(Port_trigger_clear); + write_reg_pin(Port_level_clear); + break; + case L4_IRQ_F_POS_EDGE: + write_reg_pin(Port_int_set); + write_reg_pin(Port_trigger_set); + write_reg_pin(Port_level_set); + break; + case L4_IRQ_F_NEG_EDGE: + write_reg_pin(Port_int_set); + write_reg_pin(Port_trigger_set); + write_reg_pin(Port_level_clear); + break; + + default: + return false; + } + + if (enabled()) + do_unmask(); + + return true; +} + +int Gpio_x1600_irq_pin::clear() +{ + // Obtain the flag status for the pin, clearing it if set. + + l4_uint32_t e = _regs[Irq_flag] & (1UL << _pin); + if (e) + _regs[Irq_flag_clear] = e; + + return (e >> _pin); +} + +bool Gpio_x1600_irq_pin::enabled() +{ + return true; +} + + + +// Initialise the GPIO controller. + +Gpio_x1600_chip::Gpio_x1600_chip(l4_addr_t start, l4_addr_t end, + unsigned nr_pins, + l4_uint32_t pull_ups, l4_uint32_t pull_downs) +: _start(start), _end(end), + _nr_pins(nr_pins), + _pull_ups(pull_ups), _pull_downs(pull_downs) +{ + _regs = new Hw::Mmio_register_block<32>(_start); +} + +// Return the value of a pin. + +int +Gpio_x1600_chip::get(unsigned pin) +{ + if (pin >= _nr_pins) + throw -L4_EINVAL; + + l4_uint32_t val = _regs[Pin_level]; + return (val >> _pin_shift(pin)) & 1; +} + +// Return multiple pin values. + +unsigned +Gpio_x1600_chip::multi_get(unsigned offset) +{ + _reg_offset_check(offset); + return _regs[Pin_level]; +} + +// Set the value of a pin. + +void +Gpio_x1600_chip::set(unsigned pin, int value) +{ + if (pin >= _nr_pins) + throw -L4_EINVAL; + + l4_uint32_t reg_set = value ? Port_data_set : Port_data_clear; + _regs[reg_set] = _pin_bit(pin); +} + +// Set multiple pin values. + +void +Gpio_x1600_chip::multi_set(Pin_slice const &mask, unsigned data) +{ + _reg_offset_check(mask.offset); + if (mask.mask & data) + _regs[Port_data_set] = (mask.mask & data); + if (mask.mask & ~data) + _regs[Port_data_clear] = (mask.mask & ~data); +} + +// Set a pin up with the given mode and value (if appropriate). + +void +Gpio_x1600_chip::setup(unsigned pin, unsigned mode, int value) +{ + if (pin >= _nr_pins) + throw -L4_EINVAL; + + config(pin, mode); + + if (mode == Output) + set(pin, value); +} + +// Configuration of a pin using the generic input/output/IRQ mode. + +void +Gpio_x1600_chip::config(unsigned pin, unsigned mode) +{ + switch (mode) + { + case Input: + _regs[Port_int_clear] = _pin_bit(pin); + _regs[Port_gpio_set] = _pin_bit(pin); + _regs[Port_dir_set] = _pin_bit(pin); + break; + case Output: + _regs[Port_int_clear] = _pin_bit(pin); + _regs[Port_gpio_set] = _pin_bit(pin); + _regs[Port_dir_clear] = _pin_bit(pin); + break; + case Irq: + _regs[Port_int_set] = _pin_bit(pin); + // Other details depend on the actual trigger mode. + break; + default: + break; + } +} + +// Pull-up/down configuration for a pin. + +void +Gpio_x1600_chip::config_pull(unsigned pin, unsigned mode) +{ + if (pin >= _nr_pins) + throw -L4_EINVAL; + + switch (mode) + { + case Pull_none: + _regs[Pull_enable_clear] = _pin_bit(pin); + break; + case Pull_down: + if (_pin_bit(pin) & _pull_downs) + _regs[Pull_enable_set] = _pin_bit(pin); + break; + case Pull_up: + if (_pin_bit(pin) & _pull_ups) + _regs[Pull_enable_set] = _pin_bit(pin); + break; + default: + // Invalid pull-up/down mode for pin. + throw -L4_EINVAL; + } +} + +// Pin function configuration. + +void +Gpio_x1600_chip::config_pad(unsigned pin, unsigned func, unsigned value) +{ + if (pin >= _nr_pins) + throw -L4_EINVAL; + + if (value > 3) + throw -L4_EINVAL; + + switch (func) + { + // Support two different outputs. + + case Hw::Gpio_chip::Function_gpio: + _regs[Port_int_clear] = _pin_bit(pin); + _regs[Port_gpio_set] = _pin_bit(pin); + _regs[value & 1 ? Port_data_set : Port_data_clear] = _pin_bit(pin); + break; + + // Support four different device functions. + + case Hw::Gpio_chip::Function_alt: + _regs[Port_int_clear] = _pin_bit(pin); + _regs[Port_gpio_clear] = _pin_bit(pin); + _regs[value & 2 ? Port_group1_set : Port_group1_clear] = _pin_bit(pin); + _regs[value & 1 ? Port_group0_set : Port_group0_clear] = _pin_bit(pin); + break; + default: + throw -L4_EINVAL; + } +} + +// Obtain a pin's configuration from a register in the supplied value. + +void +Gpio_x1600_chip::config_get(unsigned pin, unsigned reg, unsigned *value) +{ + if (pin >= _nr_pins) + throw -L4_EINVAL; + + *value = (_regs[reg] >> _pin_shift(pin)) & 1; +} + +// Return function and function-specific configuration for a pin. + +void +Gpio_x1600_chip::config_pad_get(unsigned pin, unsigned *func, unsigned *value) +{ + unsigned direction, gpio, group0, group1, interrupt, level, trigger; + + config_get(pin, Port_int, &interrupt); + + if (interrupt) + { + config_get(pin, Port_trigger, &trigger); + config_get(pin, Port_level, &level); + + *func = Hw::Gpio_chip::Function_irq; + *value = (trigger ? (level ? L4_IRQ_F_POS_EDGE : L4_IRQ_F_NEG_EDGE) + : (level ? L4_IRQ_F_LEVEL_HIGH : L4_IRQ_F_LEVEL_LOW)); + return; + } + + config_get(pin, Port_gpio, &gpio); + + if (gpio) + { + config_get(pin, Port_dir, &direction); + + *func = Hw::Gpio_chip::Function_gpio; + *value = direction ? Input : Output; + return; + } + + *func = Hw::Gpio_chip::Function_alt; + + config_get(pin, Port_group0, &group0); + config_get(pin, Port_group1, &group1); + + *value = (group1 << 1) | group0; +} + +// Obtain an IRQ abstraction for a pin. + +Hw::Gpio_irq_pin * +Gpio_x1600_chip::get_irq(unsigned pin) +{ + if (pin >= _nr_pins) + throw -L4_EINVAL; + + return new Gpio_x1600_irq_pin(pin, _regs); +} + +// Pin function configuration for multiple pins. + +void +Gpio_x1600_chip::multi_config_pad(Pin_slice const &mask, unsigned func, unsigned val) +{ + unsigned m = mask.mask; + for (unsigned pin = mask.offset; pin < _nr_pins; ++pin, m >>= 1) + if (m & 1) + config_pad(pin, func, val); +} + +// Set up multiple pins with the given mode. + +void +Gpio_x1600_chip::multi_setup(Pin_slice const &mask, unsigned mode, unsigned outvalues) +{ + unsigned m = mask.mask; + for (unsigned pin = mask.offset; pin < _nr_pins; ++pin, m >>= 1, outvalues >>= 1) + if (m & 1) + setup(pin, mode, outvalues & 1); +} + + + +// C language interface functions. + +void *x1600_gpio_init(l4_addr_t start, l4_addr_t end, unsigned pins, + l4_uint32_t pull_ups, l4_uint32_t pull_downs) +{ + return (void *) new Gpio_x1600_chip(start, end, pins, pull_ups, pull_downs); +} + +void x1600_gpio_setup(void *gpio, unsigned pin, unsigned mode, int value) +{ + static_cast(gpio)->setup(pin, mode, value); +} + +void x1600_gpio_config_pull(void *gpio, unsigned pin, unsigned mode) +{ + static_cast(gpio)->config_pull(pin, mode); +} + +void x1600_gpio_config_pad(void *gpio, unsigned pin, unsigned func, unsigned value) +{ + static_cast(gpio)->config_pad(pin, func, value); +} + +void x1600_gpio_config_get(void *gpio, unsigned pin, unsigned reg, unsigned *value) +{ + static_cast(gpio)->config_get(pin, reg, value); +} + +void x1600_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value) +{ + static_cast(gpio)->config_pad_get(pin, func, value); +} + +void x1600_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues) +{ + static_cast(gpio)->multi_setup(*mask, mode, outvalues); +} + +void x1600_gpio_multi_config_pad(void *gpio, Pin_slice const *mask, unsigned func, unsigned value) +{ + static_cast(gpio)->multi_config_pad(*mask, func, value); +} + +void x1600_gpio_multi_set(void *gpio, Pin_slice const *mask, unsigned data) +{ + static_cast(gpio)->multi_set(*mask, data); +} + +unsigned x1600_gpio_multi_get(void *gpio, unsigned offset) +{ + return static_cast(gpio)->multi_get(offset); +} + +int x1600_gpio_get(void *gpio, unsigned pin) +{ + return static_cast(gpio)->get(pin); +} + +void x1600_gpio_set(void *gpio, unsigned pin, int value) +{ + static_cast(gpio)->set(pin, value); +} + +void *x1600_gpio_get_irq(void *gpio, unsigned pin) +{ + return (void *) static_cast(gpio)->get_irq(pin); +} + +bool x1600_gpio_irq_set_mode(void *gpio_irq, unsigned mode) +{ + return static_cast(gpio_irq)->do_set_mode(mode); +} diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/i2c/include/i2c-gpio.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/i2c/include/i2c-gpio.h Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,76 @@ +/* + * Manual GPIO-based I2C communication. + * + * Copyright (C) 2013, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include + +/* I2C modifiers. */ + +#define I2C_READ 1 +#define I2C_WRITE 0 + +#ifdef __cplusplus + +class I2c_gpio +{ + Hw::Gpio_chip *_scl_chip; + int I2C_SCL; + Hw::Gpio_chip *_sda_chip; + int I2C_SDA; + + void CLR(int pin); + void SET(int pin); + void IN(int pin); + int PIN(int pin); + void wait(); + +public: + explicit I2c_gpio(Hw::Gpio_chip *scl_chip, int scl, Hw::Gpio_chip *sda_chip, int sda); + + void start(); + void stop(); + void ack(bool ack); + uint8_t recv(); + void recvmany(uint8_t *data, uint8_t len); + bool send(uint8_t data); + bool sendmany(uint8_t *data, uint8_t len); +}; + +#endif /* __cplusplus */ + + + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *i2c_gpio_get_channel(void *scl_chip, int scl, void *sda_chip, int sda); +void i2c_gpio_start(void *channel); +void i2c_gpio_stop(void *channel); +void i2c_gpio_ack(void *channel, bool ack); +uint8_t i2c_gpio_recv(void *channel); +void i2c_gpio_recvmany(void *channel, uint8_t *data, uint8_t len); +bool i2c_gpio_send(void *channel, uint8_t data); +bool i2c_gpio_sendmany(void *channel, uint8_t *data, uint8_t len); + +EXTERN_C_END diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/i2c/include/i2c-x1600.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/i2c/include/i2c-x1600.h Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,157 @@ +/* + * I2C support for the X1600. + * + * Copyright (C) 2017, 2018, 2019, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include + + + +#ifdef __cplusplus + +#include +#include + +// I2C channel. + +class I2c_x1600_channel +{ +private: + Hw::Register_block<32> _regs; + Cpm_x1600_chip *_cpm; + uint32_t _frequency; + + // Buffer management. + + unsigned int _pos, _reqpos, _total; + uint8_t *_buf; + + // Status conditions. + + int _fail; + int _stop; + +public: + I2c_x1600_channel(l4_addr_t start, enum Clock_identifiers clock, + Cpm_x1600_chip *cpm, uint32_t frequency); + + uint32_t get_frequency(); + void set_target(uint8_t addr); + + // Reading initiation and execution. + + void start_read(uint8_t buf[], unsigned int total, int stop = 0); + void read(); + + // Writing initiation and execution. + + void start_write(uint8_t buf[], unsigned int total, int stop = 0); + void write(); + + // Transaction control. + + void stop(); + void disable(); + + // Specific status conditions. + + unsigned int have_read(); + unsigned int have_written(); + int read_done(); + int write_done(); + + int failed(); + int read_failed(); + int write_failed(); + +private: + void enable(); + + int active(); + int have_input(); + int have_output(); + int can_send(); + + void reset_flags(); + void init_parameters(); + void set_frequency(); + + void set_read_threshold(); + void queue_reads(); + void queue_writes(); + void store_reads(); +}; + +// I2C device control. + +class I2c_x1600_chip +{ +private: + l4_addr_t _start, _end; + Cpm_x1600_chip *_cpm; + uint32_t _frequency; + +public: + I2c_x1600_chip(l4_addr_t start, l4_addr_t end, Cpm_x1600_chip *cpm, + uint32_t frequency); + + I2c_x1600_channel *get_channel(uint8_t channel); +}; + +#endif /* __cplusplus */ + + + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *x1600_i2c_init(l4_addr_t start, l4_addr_t end, void *cpm, + uint32_t frequency); + +void *x1600_i2c_get_channel(void *i2c, uint8_t channel); + +uint32_t x1600_i2c_get_frequency(void *i2c_channel); + +void x1600_i2c_set_target(void *i2c_channel, uint8_t addr); + +void x1600_i2c_start_read(void *i2c_channel, uint8_t buf[], unsigned int total, + int stop); + +void x1600_i2c_read(void *i2c_channel); + +void x1600_i2c_start_write(void *i2c_channel, uint8_t buf[], unsigned int total, + int stop); + +void x1600_i2c_write(void *i2c_channel); + +int x1600_i2c_read_done(void *i2c_channel); + +int x1600_i2c_write_done(void *i2c_channel); + +unsigned int x1600_i2c_have_read(void *i2c_channel); + +unsigned int x1600_i2c_have_written(void *i2c_channel); + +int x1600_i2c_failed(void *i2c_channel); + +EXTERN_C_END diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/i2c/src/Makefile --- a/pkg/devices/lib/i2c/src/Makefile Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/i2c/src/Makefile Tue Oct 24 18:52:06 2023 +0200 @@ -4,10 +4,10 @@ TARGET = libi2c.o.a libi2c.o.so PC_FILENAME := libdrivers-i2c -SRC_CC := jz4730.cc jz4780.cc +SRC_CC := jz4730.cc jz4780.cc x1600.cc gpio.cc PRIVATE_INCDIR += $(PKGDIR)/lib/i2c/include -REQUIRES_LIBS := l4re_c l4re_c-util libdrivers-common +REQUIRES_LIBS := l4re_c l4re_c-util libdrivers-common libdrivers-cpm libdrivers-gpio include $(L4DIR)/mk/lib.mk diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/i2c/src/gpio.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/i2c/src/gpio.cc Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,243 @@ +/* + * Manual GPIO-based I2C communication. + * + * Copyright (C) 2013, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include "i2c-gpio.h" + +/* Declare the pins for initial I2C communications. */ + +I2c_gpio::I2c_gpio(Hw::Gpio_chip *scl_chip, int scl, Hw::Gpio_chip *sda_chip, int sda) +{ + _scl_chip = scl_chip; + _sda_chip = sda_chip; + I2C_SCL = scl; + I2C_SDA = sda; +} + +void I2c_gpio::CLR(int pin) +{ + (pin == I2C_SCL ? _scl_chip : _sda_chip)->setup(pin, Hw::Gpio_chip::Output, 0); +} + +void I2c_gpio::SET(int pin) +{ + (pin == I2C_SCL ? _scl_chip : _sda_chip)->setup(pin, Hw::Gpio_chip::Output, 1); +} + +void I2c_gpio::IN(int pin) +{ + (pin == I2C_SCL ? _scl_chip : _sda_chip)->setup(pin, Hw::Gpio_chip::Input, 0); +} + +int I2c_gpio::PIN(int pin) +{ + return (pin == I2C_SCL ? _scl_chip : _sda_chip)->get(pin); +} + +void I2c_gpio::wait() +{ + usleep(4); +} + +/* Initiate an I2C transaction. */ + +void I2c_gpio::start() +{ + /* Set up the data signal. */ + + CLR(I2C_SCL); + wait(); + SET(I2C_SDA); + + /* During a clock pulse, produce the start condition. */ + + SET(I2C_SCL); + wait(); + CLR(I2C_SDA); +} + +/* Terminate an I2C transaction. */ + +void I2c_gpio::stop() +{ + /* Set up the data signal. */ + + CLR(I2C_SCL); + wait(); + CLR(I2C_SDA); + + /* During a clock pulse, produce the stop condition. */ + + SET(I2C_SCL); + wait(); + SET(I2C_SDA); +} + +/* Send an I2C acknowledgement to a transmitting device. */ + +void I2c_gpio::ack(bool ack) +{ + if (ack) + CLR(I2C_SDA); + else + SET(I2C_SDA); + + SET(I2C_SCL); + wait(); + CLR(I2C_SCL); + + IN(I2C_SDA); +} + +/* Receive a single byte from an I2C device as part of a transaction. */ + +uint8_t I2c_gpio::recv() +{ + uint8_t mask, result = 0; + + IN(I2C_SDA); + CLR(I2C_SCL); + + for (mask = 0x80; mask; mask >>= 1) + { + SET(I2C_SCL); + wait(); + + if (PIN(I2C_SDA)) + result |= mask; + + CLR(I2C_SCL); + wait(); + } + + return result; +} + +/* Receive into a buffer a transmission of the given length in bytes. */ + +void I2c_gpio::recvmany(uint8_t *data, uint8_t len) +{ + uint8_t *end = data + len; + + for (; data != end; data++, len--) + { + *data = recv(); + ack(len > 1); + } +} + +/* Send a single byte of data to an I2C device as part of a transaction, + returning whether the transmission succeeded. */ + +bool I2c_gpio::send(uint8_t data) +{ + uint8_t mask; + bool status; + + CLR(I2C_SCL); + + for (mask = 0x80; mask; mask >>= 1) + { + wait(); + + if (data & mask) + SET(I2C_SDA); + else + CLR(I2C_SDA); + + SET(I2C_SCL); + wait(); + CLR(I2C_SCL); + } + + /* Wait for acknowledgement, failing if none is given. */ + + IN(I2C_SDA); + SET(I2C_SCL); + wait(); + + status = PIN(I2C_SDA); + CLR(I2C_SCL); + return !status; +} + +/* Send from the buffer provided a transmission with the given length to an I2C + device. */ + +bool I2c_gpio::sendmany(uint8_t *data, uint8_t len) +{ + uint8_t *end = data + len; + + for (; data != end; data++) + { + if (!send(*data)) + return false; + + /* NOTE: Should test for the slave holding the clock signal low. */ + } + + return true; +} + + + +// C language interface functions. + +void *i2c_gpio_get_channel(void *scl_chip, int scl, void *sda_chip, int sda) +{ + return (void *) new I2c_gpio(reinterpret_cast(scl_chip), scl, + reinterpret_cast(sda_chip), sda); +} + +void i2c_gpio_start(void *channel) +{ + static_cast(channel)->start(); +} + +void i2c_gpio_stop(void *channel) +{ + static_cast(channel)->stop(); +} + +void i2c_gpio_ack(void *channel, bool ack) +{ + static_cast(channel)->ack(ack); +} + +uint8_t i2c_gpio_recv(void *channel) +{ + return static_cast(channel)->recv(); +} + +void i2c_gpio_recvmany(void *channel, uint8_t *data, uint8_t len) +{ + static_cast(channel)->recvmany(data, len); +} + +bool i2c_gpio_send(void *channel, uint8_t data) +{ + return static_cast(channel)->send(data); +} + +bool i2c_gpio_sendmany(void *channel, uint8_t *data, uint8_t len) +{ + return static_cast(channel)->sendmany(data, len); +} diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/i2c/src/x1600.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/i2c/src/x1600.cc Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,769 @@ +/* + * I2C support for the X1600. + * + * Copyright (C) 2017, 2018, 2021, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include + +#include +#include +#include + +/* NOTE: This peripheral is very similar to the JZ4780 with the registers + renamed to I2C from SMB, with a few high speed registers added, and + with I2C_SDAHD appearing at a different location. */ + +enum Regs +{ + I2c_control = 0x000, // I2C_CON + I2c_target_address = 0x004, // I2C_TAR + I2c_slave_address = 0x008, // I2C_SAR + I2c_master_code = 0x00c, // I2C_HS_MADDR + I2c_data_command = 0x010, // I2C_DC + Std_high_count = 0x014, // I2C_SHCNT + Std_low_count = 0x018, // I2C_SLCNT + Fast_high_count = 0x01c, // I2C_FHCNT + Fast_low_count = 0x020, // I2C_FLCNT + High_high_count = 0x024, // I2C_HHCNT + High_low_count = 0x028, // I2C_HLCNT + Int_status = 0x02c, // I2C_INTST (read-only) + Int_mask = 0x030, // I2C_INTM + Int_raw_status = 0x034, // I2C_RINTST (read-only) + Rx_fifo_thold = 0x038, // I2C_RXTL + Tx_fifo_thold = 0x03c, // I2C_TXTL + Int_combined_clear = 0x040, // I2C_CINT (read-only) + Int_rx_uf_clear = 0x044, // I2C_CRXUF (read-only) + Int_rx_of_clear = 0x048, // I2C_CRXOF (read-only) + Int_tx_of_clear = 0x04c, // I2C_CTXOF (read-only) + Int_rd_req_clear = 0x050, // I2C_CRXREQ (read-only) + Int_tx_abort_clear = 0x054, // I2C_CTXABT (read-only) + Int_rx_done_clear = 0x058, // I2C_CRXDN (read-only) + Int_activity_clear = 0x05c, // I2C_CACT (read-only) + Int_stop_clear = 0x060, // I2C_CSTP (read-only) + Int_start_clear = 0x064, // I2C_CSTT (read-only) + Int_call_clear = 0x068, // I2C_CGC (read-only) + I2c_enable = 0x06c, // I2C_ENB + I2c_status = 0x070, // I2C_ST (read-only) + Tx_fifo_count = 0x074, // I2C_TXFLR (read-only) + Rx_fifo_count = 0x078, // I2C_RXFLR (read-only) + I2c_sda_hold_time = 0x07c, // I2C_SDAHD + Trans_abort_status = 0x080, // I2C_ABTSRC (read-only) + Slv_data_nack = 0x084, // I2CSDNACK + I2c_dma_ctrl = 0x088, // I2C_DMACR + I2c_trans_data_lvl = 0x08c, // I2C_DMATDLR + I2c_recv_data_lvl = 0x090, // I2C_DMARDLR + I2c_sda_setup_time = 0x094, // I2C_SDASU + I2c_ack_call = 0x098, // I2C_ACKGC + I2c_enable_status = 0x09c, // I2C_ENBST (read-only) + I2c_spike_suppress = 0x0a0, // I2C_FSPKLEN + + I2c_block_offset = 0x1000 +}; + +enum I2c_control_bits : unsigned +{ + I2c_disable_slave = 0x40, // SLVDIS (slave disabled) + I2c_enable_restart = 0x20, // RESTART + I2c_master_10bit = 0x10, // MATP (read-only) + I2c_slave_10bit = 0x08, // SATP + I2c_speed_mode_mask = 0x06, // SPEED + I2c_enable_master = 0x01, // MD (master enabled) + I2c_speed_bit = 1, // SPD +}; + +enum I2c_speed_mode_values : unsigned +{ + I2c_speed_standard = 1, + I2c_speed_fast = 2, + I2c_speed_high = 3, +}; + +enum I2c_enable_bits : unsigned +{ + I2c_enable_enabled = 0x01, // I2CEN +}; + +enum I2c_status_bits : unsigned +{ + I2c_status_master_act = 0x20, // MSTACT (master active) + I2c_status_rx_nempty = 0x08, // RFNE (read queue not empty) + I2c_status_tx_empty = 0x04, // TFE (write queue empty) + I2c_status_tx_nfull = 0x02, // TFNF (write queue not full) + I2c_status_active = 0x01, // ACT (device active as master or slave) +}; + +enum I2c_target_bits : unsigned +{ + I2c_target_master_10bit = 0x1000, + I2c_target_special = 0x0800, // SPECIAL: perform general call or start byte + I2c_target_start_byte = 0x0400, // Special: start byte (1) or general call (0) + I2c_target_10bits = 0x3ff, // Mask for 10-bit address + I2c_target_7bits = 0x7f, // Mask for 7-bit address +}; + +enum I2c_hold_control_bits : unsigned +{ + /* The hold enable flag has been removed since the JZ4780 and the hold time + field widened. */ + + I2c_hold_mask = 0xffff, +}; + +enum I2c_setup_control_bits : unsigned +{ + I2c_setup_mask = 0x0ff, // SDASU +}; + +enum I2c_command_bits : unsigned +{ + I2c_command_restart = 0x400, // RESTART: explicit restart before next byte + I2c_command_stop = 0x200, // STOP: explicit stop after next byte + I2c_command_no_stop = 0x000, + I2c_command_read = 0x100, // CMD + I2c_command_write = 0x000, // CMD +}; + +enum I2c_fifo_bits : unsigned +{ + I2c_fifo_limit = 64, // RXTL, TXTL (256 noted in field description) +}; + +enum Int_bits : unsigned +{ + Int_call = 0x800, // IGC (general call received) + Int_start = 0x400, // ISTT (start/restart condition occurred) + Int_stop = 0x200, // ISTP (stop condition occurred) + Int_activity = 0x100, // IACT (bus activity interrupt) + Int_rx_done = 0x080, // RXDN (read from master device done) + Int_tx_abort = 0x040, // TXABT (transmit abort) + Int_rd_req = 0x020, // RDREQ (read request from master device) + Int_tx_empty = 0x010, // TXEMP (threshold reached or passed) + Int_tx_of = 0x008, // TXOF (overflow when writing to queue) + Int_rx_full = 0x004, // RXFL (threshold reached or exceeded) + Int_rx_of = 0x002, // RXOF (overflow from device) + Int_rx_uf = 0x001, // RXUF (underflow when reading from queue) +}; + + + +// Initialise a channel. + +I2c_x1600_channel::I2c_x1600_channel(l4_addr_t start, + enum Clock_identifiers clock, + Cpm_x1600_chip *cpm, + uint32_t frequency) +: _cpm(cpm), _frequency(frequency) +{ + _regs = new Hw::Mmio_register_block<32>(start); + _cpm->start_clock(clock); +} + +// Enable the channel. + +void +I2c_x1600_channel::enable() +{ + _regs[I2c_enable] = I2c_enable_enabled; + while (!(_regs[I2c_enable_status] & I2c_enable_enabled)); +} + +// Disable the channel. + +void +I2c_x1600_channel::disable() +{ + _regs[I2c_enable] = 0; + while (_regs[I2c_enable_status] & I2c_enable_enabled); +} + +// Return the configured frequency. + +uint32_t +I2c_x1600_channel::get_frequency() +{ + return _frequency; +} + +// Set the frequency-related peripheral parameters. + +void +I2c_x1600_channel::set_frequency() +{ + // The APB clock (PCLK) is used to drive I2C transfers. Its value must be + // obtained from the CPM unit. It is known as I2C_DEV_CLK here and is scaled + // to kHz in order to keep the numbers easily representable, as is the bus + // frequency. + + uint32_t i2c_dev_clk = _cpm->get_frequency(Clock_pclock) / 1000; + + // Note that this is not I2C_DEV_CLK but the actual I2C bus frequency. + + uint32_t i2c_clk = _frequency / 1000; + + // Select the appropriate speed. + + unsigned int speed = (i2c_clk <= 100) ? I2c_speed_standard + : (i2c_clk <= 400 ? I2c_speed_fast + : I2c_speed_high); + + // NOTE: Permit broader configuration elsewhere. + + _regs[I2c_control] = (speed << I2c_speed_bit) | + I2c_disable_slave | + I2c_enable_restart | + I2c_enable_master; + + // According to the programming manual, if the PCLK period is T{I2C_DEV_CLK} + // then the I2C clock period is... + + // T{SCL} = T{SCL_high} + T{SCL_low} + + // Where... + + // T{SCL_low} = T{I2C_DEV_CLK} * (#cycles for low signal) + // T{SCL_high} = T{I2C_DEV_CLK} * (#cycles for high signal) + + // Since, with minimum periods being defined... + + // T{SCL} >= T{min_SCL} + // T{SCL_low} >= T{min_SCL_low} + // T{SCL_high} >= T{min_SCL_high} + // T{min_SCL} = T{min_SCL_low} + T{min_SCL_high} + + // Then the following applies... + + // T{I2C_DEV_CLK} * (#cycles for low signal)) >= T{min_SCL_low} + // T{I2C_DEV_CLK} * (#cycles for high signal) >= T{min_SCL_high} + + // To work with different clock speeds while maintaining the low-to-high + // ratios: + + // T{min_SCL_low} = T{min_SCL} * T{min_SCL_low} / T{min_SCL} + // = T{min_SCL} * (T{min_SCL_low} / (T{min_SCL_low} + T{min_SCL_high})) + + // T{min_SCL_high} = T{min_SCL} * T{min_SCL_high} / T{min_SCL} + // = T{min_SCL} * (T{min_SCL_high} / (T{min_SCL_low} + T{min_SCL_high})) + + // Constraints are given with respect to the high and low count registers. + + // #cycles for high signal = I2CxHCNT + 8 + // #cycles for low signal = I2CxLCNT + 1 + + // From earlier, this yields... + + // T{I2C_DEV_CLK} * (I2CxLCNT + 1) >= T{min_SCL_low} + // T{I2C_DEV_CLK} * (I2CxHCNT + 8) >= T{min_SCL_high} + + // Rearranging... + + // I2CxLCNT >= (T{min_SCL_low} / T{I2C_DEV_CLK}) - 1 + // >= T{min_SCL_low} * I2C_DEV_CLK - 1 + + // I2CxHCNT >= (T{min_SCL_high} / T{I2C_DEV_CLK}) - 8 + // >= T{min_SCL_high} * I2C_DEV_CLK - 8 + + // Introducing the definitions for the high and low periods... + + // I2CxLCNT >= T{min_SCL} * (T{min_SCL_low} / (T{min_SCL_low} + T{min_SCL_high})) * I2C_DEV_CLK - 1 + // >= (T{min_SCL_low} / T{min_SCL}) * I2C_DEV_CLK / I2C_BUS_CLK - 1 + + // I2CxHCNT >= T{min_SCL} * (T{min_SCL_high} / (T{min_SCL_low} + T{min_SCL_high})) * I2C_DEV_CLK - 8 + // >= (T{min_SCL_high} / T{min_SCL}) * I2C_DEV_CLK / I2C_BUS_CLK - 8 + + uint32_t high_reg, low_reg; + uint32_t high_count, low_count; + int32_t hold_count; + uint32_t setup_count; + + // Level hold times: + + // Standard Fast High + // SCL low 4.7us 1.3us 0.5us + // SCL high 4.0us 0.6us 0.26us + + // SCL period 8.7us 1.9us 0.76us = + + // See: UM10204 "I2C-bus specification and user manual" + // Table 10: t{LOW} and t{HIGH} + + if (i2c_clk <= 100) // 100 kHz + { + low_count = (i2c_dev_clk * 47) / (i2c_clk * 87) - 1; + high_count = (i2c_dev_clk * 40) / (i2c_clk * 87) - 8; + low_reg = Std_low_count; + high_reg = Std_high_count; + } + else if (i2c_clk <= 400) // 400 kHz + { + low_count = (i2c_dev_clk * 13) / (i2c_clk * 19) - 1; + high_count = (i2c_dev_clk * 6) / (i2c_clk * 19) - 8; + low_reg = Fast_low_count; + high_reg = Fast_high_count; + } + else // > 400 kHz + { + // Note how the frequencies are scaled to accommodate the extra precision + // required. + + low_count = (i2c_dev_clk / 10 * 50) / (i2c_clk / 10 * 76) - 1; + high_count = (i2c_dev_clk / 10 * 26) / (i2c_clk / 10 * 76) - 8; + low_reg = High_low_count; + high_reg = High_high_count; + } + + // Minimum counts are 8 and 6 for low and high respectively. + + _regs[low_reg] = low_count < 8 ? 8 : low_count; + _regs[high_reg] = high_count < 6 ? 6 : high_count; + + // Data hold and setup times: + + // Standard Fast High + // t{HD;DAT} 300ns 300ns 300ns + // t{SU;DAT} 250ns 100ns 50ns + + // See: UM10204 "I2C-bus specification and user manual" + // Table 10: t{HD;DAT} and t{SU;DAT}, also note [3] + + // T{delay} = (I2CSDAHD + 2) * T{I2C_DEV_CLK} + // I2CSDAHD = T{delay} / T{I2C_DEV_CLK} - 2 + // I2CSDAHD = I2C_DEV_CLK * T{delay} - 2 + + // Since the device clock is in kHz (scaled down by 1000) and the times are + // given in ns (scaled up by 1000000000), a division of 1000000 is introduced. + + hold_count = (i2c_dev_clk * 300) / 1000000 - 1; + + _regs[I2c_sda_hold_time] = (_regs[I2c_sda_hold_time] & ~I2c_hold_mask) | + (hold_count < 0 ? 0 + : (hold_count < (int) I2c_hold_mask ? (uint32_t) hold_count + : I2c_hold_mask)); + + // I2C_SDASU is apparently not used in master mode. + + // T{delay} = (I2CSDASU - 1) * T{I2C_DEV_CLK} + // I2CSDASU = T{delay} / T{I2C_DEV_CLK} + 1 + // I2CSDASU = I2C_DEV_CLK * T{delay} + 1 + + if (i2c_clk <= 100) + setup_count = (i2c_dev_clk * 250) / 1000000 + 1; + else if (i2c_clk <= 400) + setup_count = (i2c_dev_clk * 100) / 1000000 + 1; + else + setup_count = (i2c_dev_clk * 50) / 1000000 + 1; + + _regs[I2c_sda_setup_time] = (_regs[I2c_sda_setup_time] & ~I2c_setup_mask) | + (setup_count < I2c_setup_mask ? setup_count : I2c_setup_mask); +} + +// Set the target address and enable transfer. +// NOTE: Only supporting 7-bit addresses currently. + +void +I2c_x1600_channel::set_target(uint8_t address) +{ + disable(); + set_frequency(); + _regs[I2c_target_address] = address & I2c_target_7bits; + init_parameters(); + enable(); +} + + + +// Reset interrupt flags upon certain conditions. + +void +I2c_x1600_channel::reset_flags() +{ + volatile uint32_t r; + + _regs[Int_mask] = 0; + + // Read from the register to clear interrupts. + + r = _regs[Int_combined_clear]; + (void) r; +} + +// Initialise interrupt flags and queue thresholds for reading and writing. + +void +I2c_x1600_channel::init_parameters() +{ + // Handle read queue conditions for data, write queue conditions for commands. + + reset_flags(); + + _regs[Tx_fifo_thold] = 0; // write when 0 in queue +} + + + +// Return whether the device is active. + +int +I2c_x1600_channel::active() +{ + return _regs[I2c_status] & I2c_status_master_act; +} + +// Return whether data is available to receive. + +int +I2c_x1600_channel::have_input() +{ + return _regs[I2c_status] & I2c_status_rx_nempty; +} + +// Return whether data is queued for sending. + +int +I2c_x1600_channel::have_output() +{ + return !(_regs[I2c_status] & I2c_status_tx_empty); +} + +// Return whether data can be queued for sending. + +int +I2c_x1600_channel::can_send() +{ + return _regs[I2c_status] & I2c_status_tx_nfull; +} + +// Return whether a receive operation has failed. + +int +I2c_x1600_channel::read_failed() +{ + return _regs[Int_status] & Int_rx_of; +} + +// Return whether a send operation has failed. + +int +I2c_x1600_channel::write_failed() +{ + return _regs[Int_status] & Int_tx_abort; +} + +int +I2c_x1600_channel::read_done() +{ + return _pos == _total; +} + +int +I2c_x1600_channel::write_done() +{ + return (_reqpos == _total) && !have_output(); +} + +unsigned +I2c_x1600_channel::have_read() +{ + return _pos; +} + +unsigned +I2c_x1600_channel::have_written() +{ + return _reqpos; +} + +int +I2c_x1600_channel::failed() +{ + return _fail; +} + + + +// Send read commands for empty queue entries. + +void +I2c_x1600_channel::queue_reads() +{ + unsigned int remaining = _total - _reqpos; + unsigned int queued = _reqpos - _pos; + unsigned int can_queue = I2c_fifo_limit - queued; + + // Keep the number of reads in progress below the length of the read queue. + + if (!can_queue) + return; + + // At most, only queue as many reads as are remaining. + + if (remaining < can_queue) + can_queue = remaining; + + // Queue read requests for any remaining queue entries. + + while (can_queue && can_send()) + { + uint32_t stop = _stop && (_reqpos == _total - 1) ? I2c_command_stop : I2c_command_no_stop; + + _regs[I2c_data_command] = I2c_command_read | stop; + _reqpos++; + can_queue--; + } + + // Update the threshold to be notified of any reduced remaining amount. + + set_read_threshold(); +} + +// Send write commands for empty queue entries. + +void +I2c_x1600_channel::queue_writes() +{ + unsigned int remaining = _total - _reqpos; + unsigned int can_queue = I2c_fifo_limit; + + if (remaining < can_queue) + can_queue = remaining; + + // Queue write requests for any remaining queue entries. + + while (can_queue && can_send()) + { + uint32_t stop = _stop && (_reqpos == _total - 1) ? I2c_command_stop : I2c_command_no_stop; + + _regs[I2c_data_command] = I2c_command_write | _buf[_reqpos] | stop; + _reqpos++; + can_queue--; + } +} + +// Store read command results from the queue. + +void +I2c_x1600_channel::store_reads() +{ + // Read any input and store it in the buffer. + + while (have_input() && (_pos < _reqpos)) + { + _buf[_pos] = _regs[I2c_data_command] & 0xff; + _pos++; + } +} + +void +I2c_x1600_channel::set_read_threshold() +{ + unsigned int queued = _reqpos - _pos; + + if (!queued) + return; + + // Read all expected. + + _regs[Rx_fifo_thold] = queued - 1; +} + +// Read from the target device. + +void +I2c_x1600_channel::start_read(uint8_t buf[], unsigned int total, int stop) +{ + _buf = buf; + _total = total; + _pos = 0; + _reqpos = 0; + _fail = 0; + _stop = stop; + + reset_flags(); + + _regs[Int_mask] = Int_rx_full | // read condition (reading needed) + Int_rx_of | // abort condition + Int_tx_abort; // general abort condition + + // Perform initial read requests. + + read(); +} + +void +I2c_x1600_channel::read() +{ + // Test for the general transfer abort condition. + + if (read_failed() || write_failed()) + { + _fail = 1; + _regs[Int_mask] = 0; + disable(); + enable(); + return; + } + + if (_regs[Int_status] & Int_rx_full) + store_reads(); + + // Always attempt to queue more read requests. + + queue_reads(); +} + +// Write to the target device. + +void +I2c_x1600_channel::start_write(uint8_t buf[], unsigned int total, int stop) +{ + _buf = buf; + _total = total; + _reqpos = 0; + _fail = 0; + _stop = stop; + + reset_flags(); + + // Enable interrupts for further writes. + + _regs[Int_mask] = Int_tx_empty | // write condition (writing needed) + Int_tx_abort; // abort condition + + // Perform initial writes. + + write(); +} + +void +I2c_x1600_channel::write() +{ + if (write_failed()) + { + _fail = 1; + _regs[Int_mask] = 0; + disable(); + enable(); + return; + } + + if (_regs[Int_status] & Int_tx_empty) + queue_writes(); +} + +// Explicitly stop communication. + +void +I2c_x1600_channel::stop() +{ +} + + + +// Initialise the I2C controller. + +I2c_x1600_chip::I2c_x1600_chip(l4_addr_t start, l4_addr_t end, + Cpm_x1600_chip *cpm, + uint32_t frequency) +: _start(start), _end(end), _cpm(cpm), _frequency(frequency) +{ +} + +// Obtain a channel object. + +I2c_x1600_channel * +I2c_x1600_chip::get_channel(uint8_t channel) +{ + l4_addr_t block = _start + channel * I2c_block_offset; + enum Clock_identifiers clocks[] = {Clock_i2c0, Clock_i2c1}; + + if (channel < 2) + return new I2c_x1600_channel(block, clocks[channel], _cpm, _frequency); + else + throw -L4_EINVAL; +} + + + +// C language interface functions. + +void *x1600_i2c_init(l4_addr_t start, l4_addr_t end, void *cpm, uint32_t frequency) +{ + return (void *) new I2c_x1600_chip(start, end, static_cast(cpm), frequency); +} + +void *x1600_i2c_get_channel(void *i2c, uint8_t channel) +{ + return static_cast(i2c)->get_channel(channel); +} + +uint32_t x1600_i2c_get_frequency(void *i2c_channel) +{ + return static_cast(i2c_channel)->get_frequency(); +} + +void x1600_i2c_set_target(void *i2c_channel, uint8_t addr) +{ + static_cast(i2c_channel)->set_target(addr); +} + +void x1600_i2c_start_read(void *i2c_channel, uint8_t buf[], unsigned int total, + int stop) +{ + static_cast(i2c_channel)->start_read(buf, total, stop); +} + +void x1600_i2c_read(void *i2c_channel) +{ + static_cast(i2c_channel)->read(); +} + +void x1600_i2c_start_write(void *i2c_channel, uint8_t buf[], unsigned int total, + int stop) +{ + static_cast(i2c_channel)->start_write(buf, total, stop); +} + +void x1600_i2c_write(void *i2c_channel) +{ + static_cast(i2c_channel)->write(); +} + +int x1600_i2c_read_done(void *i2c_channel) +{ + return static_cast(i2c_channel)->read_done(); +} + +int x1600_i2c_write_done(void *i2c_channel) +{ + return static_cast(i2c_channel)->write_done(); +} + +unsigned int x1600_i2c_have_read(void *i2c_channel) +{ + return static_cast(i2c_channel)->have_read(); +} + +unsigned int x1600_i2c_have_written(void *i2c_channel) +{ + return static_cast(i2c_channel)->have_written(); +} + +int x1600_i2c_failed(void *i2c_channel) +{ + return static_cast(i2c_channel)->failed(); +} diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/spi/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/spi/Makefile Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,8 @@ +PKGDIR ?= ../.. +L4DIR ?= $(PKGDIR)/../.. + +TARGET := include src + +include $(L4DIR)/mk/subdir.mk + +src: include diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/spi/include/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/spi/include/Makefile Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,4 @@ +PKGDIR = ../../.. +L4DIR ?= $(PKGDIR)/../.. + +include $(L4DIR)/mk/include.mk diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/spi/include/spi-gpio.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/spi/include/spi-gpio.h Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,69 @@ +/* + * Perform SPI communication using GPIO operations. + * + * Copyright (C) 2018, 2020, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include + +#pragma once + + + +#ifdef __cplusplus + +/* SPI peripheral device. */ + +class Spi_gpio +{ + Hw::Gpio_chip *_clock_device; + int _clock_pin; + Hw::Gpio_chip *_data_device; + int _data_pin; + Hw::Gpio_chip *_enable_device; + int _enable_pin; + uint32_t _frequency; + +public: + /* Associate the device with a particular memory region. */ + + explicit Spi_gpio(Hw::Gpio_chip *clock_device, int clock_pin, + Hw::Gpio_chip *data_device, int data_pin, + Hw::Gpio_chip *enable_device, int enable_pin, + uint32_t frequency = 0); + + void send(int bytes, const uint8_t data[]); +}; + +#endif /* __cplusplus */ + + + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *spi_gpio_get_channel(void *clock_chip, int clock_pin, + void *data_chip, int data_pin, + void *enable_chip, int enable_pin, + uint32_t frequency); + +void spi_gpio_send(void *channel, int bytes, const uint8_t data[]); + +EXTERN_C_END diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/spi/src/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/spi/src/Makefile Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,13 @@ +PKGDIR ?= ../../.. +L4DIR ?= $(PKGDIR)/../.. + +TARGET = libspi.o.a libspi.o.so +PC_FILENAME := libdrivers-spi + +SRC_CC := gpio.cc + +PRIVATE_INCDIR += $(PKGDIR)/lib/spi/include + +REQUIRES_LIBS := l4re_c l4re_c-util libdrivers-common libdrivers-gpio + +include $(L4DIR)/mk/lib.mk diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/lib/spi/src/gpio.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/spi/src/gpio.cc Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,116 @@ +/* + * Perform SPI communication using GPIO operations. + * + * Copyright (C) 2018, 2020, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include + + + +Spi_gpio::Spi_gpio(Hw::Gpio_chip *clock_device, int clock_pin, + Hw::Gpio_chip *data_device, int data_pin, + Hw::Gpio_chip *enable_device, int enable_pin, + uint32_t frequency) +: _clock_device(clock_device), + _clock_pin(clock_pin), + _data_device(data_device), + _data_pin(data_pin), + _enable_device(enable_device), + _enable_pin(enable_pin), + _frequency(frequency) +{ + _clock_device->setup(_clock_pin, Hw::Gpio_chip::Output, 1); + _data_device->setup(_data_pin, Hw::Gpio_chip::Output, 0); + _enable_device->setup(_enable_pin, Hw::Gpio_chip::Output, 1); +} + +/* Send a SPI command. */ + +void Spi_gpio::send(int bytes, const uint8_t data[]) +{ + struct timespec ts; + uint8_t mask; + int bit, byte; + + if (_frequency) + { + ts.tv_sec = 0; + ts.tv_nsec = 1000000000 / _frequency; + } + + /* Initialise pin levels. */ + + _enable_device->set(_enable_pin, 1); + _clock_device->set(_clock_pin, 1); + _data_device->set(_data_pin, 0); + + /* Enter the transmission state. */ + + _enable_device->set(_enable_pin, 0); + + /* Clock data using the clock and data outputs. */ + + for (byte = 0; byte < bytes; byte++) + { + mask = 0x80; + + for (bit = 0; bit < 8; bit++) + { + /* NOTE: Data presented on falling clock level and sampled on rising clock + level. This is SPI mode 3, or 0 given that the enable level is + driven low immediately before the first bit is presented. */ + + _clock_device->set(_clock_pin, 0); + _data_device->set(_data_pin, data[byte] & mask ? 1 : 0); + + if (_frequency) + nanosleep(&ts, NULL); + + _clock_device->set(_clock_pin, 1); + + if (_frequency) + nanosleep(&ts, NULL); + + mask >>= 1; + } + } + + _enable_device->set(_enable_pin, 1); +} + + + +/* C language interface. */ + +void *spi_gpio_get_channel(void *clock_chip, int clock_pin, + void *data_chip, int data_pin, + void *enable_chip, int enable_pin, + uint32_t frequency) +{ + return (void *) new Spi_gpio(reinterpret_cast(clock_chip), clock_pin, + reinterpret_cast(data_chip), data_pin, + reinterpret_cast(enable_chip), enable_pin, + frequency); +} + +void spi_gpio_send(void *channel, int bytes, const uint8_t data[]) +{ + static_cast(channel)->send(bytes, data); +} diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/spi/src/jz4740/Makefile --- a/pkg/devices/spi/src/jz4740/Makefile Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/spi/src/jz4740/Makefile Tue Oct 24 18:52:06 2023 +0200 @@ -27,7 +27,7 @@ SRC_CC = $(SERVER_INTERFACES_SRC_CC) $(PLAIN_SRC_CC) -REQUIRES_LIBS = l4re_c l4re_c-util libdevice-util libdrivers-gpio libipc +REQUIRES_LIBS = l4re_c l4re_c-util libdevice-util libdrivers-gpio libdrivers-spi libipc PRIVATE_INCDIR = $(PKGDIR)/spi/include $(PKGDIR)/util/include \ $(IDL_BUILD_DIR) $(IDL_EXPORT_DIR) diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/spi/src/jz4740/spi-jz4740.cc --- a/pkg/devices/spi/src/jz4740/spi-jz4740.cc Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/spi/src/jz4740/spi-jz4740.cc Tue Oct 24 18:52:06 2023 +0200 @@ -20,6 +20,7 @@ */ #include +#include #include #include @@ -54,20 +55,13 @@ class SPI_server : public SPI { - Gpio_jz4740_chip *_clock_device = 0, *_data_device = 0, *_enable_device = 0; - int _clock_pin, _data_pin, _enable_pin; + Spi_gpio *_spi; public: /* Associate the device with a particular memory region. */ - explicit SPI_server(Gpio_jz4740_chip *clock_device, - Gpio_jz4740_chip *data_device, - Gpio_jz4740_chip *enable_device, - int clock_pin, int data_pin, int enable_pin) - : _clock_device(clock_device), - _data_device(data_device), - _enable_device(enable_device), - _clock_pin(clock_pin), _data_pin(data_pin), _enable_pin(enable_pin) + explicit SPI_server(Spi_gpio *spi) + : _spi(spi) { } @@ -75,31 +69,15 @@ long send(int bits, int data) { - uint32_t mask = 1 << (bits - 1); - int bit; - - /* Initialise pin levels. */ + int bytes = (bits + 7) / 8; + uint8_t buffer[bytes]; - _enable_device->set(_enable_pin, 1); - _clock_device->set(_clock_pin, 1); - _data_device->set(_data_pin, 0); - - /* Enter the transmission state. */ + /* Convert the data into a sequence of bytes. */ - _enable_device->set(_enable_pin, 0); - - /* Clock data using the clock and data outputs. */ - - for (bit = 0; bit < bits; bit++) - { - _clock_device->set(_clock_pin, 0); - _data_device->set(_data_pin, data & mask ? 1 : 0); - _clock_device->set(_clock_pin, 1); - mask >>= 1; - } + for (int byte = bytes; byte > 0; byte--) + buffer[byte - 1] = (data >> ((byte - 1) * 8)) & 0xff; - _enable_device->set(_enable_pin, 1); - + _spi->send(bytes, buffer); return L4_EOK; } }; @@ -182,10 +160,14 @@ gpio_port_enable.setup(enable_pin, Hw::Gpio_chip::Output, 0); + /* Create an object for SPI communication. */ + + Spi_gpio spi(&gpio_port_clock, clock_pin, &gpio_port_data, data_pin, + &gpio_port_enable, enable_pin); + /* Initialise and register a new server object. */ - SPI_server obj(&gpio_port_clock, &gpio_port_data, &gpio_port_enable, - clock_pin, data_pin, enable_pin); + SPI_server obj(&spi); /* Bind and start the IPC server loop. */ diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/util/include/dma.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/util/include/dma.h Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,32 @@ +/* + * DMA-related memory allocation utility functions. + * + * Copyright (C) 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include + +EXTERN_C_BEGIN + +long get_dma_region(unsigned long size, int align, l4_addr_t *vaddr, + l4re_dma_space_dma_addr_t *paddr, l4_cap_idx_t *mem); + +EXTERN_C_END diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/util/include/memory.h --- a/pkg/devices/util/include/memory.h Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/util/include/memory.h Tue Oct 24 18:52:06 2023 +0200 @@ -40,4 +40,7 @@ int get_memory(char const *hid, l4_addr_t *start, l4_addr_t *end); +int get_memory_complete(char const *hid, l4_addr_t *start, l4_addr_t *end, + l4_addr_t *phys_start, l4_addr_t *phys_end); + EXTERN_C_END diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/util/src/Makefile --- a/pkg/devices/util/src/Makefile Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/util/src/Makefile Tue Oct 24 18:52:06 2023 +0200 @@ -4,7 +4,7 @@ TARGET = libdevice_util.o.a libdevice_util.o.so PC_FILENAME := libdevice-util -SRC_CC := dataspace.cc dl.cc event-loop.cc memory.cc +SRC_CC := dataspace.cc dl.cc dma.cc event-loop.cc memory.cc PRIVATE_INCDIR += $(PKGDIR)/util/include diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/util/src/dma.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/util/src/dma.cc Tue Oct 24 18:52:06 2023 +0200 @@ -0,0 +1,108 @@ +/* + * DMA-related memory allocation utility functions. + * + * Copyright (C) 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dma.h" +#include "memory.h" + + + +// Allocate a memory region of the given size for DMA. + +long get_dma_region(unsigned long size, int align, l4_addr_t *vaddr, + l4re_dma_space_dma_addr_t *paddr, l4_cap_idx_t *mem) +{ + // Memory allocation capabilities. + + l4_cap_idx_t dma, vbus; + + // Obtain capabilities for the DMA region and the vbus. + + dma = ipc_cap_alloc(); + + if (l4_is_invalid_cap(dma)) + return -L4_ENOENT; + + vbus = l4re_env_get_cap("vbus"); + + if (l4_is_invalid_cap(vbus)) + return -L4_ENOENT; + + // Create the DMA space. + + if (l4_error(l4_factory_create(l4re_env()->mem_alloc, L4RE_PROTO_DMA_SPACE, dma))) + return -L4_ENOMEM; + + // Find the DMA domain and assign the DMA space. + + l4vbus_device_handle_t device = L4VBUS_NULL; + l4vbus_resource_t dma_resource; + + if (!find_resource(&device, &dma_resource, L4VBUS_RESOURCE_DMA_DOMAIN)) + return -L4_ENOENT; + + if (l4vbus_assign_dma_domain(vbus, dma_resource.start, + L4VBUS_DMAD_BIND | L4VBUS_DMAD_L4RE_DMA_SPACE, + dma)) + return -L4_ENOMEM; + + // Allocate memory at the given alignment. + + const l4_size_t alloc_flags = L4RE_MA_CONTINUOUS | L4RE_MA_PINNED; + const l4re_rm_flags_t attach_flags = L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW; + + // Map the allocated memory, obtaining a virtual address. + + *vaddr = 0; + + if (ipc_new_dataspace(size, alloc_flags, align, mem)) + return -L4_ENOMEM; + + if (ipc_attach_dataspace_align(*mem, size, attach_flags, align, (void **) vaddr)) + return -L4_ENOMEM; + + // Obtain a physical address. + + l4_size_t size_out = size; + *paddr = 0; + + if (l4re_dma_space_map(dma, *mem | L4_CAP_FPAGE_RW, 0, &size_out, 0, + L4RE_DMA_SPACE_TO_DEVICE, paddr)) + return -L4_ENOMEM; + + // Test the mapped region size. + + if (size_out != size) + return -L4_ENOMEM; + + return L4_EOK; +} diff -r 40a23a1e92f8 -r 934192f49457 pkg/devices/util/src/memory.cc --- a/pkg/devices/util/src/memory.cc Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/util/src/memory.cc Tue Oct 24 18:52:06 2023 +0200 @@ -76,6 +76,14 @@ int get_memory(char const *hid, l4_addr_t *start, l4_addr_t *end) { + l4_addr_t phys_start, phys_end; + + return get_memory_complete(hid, start, end, &phys_start, &phys_end); +} + +int get_memory_complete(char const *hid, l4_addr_t *start, l4_addr_t *end, + l4_addr_t *phys_start, l4_addr_t *phys_end) +{ l4io_device_handle_t dh; l4io_resource_handle_t rh; l4io_resource_t res; @@ -98,6 +106,8 @@ return result; *end = *start + (res.end - res.start + 1); + *phys_start = res.start; + *phys_end = res.end; return 0; } diff -r 40a23a1e92f8 -r 934192f49457 pkg/landfall-examples/letux400_dma/letux400_dma.cc --- a/pkg/landfall-examples/letux400_dma/letux400_dma.cc Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/landfall-examples/letux400_dma/letux400_dma.cc Tue Oct 24 18:52:06 2023 +0200 @@ -19,16 +19,14 @@ * Boston, MA 02110-1301, USA */ +#include #include #include #include #include #include -#include -#include #include -#include #include #include @@ -36,8 +34,6 @@ #include #include -#include - #include #include #include @@ -61,88 +57,27 @@ long err; void *cpm; void *dmac, *dma0; - l4_cap_idx_t dma, vbus; - - dma = l4re_util_cap_alloc(); - vbus = l4re_env_get_cap("vbus"); - - if (l4_is_invalid_cap(dma)) - { - printf("Could not allocate DMA capability.\n"); - return 1; - } - - /* Create the DMA space. */ - - err = l4_error(l4_factory_create(l4re_env()->mem_alloc, L4RE_PROTO_DMA_SPACE, dma)); - - if (err) - { - printf("Could not create DMA space: %s\n", l4sys_errtostr(err)); - return 1; - } - - l4vbus_device_handle_t device = L4VBUS_NULL; - l4vbus_resource_t dma_resource; - - if (!find_resource(&device, &dma_resource, L4VBUS_RESOURCE_DMA_DOMAIN)) - { - printf("Could not find DMA domain.\n"); - return 1; - } - - err = l4vbus_assign_dma_domain(vbus, dma_resource.start, - L4VBUS_DMAD_BIND | L4VBUS_DMAD_L4RE_DMA_SPACE, - dma); - - if (err) - { - printf("Could not assign DMA space: %s\n", l4sys_errtostr(err)); - return 1; - } /* Allocate memory to test transfers. */ l4_cap_idx_t ds0_mem, ds1_mem; - l4_size_t ds0_size = L4_PAGESIZE, ds0_psize, ds1_size = L4_PAGESIZE, ds1_psize; + l4_size_t ds0_size = L4_PAGESIZE, ds1_size = L4_PAGESIZE; l4_addr_t ds0_addr, ds1_addr; l4re_dma_space_dma_addr_t ds0_paddr, ds1_paddr; - ds0_mem = l4re_util_cap_alloc(); - ds1_mem = l4re_util_cap_alloc(); + err = get_dma_region(ds0_size, 8, &ds0_addr, &ds0_paddr, &ds0_mem); - if (l4_is_invalid_cap(ds0_mem) || l4_is_invalid_cap(ds1_mem)) - { - printf("Could not allocate memory capabilities.\n"); - return 1; - } - - if (l4re_ma_alloc_align(ds0_size, ds0_mem, L4RE_MA_CONTINUOUS | L4RE_MA_PINNED, 8) || - l4re_ma_alloc_align(ds1_size, ds1_mem, L4RE_MA_CONTINUOUS | L4RE_MA_PINNED, 8)) + if (err) { - printf("Could not allocate memory.\n"); + printf("Could not allocate region #0.\n"); return 1; } - if (l4re_rm_attach((void **) &ds0_addr, ds0_size, - L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_EAGER_MAP | L4RE_RM_F_RW, - ds0_mem, 0, L4_PAGESHIFT) || - l4re_rm_attach((void **) &ds1_addr, ds1_size, - L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_EAGER_MAP | L4RE_RM_F_RW, - ds1_mem, 0, L4_PAGESHIFT)) - { - printf("Could not map memory.\n"); - return 1; - } - - err = l4re_dma_space_map(dma, ds0_mem | L4_CAP_FPAGE_RW, 0, &ds0_psize, 0, - L4RE_DMA_SPACE_BIDIRECTIONAL, &ds0_paddr) || - l4re_dma_space_map(dma, ds1_mem | L4_CAP_FPAGE_RW, 0, &ds1_psize, 0, - L4RE_DMA_SPACE_BIDIRECTIONAL, &ds1_paddr); + err = get_dma_region(ds1_size, 8, &ds1_addr, &ds1_paddr, &ds1_mem); if (err) { - printf("Could not get physical addresses for memory: %s\n", l4sys_errtostr(err)); + printf("Could not allocate region #1.\n"); return 1; } @@ -253,13 +188,19 @@ printf("Transfer from %llx to %llx...\n", ds0_paddr, ds1_paddr); - unsigned int ntransferred = jz4730_dma_transfer(dma0, (uint32_t) ds0_paddr, - (uint32_t) ds1_paddr, - L4_PAGESIZE / 4, - Dma_trans_unit_size_32_bit, - Dma_request_auto); + unsigned int count = L4_PAGESIZE / 4; - printf("Transferred: %d\n", ntransferred); + unsigned int to_transfer = jz4730_dma_transfer(dma0, (uint32_t) ds0_paddr, + (uint32_t) ds1_paddr, + count, + 1, 1, + 4, 4, + 4, + Dma_request_auto); + + unsigned int transferred = to_transfer ? count - jz4730_dma_wait(dma0) : 0; + + printf("Transferred: %d\n", transferred); printf("Source: %s\n", (char *) ds0_addr); printf("Destination: %s\n", (char *) ds1_addr);