1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/pkg/devices/lib/dma/src/jz4730.cc Sun Jan 10 22:21:57 2021 +0100
1.3 @@ -0,0 +1,496 @@
1.4 +/*
1.5 + * DMA support for the JZ4730.
1.6 + *
1.7 + * Copyright (C) 2021 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-jz4730.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 <cstdio>
1.34 +
1.35 +enum Global_regs
1.36 +{
1.37 + Dma_irq_pending = 0xf8, // IRQP
1.38 + Dma_control = 0xfc, // DMAC
1.39 +};
1.40 +
1.41 +enum Channel_regs
1.42 +{
1.43 + Dma_source = 0x00, // DSA
1.44 + Dma_destination = 0x04, // DDA
1.45 + Dma_transfer_count = 0x08, // DTC
1.46 + Dma_request_source = 0x0c, // DRT
1.47 + Dma_control_status = 0x10, // DCS
1.48 +};
1.49 +
1.50 +enum Dma_irq_pending_shifts : unsigned
1.51 +{
1.52 + Dma_irq_pending_ch0 = 15,
1.53 + Dma_irq_pending_ch1 = 14,
1.54 + Dma_irq_pending_ch2 = 13,
1.55 + Dma_irq_pending_ch3 = 12,
1.56 + Dma_irq_pending_ch4 = 11,
1.57 + Dma_irq_pending_ch5 = 10,
1.58 +};
1.59 +
1.60 +enum Dma_control_bits : unsigned
1.61 +{
1.62 + Dma_control_priority_mode = 0x100, // PM
1.63 + Dma_control_halt_occurred = 0x008, // HLT
1.64 + Dma_control_address_error = 0x004, // AR
1.65 + Dma_control_enable = 0x001, // DMAE
1.66 +};
1.67 +
1.68 +enum Dma_control_priority_modes : unsigned
1.69 +{
1.70 + Dma_priority_mode_01234567 = 0,
1.71 + Dma_priority_mode_02314675 = 1,
1.72 + Dma_priority_mode_20136457 = 2,
1.73 + Dma_priority_mode_round_robin = 3,
1.74 +};
1.75 +
1.76 +enum Dma_transfer_count_bits : unsigned
1.77 +{
1.78 + Dma_transfer_count_mask = 0x00ffffff,
1.79 +};
1.80 +
1.81 +enum Dma_request_source_bits : unsigned
1.82 +{
1.83 + Dma_request_type_mask = 0x0000001f,
1.84 +};
1.85 +
1.86 +enum Dma_control_status_shifts : unsigned
1.87 +{
1.88 + Dma_ext_output_polarity = 31,
1.89 + Dma_ext_output_mode_cycle = 30,
1.90 + Dma_ext_req_detect_mode = 28,
1.91 + Dma_ext_end_of_process_mode = 27,
1.92 + Dma_req_detect_int_length = 16,
1.93 + Dma_source_port_width = 14,
1.94 + Dma_dest_port_width = 12,
1.95 + Dma_trans_unit_size = 8,
1.96 + Dma_trans_mode = 7,
1.97 +};
1.98 +
1.99 +enum Dma_control_status_bits : unsigned
1.100 +{
1.101 + Dma_source_address_incr = 0x00800000,
1.102 + Dma_dest_address_incr = 0x00400000,
1.103 + Dma_address_error = 0x00000010,
1.104 + Dma_trans_completed = 0x00000008,
1.105 + Dma_trans_halted = 0x00000004,
1.106 + Dma_channel_irq_enable = 0x00000002,
1.107 + Dma_channel_enable = 0x00000001,
1.108 +};
1.109 +
1.110 +enum Dma_port_width_values : unsigned
1.111 +{
1.112 + Dma_port_width_32_bit = 0,
1.113 + Dma_port_width_8_bit = 1,
1.114 + Dma_port_width_16_bit = 2,
1.115 +};
1.116 +
1.117 +enum Dma_trans_mode_values : unsigned
1.118 +{
1.119 + Dma_trans_mode_single = 0,
1.120 + Dma_trans_mode_block = 1,
1.121 +};
1.122 +
1.123 +
1.124 +
1.125 +// Initialise a channel.
1.126 +
1.127 +Dma_jz4730_channel::Dma_jz4730_channel(Dma_jz4730_chip *chip, uint8_t channel,
1.128 + l4_addr_t start, l4_cap_idx_t irq)
1.129 +: _chip(chip), _channel(channel), _irq(irq)
1.130 +{
1.131 + _regs = new Hw::Mmio_register_block<32>(start);
1.132 +}
1.133 +
1.134 +// Encode flags for an external transfer.
1.135 +
1.136 +uint32_t
1.137 +Dma_jz4730_channel::encode_external_transfer(enum Dma_jz4730_request_type type)
1.138 +{
1.139 + int external = (type == Dma_request_external) ? 1 : 0;
1.140 +
1.141 + return
1.142 + ((external ? (int) _ext_output_polarity : 0) << Dma_ext_output_polarity) |
1.143 + ((external ? (int) _ext_output_mode_cycle : 0) << Dma_ext_output_mode_cycle) |
1.144 + ((external ? (int) _ext_req_detect_mode : 0) << Dma_ext_req_detect_mode) |
1.145 + ((external ? (int) _ext_end_of_process_mode : 0) << Dma_ext_end_of_process_mode);
1.146 +}
1.147 +
1.148 +// Encode the appropriate source address increment for the given request type.
1.149 +// Here, memory-to-memory transfers and transfers to peripherals involve an
1.150 +// incrementing source address. Transfers from peripherals involve a static
1.151 +// source address.
1.152 +
1.153 +uint32_t
1.154 +Dma_jz4730_channel::encode_source_address_increment(enum Dma_jz4730_request_type type)
1.155 +{
1.156 + switch (type)
1.157 + {
1.158 + case Dma_request_auto:
1.159 + case Dma_request_pcmcia_out:
1.160 + case Dma_request_des_out:
1.161 + case Dma_request_uart3_out:
1.162 + case Dma_request_uart2_out:
1.163 + case Dma_request_uart1_out:
1.164 + case Dma_request_uart0_out:
1.165 + case Dma_request_ssi_send_empty:
1.166 + case Dma_request_aic_send_empty:
1.167 + case Dma_request_msc_send_empty:
1.168 + case Dma_request_ost2_underflow:
1.169 + return Dma_source_address_incr;
1.170 +
1.171 + default:
1.172 + return 0;
1.173 + }
1.174 +}
1.175 +
1.176 +// Encode the appropriate destination address increment for the given request
1.177 +// type. Here, memory-to-memory transfers and transfers from peripherals involve
1.178 +// an incrementing destination address. Transfers to peripherals involve a static
1.179 +// destination address.
1.180 +
1.181 +uint32_t
1.182 +Dma_jz4730_channel::encode_destination_address_increment(enum Dma_jz4730_request_type type)
1.183 +{
1.184 + switch (type)
1.185 + {
1.186 + case Dma_request_auto:
1.187 + case Dma_request_pcmcia_in:
1.188 + case Dma_request_des_in:
1.189 + case Dma_request_uart3_in:
1.190 + case Dma_request_uart2_in:
1.191 + case Dma_request_uart1_in:
1.192 + case Dma_request_uart0_in:
1.193 + case Dma_request_ssi_recv_full:
1.194 + case Dma_request_aic_recv_full:
1.195 + case Dma_request_msc_recv_full:
1.196 + case Dma_request_ost2_underflow:
1.197 + return Dma_dest_address_incr;
1.198 +
1.199 + default:
1.200 + return 0;
1.201 + }
1.202 +}
1.203 +
1.204 +// Return the closest interval length greater than or equal to the number of
1.205 +// units given encoded in the request detection interval length field of the
1.206 +// control/status register.
1.207 +
1.208 +uint32_t
1.209 +Dma_jz4730_channel::encode_req_detect_int_length(uint8_t units)
1.210 +{
1.211 + static uint8_t lengths[] = {0, 2, 4, 8, 12, 16, 20, 24, 28, 32, 48, 60, 64, 124, 128, 200};
1.212 + int i;
1.213 +
1.214 + if (!units)
1.215 + return 0;
1.216 +
1.217 + for (i = 0; i < 15; i++)
1.218 + {
1.219 + if (lengths[i++] >= units)
1.220 + break;
1.221 + }
1.222 +
1.223 + return lengths[i] << Dma_req_detect_int_length;
1.224 +}
1.225 +
1.226 +// Encode the appropriate source port width for the given request type.
1.227 +
1.228 +uint32_t
1.229 +Dma_jz4730_channel::encode_source_port_width(enum Dma_jz4730_request_type type)
1.230 +{
1.231 + switch (type)
1.232 + {
1.233 + case Dma_request_uart3_in:
1.234 + case Dma_request_uart2_in:
1.235 + case Dma_request_uart1_in:
1.236 + case Dma_request_uart0_in:
1.237 + return Dma_port_width_8_bit << Dma_source_port_width;
1.238 +
1.239 + default:
1.240 + return Dma_port_width_32_bit << Dma_source_port_width;
1.241 + }
1.242 +}
1.243 +
1.244 +// Encode the appropriate destination port width for the given request type.
1.245 +
1.246 +uint32_t
1.247 +Dma_jz4730_channel::encode_destination_port_width(enum Dma_jz4730_request_type type)
1.248 +{
1.249 + switch (type)
1.250 + {
1.251 + case Dma_request_uart3_out:
1.252 + case Dma_request_uart2_out:
1.253 + case Dma_request_uart1_out:
1.254 + case Dma_request_uart0_out:
1.255 + return Dma_port_width_8_bit << Dma_dest_port_width;
1.256 +
1.257 + default:
1.258 + return Dma_port_width_32_bit << Dma_dest_port_width;
1.259 + }
1.260 +}
1.261 +
1.262 +// Transfer data between memory locations.
1.263 +
1.264 +unsigned int
1.265 +Dma_jz4730_channel::transfer(uint32_t source, uint32_t destination,
1.266 + unsigned int count,
1.267 + enum Dma_jz4730_trans_unit_size size,
1.268 + enum Dma_jz4730_request_type type)
1.269 +{
1.270 + // Ensure an absence of address error and halt conditions globally and in this channel.
1.271 +
1.272 + if (error() || halted())
1.273 + return 0;
1.274 +
1.275 + // Ensure an absence of transaction completed and zero transfer count for this channel.
1.276 +
1.277 + if (completed() || _regs[Dma_transfer_count])
1.278 + return 0;
1.279 +
1.280 + // Disable the channel.
1.281 +
1.282 + _regs[Dma_control_status] = _regs[Dma_control_status] & ~Dma_channel_enable;
1.283 +
1.284 + // Set addresses.
1.285 +
1.286 + _regs[Dma_source] = source;
1.287 + _regs[Dma_destination] = destination;
1.288 +
1.289 + // Set transfer count to the number of units.
1.290 +
1.291 + _regs[Dma_transfer_count] = count;
1.292 +
1.293 + // Set auto-request for memory-to-memory transfers. Otherwise, set the
1.294 + // indicated request type.
1.295 +
1.296 + _regs[Dma_request_source] = type;
1.297 +
1.298 + // Set control/status fields.
1.299 + // Enable the channel (and peripheral).
1.300 +
1.301 + /* NOTE: To be considered...
1.302 + * request detection interval length (currently left as 0)
1.303 + * increments and port widths for external transfers
1.304 + * port width overriding (for AIC...)
1.305 + * transfer mode (currently left as single)
1.306 + */
1.307 +
1.308 + _regs[Dma_control_status] = encode_external_transfer(type) |
1.309 + encode_source_address_increment(type) |
1.310 + encode_destination_address_increment(type) |
1.311 + encode_source_port_width(type) |
1.312 + encode_destination_port_width(type) |
1.313 + (size << Dma_trans_unit_size) |
1.314 + (Dma_trans_mode_single << Dma_trans_mode) |
1.315 + Dma_channel_irq_enable |
1.316 + Dma_channel_enable;
1.317 +
1.318 + // An interrupt will occur upon completion, the completion flag will be set
1.319 + // and the transfer count will be zero.
1.320 +
1.321 + unsigned int remaining = count;
1.322 +
1.323 + do
1.324 + {
1.325 + if (!wait_for_irq(1000000))
1.326 + printf("status = %x\n", (uint32_t) _regs[Dma_control_status]);
1.327 +
1.328 + // Clearing the completion flag will clear the interrupt condition.
1.329 + // Any remaining units must be read before clearing the condition.
1.330 +
1.331 + else
1.332 + {
1.333 + remaining = _regs[Dma_transfer_count];
1.334 + ack_irq();
1.335 + break;
1.336 + }
1.337 + }
1.338 + while (!error() && !halted() && !completed());
1.339 +
1.340 + // Return the number of transferred units.
1.341 +
1.342 + return count - remaining;
1.343 +}
1.344 +
1.345 +// Wait indefinitely for an interrupt request, returning true if one was delivered.
1.346 +
1.347 +bool
1.348 +Dma_jz4730_channel::wait_for_irq()
1.349 +{
1.350 + return !l4_error(l4_irq_receive(_irq, L4_IPC_NEVER)) && _chip->have_interrupt(_channel);
1.351 +}
1.352 +
1.353 +// Wait up to the given timeout (in microseconds) for an interrupt request,
1.354 +// returning true if one was delivered.
1.355 +
1.356 +bool
1.357 +Dma_jz4730_channel::wait_for_irq(unsigned int timeout)
1.358 +{
1.359 + return !l4_error(l4_irq_receive(_irq, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4util_micros2l4to(timeout)))) && _chip->have_interrupt(_channel);
1.360 +}
1.361 +
1.362 +// Acknowledge an interrupt condition.
1.363 +
1.364 +void
1.365 +Dma_jz4730_channel::ack_irq()
1.366 +{
1.367 + _regs[Dma_control_status] = _regs[Dma_control_status] & ~Dma_trans_completed;
1.368 +}
1.369 +
1.370 +// Return whether a transfer has completed.
1.371 +
1.372 +bool
1.373 +Dma_jz4730_channel::completed()
1.374 +{
1.375 + return _regs[Dma_control_status] & Dma_trans_completed ? true : false;
1.376 +}
1.377 +
1.378 +// Return whether an address error condition has arisen.
1.379 +
1.380 +bool
1.381 +Dma_jz4730_channel::error()
1.382 +{
1.383 + return _regs[Dma_control_status] & Dma_address_error ? true : false;
1.384 +}
1.385 +
1.386 +// Return whether a transfer has halted.
1.387 +
1.388 +bool
1.389 +Dma_jz4730_channel::halted()
1.390 +{
1.391 + return _regs[Dma_control_status] & Dma_trans_halted ? true : false;
1.392 +}
1.393 +
1.394 +
1.395 +
1.396 +// Initialise the I2C controller.
1.397 +
1.398 +Dma_jz4730_chip::Dma_jz4730_chip(l4_addr_t start, l4_addr_t end,
1.399 + Cpm_jz4730_chip *cpm)
1.400 +: _start(start), _end(end), _cpm(cpm)
1.401 +{
1.402 + _regs = new Hw::Mmio_register_block<32>(start);
1.403 +}
1.404 +
1.405 +// Enable the peripheral.
1.406 +
1.407 +void
1.408 +Dma_jz4730_chip::enable()
1.409 +{
1.410 + // Make sure that the DMA clock is available.
1.411 +
1.412 + _cpm->start_dma();
1.413 +
1.414 + // Enable the channel.
1.415 + // NOTE: No configuration is done for channel priority mode.
1.416 +
1.417 + _regs[Dma_control] = Dma_control_enable;
1.418 + while (!(_regs[Dma_control] & Dma_control_enable));
1.419 +}
1.420 +
1.421 +// Disable the channel.
1.422 +
1.423 +void
1.424 +Dma_jz4730_chip::disable()
1.425 +{
1.426 + _regs[Dma_control] = 0;
1.427 + while (_regs[Dma_control] & Dma_control_enable);
1.428 +}
1.429 +
1.430 +// Obtain a channel object. Only one channel is supported.
1.431 +
1.432 +Dma_jz4730_channel *
1.433 +Dma_jz4730_chip::get_channel(uint8_t channel, l4_cap_idx_t irq)
1.434 +{
1.435 + if (channel < 6)
1.436 + return new Dma_jz4730_channel(this, channel, _start + 0x20 * channel, irq);
1.437 + else
1.438 + throw -L4_EINVAL;
1.439 +}
1.440 +
1.441 +// Return whether an interrupt is pending on the given channel.
1.442 +
1.443 +bool
1.444 +Dma_jz4730_chip::have_interrupt(uint8_t channel)
1.445 +{
1.446 + return _regs[Dma_irq_pending] & (1 << (Dma_irq_pending_ch0 - channel)) ? true : false;
1.447 +}
1.448 +
1.449 +
1.450 +
1.451 +// C language interface functions.
1.452 +
1.453 +void *jz4730_dma_init(l4_addr_t start, l4_addr_t end, void *cpm)
1.454 +{
1.455 + return (void *) new Dma_jz4730_chip(start, end, static_cast<Cpm_jz4730_chip *>(cpm));
1.456 +}
1.457 +
1.458 +void jz4730_dma_disable(void *dma_chip)
1.459 +{
1.460 + static_cast<Dma_jz4730_chip *>(dma_chip)->disable();
1.461 +}
1.462 +
1.463 +void jz4730_dma_enable(void *dma_chip)
1.464 +{
1.465 + static_cast<Dma_jz4730_chip *>(dma_chip)->enable();
1.466 +}
1.467 +
1.468 +void *jz4730_dma_get_channel(void *dma, uint8_t channel, l4_cap_idx_t irq)
1.469 +{
1.470 + return static_cast<Dma_jz4730_chip *>(dma)->get_channel(channel, irq);
1.471 +}
1.472 +
1.473 +void jz4730_dma_set_output_polarity(void *dma_channel, enum Dma_jz4730_ext_level polarity)
1.474 +{
1.475 + static_cast<Dma_jz4730_channel *>(dma_channel)->set_output_polarity(polarity);
1.476 +}
1.477 +
1.478 +void jz4730_dma_set_end_of_process_mode(void *dma_channel, enum Dma_jz4730_ext_level mode)
1.479 +{
1.480 + static_cast<Dma_jz4730_channel *>(dma_channel)->set_end_of_process_mode(mode);
1.481 +}
1.482 +
1.483 +void jz4730_dma_set_output_mode_cycle(void *dma_channel, enum Dma_jz4730_ext_output_mode_cycle cycle)
1.484 +{
1.485 + static_cast<Dma_jz4730_channel *>(dma_channel)->set_output_mode_cycle(cycle);
1.486 +}
1.487 +
1.488 +void jz4730_dma_set_req_detect_mode(void *dma_channel, enum Dma_jz4730_ext_req_detect_mode mode)
1.489 +{
1.490 + static_cast<Dma_jz4730_channel *>(dma_channel)->set_req_detect_mode(mode);
1.491 +}
1.492 +
1.493 +unsigned int jz4730_dma_transfer(void *dma_channel, uint32_t source,
1.494 + uint32_t destination, unsigned int count,
1.495 + enum Dma_jz4730_trans_unit_size size,
1.496 + enum Dma_jz4730_request_type type)
1.497 +{
1.498 + return static_cast<Dma_jz4730_channel *>(dma_channel)->transfer(source, destination, count, size, type);
1.499 +}