# HG changeset patch # User Paul Boddie # Date 1698161574 -7200 # Node ID 8cc46c51babe18844cb8c79e5072ed089ae7f600 # Parent 1d4ce68b74f1d41bf1e9111954ab4d1934a62ab1 Overhauled the DMA support, expanding it to the X1600. diff -r 1d4ce68b74f1 -r 8cc46c51babe pkg/devices/lib/dma/include/dma-jz4730.h --- a/pkg/devices/lib/dma/include/dma-jz4730.h Tue Oct 24 17:22:51 2023 +0200 +++ b/pkg/devices/lib/dma/include/dma-jz4730.h Tue Oct 24 17:32:54 2023 +0200 @@ -1,7 +1,7 @@ /* * DMA support for the JZ4730. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -28,7 +28,7 @@ /* Enumerated types for various transfer parameters. */ -enum Dma_jz4730_request_type : unsigned +enum Dma_jz4730_request_type { Dma_request_external = 0, Dma_request_pcmcia_out = 4, @@ -53,19 +53,19 @@ Dma_request_ost2_underflow = 28, }; -enum Dma_jz4730_ext_level : unsigned +enum Dma_jz4730_ext_level { Dma_ext_active_high = 0, Dma_ext_active_low = 1, }; -enum Dma_jz4730_ext_output_mode_cycle : unsigned +enum Dma_jz4730_ext_output_mode_cycle { Dma_ext_output_mode_read_cycle = 0, Dma_ext_output_mode_write_cycle = 1, }; -enum Dma_jz4730_ext_req_detect_mode : unsigned +enum Dma_jz4730_ext_req_detect_mode { Dma_ext_req_detect_mode_low_level = 0, Dma_ext_req_detect_mode_falling_edge = 1, @@ -73,15 +73,6 @@ Dma_ext_req_detect_mode_rising_edge = 3, }; -enum Dma_jz4730_trans_unit_size : unsigned -{ - Dma_trans_unit_size_32_bit = 0, - Dma_trans_unit_size_8_bit = 1, - Dma_trans_unit_size_16_bit = 2, - Dma_trans_unit_size_16_byte = 3, - Dma_trans_unit_size_32_byte = 4, -}; - #ifdef __cplusplus @@ -103,7 +94,7 @@ Hw::Register_block<32> _regs; Dma_jz4730_chip *_chip; uint8_t _channel; - l4_cap_idx_t _irq=L4_INVALID_CAP; + l4_cap_idx_t _irq = L4_INVALID_CAP; // External transfer properties with defaults. @@ -117,9 +108,13 @@ unsigned int transfer(uint32_t source, uint32_t destination, unsigned int count, - enum Dma_jz4730_trans_unit_size size, + bool source_increment, bool destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, enum Dma_jz4730_request_type type=Dma_request_auto); + unsigned int wait(); + // External transfer property configuration. void set_output_polarity(enum Dma_jz4730_ext_level polarity) @@ -139,15 +134,13 @@ uint32_t encode_external_transfer(enum Dma_jz4730_request_type type); - uint32_t encode_source_address_increment(enum Dma_jz4730_request_type type); - - uint32_t encode_destination_address_increment(enum Dma_jz4730_request_type type); - uint32_t encode_req_detect_int_length(uint8_t units); - uint32_t encode_source_port_width(enum Dma_jz4730_request_type type); + uint32_t encode_source_port_width(uint8_t width); - uint32_t encode_destination_port_width(enum Dma_jz4730_request_type type); + uint32_t encode_destination_port_width(uint8_t width); + + uint32_t encode_transfer_unit_size(uint8_t size); // Transaction control. @@ -209,9 +202,14 @@ void jz4730_dma_set_req_detect_mode(void *dma_channel, enum Dma_jz4730_ext_req_detect_mode mode); -unsigned int jz4730_dma_transfer(void *dma_channel, uint32_t source, - uint32_t destination, unsigned int count, - enum Dma_jz4730_trans_unit_size size, - enum Dma_jz4730_request_type type); +unsigned int jz4730_dma_transfer(void *dma_channel, + uint32_t source, uint32_t destination, + unsigned int count, + int source_increment, int destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, + enum Dma_jz4730_request_type type); + +unsigned int jz4730_dma_wait(void *dma_channel); EXTERN_C_END diff -r 1d4ce68b74f1 -r 8cc46c51babe pkg/devices/lib/dma/include/dma-x1600.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/dma/include/dma-x1600.h Tue Oct 24 17:32:54 2023 +0200 @@ -0,0 +1,180 @@ +/* + * DMA support for the X1600. + * + * Copyright (C) 2021, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include + + + +/* Enumerated types for various transfer parameters. */ + +enum Dma_x1600_request_type +{ + Dma_request_auto = 8, + Dma_request_can0_out = 10, + Dma_request_can0_in = 11, + Dma_request_can1_out = 10, + Dma_request_can1_in = 11, + Dma_request_uart3_out = 14, + Dma_request_uart3_in = 15, + Dma_request_uart2_out = 16, + Dma_request_uart2_in = 17, + Dma_request_uart1_out = 18, + Dma_request_uart1_in = 19, + Dma_request_uart0_out = 20, + Dma_request_uart0_in = 21, + Dma_request_ssi0_send_empty = 22, + Dma_request_ssi0_recv_full = 23, + Dma_request_i2c0_send_empty = 36, + Dma_request_i2c0_recv_full = 37, + Dma_request_i2c1_send_empty = 38, + Dma_request_i2c1_recv_full = 39, + Dma_request_ssi_slv_out = 42, + Dma_request_ssi_slv_in = 43, + Dma_request_msc0_send_empty = 52, + Dma_request_msc0_recv_full = 53, + Dma_request_msc1_send_empty = 54, + Dma_request_msc1_recv_full = 55, + Dma_request_sadc_in = 56, + Dma_request_aic_loop_out = 61, + Dma_request_aic_out = 62, + Dma_request_aic_in = 63, +}; + + + +#ifdef __cplusplus + +#include +#include + +// Forward declaration. + +class Dma_x1600_chip; + + + +// DMA channel. + +class Dma_x1600_channel +{ +private: + Hw::Register_block<32> _regs; + Dma_x1600_chip *_chip; + uint8_t _channel; + l4_cap_idx_t _irq = L4_INVALID_CAP; + +public: + Dma_x1600_channel(Dma_x1600_chip *chip, uint8_t channel, l4_addr_t start, l4_cap_idx_t irq); + + unsigned int transfer(uint32_t source, uint32_t destination, + unsigned int count, + bool source_increment, bool destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, + enum Dma_x1600_request_type type=Dma_request_auto); + + unsigned int wait(); + +protected: + // Transfer property configuration. + + uint32_t encode_req_detect_int_length(uint8_t units); + + uint32_t encode_source_port_width(uint8_t width); + + uint32_t encode_destination_port_width(uint8_t width); + + uint32_t encode_transfer_unit_size(uint8_t size); + + // Transaction control. + + void ack_irq(); + + bool completed(); + + bool error(); + + bool halted(); + + bool wait_for_irq(); + + bool wait_for_irq(unsigned int timeout); +}; + +// DMA device control. + +class Dma_x1600_chip +{ +private: + Hw::Register_block<32> _regs; + l4_addr_t _start, _end; + Cpm_x1600_chip *_cpm; + +public: + Dma_x1600_chip(l4_addr_t start, l4_addr_t end, Cpm_x1600_chip *cpm); + + void disable(); + + void enable(); + + Dma_x1600_channel *get_channel(uint8_t channel, l4_cap_idx_t irq); + + // Transaction control. + + void ack_irq(uint8_t channel); + + bool error(); + + bool halted(); + + bool have_interrupt(uint8_t channel); +}; + +#endif /* __cplusplus */ + + + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *x1600_dma_init(l4_addr_t start, l4_addr_t end, void *cpm); + +void x1600_dma_disable(void *dma_chip); + +void x1600_dma_enable(void *dma_chip); + +void *x1600_dma_get_channel(void *dma, uint8_t channel, l4_cap_idx_t irq); + +unsigned int x1600_dma_transfer(void *dma_channel, + uint32_t source, uint32_t destination, + unsigned int count, + int source_increment, int destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, + enum Dma_x1600_request_type type); + +unsigned int x1600_dma_wait(void *dma_channel); + +EXTERN_C_END diff -r 1d4ce68b74f1 -r 8cc46c51babe pkg/devices/lib/dma/src/Makefile --- a/pkg/devices/lib/dma/src/Makefile Tue Oct 24 17:22:51 2023 +0200 +++ b/pkg/devices/lib/dma/src/Makefile Tue Oct 24 17:32:54 2023 +0200 @@ -4,7 +4,7 @@ TARGET = libdma.o.a libdma.o.so PC_FILENAME := libdrivers-dma -SRC_CC := jz4730.cc +SRC_CC := jz4730.cc x1600.cc PRIVATE_INCDIR += $(PKGDIR)/lib/dma/include diff -r 1d4ce68b74f1 -r 8cc46c51babe pkg/devices/lib/dma/src/jz4730.cc --- a/pkg/devices/lib/dma/src/jz4730.cc Tue Oct 24 17:22:51 2023 +0200 +++ b/pkg/devices/lib/dma/src/jz4730.cc Tue Oct 24 17:32:54 2023 +0200 @@ -27,7 +27,9 @@ #include #include -#include +#include + + enum Global_regs { @@ -96,7 +98,16 @@ enum Dma_control_status_bits : unsigned { Dma_source_address_incr = 0x00800000, + Dma_source_address_no_incr = 0x00000000, Dma_dest_address_incr = 0x00400000, + Dma_dest_address_no_incr = 0x00000000, + + Dma_trans_unit_size_32_bit = 0x00000000, + Dma_trans_unit_size_8_bit = 0x00000100, + Dma_trans_unit_size_16_bit = 0x00000200, + Dma_trans_unit_size_16_byte = 0x00000300, + Dma_trans_unit_size_32_byte = 0x00000400, + Dma_address_error = 0x00000010, Dma_trans_completed = 0x00000008, Dma_trans_halted = 0x00000004, @@ -142,62 +153,6 @@ ((external ? (int) _ext_end_of_process_mode : 0) << Dma_ext_end_of_process_mode); } -// Encode the appropriate source address increment for the given request type. -// Here, memory-to-memory transfers and transfers to peripherals involve an -// incrementing source address. Transfers from peripherals involve a static -// source address. - -uint32_t -Dma_jz4730_channel::encode_source_address_increment(enum Dma_jz4730_request_type type) -{ - switch (type) - { - case Dma_request_auto: - case Dma_request_pcmcia_out: - case Dma_request_des_out: - case Dma_request_uart3_out: - case Dma_request_uart2_out: - case Dma_request_uart1_out: - case Dma_request_uart0_out: - case Dma_request_ssi_send_empty: - case Dma_request_aic_send_empty: - case Dma_request_msc_send_empty: - case Dma_request_ost2_underflow: - return Dma_source_address_incr; - - default: - return 0; - } -} - -// Encode the appropriate destination address increment for the given request -// type. Here, memory-to-memory transfers and transfers from peripherals involve -// an incrementing destination address. Transfers to peripherals involve a static -// destination address. - -uint32_t -Dma_jz4730_channel::encode_destination_address_increment(enum Dma_jz4730_request_type type) -{ - switch (type) - { - case Dma_request_auto: - case Dma_request_pcmcia_in: - case Dma_request_des_in: - case Dma_request_uart3_in: - case Dma_request_uart2_in: - case Dma_request_uart1_in: - case Dma_request_uart0_in: - case Dma_request_ssi_recv_full: - case Dma_request_aic_recv_full: - case Dma_request_msc_recv_full: - case Dma_request_ost2_underflow: - return Dma_dest_address_incr; - - default: - return 0; - } -} - // Return the closest interval length greater than or equal to the number of // units given encoded in the request detection interval length field of the // control/status register. @@ -211,26 +166,23 @@ if (!units) return 0; - for (i = 0; i < 15; i++) + for (i = 0; i <= 15; i++) { - if (lengths[i++] >= units) + if (lengths[i] >= units) break; } - return lengths[i] << Dma_req_detect_int_length; + return i << Dma_req_detect_int_length; } -// Encode the appropriate source port width for the given request type. +// Encode the appropriate source port width. uint32_t -Dma_jz4730_channel::encode_source_port_width(enum Dma_jz4730_request_type type) +Dma_jz4730_channel::encode_source_port_width(uint8_t width) { - switch (type) + switch (width) { - case Dma_request_uart3_in: - case Dma_request_uart2_in: - case Dma_request_uart1_in: - case Dma_request_uart0_in: + case 1: return Dma_port_width_8_bit << Dma_source_port_width; default: @@ -241,14 +193,11 @@ // Encode the appropriate destination port width for the given request type. uint32_t -Dma_jz4730_channel::encode_destination_port_width(enum Dma_jz4730_request_type type) +Dma_jz4730_channel::encode_destination_port_width(uint8_t width) { - switch (type) + switch (width) { - case Dma_request_uart3_out: - case Dma_request_uart2_out: - case Dma_request_uart1_out: - case Dma_request_uart0_out: + case 1: return Dma_port_width_8_bit << Dma_dest_port_width; default: @@ -256,12 +205,38 @@ } } +// Encode the transfer unit size. + +uint32_t +Dma_jz4730_channel::encode_transfer_unit_size(uint8_t size) +{ + switch (size) + { + case 1: + return Dma_trans_unit_size_8_bit; + + case 2: + return Dma_trans_unit_size_16_bit; + + case 16: + return Dma_trans_unit_size_16_byte; + + case 32: + return Dma_trans_unit_size_32_byte; + + default: + return Dma_trans_unit_size_32_bit; + } +} + // Transfer data between memory locations. unsigned int Dma_jz4730_channel::transfer(uint32_t source, uint32_t destination, unsigned int count, - enum Dma_jz4730_trans_unit_size size, + bool source_increment, bool destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, enum Dma_jz4730_request_type type) { // Ensure an absence of address error and halt conditions globally and in this channel. @@ -285,7 +260,9 @@ // Set transfer count to the number of units. - _regs[Dma_transfer_count] = count; + unsigned int units = count < Dma_transfer_count_mask ? count : Dma_transfer_count_mask; + + _regs[Dma_transfer_count] = units; // Set auto-request for memory-to-memory transfers. Otherwise, set the // indicated request type. @@ -303,19 +280,27 @@ */ _regs[Dma_control_status] = encode_external_transfer(type) | - encode_source_address_increment(type) | - encode_destination_address_increment(type) | - encode_source_port_width(type) | - encode_destination_port_width(type) | - (size << Dma_trans_unit_size) | + (source_increment ? Dma_source_address_incr : Dma_source_address_no_incr) | + (destination_increment ? Dma_dest_address_incr : Dma_dest_address_no_incr) | + encode_source_port_width(source_width) | + encode_destination_port_width(destination_width) | + encode_transfer_unit_size(transfer_unit_size) | (Dma_trans_mode_single << Dma_trans_mode) | Dma_channel_irq_enable | Dma_channel_enable; + // Return the number of units to transfer. + + return units; +} + +unsigned int +Dma_jz4730_channel::wait() +{ // An interrupt will occur upon completion, the completion flag will be set // and the transfer count will be zero. - unsigned int remaining = count; + unsigned int remaining = 0; do { @@ -334,9 +319,16 @@ } while (!error() && !halted() && !completed()); - // Return the number of transferred units. + // Reset the channel status. - return count - remaining; + _regs[Dma_control_status] = _regs[Dma_control_status] & ~(Dma_channel_enable | + Dma_trans_completed | Dma_address_error | + Dma_trans_halted); + _regs[Dma_transfer_count] = 0; + + // Return the number of remaining units. + + return remaining; } // Wait indefinitely for an interrupt request, returning true if one was delivered. @@ -424,7 +416,7 @@ while (_regs[Dma_control] & Dma_control_enable); } -// Obtain a channel object. Only one channel is supported. +// Obtain a channel object. Dma_jz4730_channel * Dma_jz4730_chip::get_channel(uint8_t channel, l4_cap_idx_t irq) @@ -487,10 +479,20 @@ static_cast(dma_channel)->set_req_detect_mode(mode); } -unsigned int jz4730_dma_transfer(void *dma_channel, uint32_t source, - uint32_t destination, unsigned int count, - enum Dma_jz4730_trans_unit_size size, +unsigned int jz4730_dma_transfer(void *dma_channel, + uint32_t source, uint32_t destination, + unsigned int count, + int source_increment, int destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, enum Dma_jz4730_request_type type) { - return static_cast(dma_channel)->transfer(source, destination, count, size, type); + return static_cast(dma_channel)->transfer(source, + destination, count, source_increment, destination_increment, source_width, + destination_width, transfer_unit_size, type); } + +unsigned int jz4730_dma_wait(void *dma_channel) +{ + return static_cast(dma_channel)->wait(); +} diff -r 1d4ce68b74f1 -r 8cc46c51babe pkg/devices/lib/dma/src/x1600.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/dma/src/x1600.cc Tue Oct 24 17:32:54 2023 +0200 @@ -0,0 +1,509 @@ +/* + * DMA support for the X1600. + * + * Copyright (C) 2021, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include + +#include +#include +#include +#include + +#include + + + +enum Global_regs +{ + Dma_control = 0x1000, // DMAC + Dma_irq_pending = 0x1004, // DIRQP +}; + +enum Channel_regs +{ + Dma_source = 0x00, // DSA + Dma_destination = 0x04, // DTA + Dma_transfer_count = 0x08, // DTC + Dma_request_source = 0x0c, // DRT + Dma_control_status = 0x10, // DCS + Dma_command = 0x14, // DCM + Dma_descriptor_address = 0x18, // DDA + Dma_stride = 0x1c, // DSD +}; + +enum Dma_control_bits : unsigned +{ + Dma_fast_msc_transfer = 0x80000000, // FMSC + Dma_fast_ssi_transfer = 0x40000000, // FSSI + Dma_fast_tssi_transfer = 0x20000000, // FTSSI + Dma_fast_uart_transfer = 0x10000000, // FUART + Dma_fast_aic_transfer = 0x08000000, // FAIC + Dma_control_trans_halted = 0x00000008, // HLT + Dma_control_address_error = 0x00000004, // AR + Dma_control_enable = 0x00000001, // DMAE +}; + +enum Dma_transfer_count_bits : unsigned +{ + Dma_transfer_count_mask = 0x00ffffff, +}; + +enum Dma_request_source_bits : unsigned +{ + Dma_request_type_mask = 0x0000001f, +}; + +enum Dma_control_status_bits : unsigned +{ + Dma_no_descriptor_transfer = 0x80000000, + Dma_8word_descriptor = 0x40000000, + Dma_copy_offset_mask = 0x0000ff00, + Dma_address_error = 0x00000010, + Dma_trans_completed = 0x00000008, + Dma_trans_halted = 0x00000004, + Dma_channel_enable = 0x00000001, + + Dma_copy_offset_shift = 8, +}; + +enum Dma_command_bits : unsigned +{ + Dma_source_address_increment = 0x800000, + Dma_source_address_no_increment = 0x000000, + Dma_destination_address_increment = 0x400000, + Dma_destination_address_no_increment = 0x000000, + + Dma_source_address_increment_wrap = 0x200000, + Dma_destination_address_increment_wrap = 0x100000, + Dma_recommended_data_unit_size_mask = 0x0f0000, + Dma_source_port_width_mask = 0x00c000, + Dma_destination_port_width_mask = 0x003000, + Dma_transfer_unit_size_mask = 0x000f00, + + Dma_trans_unit_size_32_bit = 0x000000, + Dma_trans_unit_size_8_bit = 0x000100, + Dma_trans_unit_size_16_bit = 0x000200, + Dma_trans_unit_size_16_byte = 0x000300, + Dma_trans_unit_size_32_byte = 0x000400, + Dma_trans_unit_size_64_byte = 0x000500, + Dma_trans_unit_size_128_byte = 0x000600, + Dma_trans_unit_size_autonomous = 0x000700, + Dma_trans_unit_size_external = 0x000800, + + Dma_source_address_compare_index = 0x000080, + Dma_destination_address_compare_index = 0x000040, + Dma_stride_enable = 0x000004, + Dma_transfer_irq_enable = 0x000002, + Dma_descriptor_link_enable = 0x000001, + + Dma_recommended_data_unit_size_shift = 16, + Dma_source_port_width_shift = 14, + Dma_destination_port_width_shift = 12, + Dma_transfer_unit_size_shift = 8, +}; + +enum Dma_port_width_values : unsigned +{ + Dma_port_width_32_bit = 0, + Dma_port_width_8_bit = 1, + Dma_port_width_16_bit = 2, +}; + + + +// Initialise a channel. + +Dma_x1600_channel::Dma_x1600_channel(Dma_x1600_chip *chip, uint8_t channel, + l4_addr_t start, l4_cap_idx_t irq) +: _chip(chip), _channel(channel), _irq(irq) +{ + _regs = new Hw::Mmio_register_block<32>(start); + + // Initialise the transfer count. + + _regs[Dma_transfer_count] = 0; +} + +// Return the closest interval length greater than or equal to the number of +// units given encoded in the request detection interval length field of the +// control/status register. + +uint32_t +Dma_x1600_channel::encode_req_detect_int_length(uint8_t units) +{ + static uint8_t lengths[] = {0, 1, 2, 3, 4, 8, 16, 32, 64, 128}; + int i; + + if (!units) + return 0; + + for (i = 0; i <= 9; i++) + { + if (lengths[i] >= units) + break; + } + + return i << Dma_recommended_data_unit_size_shift; +} + +// Encode the appropriate source port width for the given request type. + +uint32_t +Dma_x1600_channel::encode_source_port_width(uint8_t width) +{ + switch (width) + { + case 1: + return Dma_port_width_8_bit << Dma_source_port_width_shift; + + case 2: + return Dma_port_width_16_bit << Dma_source_port_width_shift; + + default: + return Dma_port_width_32_bit << Dma_source_port_width_shift; + } +} + +// Encode the appropriate destination port width for the given request type. + +uint32_t +Dma_x1600_channel::encode_destination_port_width(uint8_t width) +{ + switch (width) + { + case 1: + return Dma_port_width_8_bit << Dma_destination_port_width_shift; + + case 2: + return Dma_port_width_16_bit << Dma_destination_port_width_shift; + + default: + return Dma_port_width_32_bit << Dma_destination_port_width_shift; + } +} + +// Encode the transfer unit size. +// NOTE: This does not handle the external case. + +uint32_t +Dma_x1600_channel::encode_transfer_unit_size(uint8_t size) +{ + switch (size) + { + case 0: + return Dma_trans_unit_size_autonomous; + + case 1: + return Dma_trans_unit_size_8_bit; + + case 2: + return Dma_trans_unit_size_16_bit; + + case 16: + return Dma_trans_unit_size_16_byte; + + case 32: + return Dma_trans_unit_size_32_byte; + + case 64: + return Dma_trans_unit_size_64_byte; + + case 128: + return Dma_trans_unit_size_128_byte; + + default: + return Dma_trans_unit_size_32_bit; + } +} + +// Transfer data between memory locations. + +unsigned int +Dma_x1600_channel::transfer(uint32_t source, uint32_t destination, + unsigned int count, + bool source_increment, bool destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, + enum Dma_x1600_request_type type) +{ + printf("transfer:%s%s%s%s\n", error() ? " error" : "", + halted() ? " halted" : "", + completed() ? " completed" : "", + _regs[Dma_transfer_count] ? " count" : ""); + + // Ensure an absence of address error and halt conditions globally and in this channel. + + if (error() || halted()) + return 0; + + // Ensure an absence of transaction completed and zero transfer count for this channel. + + if (completed() || _regs[Dma_transfer_count]) + return 0; + + // Disable the channel. + + _regs[Dma_control_status] = _regs[Dma_control_status] & ~Dma_channel_enable; + + // Set addresses. + + _regs[Dma_source] = source; + _regs[Dma_destination] = destination; + + // Set transfer count to the number of units. + + unsigned int units = count < Dma_transfer_count_mask ? count : Dma_transfer_count_mask; + + _regs[Dma_transfer_count] = units; + + // Set auto-request for memory-to-memory transfers. Otherwise, set the + // indicated request type. + + _regs[Dma_request_source] = type; + + // For a descriptor, the actual fields would be populated instead of the + // command register, descriptor transfer would be indicated in the control/ + // status register along with the appropriate descriptor size indicator. + + /* NOTE: To be considered... + * request detection interval length (for autonomous mode) + */ + + _regs[Dma_command] = (source_increment ? Dma_source_address_increment : Dma_source_address_no_increment) | + (destination_increment ? Dma_destination_address_increment : Dma_destination_address_no_increment) | + encode_source_port_width(source_width) | + encode_destination_port_width(destination_width) | + encode_transfer_unit_size(transfer_unit_size) | + Dma_transfer_irq_enable; + + // For a descriptor, the descriptor address would be set and the doorbell + // register field for the channel set. + + // Enable the channel (and peripheral). + + _regs[Dma_control_status] = Dma_no_descriptor_transfer | + Dma_channel_enable; + + // Return the number of units to transfer. + + return units; +} + +unsigned int +Dma_x1600_channel::wait() +{ + // An interrupt will occur upon completion, the completion flag will be set + // and the transfer count will be zero. + + unsigned int remaining = 0; + + do + { + if (!wait_for_irq(1000000)) + printf("status = %x\n", (uint32_t) _regs[Dma_control_status]); + else + { + printf("status = %x\n", (uint32_t) _regs[Dma_control_status]); + remaining = _regs[Dma_transfer_count]; + ack_irq(); + break; + } + } + while (!error() && !halted() && !completed()); + + // Reset the channel status. + + _regs[Dma_control_status] = _regs[Dma_control_status] & ~(Dma_channel_enable | + Dma_trans_completed | Dma_address_error | + Dma_trans_halted); + _regs[Dma_transfer_count] = 0; + + // Return the number of remaining units. + + return remaining; +} + +// Wait indefinitely for an interrupt request, returning true if one was delivered. + +bool +Dma_x1600_channel::wait_for_irq() +{ + return !l4_error(l4_irq_receive(_irq, L4_IPC_NEVER)) && _chip->have_interrupt(_channel); +} + +// Wait up to the given timeout (in microseconds) for an interrupt request, +// returning true if one was delivered. + +bool +Dma_x1600_channel::wait_for_irq(unsigned int timeout) +{ + return !l4_error(l4_irq_receive(_irq, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4util_micros2l4to(timeout)))) && _chip->have_interrupt(_channel); +} + +// Acknowledge an interrupt condition. + +void +Dma_x1600_channel::ack_irq() +{ + _chip->ack_irq(_channel); +} + +// Return whether a transfer has completed. + +bool +Dma_x1600_channel::completed() +{ + return _regs[Dma_control_status] & Dma_trans_completed ? true : false; +} + +// Return whether an address error condition has arisen. + +bool +Dma_x1600_channel::error() +{ + return _chip->error() || (_regs[Dma_control_status] & Dma_address_error ? true : false); +} + +// Return whether a transfer has halted. + +bool +Dma_x1600_channel::halted() +{ + return _chip->halted() || (_regs[Dma_control_status] & Dma_trans_halted ? true : false); +} + + + +// Initialise the I2C controller. + +Dma_x1600_chip::Dma_x1600_chip(l4_addr_t start, l4_addr_t end, + Cpm_x1600_chip *cpm) +: _start(start), _end(end), _cpm(cpm) +{ + _regs = new Hw::Mmio_register_block<32>(start); +} + +// Enable the peripheral. + +void +Dma_x1600_chip::enable() +{ + // Make sure that the DMA clock is available. + + _cpm->start_clock(Clock_dma); + + _regs[Dma_control] = Dma_control_enable; + while (!(_regs[Dma_control] & Dma_control_enable)); +} + +// Disable the channel. + +void +Dma_x1600_chip::disable() +{ + _regs[Dma_control] = 0; + while (_regs[Dma_control] & Dma_control_enable); +} + +// Obtain a channel object. + +Dma_x1600_channel * +Dma_x1600_chip::get_channel(uint8_t channel, l4_cap_idx_t irq) +{ + if (channel < 32) + return new Dma_x1600_channel(this, channel, _start + 0x20 * channel, irq); + else + throw -L4_EINVAL; +} + +// Return whether an interrupt is pending on the given channel. + +bool +Dma_x1600_chip::have_interrupt(uint8_t channel) +{ + return _regs[Dma_irq_pending] & (1 << channel) ? true : false; +} + +// Acknowledge an interrupt condition on the given channel. + +void +Dma_x1600_chip::ack_irq(uint8_t channel) +{ + _regs[Dma_irq_pending] = _regs[Dma_irq_pending] & ~(1 << channel); +} + +// Return whether an address error condition has arisen. + +bool +Dma_x1600_chip::error() +{ + return _regs[Dma_control] & Dma_control_address_error ? true : false; +} + +// Return whether a transfer has halted. + +bool +Dma_x1600_chip::halted() +{ + return _regs[Dma_control] & Dma_control_trans_halted ? true : false; +} + + + +// C language interface functions. + +void *x1600_dma_init(l4_addr_t start, l4_addr_t end, void *cpm) +{ + return (void *) new Dma_x1600_chip(start, end, static_cast(cpm)); +} + +void x1600_dma_disable(void *dma_chip) +{ + static_cast(dma_chip)->disable(); +} + +void x1600_dma_enable(void *dma_chip) +{ + static_cast(dma_chip)->enable(); +} + +void *x1600_dma_get_channel(void *dma, uint8_t channel, l4_cap_idx_t irq) +{ + return static_cast(dma)->get_channel(channel, irq); +} + +unsigned int x1600_dma_transfer(void *dma_channel, + uint32_t source, uint32_t destination, + unsigned int count, + int source_increment, int destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, + enum Dma_x1600_request_type type) +{ + return static_cast(dma_channel)->transfer(source, + destination, count, source_increment, destination_increment, source_width, + destination_width, transfer_unit_size, type); +} + +unsigned int x1600_dma_wait(void *dma_channel) +{ + return static_cast(dma_channel)->wait(); +}