1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/pkg/devices/lib/dma/src/jz4780.cc Sun Oct 29 16:14:38 2023 +0100
1.3 @@ -0,0 +1,520 @@
1.4 +/*
1.5 + * DMA support for the JZ4780.
1.6 + * NOTE: This should be combined with the X1600 support.
1.7 + *
1.8 + * Copyright (C) 2021, 2023 Paul Boddie <paul@boddie.org.uk>
1.9 + *
1.10 + * This program is free software; you can redistribute it and/or
1.11 + * modify it under the terms of the GNU General Public License as
1.12 + * published by the Free Software Foundation; either version 2 of
1.13 + * the License, or (at your option) any later version.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful,
1.16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.18 + * GNU General Public License for more details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License
1.21 + * along with this program; if not, write to the Free Software
1.22 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.23 + * Boston, MA 02110-1301, USA
1.24 + */
1.25 +
1.26 +#include <l4/devices/dma-jz4780.h>
1.27 +#include <l4/devices/hw_mmio_register_block.h>
1.28 +
1.29 +#include <l4/sys/icu.h>
1.30 +#include <l4/sys/ipc.h>
1.31 +#include <l4/sys/irq.h>
1.32 +#include <l4/util/util.h>
1.33 +
1.34 +#include <stdio.h>
1.35 +
1.36 +
1.37 +
1.38 +enum Global_regs
1.39 +{
1.40 + Dma_control = 0x1000, // DMAC
1.41 + Dma_irq_pending = 0x1004, // DIRQP
1.42 +};
1.43 +
1.44 +enum Channel_regs
1.45 +{
1.46 + Dma_source = 0x00, // DSA
1.47 + Dma_destination = 0x04, // DTA
1.48 + Dma_transfer_count = 0x08, // DTC
1.49 + Dma_request_source = 0x0c, // DRT
1.50 + Dma_control_status = 0x10, // DCS
1.51 + Dma_command = 0x14, // DCM
1.52 + Dma_descriptor_address = 0x18, // DDA
1.53 + Dma_stride = 0x1c, // DSD
1.54 +};
1.55 +
1.56 +enum Dma_control_bits : unsigned
1.57 +{
1.58 + Dma_fast_msc_transfer = 0x80000000, // FMSC
1.59 + Dma_fast_ssi_transfer = 0x40000000, // FSSI
1.60 + Dma_fast_tssi_transfer = 0x20000000, // FTSSI
1.61 + Dma_fast_uart_transfer = 0x10000000, // FUART
1.62 + Dma_fast_aic_transfer = 0x08000000, // FAIC
1.63 +
1.64 + Dma_intc_irq_channel_mask = 0x003e0000, // INTCC
1.65 + Dma_intc_irq_channel_bind = 0x00010000, // INTCE
1.66 +
1.67 + Dma_control_trans_halted = 0x00000008, // HLT
1.68 + Dma_control_address_error = 0x00000004, // AR
1.69 + Dma_control_special_ch01 = 0x00000002, // CH01
1.70 + Dma_control_enable = 0x00000001, // DMAE
1.71 +};
1.72 +
1.73 +enum Dma_transfer_count_bits : unsigned
1.74 +{
1.75 + Dma_transfer_count_mask = 0x00ffffff,
1.76 +};
1.77 +
1.78 +enum Dma_request_source_bits : unsigned
1.79 +{
1.80 + Dma_request_type_mask = 0x0000003f,
1.81 +};
1.82 +
1.83 +enum Dma_control_status_bits : unsigned
1.84 +{
1.85 + Dma_no_descriptor_transfer = 0x80000000,
1.86 + Dma_8word_descriptor = 0x40000000,
1.87 + Dma_copy_offset_mask = 0x0000ff00,
1.88 + Dma_address_error = 0x00000010,
1.89 + Dma_trans_completed = 0x00000008,
1.90 + Dma_trans_halted = 0x00000004,
1.91 + Dma_channel_enable = 0x00000001,
1.92 +
1.93 + Dma_copy_offset_shift = 8,
1.94 +};
1.95 +
1.96 +enum Dma_command_bits : unsigned
1.97 +{
1.98 + Dma_special_source_mask = 0x0c000000,
1.99 + Dma_special_source_tcsm = 0x00000000,
1.100 + Dma_special_source_bch_nemc = 0x04000000,
1.101 + Dma_special_source_reserved_ddr = 0x08000000,
1.102 +
1.103 + Dma_special_destination_mask = 0x03000000,
1.104 + Dma_special_destination_tcsm = 0x00000000,
1.105 + Dma_special_destination_bch_nemc = 0x01000000,
1.106 + Dma_special_destination_reserved_ddr = 0x02000000,
1.107 +
1.108 + Dma_source_address_increment = 0x00800000,
1.109 + Dma_source_address_no_increment = 0x00000000,
1.110 + Dma_destination_address_increment = 0x00400000,
1.111 + Dma_destination_address_no_increment = 0x00000000,
1.112 +
1.113 + Dma_recommended_data_unit_size_mask = 0x000f0000,
1.114 + Dma_source_port_width_mask = 0x0000c000,
1.115 + Dma_destination_port_width_mask = 0x00003000,
1.116 + Dma_transfer_unit_size_mask = 0x00000f00,
1.117 +
1.118 + Dma_trans_unit_size_32_bit = 0x00000000,
1.119 + Dma_trans_unit_size_8_bit = 0x00000100,
1.120 + Dma_trans_unit_size_16_bit = 0x00000200,
1.121 + Dma_trans_unit_size_16_byte = 0x00000300,
1.122 + Dma_trans_unit_size_32_byte = 0x00000400,
1.123 + Dma_trans_unit_size_64_byte = 0x00000500,
1.124 + Dma_trans_unit_size_128_byte = 0x00000600,
1.125 + Dma_trans_unit_size_autonomous = 0x00000700,
1.126 +
1.127 + Dma_stride_enable = 0x00000004,
1.128 + Dma_transfer_irq_enable = 0x00000002,
1.129 + Dma_descriptor_link_enable = 0x00000001,
1.130 +
1.131 + Dma_recommended_data_unit_size_shift = 16,
1.132 + Dma_source_port_width_shift = 14,
1.133 + Dma_destination_port_width_shift = 12,
1.134 + Dma_transfer_unit_size_shift = 8,
1.135 +};
1.136 +
1.137 +enum Dma_port_width_values : unsigned
1.138 +{
1.139 + Dma_port_width_32_bit = 0,
1.140 + Dma_port_width_8_bit = 1,
1.141 + Dma_port_width_16_bit = 2,
1.142 +};
1.143 +
1.144 +
1.145 +
1.146 +// Initialise a channel.
1.147 +
1.148 +Dma_jz4780_channel::Dma_jz4780_channel(Dma_jz4780_chip *chip, uint8_t channel,
1.149 + l4_addr_t start, l4_cap_idx_t irq)
1.150 +: _chip(chip), _channel(channel), _irq(irq)
1.151 +{
1.152 + _regs = new Hw::Mmio_register_block<32>(start);
1.153 +
1.154 + // Initialise the transfer count.
1.155 +
1.156 + _regs[Dma_transfer_count] = 0;
1.157 +}
1.158 +
1.159 +// Return the closest interval length greater than or equal to the number of
1.160 +// units given encoded in the request detection interval length field of the
1.161 +// control/status register.
1.162 +
1.163 +uint32_t
1.164 +Dma_jz4780_channel::encode_req_detect_int_length(uint8_t units)
1.165 +{
1.166 + static uint8_t lengths[] = {0, 1, 2, 3, 4, 8, 16, 32, 64, 128};
1.167 + int i;
1.168 +
1.169 + if (!units)
1.170 + return 0;
1.171 +
1.172 + for (i = 0; i <= 9; i++)
1.173 + {
1.174 + if (lengths[i] >= units)
1.175 + break;
1.176 + }
1.177 +
1.178 + return i << Dma_recommended_data_unit_size_shift;
1.179 +}
1.180 +
1.181 +// Encode the appropriate source port width for the given request type.
1.182 +
1.183 +uint32_t
1.184 +Dma_jz4780_channel::encode_source_port_width(uint8_t width)
1.185 +{
1.186 + switch (width)
1.187 + {
1.188 + case 1:
1.189 + return Dma_port_width_8_bit << Dma_source_port_width_shift;
1.190 +
1.191 + case 2:
1.192 + return Dma_port_width_16_bit << Dma_source_port_width_shift;
1.193 +
1.194 + default:
1.195 + return Dma_port_width_32_bit << Dma_source_port_width_shift;
1.196 + }
1.197 +}
1.198 +
1.199 +// Encode the appropriate destination port width for the given request type.
1.200 +
1.201 +uint32_t
1.202 +Dma_jz4780_channel::encode_destination_port_width(uint8_t width)
1.203 +{
1.204 + switch (width)
1.205 + {
1.206 + case 1:
1.207 + return Dma_port_width_8_bit << Dma_destination_port_width_shift;
1.208 +
1.209 + case 2:
1.210 + return Dma_port_width_16_bit << Dma_destination_port_width_shift;
1.211 +
1.212 + default:
1.213 + return Dma_port_width_32_bit << Dma_destination_port_width_shift;
1.214 + }
1.215 +}
1.216 +
1.217 +// Encode the transfer unit size.
1.218 +// NOTE: This does not handle the external case.
1.219 +
1.220 +uint32_t
1.221 +Dma_jz4780_channel::encode_transfer_unit_size(uint8_t size)
1.222 +{
1.223 + switch (size)
1.224 + {
1.225 + case 0:
1.226 + return Dma_trans_unit_size_autonomous;
1.227 +
1.228 + case 1:
1.229 + return Dma_trans_unit_size_8_bit;
1.230 +
1.231 + case 2:
1.232 + return Dma_trans_unit_size_16_bit;
1.233 +
1.234 + case 16:
1.235 + return Dma_trans_unit_size_16_byte;
1.236 +
1.237 + case 32:
1.238 + return Dma_trans_unit_size_32_byte;
1.239 +
1.240 + case 64:
1.241 + return Dma_trans_unit_size_64_byte;
1.242 +
1.243 + case 128:
1.244 + return Dma_trans_unit_size_128_byte;
1.245 +
1.246 + default:
1.247 + return Dma_trans_unit_size_32_bit;
1.248 + }
1.249 +}
1.250 +
1.251 +// Transfer data between memory locations.
1.252 +
1.253 +unsigned int
1.254 +Dma_jz4780_channel::transfer(uint32_t source, uint32_t destination,
1.255 + unsigned int count,
1.256 + bool source_increment, bool destination_increment,
1.257 + uint8_t source_width, uint8_t destination_width,
1.258 + uint8_t transfer_unit_size,
1.259 + enum Dma_jz4780_request_type type)
1.260 +{
1.261 + printf("transfer:%s%s%s%s\n", error() ? " error" : "",
1.262 + halted() ? " halted" : "",
1.263 + completed() ? " completed" : "",
1.264 + _regs[Dma_transfer_count] ? " count" : "");
1.265 +
1.266 + // Ensure an absence of address error and halt conditions globally and in this channel.
1.267 +
1.268 + if (error() || halted())
1.269 + return 0;
1.270 +
1.271 + // Ensure an absence of transaction completed and zero transfer count for this channel.
1.272 +
1.273 + if (completed() || _regs[Dma_transfer_count])
1.274 + return 0;
1.275 +
1.276 + // Disable the channel.
1.277 +
1.278 + _regs[Dma_control_status] = _regs[Dma_control_status] & ~Dma_channel_enable;
1.279 +
1.280 + // Set addresses.
1.281 +
1.282 + _regs[Dma_source] = source;
1.283 + _regs[Dma_destination] = destination;
1.284 +
1.285 + // Set transfer count to the number of units.
1.286 +
1.287 + unsigned int units = count < Dma_transfer_count_mask ? count : Dma_transfer_count_mask;
1.288 +
1.289 + _regs[Dma_transfer_count] = units;
1.290 +
1.291 + // Set auto-request for memory-to-memory transfers. Otherwise, set the
1.292 + // indicated request type.
1.293 +
1.294 + _regs[Dma_request_source] = type;
1.295 +
1.296 + // For a descriptor, the actual fields would be populated instead of the
1.297 + // command register, descriptor transfer would be indicated in the control/
1.298 + // status register along with the appropriate descriptor size indicator.
1.299 +
1.300 + /* NOTE: To be considered...
1.301 + * request detection interval length (for autonomous mode)
1.302 + */
1.303 +
1.304 + _regs[Dma_command] = (source_increment ? Dma_source_address_increment : Dma_source_address_no_increment) |
1.305 + (destination_increment ? Dma_destination_address_increment : Dma_destination_address_no_increment) |
1.306 + encode_source_port_width(source_width) |
1.307 + encode_destination_port_width(destination_width) |
1.308 + encode_transfer_unit_size(transfer_unit_size) |
1.309 + Dma_transfer_irq_enable;
1.310 +
1.311 + // For a descriptor, the descriptor address would be set and the doorbell
1.312 + // register field for the channel set.
1.313 +
1.314 + // Enable the channel (and peripheral).
1.315 +
1.316 + _regs[Dma_control_status] = Dma_no_descriptor_transfer |
1.317 + Dma_channel_enable;
1.318 +
1.319 + // Return the number of units to transfer.
1.320 +
1.321 + return units;
1.322 +}
1.323 +
1.324 +unsigned int
1.325 +Dma_jz4780_channel::wait()
1.326 +{
1.327 + // An interrupt will occur upon completion, the completion flag will be set
1.328 + // and the transfer count will be zero.
1.329 +
1.330 + unsigned int remaining = 0;
1.331 +
1.332 + do
1.333 + {
1.334 + if (!wait_for_irq(1000000))
1.335 + printf("status = %x\n", (uint32_t) _regs[Dma_control_status]);
1.336 + else
1.337 + {
1.338 + printf("status = %x\n", (uint32_t) _regs[Dma_control_status]);
1.339 + remaining = _regs[Dma_transfer_count];
1.340 + ack_irq();
1.341 + break;
1.342 + }
1.343 + }
1.344 + while (!error() && !halted() && !completed());
1.345 +
1.346 + // Reset the channel status.
1.347 +
1.348 + _regs[Dma_control_status] = _regs[Dma_control_status] & ~(Dma_channel_enable |
1.349 + Dma_trans_completed | Dma_address_error |
1.350 + Dma_trans_halted);
1.351 + _regs[Dma_transfer_count] = 0;
1.352 +
1.353 + // Return the number of remaining units.
1.354 +
1.355 + return remaining;
1.356 +}
1.357 +
1.358 +// Wait indefinitely for an interrupt request, returning true if one was delivered.
1.359 +
1.360 +bool
1.361 +Dma_jz4780_channel::wait_for_irq()
1.362 +{
1.363 + return !l4_error(l4_irq_receive(_irq, L4_IPC_NEVER)) && _chip->have_interrupt(_channel);
1.364 +}
1.365 +
1.366 +// Wait up to the given timeout (in microseconds) for an interrupt request,
1.367 +// returning true if one was delivered.
1.368 +
1.369 +bool
1.370 +Dma_jz4780_channel::wait_for_irq(unsigned int timeout)
1.371 +{
1.372 + return !l4_error(l4_irq_receive(_irq, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4util_micros2l4to(timeout)))) && _chip->have_interrupt(_channel);
1.373 +}
1.374 +
1.375 +// Acknowledge an interrupt condition.
1.376 +
1.377 +void
1.378 +Dma_jz4780_channel::ack_irq()
1.379 +{
1.380 + _chip->ack_irq(_channel);
1.381 +}
1.382 +
1.383 +// Return whether a transfer has completed.
1.384 +
1.385 +bool
1.386 +Dma_jz4780_channel::completed()
1.387 +{
1.388 + return _regs[Dma_control_status] & Dma_trans_completed ? true : false;
1.389 +}
1.390 +
1.391 +// Return whether an address error condition has arisen.
1.392 +
1.393 +bool
1.394 +Dma_jz4780_channel::error()
1.395 +{
1.396 + return _chip->error() || (_regs[Dma_control_status] & Dma_address_error ? true : false);
1.397 +}
1.398 +
1.399 +// Return whether a transfer has halted.
1.400 +
1.401 +bool
1.402 +Dma_jz4780_channel::halted()
1.403 +{
1.404 + return _chip->halted() || (_regs[Dma_control_status] & Dma_trans_halted ? true : false);
1.405 +}
1.406 +
1.407 +
1.408 +
1.409 +// Initialise the I2C controller.
1.410 +
1.411 +Dma_jz4780_chip::Dma_jz4780_chip(l4_addr_t start, l4_addr_t end,
1.412 + Cpm_jz4780_chip *cpm)
1.413 +: _start(start), _end(end), _cpm(cpm)
1.414 +{
1.415 + _regs = new Hw::Mmio_register_block<32>(start);
1.416 +}
1.417 +
1.418 +// Enable the peripheral.
1.419 +
1.420 +void
1.421 +Dma_jz4780_chip::enable()
1.422 +{
1.423 + // Make sure that the DMA clock is available.
1.424 +
1.425 + _cpm->start_clock(Clock_dma);
1.426 +
1.427 + _regs[Dma_control] = Dma_control_enable;
1.428 + while (!(_regs[Dma_control] & Dma_control_enable));
1.429 +}
1.430 +
1.431 +// Disable the channel.
1.432 +
1.433 +void
1.434 +Dma_jz4780_chip::disable()
1.435 +{
1.436 + _regs[Dma_control] = 0;
1.437 + while (_regs[Dma_control] & Dma_control_enable);
1.438 +}
1.439 +
1.440 +// Obtain a channel object.
1.441 +
1.442 +Dma_jz4780_channel *
1.443 +Dma_jz4780_chip::get_channel(uint8_t channel, l4_cap_idx_t irq)
1.444 +{
1.445 + if (channel < 32)
1.446 + return new Dma_jz4780_channel(this, channel, _start + 0x20 * channel, irq);
1.447 + else
1.448 + throw -L4_EINVAL;
1.449 +}
1.450 +
1.451 +// Return whether an interrupt is pending on the given channel.
1.452 +
1.453 +bool
1.454 +Dma_jz4780_chip::have_interrupt(uint8_t channel)
1.455 +{
1.456 + return _regs[Dma_irq_pending] & (1 << channel) ? true : false;
1.457 +}
1.458 +
1.459 +// Acknowledge an interrupt condition on the given channel.
1.460 +
1.461 +void
1.462 +Dma_jz4780_chip::ack_irq(uint8_t channel)
1.463 +{
1.464 + _regs[Dma_irq_pending] = _regs[Dma_irq_pending] & ~(1 << channel);
1.465 +}
1.466 +
1.467 +// Return whether an address error condition has arisen.
1.468 +
1.469 +bool
1.470 +Dma_jz4780_chip::error()
1.471 +{
1.472 + return _regs[Dma_control] & Dma_control_address_error ? true : false;
1.473 +}
1.474 +
1.475 +// Return whether a transfer has halted.
1.476 +
1.477 +bool
1.478 +Dma_jz4780_chip::halted()
1.479 +{
1.480 + return _regs[Dma_control] & Dma_control_trans_halted ? true : false;
1.481 +}
1.482 +
1.483 +
1.484 +
1.485 +// C language interface functions.
1.486 +
1.487 +void *jz4780_dma_init(l4_addr_t start, l4_addr_t end, void *cpm)
1.488 +{
1.489 + return (void *) new Dma_jz4780_chip(start, end, static_cast<Cpm_jz4780_chip *>(cpm));
1.490 +}
1.491 +
1.492 +void jz4780_dma_disable(void *dma_chip)
1.493 +{
1.494 + static_cast<Dma_jz4780_chip *>(dma_chip)->disable();
1.495 +}
1.496 +
1.497 +void jz4780_dma_enable(void *dma_chip)
1.498 +{
1.499 + static_cast<Dma_jz4780_chip *>(dma_chip)->enable();
1.500 +}
1.501 +
1.502 +void *jz4780_dma_get_channel(void *dma, uint8_t channel, l4_cap_idx_t irq)
1.503 +{
1.504 + return static_cast<Dma_jz4780_chip *>(dma)->get_channel(channel, irq);
1.505 +}
1.506 +
1.507 +unsigned int jz4780_dma_transfer(void *dma_channel,
1.508 + uint32_t source, uint32_t destination,
1.509 + unsigned int count,
1.510 + int source_increment, int destination_increment,
1.511 + uint8_t source_width, uint8_t destination_width,
1.512 + uint8_t transfer_unit_size,
1.513 + enum Dma_jz4780_request_type type)
1.514 +{
1.515 + return static_cast<Dma_jz4780_channel *>(dma_channel)->transfer(source,
1.516 + destination, count, source_increment, destination_increment, source_width,
1.517 + destination_width, transfer_unit_size, type);
1.518 +}
1.519 +
1.520 +unsigned int jz4780_dma_wait(void *dma_channel)
1.521 +{
1.522 + return static_cast<Dma_jz4780_channel *>(dma_channel)->wait();
1.523 +}