1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/pkg/devices/lib/spi/include/spi-common.h Fri Jun 07 16:12:32 2024 +0200
1.3 @@ -0,0 +1,102 @@
1.4 +/*
1.5 + * Perform SPI communication using the SPI peripheral.
1.6 + *
1.7 + * Copyright (C) 2023, 2024 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#pragma once
1.26 +
1.27 +#include <l4/re/c/dma_space.h>
1.28 +#include <l4/sys/types.h>
1.29 +#include <stdint.h>
1.30 +
1.31 +
1.32 +
1.33 +#ifdef __cplusplus
1.34 +
1.35 +#include <l4/devices/cpm-generic.h>
1.36 +#include <l4/devices/dma-generic.h>
1.37 +#include <l4/devices/hw_mmio_register_block.h>
1.38 +#include <l4/devices/spi.h>
1.39 +
1.40 +/* SPI peripheral channel. */
1.41 +
1.42 +class Spi_channel : public Spi_channel_base
1.43 +{
1.44 + Hw::Register_block<32> _regs;
1.45 +
1.46 + /* Initialisation parameters. */
1.47 +
1.48 + l4_addr_t _spi_start;
1.49 + enum Clock_identifiers _clock;
1.50 + Cpm_chip *_cpm;
1.51 + Dma_channel *_dma;
1.52 + int _request_type;
1.53 + uint64_t _frequency;
1.54 +
1.55 + /* Common utilities. */
1.56 +
1.57 + void configure_transfer(uint8_t char_size);
1.58 + void wait_busy();
1.59 +
1.60 +public:
1.61 + explicit Spi_channel(l4_addr_t spi_start, l4_addr_t start,
1.62 + enum Clock_identifiers clock,
1.63 + Cpm_chip *cpm,
1.64 + Dma_channel *dma,
1.65 + int request_type,
1.66 + uint64_t frequency);
1.67 +
1.68 + /* Convenience operations. */
1.69 +
1.70 + virtual uint32_t send(uint32_t bytes, const uint8_t data[]);
1.71 +
1.72 + virtual uint32_t send_dc(uint32_t bytes, const uint8_t data[], const int dc[],
1.73 + uint8_t char_size, bool big_endian = true);
1.74 +
1.75 + uint32_t send_units(uint32_t bytes, const uint8_t data[], uint8_t unit_size,
1.76 + uint8_t char_size, bool big_endian = true);
1.77 +
1.78 + /* DMA operations. */
1.79 +
1.80 + uint32_t transfer(l4_addr_t vaddr, l4re_dma_space_dma_addr_t paddr,
1.81 + uint32_t count, uint8_t unit_size, uint8_t char_size,
1.82 + l4_addr_t desc_vaddr = 0,
1.83 + l4re_dma_space_dma_addr_t desc_paddr = 0);
1.84 +};
1.85 +
1.86 +/* SPI peripheral. */
1.87 +
1.88 +class Spi_chip
1.89 +{
1.90 +protected:
1.91 + l4_addr_t _spi_start, _start, _end;
1.92 + Cpm_chip *_cpm;
1.93 +
1.94 + virtual unsigned int num_channels() = 0;
1.95 +
1.96 + virtual Spi_channel *_get_channel(uint8_t channel, Dma_channel *dma,
1.97 + uint64_t frequency) = 0;
1.98 +
1.99 +public:
1.100 + explicit Spi_chip(l4_addr_t spi_start, l4_addr_t start, l4_addr_t end, Cpm_chip *cpm);
1.101 +
1.102 + Spi_channel *get_channel(uint8_t channel, Dma_channel *dma, uint64_t frequency);
1.103 +};
1.104 +
1.105 +#endif /* __cplusplus */
2.1 --- a/pkg/devices/lib/spi/include/spi-jz4780.h Fri Jun 07 16:08:15 2024 +0200
2.2 +++ b/pkg/devices/lib/spi/include/spi-jz4780.h Fri Jun 07 16:12:32 2024 +0200
2.3 @@ -29,32 +29,13 @@
2.4
2.5 #ifdef __cplusplus
2.6
2.7 -#include <l4/devices/cpm-jz4780.h>
2.8 #include <l4/devices/dma-jz4780.h>
2.9 -#include <l4/devices/hw_mmio_register_block.h>
2.10 -#include <l4/devices/spi.h>
2.11 +#include <l4/devices/spi-common.h>
2.12
2.13 /* SPI peripheral channel. */
2.14
2.15 -class Spi_jz4780_channel : public Spi_channel_base
2.16 +class Spi_jz4780_channel : public Spi_channel
2.17 {
2.18 - Hw::Register_block<32> _regs;
2.19 -
2.20 - /* Initialisation parameters. */
2.21 -
2.22 - l4_addr_t _spi_start;
2.23 - enum Clock_identifiers _clock;
2.24 - Cpm_chip *_cpm;
2.25 - Dma_channel *_dma;
2.26 - enum Dma_jz4780_request_type _request_type;
2.27 - uint64_t _frequency;
2.28 -
2.29 - /* Common utilities. */
2.30 -
2.31 - void configure_transfer(uint8_t char_size);
2.32 -
2.33 - void wait_busy();
2.34 -
2.35 public:
2.36 explicit Spi_jz4780_channel(l4_addr_t spi_start, l4_addr_t start,
2.37 enum Clock_identifiers clock,
2.38 @@ -62,37 +43,21 @@
2.39 Dma_channel *dma,
2.40 enum Dma_jz4780_request_type request_type,
2.41 uint64_t frequency);
2.42 -
2.43 - /* Convenience operations. */
2.44 -
2.45 - virtual uint32_t send(uint32_t bytes, const uint8_t data[]);
2.46 -
2.47 - virtual uint32_t send_dc(uint32_t bytes, const uint8_t data[], const int dc[],
2.48 - uint8_t char_size, bool big_endian = true);
2.49 -
2.50 - uint32_t send_units(uint32_t bytes, const uint8_t data[], uint8_t unit_size,
2.51 - uint8_t char_size, bool big_endian = true);
2.52 -
2.53 - /* DMA operations. */
2.54 -
2.55 - uint32_t transfer(l4_addr_t vaddr, l4re_dma_space_dma_addr_t paddr,
2.56 - uint32_t count, uint8_t unit_size, uint8_t char_size,
2.57 - l4_addr_t desc_vaddr = 0,
2.58 - l4re_dma_space_dma_addr_t desc_paddr = 0);
2.59 };
2.60
2.61 /* SPI peripheral. */
2.62
2.63 -class Spi_jz4780_chip
2.64 +class Spi_jz4780_chip : public Spi_chip
2.65 {
2.66 -private:
2.67 - l4_addr_t _spi_start, _start, _end;
2.68 - Cpm_chip *_cpm;
2.69 +protected:
2.70 + unsigned int num_channels()
2.71 + { return 2; }
2.72 +
2.73 + Spi_channel *_get_channel(uint8_t channel, Dma_channel *dma,
2.74 + uint64_t frequency);
2.75
2.76 public:
2.77 explicit Spi_jz4780_chip(l4_addr_t spi_start, l4_addr_t start, l4_addr_t end, Cpm_chip *cpm);
2.78 -
2.79 - Spi_jz4780_channel *get_channel(uint8_t channel, Dma_channel *dma, uint64_t frequency);
2.80 };
2.81
2.82 #endif /* __cplusplus */
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/pkg/devices/lib/spi/include/spi-x1600.h Fri Jun 07 16:12:32 2024 +0200
3.3 @@ -0,0 +1,94 @@
3.4 +/*
3.5 + * Perform SPI communication using the X1600 SPI peripheral.
3.6 + *
3.7 + * Copyright (C) 2023, 2024 Paul Boddie <paul@boddie.org.uk>
3.8 + *
3.9 + * This program is free software; you can redistribute it and/or
3.10 + * modify it under the terms of the GNU General Public License as
3.11 + * published by the Free Software Foundation; either version 2 of
3.12 + * the License, or (at your option) any later version.
3.13 + *
3.14 + * This program is distributed in the hope that it will be useful,
3.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
3.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3.17 + * GNU General Public License for more details.
3.18 + *
3.19 + * You should have received a copy of the GNU General Public License
3.20 + * along with this program; if not, write to the Free Software
3.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
3.22 + * Boston, MA 02110-1301, USA
3.23 + */
3.24 +
3.25 +#pragma once
3.26 +
3.27 +#include <l4/re/c/dma_space.h>
3.28 +#include <l4/sys/types.h>
3.29 +#include <stdint.h>
3.30 +
3.31 +
3.32 +
3.33 +#ifdef __cplusplus
3.34 +
3.35 +#include <l4/devices/dma-x1600.h>
3.36 +#include <l4/devices/spi-common.h>
3.37 +
3.38 +/* SPI peripheral channel. */
3.39 +
3.40 +class Spi_x1600_channel : public Spi_channel
3.41 +{
3.42 +public:
3.43 + explicit Spi_x1600_channel(l4_addr_t spi_start, l4_addr_t start,
3.44 + enum Clock_identifiers clock,
3.45 + Cpm_chip *cpm,
3.46 + Dma_channel *dma,
3.47 + enum Dma_x1600_request_type request_type,
3.48 + uint64_t frequency);
3.49 +};
3.50 +
3.51 +/* SPI peripheral. */
3.52 +
3.53 +class Spi_x1600_chip : public Spi_chip
3.54 +{
3.55 +protected:
3.56 + unsigned int num_channels()
3.57 + { return 1; }
3.58 +
3.59 + Spi_channel *_get_channel(uint8_t channel, Dma_channel *dma,
3.60 + uint64_t frequency);
3.61 +
3.62 +public:
3.63 + explicit Spi_x1600_chip(l4_addr_t spi_start, l4_addr_t start, l4_addr_t end, Cpm_chip *cpm);
3.64 +};
3.65 +
3.66 +#endif /* __cplusplus */
3.67 +
3.68 +
3.69 +
3.70 +/* C language interface. */
3.71 +
3.72 +EXTERN_C_BEGIN
3.73 +
3.74 +void *x1600_spi_init(l4_addr_t spi_start, l4_addr_t start, l4_addr_t end,
3.75 + void *cpm);
3.76 +
3.77 +void *x1600_spi_get_channel(void *spi, uint8_t channel, void *dma, uint64_t frequency);
3.78 +
3.79 +uint32_t x1600_spi_send(void *channel, uint32_t bytes, const uint8_t data[]);
3.80 +
3.81 +uint32_t x1600_spi_send_dc(void *channel, uint32_t bytes, const uint8_t data[],
3.82 + const int dc[], uint8_t char_size, int big_endian);
3.83 +
3.84 +uint32_t x1600_spi_send_units(void *channel, uint32_t bytes, const uint8_t data[],
3.85 + uint8_t unit_size, uint8_t char_size, int big_endian);
3.86 +
3.87 +uint32_t x1600_spi_transfer(void *channel, l4_addr_t vaddr,
3.88 + l4re_dma_space_dma_addr_t paddr, uint32_t count,
3.89 + uint8_t unit_size, uint8_t char_size);
3.90 +
3.91 +uint32_t x1600_spi_transfer_descriptor(void *channel, l4_addr_t vaddr,
3.92 + l4re_dma_space_dma_addr_t paddr,
3.93 + uint32_t count, uint8_t unit_size,
3.94 + uint8_t char_size, l4_addr_t desc_vaddr,
3.95 + l4re_dma_space_dma_addr_t desc_paddr);
3.96 +
3.97 +EXTERN_C_END
4.1 --- a/pkg/devices/lib/spi/include/spi.h Fri Jun 07 16:08:15 2024 +0200
4.2 +++ b/pkg/devices/lib/spi/include/spi.h Fri Jun 07 16:12:32 2024 +0200
4.3 @@ -30,9 +30,23 @@
4.4
4.5 /* SPI channel abstractions. */
4.6
4.7 +class Spi_control_base
4.8 +{
4.9 +public:
4.10 + virtual void acquire_control(bool asserted) = 0;
4.11 +
4.12 + virtual void release_control() = 0;
4.13 +};
4.14 +
4.15 class Spi_channel_base
4.16 {
4.17 +protected:
4.18 + Spi_control_base *_control = NULL;
4.19 +
4.20 public:
4.21 + void set_control(Spi_control_base *control)
4.22 + { _control = control; }
4.23 +
4.24 virtual uint32_t send(uint32_t bytes, const uint8_t data[]) = 0;
4.25
4.26 virtual uint32_t send_dc(uint32_t bytes, const uint8_t data[],
4.27 @@ -49,12 +63,4 @@
4.28 l4re_dma_space_dma_addr_t desc_paddr = 0) = 0;
4.29 };
4.30
4.31 -class Spi_control_base
4.32 -{
4.33 -public:
4.34 - virtual void acquire_control(bool asserted) = 0;
4.35 -
4.36 - virtual void release_control() = 0;
4.37 -};
4.38 -
4.39 #endif /* __cplusplus */
5.1 --- a/pkg/devices/lib/spi/src/Makefile Fri Jun 07 16:08:15 2024 +0200
5.2 +++ b/pkg/devices/lib/spi/src/Makefile Fri Jun 07 16:12:32 2024 +0200
5.3 @@ -4,7 +4,7 @@
5.4 TARGET = libspi.o.a libspi.o.so
5.5 PC_FILENAME := libdrivers-spi
5.6
5.7 -SRC_CC := gpio.cc hybrid.cc jz4780.cc
5.8 +SRC_CC := common.cc gpio.cc hybrid.cc jz4780.cc x1600.cc
5.9
5.10 PRIVATE_INCDIR += $(PKGDIR)/lib/spi/include
5.11
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/pkg/devices/lib/spi/src/common.cc Fri Jun 07 16:12:32 2024 +0200
6.3 @@ -0,0 +1,465 @@
6.4 +/*
6.5 + * Perform SPI communication using the JZ4780/X1600 SPI peripheral.
6.6 + *
6.7 + * Copyright (C) 2023, 2024 Paul Boddie <paul@boddie.org.uk>
6.8 + *
6.9 + * This program is free software; you can redistribute it and/or
6.10 + * modify it under the terms of the GNU General Public License as
6.11 + * published by the Free Software Foundation; either version 2 of
6.12 + * the License, or (at your option) any later version.
6.13 + *
6.14 + * This program is distributed in the hope that it will be useful,
6.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
6.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6.17 + * GNU General Public License for more details.
6.18 + *
6.19 + * You should have received a copy of the GNU General Public License
6.20 + * along with this program; if not, write to the Free Software
6.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
6.22 + * Boston, MA 02110-1301, USA
6.23 + */
6.24 +
6.25 +#include <l4/devices/byteorder.h>
6.26 +#include <l4/devices/dma.h>
6.27 +#include <l4/devices/spi-common.h>
6.28 +#include <l4/sys/err.h>
6.29 +#include <string.h>
6.30 +#include <stdio.h>
6.31 +
6.32 +
6.33 +
6.34 +/* Register definitions. */
6.35 +
6.36 +enum Regs
6.37 +{
6.38 + Ssi_data = 0x00, // SSIDR
6.39 + Ssi_control0 = 0x04, // SSICR0
6.40 + Ssi_control1 = 0x08, // SSICR1
6.41 + Ssi_status = 0x0c, // SSISR
6.42 + Ssi_interval_time = 0x10, // SSIITR
6.43 + Ssi_char_per_frame = 0x14, // SSIICR
6.44 + Ssi_clock = 0x18, // SSICGR
6.45 + Ssi_recv_counter = 0x1c, // SSIRCNT
6.46 +};
6.47 +
6.48 +enum Ssi_data_bits : unsigned
6.49 +{
6.50 + Ssi_data_gpc_set = 0x10000,
6.51 + Ssi_data_gpc_unset = 0x00000,
6.52 +};
6.53 +
6.54 +enum Ssi_control0_bits : unsigned
6.55 +{
6.56 + Ssi_trans_endian_mask = 0xc0000,
6.57 + Ssi_trans_endian_msbyte_msbit = 0x00000,
6.58 + Ssi_trans_endian_msbyte_lsbit = 0x40000,
6.59 + Ssi_trans_endian_lsbyte_lsbit = 0x80000,
6.60 + Ssi_trans_endian_lsbyte_msbit = 0xc0000,
6.61 +
6.62 + Ssi_recv_endian_mask = 0x30000,
6.63 + Ssi_recv_endian_msbyte_msbit = 0x00000,
6.64 + Ssi_recv_endian_msbyte_lsbit = 0x10000,
6.65 + Ssi_recv_endian_lsbyte_lsbit = 0x20000,
6.66 + Ssi_recv_endian_lsbyte_msbit = 0x30000,
6.67 +
6.68 + Ssi_enable = 0x08000,
6.69 + Ssi_enable_trans_half_empty = 0x04000,
6.70 + Ssi_enable_recv_half_full = 0x02000,
6.71 + Ssi_enable_trans_error = 0x01000,
6.72 + Ssi_enable_recv_error = 0x00800,
6.73 + Ssi_loopback = 0x00400,
6.74 + Ssi_recv_finish_control = 0x00200,
6.75 + Ssi_recv_finished = 0x00100,
6.76 + Ssi_enable_auto_clear_underrun = 0x00080,
6.77 + Ssi_select_pin_is_ce2 = 0x00040,
6.78 + Ssi_use_recv_count = 0x00010,
6.79 + Ssi_old_fifo_empty_mode = 0x00008,
6.80 + Ssi_trans_flush = 0x00004,
6.81 + Ssi_recv_flush = 0x00002,
6.82 + Ssi_disable_recv = 0x00001,
6.83 +};
6.84 +
6.85 +enum Ssi_control1_bits : unsigned
6.86 +{
6.87 + Ssi_active_mask = 0xc0000000,
6.88 + Ssi_active_ce_low = 0x00000000,
6.89 + Ssi_active_ce_high = 0x40000000,
6.90 + Ssi_active_ce2_low = 0x00000000,
6.91 + Ssi_active_ce2_high = 0x80000000,
6.92 +
6.93 + Ssi_clock_start_delay_mask = 0x30000000,
6.94 + Ssi_clock_start_delay_default = 0x00000000,
6.95 + Ssi_clock_start_delay_plus_1 = 0x10000000,
6.96 + Ssi_clock_start_delay_plus_2 = 0x20000000,
6.97 + Ssi_clock_start_delay_plus_3 = 0x30000000,
6.98 +
6.99 + Ssi_clock_stop_delay_mask = 0x0c000000,
6.100 + Ssi_clock_stop_delay_default = 0x00000000,
6.101 + Ssi_clock_stop_delay_plus_1 = 0x04000000,
6.102 + Ssi_clock_stop_delay_plus_2 = 0x08000000,
6.103 + Ssi_clock_stop_delay_plus_3 = 0x0c000000,
6.104 +
6.105 + /* X1600... */
6.106 +
6.107 + Ssi_gpc_level_from_gpc_bit = 0x00000000,
6.108 + Ssi_gpc_level_from_gpc_level = 0x02000000,
6.109 +
6.110 + /* Common... */
6.111 +
6.112 + Ssi_interval_assert_ce_or_ce2 = 0x01000000,
6.113 + Ssi_trans_empty_unfinished = 0x00800000,
6.114 +
6.115 + Ssi_format_mask = 0x00300000,
6.116 + Ssi_format_spi = 0x00000000,
6.117 + Ssi_format_ssp = 0x00100000,
6.118 + Ssi_format_microwire1 = 0x00200000,
6.119 + Ssi_format_microwire2 = 0x00300000,
6.120 +
6.121 + Ssi_trans_threshold_mask = 0x000f0000,
6.122 + Ssi_command_length_mask = 0x0000f000,
6.123 + Ssi_recv_threshold_mask = 0x00000f00,
6.124 + Ssi_char_length_mask = 0x000000f8,
6.125 +
6.126 + /* X1600... */
6.127 +
6.128 + Ssi_gpc_level = 0x00000004, // see Ssi_gpc_level_from_gpc_level
6.129 +
6.130 + /* Common... */
6.131 +
6.132 + Spi_clock_assert_sample = 0x00000000, // phase #0
6.133 + Spi_clock_assert_drive = 0x00000002, // phase #1
6.134 + Spi_clock_idle_low_level = 0x00000000, // polarity #0
6.135 + Spi_clock_idle_high_level = 0x00000001, // polarity #1
6.136 +};
6.137 +
6.138 +enum Ssi_control1_shifts : unsigned
6.139 +{
6.140 + Ssi_trans_threshold_shift = 16,
6.141 + Ssi_command_length_shift = 12,
6.142 + Ssi_recv_threshold_shift = 8,
6.143 + Ssi_char_length_shift = 3,
6.144 +};
6.145 +
6.146 +enum Ssi_control1_limits : unsigned
6.147 +{
6.148 + Ssi_trans_threshold_limit = 15,
6.149 + Ssi_command_length_limit = 15,
6.150 + Ssi_recv_threshold_limit = 15,
6.151 + Ssi_char_length_limit = 30,
6.152 +};
6.153 +
6.154 +enum Ssi_status_bits : unsigned
6.155 +{
6.156 + Ssi_trans_char_count_mask = 0x00ff0000,
6.157 + Ssi_recv_char_count_mask = 0x0000ff00,
6.158 + Ssi_trans_ended = 0x00000080,
6.159 + Ssi_trans_busy = 0x00000040,
6.160 + Ssi_trans_fifo_full = 0x00000020,
6.161 + Ssi_recv_fifo_empty = 0x00000010,
6.162 + Ssi_trans_fifo_half_empty = 0x00000008,
6.163 + Ssi_recv_fifo_half_full = 0x00000004,
6.164 + Ssi_trans_underrun = 0x00000002,
6.165 + Ssi_recv_overrun = 0x00000001,
6.166 +};
6.167 +
6.168 +enum Ssi_status_shifts : unsigned
6.169 +{
6.170 + Ssi_trans_char_count_shift = 16,
6.171 + Ssi_recv_char_count_shift = 8,
6.172 +};
6.173 +
6.174 +enum Ssi_status_limits : unsigned
6.175 +{
6.176 + Ssi_trans_char_count_limit = 0xff,
6.177 + Ssi_recv_char_count_limit = 0xff,
6.178 +};
6.179 +
6.180 +enum Ssi_interval_time_bits : unsigned
6.181 +{
6.182 + Ssi_interval_clock_mask = 0x8000,
6.183 + Ssi_interval_clock_bit_clock = 0x0000,
6.184 + Ssi_interval_clock_32k_clock = 0x8000,
6.185 + Ssi_interval_time_mask = 0x3fff,
6.186 +};
6.187 +
6.188 +enum Ssi_char_per_frame_bits : unsigned
6.189 +{
6.190 + Ssi_char_per_frame_mask = 0x7,
6.191 +};
6.192 +
6.193 +enum Ssi_clock_bits : unsigned
6.194 +{
6.195 + Ssi_clock_frequency_mask = 0xff,
6.196 +};
6.197 +
6.198 +enum Ssi_recv_counter_bits : unsigned
6.199 +{
6.200 + Ssi_recv_counter_mask = 0xffff,
6.201 +};
6.202 +
6.203 +
6.204 +
6.205 +/* Initialise a channel. */
6.206 +
6.207 +Spi_channel::Spi_channel(l4_addr_t spi_start, l4_addr_t start,
6.208 + enum Clock_identifiers clock,
6.209 + Cpm_chip *cpm,
6.210 + Dma_channel *dma,
6.211 + int request_type,
6.212 + uint64_t frequency)
6.213 +: _spi_start(spi_start), _clock(clock), _cpm(cpm), _dma(dma),
6.214 + _request_type(request_type), _frequency(frequency)
6.215 +{
6.216 + _regs = new Hw::Mmio_register_block<32>(start);
6.217 + _cpm->start_clock(clock);
6.218 +
6.219 + /* Disable the channel while configuring: send MSB first, big endian wire
6.220 + representation. Disable reception. */
6.221 +
6.222 + _regs[Ssi_control0] = Ssi_trans_endian_msbyte_msbit |
6.223 + Ssi_select_pin_is_ce2 |
6.224 + Ssi_disable_recv;
6.225 +
6.226 + /* Set default transfer properties. */
6.227 +
6.228 + configure_transfer(8);
6.229 +
6.230 + /* Select "normal" mode. */
6.231 +
6.232 + _regs[Ssi_interval_time] = 0;
6.233 +
6.234 + /* Limit the frequency to half that of the device clock. */
6.235 +
6.236 + if (_frequency >= _cpm->get_frequency(_clock))
6.237 + _frequency = _cpm->get_frequency(_clock) / 2;
6.238 +
6.239 + /* SSI_CLK = DEV_CLK / (2 * (divider + 1)) */
6.240 +
6.241 + uint32_t divider = _cpm->get_frequency(_clock) / (_frequency * 2) - 1;
6.242 +
6.243 + _regs[Ssi_clock] = divider < Ssi_clock_frequency_mask ? divider : Ssi_clock_frequency_mask;
6.244 +
6.245 + /* Enable the channel. */
6.246 +
6.247 + _regs[Ssi_control0] = _regs[Ssi_control0] | Ssi_enable;
6.248 +}
6.249 +
6.250 +/* NOTE: More transfer characteristics should be configurable. */
6.251 +
6.252 +void Spi_channel::configure_transfer(uint8_t char_size)
6.253 +{
6.254 + uint32_t char_length;
6.255 +
6.256 + if (char_size < 2)
6.257 + char_length = 0;
6.258 + else
6.259 + {
6.260 + char_length = char_size - 2;
6.261 +
6.262 + if (char_size > Ssi_char_length_limit)
6.263 + char_length = Ssi_char_length_limit;
6.264 + }
6.265 +
6.266 + /* Clear the status. */
6.267 +
6.268 + _regs[Ssi_control0] = _regs[Ssi_control0] | Ssi_trans_flush | Ssi_recv_flush;
6.269 + _regs[Ssi_status] = 0;
6.270 +
6.271 + /* Indicate the desired character size.
6.272 +
6.273 + Use active low device selection, SPI format with active low clock, with
6.274 + data driven on the falling (asserted) clock and sampled on the rising
6.275 + clock. */
6.276 +
6.277 + _regs[Ssi_control1] = (char_length << Ssi_char_length_shift) |
6.278 + ((Ssi_trans_threshold_limit / 2) << Ssi_trans_threshold_shift) |
6.279 + Ssi_format_spi | Ssi_active_ce2_low |
6.280 + Spi_clock_assert_sample | Spi_clock_idle_low_level;
6.281 +}
6.282 +
6.283 +/* Transfer the given number of bytes from a buffer. */
6.284 +
6.285 +uint32_t
6.286 +Spi_channel::send(uint32_t bytes, const uint8_t data[])
6.287 +{
6.288 + return send_units(bytes, data, 1, 8, false);
6.289 +}
6.290 +
6.291 +/* Transfer the given number of bytes from a buffer together with control
6.292 + values. Return the number of bytes transferred. */
6.293 +
6.294 +uint32_t
6.295 +Spi_channel::send_dc(uint32_t bytes, const uint8_t data[],
6.296 + const int dc[], uint8_t char_size, bool big_endian)
6.297 +{
6.298 + configure_transfer(char_size);
6.299 +
6.300 + uint32_t transferred, char_unit;
6.301 + uint8_t char_unit_size = ((char_size ? char_size - 1 : 0) / 8) + 1;
6.302 + uint32_t char_mask = (1 << char_size) - 1;
6.303 + bool last_control;
6.304 +
6.305 + for (transferred = 0, char_unit = 0; transferred < bytes;
6.306 + transferred += char_unit_size, char_unit++)
6.307 + {
6.308 + uint32_t value = get_stored_value(&data[transferred], char_unit_size, big_endian);
6.309 +
6.310 + /* Relocate the data/command level to bit 16. */
6.311 +
6.312 + uint32_t command = dc[char_unit] ? Ssi_data_gpc_set : Ssi_data_gpc_unset;
6.313 +
6.314 + /* Wait if the FIFO is full before sending. */
6.315 +
6.316 + while (_regs[Ssi_status] & Ssi_trans_fifo_full);
6.317 +
6.318 + /* Combine the character with the data/command bit. */
6.319 +
6.320 + if (_control == NULL)
6.321 + _regs[Ssi_data] = (value & char_mask) | command;
6.322 + else
6.323 + {
6.324 + /* Set the control level after waiting for sending to complete to ensure
6.325 + synchronisation. */
6.326 +
6.327 + bool this_control = dc[char_unit];
6.328 +
6.329 + if (transferred && (this_control != last_control))
6.330 + wait_busy();
6.331 +
6.332 + _control->acquire_control(this_control ? 1 : 0);
6.333 + _regs[Ssi_data] = (value & char_mask);
6.334 + last_control = this_control;
6.335 + }
6.336 + }
6.337 +
6.338 + wait_busy();
6.339 +
6.340 + return transferred;
6.341 +}
6.342 +
6.343 +/* Transfer the given number of bytes from a buffer using the given unit size in
6.344 + bytes and character size in bits. The bytes are stored in a big endian
6.345 + arrangement. Return the number of bytes transferred. */
6.346 +
6.347 +uint32_t
6.348 +Spi_channel::send_units(uint32_t bytes, const uint8_t data[],
6.349 + uint8_t unit_size, uint8_t char_size,
6.350 + bool big_endian)
6.351 +{
6.352 + configure_transfer(char_size);
6.353 +
6.354 + uint32_t transferred;
6.355 + uint32_t char_mask = (1 << char_size) - 1;
6.356 + bool last_control;
6.357 +
6.358 + for (transferred = 0; transferred < bytes; transferred += unit_size)
6.359 + {
6.360 + uint32_t value = get_stored_value(&data[transferred], unit_size, big_endian);
6.361 +
6.362 + /* Relocate any command bit to bit 16 for byte characters. */
6.363 +
6.364 + bool data_only = unit_size * 8 == char_size;
6.365 + bool gpc_set = (char_size < 16) && (value & (1 << char_size));
6.366 + uint32_t command = gpc_set ? Ssi_data_gpc_set : Ssi_data_gpc_unset;
6.367 +
6.368 + /* Wait if the FIFO is full before sending. */
6.369 +
6.370 + while (_regs[Ssi_status] & Ssi_trans_fifo_full);
6.371 +
6.372 + /* Combine the character portion of the unit with the command. */
6.373 +
6.374 + if (_control == NULL)
6.375 + _regs[Ssi_data] = (value & char_mask) | command;
6.376 + else
6.377 + {
6.378 + /* Set the control level after waiting for sending to complete to ensure
6.379 + synchronisation. */
6.380 +
6.381 + bool this_control = gpc_set || data_only;
6.382 +
6.383 + if (transferred && (this_control != last_control))
6.384 + wait_busy();
6.385 +
6.386 + _control->acquire_control(this_control ? 1 : 0);
6.387 + _regs[Ssi_data] = (value & char_mask);
6.388 + last_control = this_control;
6.389 + }
6.390 + }
6.391 +
6.392 + wait_busy();
6.393 +
6.394 + return transferred;
6.395 +}
6.396 +
6.397 +/* Transfer the given number of bytes from a DMA region using the given
6.398 + unit size in bytes and character size in bits. Return the number of bytes
6.399 + transferred. */
6.400 +
6.401 +uint32_t
6.402 +Spi_channel::transfer(l4_addr_t vaddr,
6.403 + l4re_dma_space_dma_addr_t paddr,
6.404 + uint32_t count, uint8_t unit_size,
6.405 + uint8_t char_size,
6.406 + l4_addr_t desc_vaddr,
6.407 + l4re_dma_space_dma_addr_t desc_paddr)
6.408 +{
6.409 + /* Employ a non-DMA transfer if no usable physical address is provided.
6.410 + Assume little endian byte ordering in line with the native value
6.411 + representation. */
6.412 +
6.413 + if (!paddr)
6.414 + return send_units(count, (const uint8_t *) vaddr, unit_size, char_size,
6.415 + false);
6.416 +
6.417 + /* Configure and initiate a DMA transfer with optional descriptor. */
6.418 +
6.419 + configure_transfer(char_size);
6.420 +
6.421 + uint32_t transferred = 0;
6.422 + uint32_t unit_count = count / unit_size;
6.423 + uint32_t to_transfer = _dma->transfer(paddr, _spi_start + Ssi_data,
6.424 + unit_count, true, false,
6.425 + unit_size, unit_size, unit_size,
6.426 + _request_type, desc_vaddr, desc_paddr);
6.427 +
6.428 + /* Wait if not using a descriptor, which could be configured in a cycle to
6.429 + cause an endless, repeating transfer, perhaps updating a display, for
6.430 + example. */
6.431 +
6.432 + if (to_transfer && !desc_vaddr)
6.433 + {
6.434 + transferred = to_transfer ? (unit_count - _dma->wait()) * unit_size : 0;
6.435 + wait_busy();
6.436 + }
6.437 + else
6.438 + transferred = to_transfer * unit_size;
6.439 +
6.440 + return transferred;
6.441 +}
6.442 +
6.443 +/* Wait for the busy condition to clear or for a limited period. */
6.444 +
6.445 +void
6.446 +Spi_channel::wait_busy()
6.447 +{
6.448 + for (unsigned int i = 0; i < (1 << 20) && (_regs[Ssi_status] & Ssi_trans_busy); i++);
6.449 +}
6.450 +
6.451 +
6.452 +
6.453 +/* Initialise the peripheral abstraction. */
6.454 +
6.455 +Spi_chip::Spi_chip(l4_addr_t spi_start, l4_addr_t start, l4_addr_t end,
6.456 + Cpm_chip *cpm)
6.457 +: _spi_start(spi_start), _start(start), _end(end), _cpm(cpm)
6.458 +{
6.459 +}
6.460 +
6.461 +Spi_channel *
6.462 +Spi_chip::get_channel(uint8_t channel, Dma_channel *dma, uint64_t frequency)
6.463 +{
6.464 + if (channel < num_channels())
6.465 + return _get_channel(channel, dma, frequency);
6.466 + else
6.467 + throw -L4_EINVAL;
6.468 +}
7.1 --- a/pkg/devices/lib/spi/src/gpio.cc Fri Jun 07 16:08:15 2024 +0200
7.2 +++ b/pkg/devices/lib/spi/src/gpio.cc Fri Jun 07 16:12:32 2024 +0200
7.3 @@ -83,7 +83,7 @@
7.4 /* Initialise pin levels. */
7.5
7.6 _enable_device->set(_enable_pin, 1);
7.7 - _clock_device->set(_clock_pin, 1);
7.8 + _clock_device->set(_clock_pin, 0);
7.9 _data_device->set(_data_pin, 0);
7.10
7.11 if ((_control_device != NULL) && (_control_pin >= 0) && (dc != NULL))
7.12 @@ -109,8 +109,8 @@
7.13 for (uint8_t bit = 0; bit < char_size; bit++)
7.14 {
7.15 /* NOTE: Data presented on falling clock level and sampled on rising clock
7.16 - level. This is SPI mode 3, or 0 given that the enable level is
7.17 - driven low immediately before the first bit is presented. */
7.18 + level. This is SPI mode 0 given that the enable level is driven
7.19 + low immediately before the first bit is presented. */
7.20
7.21 _clock_device->set(_clock_pin, 0);
7.22 _data_device->set(_data_pin, value & mask ? 1 : 0);
7.23 @@ -128,6 +128,7 @@
7.24 }
7.25
7.26 _enable_device->set(_enable_pin, 1);
7.27 + _clock_device->set(_clock_pin, 0);
7.28
7.29 return bytes;
7.30 }
8.1 --- a/pkg/devices/lib/spi/src/hybrid.cc Fri Jun 07 16:08:15 2024 +0200
8.2 +++ b/pkg/devices/lib/spi/src/hybrid.cc Fri Jun 07 16:12:32 2024 +0200
8.3 @@ -32,6 +32,10 @@
8.4 _control_pin(control_pin),
8.5 _control_alt_func(control_alt_func)
8.6 {
8.7 + /* Provide this control interface to the channel. */
8.8 +
8.9 + if (_control_alt_func < 0)
8.10 + _channel->set_control(this);
8.11 }
8.12
8.13 Spi_channel_base *Spi_hybrid::get_channel()
9.1 --- a/pkg/devices/lib/spi/src/jz4780.cc Fri Jun 07 16:08:15 2024 +0200
9.2 +++ b/pkg/devices/lib/spi/src/jz4780.cc Fri Jun 07 16:12:32 2024 +0200
9.3 @@ -1,5 +1,5 @@
9.4 /*
9.5 - * Perform SPI communication using the JZ4780/X1600 SPI peripheral.
9.6 + * Perform SPI communication using the JZ4780 SPI peripheral.
9.7 *
9.8 * Copyright (C) 2023, 2024 Paul Boddie <paul@boddie.org.uk>
9.9 *
9.10 @@ -19,187 +19,15 @@
9.11 * Boston, MA 02110-1301, USA
9.12 */
9.13
9.14 -#include <l4/devices/byteorder.h>
9.15 -#include <l4/devices/dma.h>
9.16 #include <l4/devices/spi-jz4780.h>
9.17 -#include <l4/sys/err.h>
9.18 -#include <string.h>
9.19 -
9.20 -
9.21 -
9.22 -/* Register definitions. */
9.23
9.24 -enum Regs
9.25 +enum Regs_jz4780
9.26 {
9.27 - Ssi_data = 0x00, // SSIDR
9.28 - Ssi_control0 = 0x04, // SSICR0
9.29 - Ssi_control1 = 0x08, // SSICR1
9.30 - Ssi_status = 0x0c, // SSISR
9.31 - Ssi_interval_time = 0x10, // SSIITR
9.32 - Ssi_char_per_frame = 0x14, // SSIICR
9.33 - Ssi_clock = 0x18, // SSICGR
9.34 - Ssi_recv_counter = 0x1c, // SSIRCNT
9.35 -
9.36 /* Register block offset. */
9.37
9.38 Ssi_block_offset = 0x1000,
9.39 };
9.40
9.41 -enum Ssi_data_bits : unsigned
9.42 -{
9.43 - Ssi_data_gpc_set = 0x10000,
9.44 - Ssi_data_gpc_unset = 0x00000,
9.45 -};
9.46 -
9.47 -enum Ssi_control0_bits : unsigned
9.48 -{
9.49 - Ssi_trans_endian_mask = 0xc0000,
9.50 - Ssi_trans_endian_msbyte_msbit = 0x00000,
9.51 - Ssi_trans_endian_msbyte_lsbit = 0x40000,
9.52 - Ssi_trans_endian_lsbyte_lsbit = 0x80000,
9.53 - Ssi_trans_endian_lsbyte_msbit = 0xc0000,
9.54 -
9.55 - Ssi_recv_endian_mask = 0x30000,
9.56 - Ssi_recv_endian_msbyte_msbit = 0x00000,
9.57 - Ssi_recv_endian_msbyte_lsbit = 0x10000,
9.58 - Ssi_recv_endian_lsbyte_lsbit = 0x20000,
9.59 - Ssi_recv_endian_lsbyte_msbit = 0x30000,
9.60 -
9.61 - Ssi_enable = 0x08000,
9.62 - Ssi_enable_trans_half_empty = 0x04000,
9.63 - Ssi_enable_recv_half_full = 0x02000,
9.64 - Ssi_enable_trans_error = 0x01000,
9.65 - Ssi_enable_recv_error = 0x00800,
9.66 - Ssi_loopback = 0x00400,
9.67 - Ssi_recv_finish_control = 0x00200,
9.68 - Ssi_recv_finished = 0x00100,
9.69 - Ssi_enable_auto_clear_underrun = 0x00080,
9.70 - Ssi_select_pin_is_ce2 = 0x00040,
9.71 - Ssi_use_recv_count = 0x00010,
9.72 - Ssi_old_fifo_empty_mode = 0x00008,
9.73 - Ssi_trans_flush = 0x00004,
9.74 - Ssi_recv_flush = 0x00002,
9.75 - Ssi_disable_recv = 0x00001,
9.76 -};
9.77 -
9.78 -enum Ssi_control1_bits : unsigned
9.79 -{
9.80 - Ssi_active_mask = 0xc0000000,
9.81 - Ssi_active_ce_low = 0x00000000,
9.82 - Ssi_active_ce_high = 0x40000000,
9.83 - Ssi_active_ce2_low = 0x00000000,
9.84 - Ssi_active_ce2_high = 0x80000000,
9.85 -
9.86 - Ssi_clock_start_delay_mask = 0x30000000,
9.87 - Ssi_clock_start_delay_default = 0x00000000,
9.88 - Ssi_clock_start_delay_plus_1 = 0x10000000,
9.89 - Ssi_clock_start_delay_plus_2 = 0x20000000,
9.90 - Ssi_clock_start_delay_plus_3 = 0x30000000,
9.91 -
9.92 - Ssi_clock_stop_delay_mask = 0x0c000000,
9.93 - Ssi_clock_stop_delay_default = 0x00000000,
9.94 - Ssi_clock_stop_delay_plus_1 = 0x04000000,
9.95 - Ssi_clock_stop_delay_plus_2 = 0x08000000,
9.96 - Ssi_clock_stop_delay_plus_3 = 0x0c000000,
9.97 -
9.98 - /* X1600... */
9.99 -
9.100 - Ssi_gpc_level_from_gpc_bit = 0x00000000,
9.101 - Ssi_gpc_level_from_gpc_level = 0x02000000,
9.102 -
9.103 - /* Common... */
9.104 -
9.105 - Ssi_interval_assert_ce_or_ce2 = 0x01000000,
9.106 - Ssi_trans_empty_unfinished = 0x00800000,
9.107 -
9.108 - Ssi_format_mask = 0x00300000,
9.109 - Ssi_format_spi = 0x00000000,
9.110 - Ssi_format_ssp = 0x00100000,
9.111 - Ssi_format_microwire1 = 0x00200000,
9.112 - Ssi_format_microwire2 = 0x00300000,
9.113 -
9.114 - Ssi_trans_threshold_mask = 0x000f0000,
9.115 - Ssi_command_length_mask = 0x0000f000,
9.116 - Ssi_recv_threshold_mask = 0x00000f00,
9.117 - Ssi_char_length_mask = 0x000000f8,
9.118 -
9.119 - /* X1600... */
9.120 -
9.121 - Ssi_gpc_level = 0x00000004, // see Ssi_gpc_level_from_gpc_level
9.122 -
9.123 - /* Common... */
9.124 -
9.125 - Spi_clock_assert_sample = 0x00000000, // phase #0
9.126 - Spi_clock_assert_drive = 0x00000002, // phase #1
9.127 - Spi_clock_idle_low_level = 0x00000000, // polarity #0
9.128 - Spi_clock_idle_high_level = 0x00000001, // polarity #1
9.129 -};
9.130 -
9.131 -enum Ssi_control1_shifts : unsigned
9.132 -{
9.133 - Ssi_trans_threshold_shift = 16,
9.134 - Ssi_command_length_shift = 12,
9.135 - Ssi_recv_threshold_shift = 8,
9.136 - Ssi_char_length_shift = 3,
9.137 -};
9.138 -
9.139 -enum Ssi_control1_limits : unsigned
9.140 -{
9.141 - Ssi_trans_threshold_limit = 15,
9.142 - Ssi_command_length_limit = 15,
9.143 - Ssi_recv_threshold_limit = 15,
9.144 - Ssi_char_length_limit = 30,
9.145 -};
9.146 -
9.147 -enum Ssi_status_bits : unsigned
9.148 -{
9.149 - Ssi_trans_char_count_mask = 0x00ff0000,
9.150 - Ssi_recv_char_count_mask = 0x0000ff00,
9.151 - Ssi_trans_ended = 0x00000080,
9.152 - Ssi_trans_busy = 0x00000040,
9.153 - Ssi_trans_fifo_full = 0x00000020,
9.154 - Ssi_recv_fifo_empty = 0x00000010,
9.155 - Ssi_trans_fifo_half_empty = 0x00000008,
9.156 - Ssi_recv_fifo_half_full = 0x00000004,
9.157 - Ssi_trans_underrun = 0x00000002,
9.158 - Ssi_recv_overrun = 0x00000001,
9.159 -};
9.160 -
9.161 -enum Ssi_status_shifts : unsigned
9.162 -{
9.163 - Ssi_trans_char_count_shift = 16,
9.164 - Ssi_recv_char_count_shift = 8,
9.165 -};
9.166 -
9.167 -enum Ssi_status_limits : unsigned
9.168 -{
9.169 - Ssi_trans_char_count_limit = 0xff,
9.170 - Ssi_recv_char_count_limit = 0xff,
9.171 -};
9.172 -
9.173 -enum Ssi_interval_time_bits : unsigned
9.174 -{
9.175 - Ssi_interval_clock_mask = 0x8000,
9.176 - Ssi_interval_clock_bit_clock = 0x0000,
9.177 - Ssi_interval_clock_32k_clock = 0x8000,
9.178 - Ssi_interval_time_mask = 0x3fff,
9.179 -};
9.180 -
9.181 -enum Ssi_char_per_frame_bits : unsigned
9.182 -{
9.183 - Ssi_char_per_frame_mask = 0x7,
9.184 -};
9.185 -
9.186 -enum Ssi_clock_bits : unsigned
9.187 -{
9.188 - Ssi_clock_frequency_mask = 0xff,
9.189 -};
9.190 -
9.191 -enum Ssi_recv_counter_bits : unsigned
9.192 -{
9.193 - Ssi_recv_counter_mask = 0xffff,
9.194 -};
9.195 -
9.196
9.197
9.198 /* Initialise a channel. */
9.199 @@ -210,205 +38,8 @@
9.200 Dma_channel *dma,
9.201 enum Dma_jz4780_request_type request_type,
9.202 uint64_t frequency)
9.203 -: _spi_start(spi_start), _clock(clock), _cpm(cpm), _dma(dma),
9.204 - _request_type(request_type), _frequency(frequency)
9.205 -{
9.206 - _regs = new Hw::Mmio_register_block<32>(start);
9.207 - _cpm->start_clock(clock);
9.208 -
9.209 - /* Disable the channel while configuring: send MSB first, big endian wire
9.210 - representation. Disable reception. */
9.211 -
9.212 - _regs[Ssi_control0] = Ssi_trans_endian_msbyte_msbit |
9.213 - Ssi_select_pin_is_ce2 |
9.214 - Ssi_disable_recv;
9.215 -
9.216 - /* Set default transfer properties. */
9.217 -
9.218 - configure_transfer(8);
9.219 -
9.220 - /* Select "normal" mode. */
9.221 -
9.222 - _regs[Ssi_interval_time] = 0;
9.223 -
9.224 - /* Limit the frequency to half that of the device clock. */
9.225 -
9.226 - if (_frequency >= _cpm->get_frequency(_clock))
9.227 - _frequency = _cpm->get_frequency(_clock) / 2;
9.228 -
9.229 - /* SSI_CLK = DEV_CLK / (2 * (divider + 1)) */
9.230 -
9.231 - uint32_t divider = _cpm->get_frequency(_clock) / (_frequency * 2) - 1;
9.232 -
9.233 - _regs[Ssi_clock] = divider < Ssi_clock_frequency_mask ? divider : Ssi_clock_frequency_mask;
9.234 -
9.235 - /* Enable the channel. */
9.236 -
9.237 - _regs[Ssi_control0] = _regs[Ssi_control0] | Ssi_enable;
9.238 -}
9.239 -
9.240 -/* NOTE: More transfer characteristics should be configurable. */
9.241 -
9.242 -void Spi_jz4780_channel::configure_transfer(uint8_t char_size)
9.243 -{
9.244 - uint32_t char_length;
9.245 -
9.246 - if (char_size < 2)
9.247 - char_length = 0;
9.248 - else
9.249 - {
9.250 - char_length = char_size - 2;
9.251 -
9.252 - if (char_size > Ssi_char_length_limit)
9.253 - char_length = Ssi_char_length_limit;
9.254 - }
9.255 -
9.256 - /* Clear the status. */
9.257 -
9.258 - _regs[Ssi_control0] = _regs[Ssi_control0] | Ssi_trans_flush | Ssi_recv_flush;
9.259 - _regs[Ssi_status] = 0;
9.260 -
9.261 - /* Indicate the desired character size.
9.262 -
9.263 - Use active low device selection, SPI format with active low clock, with
9.264 - data driven on the falling (asserted) clock and sampled on the rising
9.265 - clock
9.266 -
9.267 - The unfinished flag prevents the transaction from finishing if the FIFO is
9.268 - empty. It is not used here since it appears to prevent any meaningful
9.269 - testing of the busy and end flags. */
9.270 -
9.271 - _regs[Ssi_control1] = (char_length << Ssi_char_length_shift) |
9.272 - ((Ssi_trans_threshold_limit / 2) << Ssi_trans_threshold_shift) |
9.273 - Ssi_format_spi | Ssi_active_ce2_low |
9.274 - Spi_clock_assert_drive | Spi_clock_idle_high_level;
9.275 -}
9.276 -
9.277 -/* Transfer the given number of bytes from a buffer. */
9.278 -
9.279 -uint32_t
9.280 -Spi_jz4780_channel::send(uint32_t bytes, const uint8_t data[])
9.281 -{
9.282 - return send_units(bytes, data, 1, 8, false);
9.283 -}
9.284 -
9.285 -/* Transfer the given number of bytes from a buffer together with control
9.286 - values. Return the number of bytes transferred. */
9.287 -
9.288 -uint32_t
9.289 -Spi_jz4780_channel::send_dc(uint32_t bytes, const uint8_t data[],
9.290 - const int dc[], uint8_t char_size, bool big_endian)
9.291 +: Spi_channel(spi_start, start, clock, cpm, dma, request_type, frequency)
9.292 {
9.293 - configure_transfer(char_size);
9.294 -
9.295 - uint32_t transferred, char_unit;
9.296 - uint8_t char_unit_size = ((char_size ? char_size - 1 : 0) / 8) + 1;
9.297 - uint32_t char_mask = (1 << char_size) - 1;
9.298 -
9.299 - for (transferred = 0, char_unit = 0; transferred < bytes;
9.300 - transferred += char_unit_size, char_unit++)
9.301 - {
9.302 - uint32_t value = get_stored_value(&data[transferred], char_unit_size, big_endian);
9.303 -
9.304 - /* Relocate the data/command level to bit 16. */
9.305 -
9.306 - uint32_t command = dc[char_unit] ? Ssi_data_gpc_set : Ssi_data_gpc_unset;
9.307 -
9.308 - /* Combine the character with the data/command bit. */
9.309 -
9.310 - _regs[Ssi_data] = (value & char_mask) | command;
9.311 - }
9.312 -
9.313 - wait_busy();
9.314 -
9.315 - return transferred;
9.316 -}
9.317 -
9.318 -/* Transfer the given number of bytes from a buffer using the given unit size in
9.319 - bytes and character size in bits. The bytes are stored in a big endian
9.320 - arrangement. Return the number of bytes transferred. */
9.321 -
9.322 -uint32_t
9.323 -Spi_jz4780_channel::send_units(uint32_t bytes, const uint8_t data[],
9.324 - uint8_t unit_size, uint8_t char_size,
9.325 - bool big_endian)
9.326 -{
9.327 - configure_transfer(char_size);
9.328 -
9.329 - uint32_t transferred;
9.330 - uint32_t char_mask = (1 << char_size) - 1;
9.331 -
9.332 - for (transferred = 0; transferred < bytes; transferred += unit_size)
9.333 - {
9.334 - uint32_t value = get_stored_value(&data[transferred], unit_size, big_endian);
9.335 -
9.336 - /* Relocate any command bit to bit 16 for byte characters. */
9.337 -
9.338 - uint32_t command = (char_size < 16) && (value & (1 << char_size))
9.339 - ? Ssi_data_gpc_set : Ssi_data_gpc_unset;
9.340 -
9.341 - /* Combine the character portion of the unit with the command. */
9.342 -
9.343 - _regs[Ssi_data] = (value & char_mask) | command;
9.344 - }
9.345 -
9.346 - wait_busy();
9.347 -
9.348 - return transferred;
9.349 -}
9.350 -
9.351 -/* Transfer the given number of bytes from a DMA region using the given
9.352 - unit size in bytes and character size in bits. Return the number of bytes
9.353 - transferred. */
9.354 -
9.355 -uint32_t
9.356 -Spi_jz4780_channel::transfer(l4_addr_t vaddr,
9.357 - l4re_dma_space_dma_addr_t paddr,
9.358 - uint32_t count, uint8_t unit_size,
9.359 - uint8_t char_size,
9.360 - l4_addr_t desc_vaddr,
9.361 - l4re_dma_space_dma_addr_t desc_paddr)
9.362 -{
9.363 - /* Employ a non-DMA transfer if no usable physical address is provided.
9.364 - Assume little endian byte ordering in line with the native value
9.365 - representation. */
9.366 -
9.367 - if (!paddr)
9.368 - return send_units(count, (const uint8_t *) vaddr, unit_size, char_size,
9.369 - false);
9.370 -
9.371 - /* Configure and initiate a DMA transfer with optional descriptor. */
9.372 -
9.373 - configure_transfer(char_size);
9.374 -
9.375 - uint32_t transferred = 0;
9.376 - uint32_t unit_count = count / unit_size;
9.377 - uint32_t to_transfer = _dma->transfer(paddr, _spi_start + Ssi_data,
9.378 - unit_count, true, false,
9.379 - unit_size, unit_size, unit_size,
9.380 - _request_type, desc_vaddr, desc_paddr);
9.381 -
9.382 - /* Wait if not using a descriptor, which could be configured in a cycle to
9.383 - cause an endless, repeating transfer, perhaps updating a display, for
9.384 - example. */
9.385 -
9.386 - if (to_transfer && !desc_vaddr)
9.387 - {
9.388 - transferred = to_transfer ? (unit_count - _dma->wait()) * unit_size : 0;
9.389 - wait_busy();
9.390 - }
9.391 - else
9.392 - transferred = to_transfer * unit_size;
9.393 -
9.394 - return transferred;
9.395 -}
9.396 -
9.397 -/* Wait for the busy condition to clear or for a limited period. */
9.398 -
9.399 -void
9.400 -Spi_jz4780_channel::wait_busy()
9.401 -{
9.402 - for (unsigned int i = 0; i < (1 << 20) && (_regs[Ssi_status] & Ssi_trans_busy); i++);
9.403 }
9.404
9.405
9.406 @@ -417,26 +48,23 @@
9.407
9.408 Spi_jz4780_chip::Spi_jz4780_chip(l4_addr_t spi_start, l4_addr_t start,
9.409 l4_addr_t end, Cpm_chip *cpm)
9.410 -: _spi_start(spi_start), _start(start), _end(end), _cpm(cpm)
9.411 +: Spi_chip(spi_start, start, end, cpm)
9.412 {
9.413 }
9.414
9.415 -Spi_jz4780_channel *
9.416 -Spi_jz4780_chip::get_channel(uint8_t channel, Dma_channel *dma,
9.417 - uint64_t frequency)
9.418 +Spi_channel *
9.419 +Spi_jz4780_chip::_get_channel(uint8_t channel, Dma_channel *dma,
9.420 + uint64_t frequency)
9.421 {
9.422 // NOTE: Only sending is supported.
9.423
9.424 enum Dma_jz4780_request_type request_types[] = {Dma_request_ssi0_out, Dma_request_ssi1_out};
9.425 enum Clock_identifiers clocks[] = {Clock_ssi0, Clock_ssi1};
9.426
9.427 - if (channel < 2)
9.428 - return new Spi_jz4780_channel(_spi_start + channel * Ssi_block_offset,
9.429 - _start + channel * Ssi_block_offset,
9.430 - clocks[channel],
9.431 - _cpm, dma, request_types[channel], frequency);
9.432 - else
9.433 - throw -L4_EINVAL;
9.434 + return new Spi_jz4780_channel(_spi_start + channel * Ssi_block_offset,
9.435 + _start + channel * Ssi_block_offset,
9.436 + clocks[channel],
9.437 + _cpm, dma, request_types[channel], frequency);
9.438 }
9.439
9.440
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2 +++ b/pkg/devices/lib/spi/src/x1600.cc Fri Jun 07 16:12:32 2024 +0200
10.3 @@ -0,0 +1,111 @@
10.4 +/*
10.5 + * Perform SPI communication using the X1600 SPI peripheral.
10.6 + *
10.7 + * Copyright (C) 2023, 2024 Paul Boddie <paul@boddie.org.uk>
10.8 + *
10.9 + * This program is free software; you can redistribute it and/or
10.10 + * modify it under the terms of the GNU General Public License as
10.11 + * published by the Free Software Foundation; either version 2 of
10.12 + * the License, or (at your option) any later version.
10.13 + *
10.14 + * This program is distributed in the hope that it will be useful,
10.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
10.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10.17 + * GNU General Public License for more details.
10.18 + *
10.19 + * You should have received a copy of the GNU General Public License
10.20 + * along with this program; if not, write to the Free Software
10.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
10.22 + * Boston, MA 02110-1301, USA
10.23 + */
10.24 +
10.25 +#include <l4/devices/spi-x1600.h>
10.26 +
10.27 +
10.28 +
10.29 +/* Initialise a channel. */
10.30 +
10.31 +Spi_x1600_channel::Spi_x1600_channel(l4_addr_t spi_start, l4_addr_t start,
10.32 + enum Clock_identifiers clock,
10.33 + Cpm_chip *cpm,
10.34 + Dma_channel *dma,
10.35 + enum Dma_x1600_request_type request_type,
10.36 + uint64_t frequency)
10.37 +: Spi_channel(spi_start, start, clock, cpm, dma, request_type, frequency)
10.38 +{
10.39 +}
10.40 +
10.41 +
10.42 +
10.43 +/* Initialise the peripheral abstraction. */
10.44 +
10.45 +Spi_x1600_chip::Spi_x1600_chip(l4_addr_t spi_start, l4_addr_t start,
10.46 + l4_addr_t end, Cpm_chip *cpm)
10.47 +: Spi_chip(spi_start, start, end, cpm)
10.48 +{
10.49 +}
10.50 +
10.51 +Spi_channel *
10.52 +Spi_x1600_chip::_get_channel(uint8_t channel, Dma_channel *dma,
10.53 + uint64_t frequency)
10.54 +{
10.55 + (void) channel;
10.56 +
10.57 + // NOTE: Only sending is supported.
10.58 +
10.59 + return new Spi_x1600_channel(_spi_start, _start, Clock_ssi0,
10.60 + _cpm, dma, Dma_request_ssi0_out, frequency);
10.61 +}
10.62 +
10.63 +
10.64 +
10.65 +/* C language interface. */
10.66 +
10.67 +void *x1600_spi_init(l4_addr_t spi_start, l4_addr_t start, l4_addr_t end, void *cpm)
10.68 +{
10.69 + return new Spi_x1600_chip(spi_start, start, end, static_cast<Cpm_chip *>(cpm));
10.70 +}
10.71 +
10.72 +void *x1600_spi_get_channel(void *spi, uint8_t channel, void *dma, uint64_t frequency)
10.73 +{
10.74 + return static_cast<Spi_x1600_chip *>(spi)->get_channel(channel,
10.75 + static_cast<Dma_channel *>(dma), frequency);
10.76 +}
10.77 +
10.78 +uint32_t x1600_spi_send(void *channel, uint32_t bytes, const uint8_t data[])
10.79 +{
10.80 + return static_cast<Spi_x1600_channel *>(channel)->send(bytes, data);
10.81 +}
10.82 +
10.83 +uint32_t x1600_spi_send_dc(void *channel, uint32_t bytes, const uint8_t data[],
10.84 + const int dc[], uint8_t char_size, int big_endian)
10.85 +{
10.86 + return static_cast<Spi_x1600_channel *>(channel)->send_dc(bytes, data, dc,
10.87 + char_size, big_endian);
10.88 +}
10.89 +
10.90 +uint32_t x1600_spi_send_units(void *channel, uint32_t bytes, const uint8_t data[],
10.91 + uint8_t unit_size, uint8_t char_size, int big_endian)
10.92 +{
10.93 + return static_cast<Spi_x1600_channel *>(channel)->send_units(bytes, data,
10.94 + unit_size, char_size, big_endian);
10.95 +}
10.96 +
10.97 +uint32_t x1600_spi_transfer(void *channel, l4_addr_t vaddr,
10.98 + l4re_dma_space_dma_addr_t paddr,
10.99 + uint32_t count, uint8_t unit_size,
10.100 + uint8_t char_size)
10.101 +{
10.102 + return static_cast<Spi_x1600_channel *>(channel)->transfer(vaddr, paddr,
10.103 + count, unit_size, char_size);
10.104 +}
10.105 +
10.106 +uint32_t x1600_spi_transfer_descriptor(void *channel, l4_addr_t vaddr,
10.107 + l4re_dma_space_dma_addr_t paddr,
10.108 + uint32_t count, uint8_t unit_size,
10.109 + uint8_t char_size, l4_addr_t desc_vaddr,
10.110 + l4re_dma_space_dma_addr_t desc_paddr)
10.111 +{
10.112 + return static_cast<Spi_x1600_channel *>(channel)->transfer(vaddr, paddr,
10.113 + count, unit_size, char_size, desc_vaddr, desc_paddr);
10.114 +}