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