1.1 --- a/pkg/devices/lib/spi/src/jz4780.cc Fri Jun 07 16:08:15 2024 +0200
1.2 +++ b/pkg/devices/lib/spi/src/jz4780.cc Fri Jun 07 16:12:32 2024 +0200
1.3 @@ -1,5 +1,5 @@
1.4 /*
1.5 - * Perform SPI communication using the JZ4780/X1600 SPI peripheral.
1.6 + * Perform SPI communication using the JZ4780 SPI peripheral.
1.7 *
1.8 * Copyright (C) 2023, 2024 Paul Boddie <paul@boddie.org.uk>
1.9 *
1.10 @@ -19,187 +19,15 @@
1.11 * Boston, MA 02110-1301, USA
1.12 */
1.13
1.14 -#include <l4/devices/byteorder.h>
1.15 -#include <l4/devices/dma.h>
1.16 #include <l4/devices/spi-jz4780.h>
1.17 -#include <l4/sys/err.h>
1.18 -#include <string.h>
1.19 -
1.20 -
1.21 -
1.22 -/* Register definitions. */
1.23
1.24 -enum Regs
1.25 +enum Regs_jz4780
1.26 {
1.27 - Ssi_data = 0x00, // SSIDR
1.28 - Ssi_control0 = 0x04, // SSICR0
1.29 - Ssi_control1 = 0x08, // SSICR1
1.30 - Ssi_status = 0x0c, // SSISR
1.31 - Ssi_interval_time = 0x10, // SSIITR
1.32 - Ssi_char_per_frame = 0x14, // SSIICR
1.33 - Ssi_clock = 0x18, // SSICGR
1.34 - Ssi_recv_counter = 0x1c, // SSIRCNT
1.35 -
1.36 /* Register block offset. */
1.37
1.38 Ssi_block_offset = 0x1000,
1.39 };
1.40
1.41 -enum Ssi_data_bits : unsigned
1.42 -{
1.43 - Ssi_data_gpc_set = 0x10000,
1.44 - Ssi_data_gpc_unset = 0x00000,
1.45 -};
1.46 -
1.47 -enum Ssi_control0_bits : unsigned
1.48 -{
1.49 - Ssi_trans_endian_mask = 0xc0000,
1.50 - Ssi_trans_endian_msbyte_msbit = 0x00000,
1.51 - Ssi_trans_endian_msbyte_lsbit = 0x40000,
1.52 - Ssi_trans_endian_lsbyte_lsbit = 0x80000,
1.53 - Ssi_trans_endian_lsbyte_msbit = 0xc0000,
1.54 -
1.55 - Ssi_recv_endian_mask = 0x30000,
1.56 - Ssi_recv_endian_msbyte_msbit = 0x00000,
1.57 - Ssi_recv_endian_msbyte_lsbit = 0x10000,
1.58 - Ssi_recv_endian_lsbyte_lsbit = 0x20000,
1.59 - Ssi_recv_endian_lsbyte_msbit = 0x30000,
1.60 -
1.61 - Ssi_enable = 0x08000,
1.62 - Ssi_enable_trans_half_empty = 0x04000,
1.63 - Ssi_enable_recv_half_full = 0x02000,
1.64 - Ssi_enable_trans_error = 0x01000,
1.65 - Ssi_enable_recv_error = 0x00800,
1.66 - Ssi_loopback = 0x00400,
1.67 - Ssi_recv_finish_control = 0x00200,
1.68 - Ssi_recv_finished = 0x00100,
1.69 - Ssi_enable_auto_clear_underrun = 0x00080,
1.70 - Ssi_select_pin_is_ce2 = 0x00040,
1.71 - Ssi_use_recv_count = 0x00010,
1.72 - Ssi_old_fifo_empty_mode = 0x00008,
1.73 - Ssi_trans_flush = 0x00004,
1.74 - Ssi_recv_flush = 0x00002,
1.75 - Ssi_disable_recv = 0x00001,
1.76 -};
1.77 -
1.78 -enum Ssi_control1_bits : unsigned
1.79 -{
1.80 - Ssi_active_mask = 0xc0000000,
1.81 - Ssi_active_ce_low = 0x00000000,
1.82 - Ssi_active_ce_high = 0x40000000,
1.83 - Ssi_active_ce2_low = 0x00000000,
1.84 - Ssi_active_ce2_high = 0x80000000,
1.85 -
1.86 - Ssi_clock_start_delay_mask = 0x30000000,
1.87 - Ssi_clock_start_delay_default = 0x00000000,
1.88 - Ssi_clock_start_delay_plus_1 = 0x10000000,
1.89 - Ssi_clock_start_delay_plus_2 = 0x20000000,
1.90 - Ssi_clock_start_delay_plus_3 = 0x30000000,
1.91 -
1.92 - Ssi_clock_stop_delay_mask = 0x0c000000,
1.93 - Ssi_clock_stop_delay_default = 0x00000000,
1.94 - Ssi_clock_stop_delay_plus_1 = 0x04000000,
1.95 - Ssi_clock_stop_delay_plus_2 = 0x08000000,
1.96 - Ssi_clock_stop_delay_plus_3 = 0x0c000000,
1.97 -
1.98 - /* X1600... */
1.99 -
1.100 - Ssi_gpc_level_from_gpc_bit = 0x00000000,
1.101 - Ssi_gpc_level_from_gpc_level = 0x02000000,
1.102 -
1.103 - /* Common... */
1.104 -
1.105 - Ssi_interval_assert_ce_or_ce2 = 0x01000000,
1.106 - Ssi_trans_empty_unfinished = 0x00800000,
1.107 -
1.108 - Ssi_format_mask = 0x00300000,
1.109 - Ssi_format_spi = 0x00000000,
1.110 - Ssi_format_ssp = 0x00100000,
1.111 - Ssi_format_microwire1 = 0x00200000,
1.112 - Ssi_format_microwire2 = 0x00300000,
1.113 -
1.114 - Ssi_trans_threshold_mask = 0x000f0000,
1.115 - Ssi_command_length_mask = 0x0000f000,
1.116 - Ssi_recv_threshold_mask = 0x00000f00,
1.117 - Ssi_char_length_mask = 0x000000f8,
1.118 -
1.119 - /* X1600... */
1.120 -
1.121 - Ssi_gpc_level = 0x00000004, // see Ssi_gpc_level_from_gpc_level
1.122 -
1.123 - /* Common... */
1.124 -
1.125 - Spi_clock_assert_sample = 0x00000000, // phase #0
1.126 - Spi_clock_assert_drive = 0x00000002, // phase #1
1.127 - Spi_clock_idle_low_level = 0x00000000, // polarity #0
1.128 - Spi_clock_idle_high_level = 0x00000001, // polarity #1
1.129 -};
1.130 -
1.131 -enum Ssi_control1_shifts : unsigned
1.132 -{
1.133 - Ssi_trans_threshold_shift = 16,
1.134 - Ssi_command_length_shift = 12,
1.135 - Ssi_recv_threshold_shift = 8,
1.136 - Ssi_char_length_shift = 3,
1.137 -};
1.138 -
1.139 -enum Ssi_control1_limits : unsigned
1.140 -{
1.141 - Ssi_trans_threshold_limit = 15,
1.142 - Ssi_command_length_limit = 15,
1.143 - Ssi_recv_threshold_limit = 15,
1.144 - Ssi_char_length_limit = 30,
1.145 -};
1.146 -
1.147 -enum Ssi_status_bits : unsigned
1.148 -{
1.149 - Ssi_trans_char_count_mask = 0x00ff0000,
1.150 - Ssi_recv_char_count_mask = 0x0000ff00,
1.151 - Ssi_trans_ended = 0x00000080,
1.152 - Ssi_trans_busy = 0x00000040,
1.153 - Ssi_trans_fifo_full = 0x00000020,
1.154 - Ssi_recv_fifo_empty = 0x00000010,
1.155 - Ssi_trans_fifo_half_empty = 0x00000008,
1.156 - Ssi_recv_fifo_half_full = 0x00000004,
1.157 - Ssi_trans_underrun = 0x00000002,
1.158 - Ssi_recv_overrun = 0x00000001,
1.159 -};
1.160 -
1.161 -enum Ssi_status_shifts : unsigned
1.162 -{
1.163 - Ssi_trans_char_count_shift = 16,
1.164 - Ssi_recv_char_count_shift = 8,
1.165 -};
1.166 -
1.167 -enum Ssi_status_limits : unsigned
1.168 -{
1.169 - Ssi_trans_char_count_limit = 0xff,
1.170 - Ssi_recv_char_count_limit = 0xff,
1.171 -};
1.172 -
1.173 -enum Ssi_interval_time_bits : unsigned
1.174 -{
1.175 - Ssi_interval_clock_mask = 0x8000,
1.176 - Ssi_interval_clock_bit_clock = 0x0000,
1.177 - Ssi_interval_clock_32k_clock = 0x8000,
1.178 - Ssi_interval_time_mask = 0x3fff,
1.179 -};
1.180 -
1.181 -enum Ssi_char_per_frame_bits : unsigned
1.182 -{
1.183 - Ssi_char_per_frame_mask = 0x7,
1.184 -};
1.185 -
1.186 -enum Ssi_clock_bits : unsigned
1.187 -{
1.188 - Ssi_clock_frequency_mask = 0xff,
1.189 -};
1.190 -
1.191 -enum Ssi_recv_counter_bits : unsigned
1.192 -{
1.193 - Ssi_recv_counter_mask = 0xffff,
1.194 -};
1.195 -
1.196
1.197
1.198 /* Initialise a channel. */
1.199 @@ -210,205 +38,8 @@
1.200 Dma_channel *dma,
1.201 enum Dma_jz4780_request_type request_type,
1.202 uint64_t frequency)
1.203 -: _spi_start(spi_start), _clock(clock), _cpm(cpm), _dma(dma),
1.204 - _request_type(request_type), _frequency(frequency)
1.205 -{
1.206 - _regs = new Hw::Mmio_register_block<32>(start);
1.207 - _cpm->start_clock(clock);
1.208 -
1.209 - /* Disable the channel while configuring: send MSB first, big endian wire
1.210 - representation. Disable reception. */
1.211 -
1.212 - _regs[Ssi_control0] = Ssi_trans_endian_msbyte_msbit |
1.213 - Ssi_select_pin_is_ce2 |
1.214 - Ssi_disable_recv;
1.215 -
1.216 - /* Set default transfer properties. */
1.217 -
1.218 - configure_transfer(8);
1.219 -
1.220 - /* Select "normal" mode. */
1.221 -
1.222 - _regs[Ssi_interval_time] = 0;
1.223 -
1.224 - /* Limit the frequency to half that of the device clock. */
1.225 -
1.226 - if (_frequency >= _cpm->get_frequency(_clock))
1.227 - _frequency = _cpm->get_frequency(_clock) / 2;
1.228 -
1.229 - /* SSI_CLK = DEV_CLK / (2 * (divider + 1)) */
1.230 -
1.231 - uint32_t divider = _cpm->get_frequency(_clock) / (_frequency * 2) - 1;
1.232 -
1.233 - _regs[Ssi_clock] = divider < Ssi_clock_frequency_mask ? divider : Ssi_clock_frequency_mask;
1.234 -
1.235 - /* Enable the channel. */
1.236 -
1.237 - _regs[Ssi_control0] = _regs[Ssi_control0] | Ssi_enable;
1.238 -}
1.239 -
1.240 -/* NOTE: More transfer characteristics should be configurable. */
1.241 -
1.242 -void Spi_jz4780_channel::configure_transfer(uint8_t char_size)
1.243 -{
1.244 - uint32_t char_length;
1.245 -
1.246 - if (char_size < 2)
1.247 - char_length = 0;
1.248 - else
1.249 - {
1.250 - char_length = char_size - 2;
1.251 -
1.252 - if (char_size > Ssi_char_length_limit)
1.253 - char_length = Ssi_char_length_limit;
1.254 - }
1.255 -
1.256 - /* Clear the status. */
1.257 -
1.258 - _regs[Ssi_control0] = _regs[Ssi_control0] | Ssi_trans_flush | Ssi_recv_flush;
1.259 - _regs[Ssi_status] = 0;
1.260 -
1.261 - /* Indicate the desired character size.
1.262 -
1.263 - Use active low device selection, SPI format with active low clock, with
1.264 - data driven on the falling (asserted) clock and sampled on the rising
1.265 - clock
1.266 -
1.267 - The unfinished flag prevents the transaction from finishing if the FIFO is
1.268 - empty. It is not used here since it appears to prevent any meaningful
1.269 - testing of the busy and end flags. */
1.270 -
1.271 - _regs[Ssi_control1] = (char_length << Ssi_char_length_shift) |
1.272 - ((Ssi_trans_threshold_limit / 2) << Ssi_trans_threshold_shift) |
1.273 - Ssi_format_spi | Ssi_active_ce2_low |
1.274 - Spi_clock_assert_drive | Spi_clock_idle_high_level;
1.275 -}
1.276 -
1.277 -/* Transfer the given number of bytes from a buffer. */
1.278 -
1.279 -uint32_t
1.280 -Spi_jz4780_channel::send(uint32_t bytes, const uint8_t data[])
1.281 -{
1.282 - return send_units(bytes, data, 1, 8, false);
1.283 -}
1.284 -
1.285 -/* Transfer the given number of bytes from a buffer together with control
1.286 - values. Return the number of bytes transferred. */
1.287 -
1.288 -uint32_t
1.289 -Spi_jz4780_channel::send_dc(uint32_t bytes, const uint8_t data[],
1.290 - const int dc[], uint8_t char_size, bool big_endian)
1.291 +: Spi_channel(spi_start, start, clock, cpm, dma, request_type, frequency)
1.292 {
1.293 - configure_transfer(char_size);
1.294 -
1.295 - uint32_t transferred, char_unit;
1.296 - uint8_t char_unit_size = ((char_size ? char_size - 1 : 0) / 8) + 1;
1.297 - uint32_t char_mask = (1 << char_size) - 1;
1.298 -
1.299 - for (transferred = 0, char_unit = 0; transferred < bytes;
1.300 - transferred += char_unit_size, char_unit++)
1.301 - {
1.302 - uint32_t value = get_stored_value(&data[transferred], char_unit_size, big_endian);
1.303 -
1.304 - /* Relocate the data/command level to bit 16. */
1.305 -
1.306 - uint32_t command = dc[char_unit] ? Ssi_data_gpc_set : Ssi_data_gpc_unset;
1.307 -
1.308 - /* Combine the character with the data/command bit. */
1.309 -
1.310 - _regs[Ssi_data] = (value & char_mask) | command;
1.311 - }
1.312 -
1.313 - wait_busy();
1.314 -
1.315 - return transferred;
1.316 -}
1.317 -
1.318 -/* Transfer the given number of bytes from a buffer using the given unit size in
1.319 - bytes and character size in bits. The bytes are stored in a big endian
1.320 - arrangement. Return the number of bytes transferred. */
1.321 -
1.322 -uint32_t
1.323 -Spi_jz4780_channel::send_units(uint32_t bytes, const uint8_t data[],
1.324 - uint8_t unit_size, uint8_t char_size,
1.325 - bool big_endian)
1.326 -{
1.327 - configure_transfer(char_size);
1.328 -
1.329 - uint32_t transferred;
1.330 - uint32_t char_mask = (1 << char_size) - 1;
1.331 -
1.332 - for (transferred = 0; transferred < bytes; transferred += unit_size)
1.333 - {
1.334 - uint32_t value = get_stored_value(&data[transferred], unit_size, big_endian);
1.335 -
1.336 - /* Relocate any command bit to bit 16 for byte characters. */
1.337 -
1.338 - uint32_t command = (char_size < 16) && (value & (1 << char_size))
1.339 - ? Ssi_data_gpc_set : Ssi_data_gpc_unset;
1.340 -
1.341 - /* Combine the character portion of the unit with the command. */
1.342 -
1.343 - _regs[Ssi_data] = (value & char_mask) | command;
1.344 - }
1.345 -
1.346 - wait_busy();
1.347 -
1.348 - return transferred;
1.349 -}
1.350 -
1.351 -/* Transfer the given number of bytes from a DMA region using the given
1.352 - unit size in bytes and character size in bits. Return the number of bytes
1.353 - transferred. */
1.354 -
1.355 -uint32_t
1.356 -Spi_jz4780_channel::transfer(l4_addr_t vaddr,
1.357 - l4re_dma_space_dma_addr_t paddr,
1.358 - uint32_t count, uint8_t unit_size,
1.359 - uint8_t char_size,
1.360 - l4_addr_t desc_vaddr,
1.361 - l4re_dma_space_dma_addr_t desc_paddr)
1.362 -{
1.363 - /* Employ a non-DMA transfer if no usable physical address is provided.
1.364 - Assume little endian byte ordering in line with the native value
1.365 - representation. */
1.366 -
1.367 - if (!paddr)
1.368 - return send_units(count, (const uint8_t *) vaddr, unit_size, char_size,
1.369 - false);
1.370 -
1.371 - /* Configure and initiate a DMA transfer with optional descriptor. */
1.372 -
1.373 - configure_transfer(char_size);
1.374 -
1.375 - uint32_t transferred = 0;
1.376 - uint32_t unit_count = count / unit_size;
1.377 - uint32_t to_transfer = _dma->transfer(paddr, _spi_start + Ssi_data,
1.378 - unit_count, true, false,
1.379 - unit_size, unit_size, unit_size,
1.380 - _request_type, desc_vaddr, desc_paddr);
1.381 -
1.382 - /* Wait if not using a descriptor, which could be configured in a cycle to
1.383 - cause an endless, repeating transfer, perhaps updating a display, for
1.384 - example. */
1.385 -
1.386 - if (to_transfer && !desc_vaddr)
1.387 - {
1.388 - transferred = to_transfer ? (unit_count - _dma->wait()) * unit_size : 0;
1.389 - wait_busy();
1.390 - }
1.391 - else
1.392 - transferred = to_transfer * unit_size;
1.393 -
1.394 - return transferred;
1.395 -}
1.396 -
1.397 -/* Wait for the busy condition to clear or for a limited period. */
1.398 -
1.399 -void
1.400 -Spi_jz4780_channel::wait_busy()
1.401 -{
1.402 - for (unsigned int i = 0; i < (1 << 20) && (_regs[Ssi_status] & Ssi_trans_busy); i++);
1.403 }
1.404
1.405
1.406 @@ -417,26 +48,23 @@
1.407
1.408 Spi_jz4780_chip::Spi_jz4780_chip(l4_addr_t spi_start, l4_addr_t start,
1.409 l4_addr_t end, Cpm_chip *cpm)
1.410 -: _spi_start(spi_start), _start(start), _end(end), _cpm(cpm)
1.411 +: Spi_chip(spi_start, start, end, cpm)
1.412 {
1.413 }
1.414
1.415 -Spi_jz4780_channel *
1.416 -Spi_jz4780_chip::get_channel(uint8_t channel, Dma_channel *dma,
1.417 - uint64_t frequency)
1.418 +Spi_channel *
1.419 +Spi_jz4780_chip::_get_channel(uint8_t channel, Dma_channel *dma,
1.420 + uint64_t frequency)
1.421 {
1.422 // NOTE: Only sending is supported.
1.423
1.424 enum Dma_jz4780_request_type request_types[] = {Dma_request_ssi0_out, Dma_request_ssi1_out};
1.425 enum Clock_identifiers clocks[] = {Clock_ssi0, Clock_ssi1};
1.426
1.427 - if (channel < 2)
1.428 - return new Spi_jz4780_channel(_spi_start + channel * Ssi_block_offset,
1.429 - _start + channel * Ssi_block_offset,
1.430 - clocks[channel],
1.431 - _cpm, dma, request_types[channel], frequency);
1.432 - else
1.433 - throw -L4_EINVAL;
1.434 + return new Spi_jz4780_channel(_spi_start + channel * Ssi_block_offset,
1.435 + _start + channel * Ssi_block_offset,
1.436 + clocks[channel],
1.437 + _cpm, dma, request_types[channel], frequency);
1.438 }
1.439
1.440