# HG changeset patch # User Paul Boddie # Date 1610834445 -3600 # Node ID 9105804e323af1f40443bd1f6777637da82e5617 # Parent 4a12b51403dc2b5ac9682559a6b0af52f29d6f88# Parent d58099373644bbf2a9b00fad9f01264eea9bd899 Merged changes from the default branch. diff -r 4a12b51403dc -r 9105804e323a conf/landfall-examples/mips-letux400-common.io --- a/conf/landfall-examples/mips-letux400-common.io Wed Jan 06 23:54:45 2021 +0100 +++ b/conf/landfall-examples/mips-letux400-common.io Sat Jan 16 23:00:45 2021 +0100 @@ -9,11 +9,23 @@ CPM = wrap(hw:match("jz4730-cpm")); }) +Io.add_vbus("dma", Io.Vi.System_bus +{ + CPM = wrap(hw:match("jz4730-cpm")); + DMA = wrap(hw:match("jz4730-dma")); +}) + Io.add_vbus("gpio", Io.Vi.System_bus { GPIO = wrap(hw:match("jz4730-gpio")); }) +Io.add_vbus("i2c", Io.Vi.System_bus +{ + CPM = wrap(hw:match("jz4730-cpm")); + I2C = wrap(hw:match("jz4730-i2c")); +}) + Io.add_vbus("lcd", Io.Vi.System_bus { LCD = wrap(hw:match("jz4740-lcd")); diff -r 4a12b51403dc -r 9105804e323a conf/landfall-examples/mips-letux400-dma.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/landfall-examples/mips-letux400-dma.cfg Sat Jan 16 23:00:45 2021 +0100 @@ -0,0 +1,136 @@ +-- vim: ft=lua ts=2 et sw=2 + +-- Start Mag to multiplex the framebuffer showing only a single program. +-- This example shows a framebuffer terminal showing the hello example's output. +-- The target platform is the Letux 400 notebook computer. + +local L4 = require("L4"); + +local l = L4.default_loader; + +-- Define general access to peripherals. + +local io_buses = { + cpm = l:new_channel(); + dma = l:new_channel(); + gpio = l:new_channel(); + lcd = l:new_channel(); + pwm = l:new_channel(); -- exposes GPIO, PWM + }; + +l:start({ + caps = { + cpm = io_buses.cpm:svr(), + dma = io_buses.dma:svr(), + gpio = io_buses.gpio:svr(), + lcd = io_buses.lcd:svr(), + pwm = io_buses.pwm:svr(), + + icu = L4.Env.icu, + sigma0 = L4.cast(L4.Proto.Factory, L4.Env.sigma0):create(L4.Proto.Sigma0), + }, + }, + "rom/io rom/hw_devices.io rom/mips-letux400-common.io"); + +-- Expose a PWM peripheral as a device. + +local pwm = l:new_channel(); + +l:startv({ + caps = { + vbus = io_buses.pwm, + pwm = pwm:svr(), + }, + }, + "rom/dev_pwm_jz4730", "0", "250", "299", "47"); -- specifying peripheral number, parameters + +-- Expose a PWM backlight device. + +local backlight = l:new_channel(); -- exposes backlight device + +l:startv({ + caps = { + pwm = pwm, + backlight = backlight:svr(), + }, + }, + "rom/dev_backlight_pwm", "0", "300"); -- specifying limits + +-- Expose a display device for the Letux. + +local display = l:new_channel(); -- exposes display device + +l:start({ + caps = { + backlight = backlight, + display = display:svr(), + vbus = io_buses.gpio, + }, + }, + "rom/dev_display_letux400"); + +-- Expose the CPM peripheral. + +local cpm = l:new_channel(); + +l:start({ + caps = { + vbus = io_buses.cpm, + cpm = cpm:svr(), + }, + }, + "rom/dev_cpm_jz4730"); + +-- Expose a framebuffer device. + +local fbdrv_fb = l:new_channel(); + +l:start({ + caps = { + vbus = io_buses.lcd, + fb = fbdrv_fb:svr(), + cpm = cpm, + display = display, -- needed by LCD driver + }, + }, + "rom/fb-drv"); + +-- Multiplex the framebuffer. + +local mag_caps = { + mag = l:new_channel(), + svc = l:new_channel(), + }; + +l:start({ + caps = { + vbus = io_buses.gpio, -- needed by input driver + fb = fbdrv_fb, + mag = mag_caps.mag:svr(), + svc = mag_caps.svc:svr(), + }, + }, + "rom/mag"); + +-- Show the terminal. + +local term = l:new_channel(); + +l:start({ + caps = { + fb = mag_caps.svc:create(L4.Proto.Goos, "g=800x460+0+0", "barheight=20"), + term = term:svr(), + }, + }, + "rom/fbterminal"); + +-- Show the DMA example. + +l:start({ + caps = { + icu = L4.Env.icu, + vbus = io_buses.dma, + }, + log_cap = term, + }, + "rom/ex_letux400_dma"); diff -r 4a12b51403dc -r 9105804e323a conf/landfall-examples/mips-letux400-dma.list --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/landfall-examples/mips-letux400-dma.list Sat Jan 16 23:00:45 2021 +0100 @@ -0,0 +1,47 @@ + +modaddr 0x1100000 + +entry mips-letux400-dma-example +bootstrap bootstrap -serial +kernel fiasco -serial_esc +roottask moe rom/mips-letux400-dma.cfg +module mips-letux400-dma.cfg +module mips-letux400-common.io +module plat-letux400/hw_devices.io +module l4re +module io +module ned +module fb-drv +module mag +module dev_pwm_jz4730 +module dev_backlight_pwm +module dev_display_letux400 +module dev_cpm_jz4730 +module fbterminal +module ex_letux400_dma +module libpanel_letux400.so +module mips-jz4740-panel.txt +module lib4re-c.so +module lib4re-c-util.so +module lib4re.so +module lib4re-util.so +module libc_be_l4refile.so +module libc_be_l4re.so +module libc_be_socket_noop.so +module libcpm.o.so +module libc_support_misc.so +module libdevice_util.o.so +module libdl.so +module libgpio.o.so +module libio-io.so +module libio-vbus.so +module libipc.so +module libl4sys-direct.so +module libl4sys.so +module libl4util.so +module liblcd_jz4740.o.so +module libld-l4.so +module libpthread.so +module libpwm.o.so +module libsupc++.so +module libuc_c.so diff -r 4a12b51403dc -r 9105804e323a conf/landfall-examples/mips-letux400-i2c.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/landfall-examples/mips-letux400-i2c.cfg Sat Jan 16 23:00:45 2021 +0100 @@ -0,0 +1,136 @@ +-- vim: ft=lua ts=2 et sw=2 + +-- Start Mag to multiplex the framebuffer showing only a single program. +-- This example shows a framebuffer terminal showing the hello example's output. +-- The target platform is the Letux 400 notebook computer. + +local L4 = require("L4"); + +local l = L4.default_loader; + +-- Define general access to peripherals. + +local io_buses = { + cpm = l:new_channel(); + gpio = l:new_channel(); + i2c = l:new_channel(); + lcd = l:new_channel(); + pwm = l:new_channel(); -- exposes GPIO, PWM + }; + +l:start({ + caps = { + cpm = io_buses.cpm:svr(), + gpio = io_buses.gpio:svr(), + i2c = io_buses.i2c:svr(), + lcd = io_buses.lcd:svr(), + pwm = io_buses.pwm:svr(), + + icu = L4.Env.icu, + sigma0 = L4.cast(L4.Proto.Factory, L4.Env.sigma0):create(L4.Proto.Sigma0), + }, + }, + "rom/io rom/hw_devices.io rom/mips-letux400-common.io"); + +-- Expose a PWM peripheral as a device. + +local pwm = l:new_channel(); + +l:startv({ + caps = { + vbus = io_buses.pwm, + pwm = pwm:svr(), + }, + }, + "rom/dev_pwm_jz4730", "0", "250", "299", "47"); -- specifying peripheral number, parameters + +-- Expose a PWM backlight device. + +local backlight = l:new_channel(); -- exposes backlight device + +l:startv({ + caps = { + pwm = pwm, + backlight = backlight:svr(), + }, + }, + "rom/dev_backlight_pwm", "0", "300"); -- specifying limits + +-- Expose a display device for the Letux. + +local display = l:new_channel(); -- exposes display device + +l:start({ + caps = { + backlight = backlight, + display = display:svr(), + vbus = io_buses.gpio, + }, + }, + "rom/dev_display_letux400"); + +-- Expose the CPM peripheral. + +local cpm = l:new_channel(); + +l:start({ + caps = { + vbus = io_buses.cpm, + cpm = cpm:svr(), + }, + }, + "rom/dev_cpm_jz4730"); + +-- Expose a framebuffer device. + +local fbdrv_fb = l:new_channel(); + +l:start({ + caps = { + vbus = io_buses.lcd, + fb = fbdrv_fb:svr(), + cpm = cpm, + display = display, -- needed by LCD driver + }, + }, + "rom/fb-drv"); + +-- Multiplex the framebuffer. + +local mag_caps = { + mag = l:new_channel(), + svc = l:new_channel(), + }; + +l:start({ + caps = { + vbus = io_buses.gpio, -- needed by input driver + fb = fbdrv_fb, + mag = mag_caps.mag:svr(), + svc = mag_caps.svc:svr(), + }, + }, + "rom/mag"); + +-- Show the terminal. + +local term = l:new_channel(); + +l:start({ + caps = { + fb = mag_caps.svc:create(L4.Proto.Goos, "g=800x460+0+0", "barheight=20"), + term = term:svr(), + }, + }, + "rom/fbterminal"); + +-- Show the I2C example. + +l:start({ + caps = { + icu = L4.Env.icu, + vbus = io_buses.i2c, + }, + log_cap = term, + }, + "rom/ex_letux400_i2c"); diff -r 4a12b51403dc -r 9105804e323a conf/landfall-examples/mips-letux400-i2c.list --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/landfall-examples/mips-letux400-i2c.list Sat Jan 16 23:00:45 2021 +0100 @@ -0,0 +1,47 @@ + +modaddr 0x1100000 + +entry mips-letux400-i2c-example +bootstrap bootstrap -serial +kernel fiasco -serial_esc +roottask moe rom/mips-letux400-i2c.cfg +module mips-letux400-i2c.cfg +module mips-letux400-common.io +module plat-letux400/hw_devices.io +module l4re +module io +module ned +module fb-drv +module mag +module dev_pwm_jz4730 +module dev_backlight_pwm +module dev_display_letux400 +module dev_cpm_jz4730 +module fbterminal +module ex_letux400_i2c +module libpanel_letux400.so +module mips-jz4740-panel.txt +module lib4re-c.so +module lib4re-c-util.so +module lib4re.so +module lib4re-util.so +module libc_be_l4refile.so +module libc_be_l4re.so +module libc_be_socket_noop.so +module libcpm.o.so +module libc_support_misc.so +module libdevice_util.o.so +module libdl.so +module libgpio.o.so +module libio-io.so +module libio-vbus.so +module libipc.so +module libl4sys-direct.so +module libl4sys.so +module libl4util.so +module liblcd_jz4740.o.so +module libld-l4.so +module libpthread.so +module libpwm.o.so +module libsupc++.so +module libuc_c.so diff -r 4a12b51403dc -r 9105804e323a pkg/devices/lib/dma/include/dma-jz4730.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/dma/include/dma-jz4730.h Sat Jan 16 23:00:45 2021 +0100 @@ -0,0 +1,217 @@ +/* + * DMA support for the JZ4730. + * + * Copyright (C) 2021 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_jz4730_request_type : unsigned +{ + Dma_request_external = 0, + Dma_request_pcmcia_out = 4, + Dma_request_pcmcia_in = 5, + Dma_request_auto = 8, + Dma_request_des_out = 10, + Dma_request_des_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_ssi_send_empty = 22, + Dma_request_ssi_recv_full = 23, + Dma_request_aic_send_empty = 24, + Dma_request_aic_recv_full = 25, + Dma_request_msc_send_empty = 26, + Dma_request_msc_recv_full = 27, + Dma_request_ost2_underflow = 28, +}; + +enum Dma_jz4730_ext_level : unsigned +{ + Dma_ext_active_high = 0, + Dma_ext_active_low = 1, +}; + +enum Dma_jz4730_ext_output_mode_cycle : unsigned +{ + Dma_ext_output_mode_read_cycle = 0, + Dma_ext_output_mode_write_cycle = 1, +}; + +enum Dma_jz4730_ext_req_detect_mode : unsigned +{ + Dma_ext_req_detect_mode_low_level = 0, + Dma_ext_req_detect_mode_falling_edge = 1, + Dma_ext_req_detect_mode_high_level = 2, + 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 + +#include +#include + +// Forward declaration. + +class Dma_jz4730_chip; + + + +// DMA channel. + +class Dma_jz4730_channel +{ +private: + Hw::Register_block<32> _regs; + Dma_jz4730_chip *_chip; + uint8_t _channel; + l4_cap_idx_t _irq=L4_INVALID_CAP; + + // External transfer properties with defaults. + + enum Dma_jz4730_ext_level _ext_output_polarity = Dma_ext_active_high; + enum Dma_jz4730_ext_level _ext_end_of_process_mode = Dma_ext_active_high; + enum Dma_jz4730_ext_output_mode_cycle _ext_output_mode_cycle = Dma_ext_output_mode_read_cycle; + enum Dma_jz4730_ext_req_detect_mode _ext_req_detect_mode = Dma_ext_req_detect_mode_high_level; + +public: + Dma_jz4730_channel(Dma_jz4730_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, + enum Dma_jz4730_trans_unit_size size, + enum Dma_jz4730_request_type type=Dma_request_auto); + + // External transfer property configuration. + + void set_output_polarity(enum Dma_jz4730_ext_level polarity) + { _ext_output_polarity = polarity; } + + void set_end_of_process_mode(enum Dma_jz4730_ext_level mode) + { _ext_end_of_process_mode = mode; } + + void set_output_mode_cycle(enum Dma_jz4730_ext_output_mode_cycle cycle) + { _ext_output_mode_cycle = cycle; } + + void set_req_detect_mode(enum Dma_jz4730_ext_req_detect_mode mode) + { _ext_req_detect_mode = mode; } + +protected: + // Transfer property configuration. + + 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_destination_port_width(enum Dma_jz4730_request_type type); + + // 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_jz4730_chip +{ +private: + Hw::Register_block<32> _regs; + l4_addr_t _start, _end; + Cpm_jz4730_chip *_cpm; + +public: + Dma_jz4730_chip(l4_addr_t start, l4_addr_t end, Cpm_jz4730_chip *cpm); + + void disable(); + + void enable(); + + Dma_jz4730_channel *get_channel(uint8_t channel, l4_cap_idx_t irq); + + bool have_interrupt(uint8_t channel); +}; + +#endif /* __cplusplus */ + + + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *jz4730_dma_init(l4_addr_t start, l4_addr_t end, void *cpm); + +void jz4730_dma_disable(void *dma_chip); + +void jz4730_dma_enable(void *dma_chip); + +void *jz4730_dma_get_channel(void *dma, uint8_t channel, l4_cap_idx_t irq); + +void jz4730_dma_set_output_polarity(void *dma_channel, enum Dma_jz4730_ext_level polarity); + +void jz4730_dma_set_end_of_process_mode(void *dma_channel, enum Dma_jz4730_ext_level mode); + +void jz4730_dma_set_output_mode_cycle(void *dma_channel, enum Dma_jz4730_ext_output_mode_cycle cycle); + +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); + +EXTERN_C_END diff -r 4a12b51403dc -r 9105804e323a pkg/devices/lib/dma/src/jz4730.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/dma/src/jz4730.cc Sat Jan 16 23:00:45 2021 +0100 @@ -0,0 +1,496 @@ +/* + * DMA support for the JZ4730. + * + * Copyright (C) 2021 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_irq_pending = 0xf8, // IRQP + Dma_control = 0xfc, // DMAC +}; + +enum Channel_regs +{ + Dma_source = 0x00, // DSA + Dma_destination = 0x04, // DDA + Dma_transfer_count = 0x08, // DTC + Dma_request_source = 0x0c, // DRT + Dma_control_status = 0x10, // DCS +}; + +enum Dma_irq_pending_shifts : unsigned +{ + Dma_irq_pending_ch0 = 15, + Dma_irq_pending_ch1 = 14, + Dma_irq_pending_ch2 = 13, + Dma_irq_pending_ch3 = 12, + Dma_irq_pending_ch4 = 11, + Dma_irq_pending_ch5 = 10, +}; + +enum Dma_control_bits : unsigned +{ + Dma_control_priority_mode = 0x100, // PM + Dma_control_halt_occurred = 0x008, // HLT + Dma_control_address_error = 0x004, // AR + Dma_control_enable = 0x001, // DMAE +}; + +enum Dma_control_priority_modes : unsigned +{ + Dma_priority_mode_01234567 = 0, + Dma_priority_mode_02314675 = 1, + Dma_priority_mode_20136457 = 2, + Dma_priority_mode_round_robin = 3, +}; + +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_shifts : unsigned +{ + Dma_ext_output_polarity = 31, + Dma_ext_output_mode_cycle = 30, + Dma_ext_req_detect_mode = 28, + Dma_ext_end_of_process_mode = 27, + Dma_req_detect_int_length = 16, + Dma_source_port_width = 14, + Dma_dest_port_width = 12, + Dma_trans_unit_size = 8, + Dma_trans_mode = 7, +}; + +enum Dma_control_status_bits : unsigned +{ + Dma_source_address_incr = 0x00800000, + Dma_dest_address_incr = 0x00400000, + Dma_address_error = 0x00000010, + Dma_trans_completed = 0x00000008, + Dma_trans_halted = 0x00000004, + Dma_channel_irq_enable = 0x00000002, + Dma_channel_enable = 0x00000001, +}; + +enum Dma_port_width_values : unsigned +{ + Dma_port_width_32_bit = 0, + Dma_port_width_8_bit = 1, + Dma_port_width_16_bit = 2, +}; + +enum Dma_trans_mode_values : unsigned +{ + Dma_trans_mode_single = 0, + Dma_trans_mode_block = 1, +}; + + + +// Initialise a channel. + +Dma_jz4730_channel::Dma_jz4730_channel(Dma_jz4730_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); +} + +// Encode flags for an external transfer. + +uint32_t +Dma_jz4730_channel::encode_external_transfer(enum Dma_jz4730_request_type type) +{ + int external = (type == Dma_request_external) ? 1 : 0; + + return + ((external ? (int) _ext_output_polarity : 0) << Dma_ext_output_polarity) | + ((external ? (int) _ext_output_mode_cycle : 0) << Dma_ext_output_mode_cycle) | + ((external ? (int) _ext_req_detect_mode : 0) << Dma_ext_req_detect_mode) | + ((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. + +uint32_t +Dma_jz4730_channel::encode_req_detect_int_length(uint8_t units) +{ + static uint8_t lengths[] = {0, 2, 4, 8, 12, 16, 20, 24, 28, 32, 48, 60, 64, 124, 128, 200}; + int i; + + if (!units) + return 0; + + for (i = 0; i < 15; i++) + { + if (lengths[i++] >= units) + break; + } + + return lengths[i] << Dma_req_detect_int_length; +} + +// Encode the appropriate source port width for the given request type. + +uint32_t +Dma_jz4730_channel::encode_source_port_width(enum Dma_jz4730_request_type type) +{ + switch (type) + { + case Dma_request_uart3_in: + case Dma_request_uart2_in: + case Dma_request_uart1_in: + case Dma_request_uart0_in: + return Dma_port_width_8_bit << Dma_source_port_width; + + default: + return Dma_port_width_32_bit << Dma_source_port_width; + } +} + +// 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) +{ + switch (type) + { + case Dma_request_uart3_out: + case Dma_request_uart2_out: + case Dma_request_uart1_out: + case Dma_request_uart0_out: + return Dma_port_width_8_bit << Dma_dest_port_width; + + default: + return Dma_port_width_32_bit << Dma_dest_port_width; + } +} + +// 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, + enum Dma_jz4730_request_type type) +{ + // 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. + + _regs[Dma_transfer_count] = count; + + // Set auto-request for memory-to-memory transfers. Otherwise, set the + // indicated request type. + + _regs[Dma_request_source] = type; + + // Set control/status fields. + // Enable the channel (and peripheral). + + /* NOTE: To be considered... + * request detection interval length (currently left as 0) + * increments and port widths for external transfers + * port width overriding (for AIC...) + * transfer mode (currently left as single) + */ + + _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) | + (Dma_trans_mode_single << Dma_trans_mode) | + Dma_channel_irq_enable | + Dma_channel_enable; + + // An interrupt will occur upon completion, the completion flag will be set + // and the transfer count will be zero. + + unsigned int remaining = count; + + do + { + if (!wait_for_irq(1000000)) + printf("status = %x\n", (uint32_t) _regs[Dma_control_status]); + + // Clearing the completion flag will clear the interrupt condition. + // Any remaining units must be read before clearing the condition. + + else + { + remaining = _regs[Dma_transfer_count]; + ack_irq(); + break; + } + } + while (!error() && !halted() && !completed()); + + // Return the number of transferred units. + + return count - remaining; +} + +// Wait indefinitely for an interrupt request, returning true if one was delivered. + +bool +Dma_jz4730_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_jz4730_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_jz4730_channel::ack_irq() +{ + _regs[Dma_control_status] = _regs[Dma_control_status] & ~Dma_trans_completed; +} + +// Return whether a transfer has completed. + +bool +Dma_jz4730_channel::completed() +{ + return _regs[Dma_control_status] & Dma_trans_completed ? true : false; +} + +// Return whether an address error condition has arisen. + +bool +Dma_jz4730_channel::error() +{ + return _regs[Dma_control_status] & Dma_address_error ? true : false; +} + +// Return whether a transfer has halted. + +bool +Dma_jz4730_channel::halted() +{ + return _regs[Dma_control_status] & Dma_trans_halted ? true : false; +} + + + +// Initialise the I2C controller. + +Dma_jz4730_chip::Dma_jz4730_chip(l4_addr_t start, l4_addr_t end, + Cpm_jz4730_chip *cpm) +: _start(start), _end(end), _cpm(cpm) +{ + _regs = new Hw::Mmio_register_block<32>(start); +} + +// Enable the peripheral. + +void +Dma_jz4730_chip::enable() +{ + // Make sure that the DMA clock is available. + + _cpm->start_dma(); + + // Enable the channel. + // NOTE: No configuration is done for channel priority mode. + + _regs[Dma_control] = Dma_control_enable; + while (!(_regs[Dma_control] & Dma_control_enable)); +} + +// Disable the channel. + +void +Dma_jz4730_chip::disable() +{ + _regs[Dma_control] = 0; + while (_regs[Dma_control] & Dma_control_enable); +} + +// Obtain a channel object. Only one channel is supported. + +Dma_jz4730_channel * +Dma_jz4730_chip::get_channel(uint8_t channel, l4_cap_idx_t irq) +{ + if (channel < 6) + return new Dma_jz4730_channel(this, channel, _start + 0x20 * channel, irq); + else + throw -L4_EINVAL; +} + +// Return whether an interrupt is pending on the given channel. + +bool +Dma_jz4730_chip::have_interrupt(uint8_t channel) +{ + return _regs[Dma_irq_pending] & (1 << (Dma_irq_pending_ch0 - channel)) ? true : false; +} + + + +// C language interface functions. + +void *jz4730_dma_init(l4_addr_t start, l4_addr_t end, void *cpm) +{ + return (void *) new Dma_jz4730_chip(start, end, static_cast(cpm)); +} + +void jz4730_dma_disable(void *dma_chip) +{ + static_cast(dma_chip)->disable(); +} + +void jz4730_dma_enable(void *dma_chip) +{ + static_cast(dma_chip)->enable(); +} + +void *jz4730_dma_get_channel(void *dma, uint8_t channel, l4_cap_idx_t irq) +{ + return static_cast(dma)->get_channel(channel, irq); +} + +void jz4730_dma_set_output_polarity(void *dma_channel, enum Dma_jz4730_ext_level polarity) +{ + static_cast(dma_channel)->set_output_polarity(polarity); +} + +void jz4730_dma_set_end_of_process_mode(void *dma_channel, enum Dma_jz4730_ext_level mode) +{ + static_cast(dma_channel)->set_end_of_process_mode(mode); +} + +void jz4730_dma_set_output_mode_cycle(void *dma_channel, enum Dma_jz4730_ext_output_mode_cycle cycle) +{ + static_cast(dma_channel)->set_output_mode_cycle(cycle); +} + +void jz4730_dma_set_req_detect_mode(void *dma_channel, enum Dma_jz4730_ext_req_detect_mode mode) +{ + 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, + enum Dma_jz4730_request_type type) +{ + return static_cast(dma_channel)->transfer(source, destination, count, size, type); +} diff -r 4a12b51403dc -r 9105804e323a pkg/devices/lib/i2c/src/jz4730.cc --- a/pkg/devices/lib/i2c/src/jz4730.cc Wed Jan 06 23:54:45 2021 +0100 +++ b/pkg/devices/lib/i2c/src/jz4730.cc Sat Jan 16 23:00:45 2021 +0100 @@ -138,6 +138,10 @@ bool I2c_jz4730_channel::set_address(uint8_t address, bool read) { + // Waiting for long enough may eliminate a busy condition and thus permit a + // new transaction. 10ms appears to be long enough, whereas 1ms does not + // appear to be. In case this is insufficient, permit failure. + unsigned int limit = 10; do diff -r 4a12b51403dc -r 9105804e323a pkg/landfall-examples/letux400_dma/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/landfall-examples/letux400_dma/Makefile Sat Jan 16 23:00:45 2021 +0100 @@ -0,0 +1,10 @@ +PKGDIR ?= .. +L4DIR ?= $(PKGDIR)/../.. + +TARGET = ex_letux400_dma + +SRC_CC = letux400_dma.cc + +REQUIRES_LIBS = l4re_c-util libdrivers-cpm libdrivers-dma libdevice-util + +include $(L4DIR)/mk/prog.mk diff -r 4a12b51403dc -r 9105804e323a pkg/landfall-examples/letux400_dma/letux400_dma.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/landfall-examples/letux400_dma/letux400_dma.cc Sat Jan 16 23:00:45 2021 +0100 @@ -0,0 +1,226 @@ +/* + * Test DMA transfers. + * + * Copyright (C) 2018, 2020, 2021 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 + +#include +#include +#include +#include + +#include +#include +#include + + + +/* Device and resource discovery. */ + +static long item_in_range(long start, long end, long index) +{ + if (start < end) + return start + index; + else + return start - index; +} + + + +int main(void) +{ + void *cpm; + void *dma, *dma0; + + /* Allocate memory to test transfers. */ + + l4_cap_idx_t ds0_mem, ds1_mem; + l4_size_t ds0_size = L4_PAGESIZE, ds0_psize, ds1_size = L4_PAGESIZE, ds1_psize; + l4_addr_t ds0_addr, ds0_paddr, ds1_addr, ds1_paddr; + + ds0_mem = l4re_util_cap_alloc(); + ds1_mem = l4re_util_cap_alloc(); + + if (l4_is_invalid_cap(ds0_mem) || l4_is_invalid_cap(ds1_mem)) + { + printf("Could not allocate memory capabilities.\n"); + return 1; + } + + if (l4re_ma_alloc_align(ds0_size, ds0_mem, L4RE_MA_CONTINUOUS | L4RE_MA_PINNED, 8) || + l4re_ma_alloc_align(ds1_size, ds1_mem, L4RE_MA_CONTINUOUS | L4RE_MA_PINNED, 8)) + { + printf("Could not allocate memory.\n"); + return 1; + } + + if (l4re_rm_attach((void **) &ds0_addr, ds0_size, + L4RE_RM_SEARCH_ADDR | L4RE_RM_EAGER_MAP, + ds0_mem, 0, L4_PAGESHIFT) || + l4re_rm_attach((void **) &ds1_addr, ds1_size, + L4RE_RM_SEARCH_ADDR | L4RE_RM_EAGER_MAP, + ds1_mem, 0, L4_PAGESHIFT)) + { + printf("Could not map memory.\n"); + return 1; + } + + if (l4re_ds_phys(ds0_mem, 0, &ds0_paddr, &ds0_psize) || + l4re_ds_phys(ds1_mem, 0, &ds1_paddr, &ds1_psize)) + { + printf("Could not get physical addresses for memory.\n"); + return 1; + } + + /* Fill the allocated memory. */ + + memset((void *) ds0_addr, 0, ds0_size); + memset((void *) ds1_addr, 0, ds1_size); + + sprintf((char *) ds0_addr, "The quick brown fox jumped over the lazy dog.\n"); + + /* Interrupts. */ + + l4_uint32_t dma_irq_start = 0, dma_irq_end = 0; + l4_cap_idx_t icucap, irq0cap; + + /* Obtain resource details describing the interrupt for DMA channel 0. */ + + printf("Access IRQ...\n"); + + if (get_irq("jz4730-dma", &dma_irq_start, &dma_irq_end) < 0) + return 1; + + printf("IRQ range at %d...%d.\n", dma_irq_start, dma_irq_end); + + /* Obtain capabilities for the interrupt controller and an interrupt. */ + + irq0cap = l4re_util_cap_alloc(); + icucap = l4re_env_get_cap("icu"); + + if (l4_is_invalid_cap(icucap)) + { + printf("No 'icu' capability available in the virtual bus.\n"); + return 1; + } + + if (l4_is_invalid_cap(irq0cap)) + { + printf("Capabilities not available for interrupts.\n"); + return 1; + } + + /* Create interrupt objects. */ + + long err; + + err = l4_error(l4_factory_create_irq(l4re_global_env->factory, irq0cap)); + + if (err) + { + printf("Could not create IRQ object: %lx\n", err); + return 1; + } + + /* Bind interrupt objects to IRQ numbers. */ + + err = l4_error(l4_icu_bind(icucap, + item_in_range(dma_irq_start, dma_irq_end, 0), + irq0cap)); + + if (err) + { + printf("Could not bind IRQ to the ICU: %ld\n", err); + return 1; + } + + /* Attach ourselves to the interrupt handler. */ + + err = l4_error(l4_rcv_ep_bind_thread(irq0cap, l4re_env()->main_thread, 0)); + + if (err) + { + printf("Could not attach to IRQs: %ld\n", err); + return 1; + } + + /* Peripheral memory. */ + + l4_addr_t cpm_base = 0, cpm_base_end = 0; + l4_addr_t dma_base = 0, dma_base_end = 0; + + /* Obtain resource details describing I/O memory. */ + + printf("Access CPM...\n"); + + if (get_memory("jz4730-cpm", &cpm_base, &cpm_base_end) < 0) + return 1; + + printf("CPM at 0x%lx...0x%lx.\n", cpm_base, cpm_base_end); + + printf("Access DMA...\n"); + + if (get_memory("jz4730-dma", &dma_base, &dma_base_end) < 0) + return 1; + + printf("DMA at 0x%lx...0x%lx.\n", dma_base, dma_base_end); + + /* Obtain CPM and DMA references. */ + + cpm = jz4730_cpm_init(cpm_base); + dma = jz4730_dma_init(dma_base, dma_base_end, cpm); + dma0 = jz4730_dma_get_channel(dma, 0, irq0cap); + + /* Enable DMA. */ + + printf("Enable DMA...\n"); + + jz4730_dma_enable(dma); + + /* Transfer data between the allocated memory regions. */ + + printf("Transfer from %lx to %lx...\n", ds0_paddr, ds1_paddr); + + unsigned int ntransferred = jz4730_dma_transfer(dma0, (uint32_t) ds0_paddr, + (uint32_t) ds1_paddr, + L4_PAGESIZE / 4, + Dma_trans_unit_size_32_bit, + Dma_request_auto); + + printf("Transferred: %d\n", ntransferred); + printf("Source: %s\n", (char *) ds0_addr); + printf("Destination: %s\n", (char *) ds1_addr); + + /* Detach from the interrupt. */ + + err = l4_error(l4_irq_detach(irq0cap)); + + if (err) + printf("Error detaching from IRQ objects: %ld\n", err); + + return 0; +}