Landfall

Changeset

307:7b56ac3b3ef4
5 months ago Paul Boddie raw files shortlog changelog graph Introduced a common base class for SPI operations, integrating the control functionality of any involved hybrid SPI channel to permit the use of a GPIO-driven data/command pin instead of a peripheral's GPC signal. Added dedicated X1600 support for SPI, largely equivalent to the JZ4780 support. cpm-library-improvements
pkg/devices/lib/spi/include/spi-common.h (file) pkg/devices/lib/spi/include/spi-jz4780.h (file) pkg/devices/lib/spi/include/spi-x1600.h (file) pkg/devices/lib/spi/include/spi.h (file) pkg/devices/lib/spi/src/Makefile (file) pkg/devices/lib/spi/src/common.cc (file) pkg/devices/lib/spi/src/gpio.cc (file) pkg/devices/lib/spi/src/hybrid.cc (file) pkg/devices/lib/spi/src/jz4780.cc (file) pkg/devices/lib/spi/src/x1600.cc (file)
     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 +}