1.1 --- a/conf/landfall-examples/mips-letux400-common.io Sat Jan 09 22:38:31 2021 +0100
1.2 +++ b/conf/landfall-examples/mips-letux400-common.io Sun Jan 10 22:21:57 2021 +0100
1.3 @@ -9,11 +9,23 @@
1.4 CPM = wrap(hw:match("jz4730-cpm"));
1.5 })
1.6
1.7 +Io.add_vbus("dma", Io.Vi.System_bus
1.8 +{
1.9 + CPM = wrap(hw:match("jz4730-cpm"));
1.10 + DMA = wrap(hw:match("jz4730-dma"));
1.11 +})
1.12 +
1.13 Io.add_vbus("gpio", Io.Vi.System_bus
1.14 {
1.15 GPIO = wrap(hw:match("jz4730-gpio"));
1.16 })
1.17
1.18 +Io.add_vbus("i2c", Io.Vi.System_bus
1.19 +{
1.20 + CPM = wrap(hw:match("jz4730-cpm"));
1.21 + I2C = wrap(hw:match("jz4730-i2c"));
1.22 +})
1.23 +
1.24 Io.add_vbus("lcd", Io.Vi.System_bus
1.25 {
1.26 LCD = wrap(hw:match("jz4740-lcd"));
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/conf/landfall-examples/mips-letux400-dma.cfg Sun Jan 10 22:21:57 2021 +0100
2.3 @@ -0,0 +1,136 @@
2.4 +-- vim: ft=lua ts=2 et sw=2
2.5 +
2.6 +-- Start Mag to multiplex the framebuffer showing only a single program.
2.7 +-- This example shows a framebuffer terminal showing the hello example's output.
2.8 +-- The target platform is the Letux 400 notebook computer.
2.9 +
2.10 +local L4 = require("L4");
2.11 +
2.12 +local l = L4.default_loader;
2.13 +
2.14 +-- Define general access to peripherals.
2.15 +
2.16 +local io_buses = {
2.17 + cpm = l:new_channel();
2.18 + dma = l:new_channel();
2.19 + gpio = l:new_channel();
2.20 + lcd = l:new_channel();
2.21 + pwm = l:new_channel(); -- exposes GPIO, PWM
2.22 + };
2.23 +
2.24 +l:start({
2.25 + caps = {
2.26 + cpm = io_buses.cpm:svr(),
2.27 + dma = io_buses.dma:svr(),
2.28 + gpio = io_buses.gpio:svr(),
2.29 + lcd = io_buses.lcd:svr(),
2.30 + pwm = io_buses.pwm:svr(),
2.31 +
2.32 + icu = L4.Env.icu,
2.33 + sigma0 = L4.cast(L4.Proto.Factory, L4.Env.sigma0):create(L4.Proto.Sigma0),
2.34 + },
2.35 + },
2.36 + "rom/io rom/hw_devices.io rom/mips-letux400-common.io");
2.37 +
2.38 +-- Expose a PWM peripheral as a device.
2.39 +
2.40 +local pwm = l:new_channel();
2.41 +
2.42 +l:startv({
2.43 + caps = {
2.44 + vbus = io_buses.pwm,
2.45 + pwm = pwm:svr(),
2.46 + },
2.47 + },
2.48 + "rom/dev_pwm_jz4730", "0", "250", "299", "47"); -- specifying peripheral number, parameters
2.49 +
2.50 +-- Expose a PWM backlight device.
2.51 +
2.52 +local backlight = l:new_channel(); -- exposes backlight device
2.53 +
2.54 +l:startv({
2.55 + caps = {
2.56 + pwm = pwm,
2.57 + backlight = backlight:svr(),
2.58 + },
2.59 + },
2.60 + "rom/dev_backlight_pwm", "0", "300"); -- specifying limits
2.61 +
2.62 +-- Expose a display device for the Letux.
2.63 +
2.64 +local display = l:new_channel(); -- exposes display device
2.65 +
2.66 +l:start({
2.67 + caps = {
2.68 + backlight = backlight,
2.69 + display = display:svr(),
2.70 + vbus = io_buses.gpio,
2.71 + },
2.72 + },
2.73 + "rom/dev_display_letux400");
2.74 +
2.75 +-- Expose the CPM peripheral.
2.76 +
2.77 +local cpm = l:new_channel();
2.78 +
2.79 +l:start({
2.80 + caps = {
2.81 + vbus = io_buses.cpm,
2.82 + cpm = cpm:svr(),
2.83 + },
2.84 + },
2.85 + "rom/dev_cpm_jz4730");
2.86 +
2.87 +-- Expose a framebuffer device.
2.88 +
2.89 +local fbdrv_fb = l:new_channel();
2.90 +
2.91 +l:start({
2.92 + caps = {
2.93 + vbus = io_buses.lcd,
2.94 + fb = fbdrv_fb:svr(),
2.95 + cpm = cpm,
2.96 + display = display, -- needed by LCD driver
2.97 + },
2.98 + },
2.99 + "rom/fb-drv");
2.100 +
2.101 +-- Multiplex the framebuffer.
2.102 +
2.103 +local mag_caps = {
2.104 + mag = l:new_channel(),
2.105 + svc = l:new_channel(),
2.106 + };
2.107 +
2.108 +l:start({
2.109 + caps = {
2.110 + vbus = io_buses.gpio, -- needed by input driver
2.111 + fb = fbdrv_fb,
2.112 + mag = mag_caps.mag:svr(),
2.113 + svc = mag_caps.svc:svr(),
2.114 + },
2.115 + },
2.116 + "rom/mag");
2.117 +
2.118 +-- Show the terminal.
2.119 +
2.120 +local term = l:new_channel();
2.121 +
2.122 +l:start({
2.123 + caps = {
2.124 + fb = mag_caps.svc:create(L4.Proto.Goos, "g=800x460+0+0", "barheight=20"),
2.125 + term = term:svr(),
2.126 + },
2.127 + },
2.128 + "rom/fbterminal");
2.129 +
2.130 +-- Show the DMA example.
2.131 +
2.132 +l:start({
2.133 + caps = {
2.134 + icu = L4.Env.icu,
2.135 + vbus = io_buses.dma,
2.136 + },
2.137 + log_cap = term,
2.138 + },
2.139 + "rom/ex_letux400_dma");
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/conf/landfall-examples/mips-letux400-dma.list Sun Jan 10 22:21:57 2021 +0100
3.3 @@ -0,0 +1,47 @@
3.4 +
3.5 +modaddr 0x1100000
3.6 +
3.7 +entry mips-letux400-dma-example
3.8 +bootstrap bootstrap -serial
3.9 +kernel fiasco -serial_esc
3.10 +roottask moe rom/mips-letux400-dma.cfg
3.11 +module mips-letux400-dma.cfg
3.12 +module mips-letux400-common.io
3.13 +module plat-letux400/hw_devices.io
3.14 +module l4re
3.15 +module io
3.16 +module ned
3.17 +module fb-drv
3.18 +module mag
3.19 +module dev_pwm_jz4730
3.20 +module dev_backlight_pwm
3.21 +module dev_display_letux400
3.22 +module dev_cpm_jz4730
3.23 +module fbterminal
3.24 +module ex_letux400_dma
3.25 +module libpanel_letux400.so
3.26 +module mips-jz4740-panel.txt
3.27 +module lib4re-c.so
3.28 +module lib4re-c-util.so
3.29 +module lib4re.so
3.30 +module lib4re-util.so
3.31 +module libc_be_l4refile.so
3.32 +module libc_be_l4re.so
3.33 +module libc_be_socket_noop.so
3.34 +module libcpm.o.so
3.35 +module libc_support_misc.so
3.36 +module libdevice_util.o.so
3.37 +module libdl.so
3.38 +module libgpio.o.so
3.39 +module libio-io.so
3.40 +module libio-vbus.so
3.41 +module libipc.so
3.42 +module libl4sys-direct.so
3.43 +module libl4sys.so
3.44 +module libl4util.so
3.45 +module liblcd_jz4740.o.so
3.46 +module libld-l4.so
3.47 +module libpthread.so
3.48 +module libpwm.o.so
3.49 +module libsupc++.so
3.50 +module libuc_c.so
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/pkg/devices/lib/dma/include/dma-jz4730.h Sun Jan 10 22:21:57 2021 +0100
4.3 @@ -0,0 +1,217 @@
4.4 +/*
4.5 + * DMA support for the JZ4730.
4.6 + *
4.7 + * Copyright (C) 2021 Paul Boddie <paul@boddie.org.uk>
4.8 + *
4.9 + * This program is free software; you can redistribute it and/or
4.10 + * modify it under the terms of the GNU General Public License as
4.11 + * published by the Free Software Foundation; either version 2 of
4.12 + * the License, or (at your option) any later version.
4.13 + *
4.14 + * This program is distributed in the hope that it will be useful,
4.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
4.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4.17 + * GNU General Public License for more details.
4.18 + *
4.19 + * You should have received a copy of the GNU General Public License
4.20 + * along with this program; if not, write to the Free Software
4.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
4.22 + * Boston, MA 02110-1301, USA
4.23 + */
4.24 +
4.25 +#pragma once
4.26 +
4.27 +#include <l4/sys/types.h>
4.28 +#include <stdint.h>
4.29 +
4.30 +
4.31 +
4.32 +/* Enumerated types for various transfer parameters. */
4.33 +
4.34 +enum Dma_jz4730_request_type : unsigned
4.35 +{
4.36 + Dma_request_external = 0,
4.37 + Dma_request_pcmcia_out = 4,
4.38 + Dma_request_pcmcia_in = 5,
4.39 + Dma_request_auto = 8,
4.40 + Dma_request_des_out = 10,
4.41 + Dma_request_des_in = 11,
4.42 + Dma_request_uart3_out = 14,
4.43 + Dma_request_uart3_in = 15,
4.44 + Dma_request_uart2_out = 16,
4.45 + Dma_request_uart2_in = 17,
4.46 + Dma_request_uart1_out = 18,
4.47 + Dma_request_uart1_in = 19,
4.48 + Dma_request_uart0_out = 20,
4.49 + Dma_request_uart0_in = 21,
4.50 + Dma_request_ssi_send_empty = 22,
4.51 + Dma_request_ssi_recv_full = 23,
4.52 + Dma_request_aic_send_empty = 24,
4.53 + Dma_request_aic_recv_full = 25,
4.54 + Dma_request_msc_send_empty = 26,
4.55 + Dma_request_msc_recv_full = 27,
4.56 + Dma_request_ost2_underflow = 28,
4.57 +};
4.58 +
4.59 +enum Dma_jz4730_ext_level : unsigned
4.60 +{
4.61 + Dma_ext_active_high = 0,
4.62 + Dma_ext_active_low = 1,
4.63 +};
4.64 +
4.65 +enum Dma_jz4730_ext_output_mode_cycle : unsigned
4.66 +{
4.67 + Dma_ext_output_mode_read_cycle = 0,
4.68 + Dma_ext_output_mode_write_cycle = 1,
4.69 +};
4.70 +
4.71 +enum Dma_jz4730_ext_req_detect_mode : unsigned
4.72 +{
4.73 + Dma_ext_req_detect_mode_low_level = 0,
4.74 + Dma_ext_req_detect_mode_falling_edge = 1,
4.75 + Dma_ext_req_detect_mode_high_level = 2,
4.76 + Dma_ext_req_detect_mode_rising_edge = 3,
4.77 +};
4.78 +
4.79 +enum Dma_jz4730_trans_unit_size : unsigned
4.80 +{
4.81 + Dma_trans_unit_size_32_bit = 0,
4.82 + Dma_trans_unit_size_8_bit = 1,
4.83 + Dma_trans_unit_size_16_bit = 2,
4.84 + Dma_trans_unit_size_16_byte = 3,
4.85 + Dma_trans_unit_size_32_byte = 4,
4.86 +};
4.87 +
4.88 +
4.89 +
4.90 +#ifdef __cplusplus
4.91 +
4.92 +#include <l4/devices/cpm-jz4730.h>
4.93 +#include <l4/devices/hw_mmio_register_block.h>
4.94 +
4.95 +// Forward declaration.
4.96 +
4.97 +class Dma_jz4730_chip;
4.98 +
4.99 +
4.100 +
4.101 +// DMA channel.
4.102 +
4.103 +class Dma_jz4730_channel
4.104 +{
4.105 +private:
4.106 + Hw::Register_block<32> _regs;
4.107 + Dma_jz4730_chip *_chip;
4.108 + uint8_t _channel;
4.109 + l4_cap_idx_t _irq=L4_INVALID_CAP;
4.110 +
4.111 + // External transfer properties with defaults.
4.112 +
4.113 + enum Dma_jz4730_ext_level _ext_output_polarity = Dma_ext_active_high;
4.114 + enum Dma_jz4730_ext_level _ext_end_of_process_mode = Dma_ext_active_high;
4.115 + enum Dma_jz4730_ext_output_mode_cycle _ext_output_mode_cycle = Dma_ext_output_mode_read_cycle;
4.116 + enum Dma_jz4730_ext_req_detect_mode _ext_req_detect_mode = Dma_ext_req_detect_mode_high_level;
4.117 +
4.118 +public:
4.119 + Dma_jz4730_channel(Dma_jz4730_chip *chip, uint8_t channel, l4_addr_t start, l4_cap_idx_t irq);
4.120 +
4.121 + unsigned int transfer(uint32_t source, uint32_t destination,
4.122 + unsigned int count,
4.123 + enum Dma_jz4730_trans_unit_size size,
4.124 + enum Dma_jz4730_request_type type=Dma_request_auto);
4.125 +
4.126 + // External transfer property configuration.
4.127 +
4.128 + void set_output_polarity(enum Dma_jz4730_ext_level polarity)
4.129 + { _ext_output_polarity = polarity; }
4.130 +
4.131 + void set_end_of_process_mode(enum Dma_jz4730_ext_level mode)
4.132 + { _ext_end_of_process_mode = mode; }
4.133 +
4.134 + void set_output_mode_cycle(enum Dma_jz4730_ext_output_mode_cycle cycle)
4.135 + { _ext_output_mode_cycle = cycle; }
4.136 +
4.137 + void set_req_detect_mode(enum Dma_jz4730_ext_req_detect_mode mode)
4.138 + { _ext_req_detect_mode = mode; }
4.139 +
4.140 +protected:
4.141 + // Transfer property configuration.
4.142 +
4.143 + uint32_t encode_external_transfer(enum Dma_jz4730_request_type type);
4.144 +
4.145 + uint32_t encode_source_address_increment(enum Dma_jz4730_request_type type);
4.146 +
4.147 + uint32_t encode_destination_address_increment(enum Dma_jz4730_request_type type);
4.148 +
4.149 + uint32_t encode_req_detect_int_length(uint8_t units);
4.150 +
4.151 + uint32_t encode_source_port_width(enum Dma_jz4730_request_type type);
4.152 +
4.153 + uint32_t encode_destination_port_width(enum Dma_jz4730_request_type type);
4.154 +
4.155 + // Transaction control.
4.156 +
4.157 + void ack_irq();
4.158 +
4.159 + bool completed();
4.160 +
4.161 + bool error();
4.162 +
4.163 + bool halted();
4.164 +
4.165 + bool wait_for_irq();
4.166 +
4.167 + bool wait_for_irq(unsigned int timeout);
4.168 +};
4.169 +
4.170 +// DMA device control.
4.171 +
4.172 +class Dma_jz4730_chip
4.173 +{
4.174 +private:
4.175 + Hw::Register_block<32> _regs;
4.176 + l4_addr_t _start, _end;
4.177 + Cpm_jz4730_chip *_cpm;
4.178 +
4.179 +public:
4.180 + Dma_jz4730_chip(l4_addr_t start, l4_addr_t end, Cpm_jz4730_chip *cpm);
4.181 +
4.182 + void disable();
4.183 +
4.184 + void enable();
4.185 +
4.186 + Dma_jz4730_channel *get_channel(uint8_t channel, l4_cap_idx_t irq);
4.187 +
4.188 + bool have_interrupt(uint8_t channel);
4.189 +};
4.190 +
4.191 +#endif /* __cplusplus */
4.192 +
4.193 +
4.194 +
4.195 +/* C language interface. */
4.196 +
4.197 +EXTERN_C_BEGIN
4.198 +
4.199 +void *jz4730_dma_init(l4_addr_t start, l4_addr_t end, void *cpm);
4.200 +
4.201 +void jz4730_dma_disable(void *dma_chip);
4.202 +
4.203 +void jz4730_dma_enable(void *dma_chip);
4.204 +
4.205 +void *jz4730_dma_get_channel(void *dma, uint8_t channel, l4_cap_idx_t irq);
4.206 +
4.207 +void jz4730_dma_set_output_polarity(void *dma_channel, enum Dma_jz4730_ext_level polarity);
4.208 +
4.209 +void jz4730_dma_set_end_of_process_mode(void *dma_channel, enum Dma_jz4730_ext_level mode);
4.210 +
4.211 +void jz4730_dma_set_output_mode_cycle(void *dma_channel, enum Dma_jz4730_ext_output_mode_cycle cycle);
4.212 +
4.213 +void jz4730_dma_set_req_detect_mode(void *dma_channel, enum Dma_jz4730_ext_req_detect_mode mode);
4.214 +
4.215 +unsigned int jz4730_dma_transfer(void *dma_channel, uint32_t source,
4.216 + uint32_t destination, unsigned int count,
4.217 + enum Dma_jz4730_trans_unit_size size,
4.218 + enum Dma_jz4730_request_type type);
4.219 +
4.220 +EXTERN_C_END
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/pkg/devices/lib/dma/src/jz4730.cc Sun Jan 10 22:21:57 2021 +0100
5.3 @@ -0,0 +1,496 @@
5.4 +/*
5.5 + * DMA support for the JZ4730.
5.6 + *
5.7 + * Copyright (C) 2021 Paul Boddie <paul@boddie.org.uk>
5.8 + *
5.9 + * This program is free software; you can redistribute it and/or
5.10 + * modify it under the terms of the GNU General Public License as
5.11 + * published by the Free Software Foundation; either version 2 of
5.12 + * the License, or (at your option) any later version.
5.13 + *
5.14 + * This program is distributed in the hope that it will be useful,
5.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
5.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
5.17 + * GNU General Public License for more details.
5.18 + *
5.19 + * You should have received a copy of the GNU General Public License
5.20 + * along with this program; if not, write to the Free Software
5.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
5.22 + * Boston, MA 02110-1301, USA
5.23 + */
5.24 +
5.25 +#include <l4/devices/dma-jz4730.h>
5.26 +#include <l4/devices/hw_mmio_register_block.h>
5.27 +
5.28 +#include <l4/sys/icu.h>
5.29 +#include <l4/sys/ipc.h>
5.30 +#include <l4/sys/irq.h>
5.31 +#include <l4/util/util.h>
5.32 +
5.33 +#include <cstdio>
5.34 +
5.35 +enum Global_regs
5.36 +{
5.37 + Dma_irq_pending = 0xf8, // IRQP
5.38 + Dma_control = 0xfc, // DMAC
5.39 +};
5.40 +
5.41 +enum Channel_regs
5.42 +{
5.43 + Dma_source = 0x00, // DSA
5.44 + Dma_destination = 0x04, // DDA
5.45 + Dma_transfer_count = 0x08, // DTC
5.46 + Dma_request_source = 0x0c, // DRT
5.47 + Dma_control_status = 0x10, // DCS
5.48 +};
5.49 +
5.50 +enum Dma_irq_pending_shifts : unsigned
5.51 +{
5.52 + Dma_irq_pending_ch0 = 15,
5.53 + Dma_irq_pending_ch1 = 14,
5.54 + Dma_irq_pending_ch2 = 13,
5.55 + Dma_irq_pending_ch3 = 12,
5.56 + Dma_irq_pending_ch4 = 11,
5.57 + Dma_irq_pending_ch5 = 10,
5.58 +};
5.59 +
5.60 +enum Dma_control_bits : unsigned
5.61 +{
5.62 + Dma_control_priority_mode = 0x100, // PM
5.63 + Dma_control_halt_occurred = 0x008, // HLT
5.64 + Dma_control_address_error = 0x004, // AR
5.65 + Dma_control_enable = 0x001, // DMAE
5.66 +};
5.67 +
5.68 +enum Dma_control_priority_modes : unsigned
5.69 +{
5.70 + Dma_priority_mode_01234567 = 0,
5.71 + Dma_priority_mode_02314675 = 1,
5.72 + Dma_priority_mode_20136457 = 2,
5.73 + Dma_priority_mode_round_robin = 3,
5.74 +};
5.75 +
5.76 +enum Dma_transfer_count_bits : unsigned
5.77 +{
5.78 + Dma_transfer_count_mask = 0x00ffffff,
5.79 +};
5.80 +
5.81 +enum Dma_request_source_bits : unsigned
5.82 +{
5.83 + Dma_request_type_mask = 0x0000001f,
5.84 +};
5.85 +
5.86 +enum Dma_control_status_shifts : unsigned
5.87 +{
5.88 + Dma_ext_output_polarity = 31,
5.89 + Dma_ext_output_mode_cycle = 30,
5.90 + Dma_ext_req_detect_mode = 28,
5.91 + Dma_ext_end_of_process_mode = 27,
5.92 + Dma_req_detect_int_length = 16,
5.93 + Dma_source_port_width = 14,
5.94 + Dma_dest_port_width = 12,
5.95 + Dma_trans_unit_size = 8,
5.96 + Dma_trans_mode = 7,
5.97 +};
5.98 +
5.99 +enum Dma_control_status_bits : unsigned
5.100 +{
5.101 + Dma_source_address_incr = 0x00800000,
5.102 + Dma_dest_address_incr = 0x00400000,
5.103 + Dma_address_error = 0x00000010,
5.104 + Dma_trans_completed = 0x00000008,
5.105 + Dma_trans_halted = 0x00000004,
5.106 + Dma_channel_irq_enable = 0x00000002,
5.107 + Dma_channel_enable = 0x00000001,
5.108 +};
5.109 +
5.110 +enum Dma_port_width_values : unsigned
5.111 +{
5.112 + Dma_port_width_32_bit = 0,
5.113 + Dma_port_width_8_bit = 1,
5.114 + Dma_port_width_16_bit = 2,
5.115 +};
5.116 +
5.117 +enum Dma_trans_mode_values : unsigned
5.118 +{
5.119 + Dma_trans_mode_single = 0,
5.120 + Dma_trans_mode_block = 1,
5.121 +};
5.122 +
5.123 +
5.124 +
5.125 +// Initialise a channel.
5.126 +
5.127 +Dma_jz4730_channel::Dma_jz4730_channel(Dma_jz4730_chip *chip, uint8_t channel,
5.128 + l4_addr_t start, l4_cap_idx_t irq)
5.129 +: _chip(chip), _channel(channel), _irq(irq)
5.130 +{
5.131 + _regs = new Hw::Mmio_register_block<32>(start);
5.132 +}
5.133 +
5.134 +// Encode flags for an external transfer.
5.135 +
5.136 +uint32_t
5.137 +Dma_jz4730_channel::encode_external_transfer(enum Dma_jz4730_request_type type)
5.138 +{
5.139 + int external = (type == Dma_request_external) ? 1 : 0;
5.140 +
5.141 + return
5.142 + ((external ? (int) _ext_output_polarity : 0) << Dma_ext_output_polarity) |
5.143 + ((external ? (int) _ext_output_mode_cycle : 0) << Dma_ext_output_mode_cycle) |
5.144 + ((external ? (int) _ext_req_detect_mode : 0) << Dma_ext_req_detect_mode) |
5.145 + ((external ? (int) _ext_end_of_process_mode : 0) << Dma_ext_end_of_process_mode);
5.146 +}
5.147 +
5.148 +// Encode the appropriate source address increment for the given request type.
5.149 +// Here, memory-to-memory transfers and transfers to peripherals involve an
5.150 +// incrementing source address. Transfers from peripherals involve a static
5.151 +// source address.
5.152 +
5.153 +uint32_t
5.154 +Dma_jz4730_channel::encode_source_address_increment(enum Dma_jz4730_request_type type)
5.155 +{
5.156 + switch (type)
5.157 + {
5.158 + case Dma_request_auto:
5.159 + case Dma_request_pcmcia_out:
5.160 + case Dma_request_des_out:
5.161 + case Dma_request_uart3_out:
5.162 + case Dma_request_uart2_out:
5.163 + case Dma_request_uart1_out:
5.164 + case Dma_request_uart0_out:
5.165 + case Dma_request_ssi_send_empty:
5.166 + case Dma_request_aic_send_empty:
5.167 + case Dma_request_msc_send_empty:
5.168 + case Dma_request_ost2_underflow:
5.169 + return Dma_source_address_incr;
5.170 +
5.171 + default:
5.172 + return 0;
5.173 + }
5.174 +}
5.175 +
5.176 +// Encode the appropriate destination address increment for the given request
5.177 +// type. Here, memory-to-memory transfers and transfers from peripherals involve
5.178 +// an incrementing destination address. Transfers to peripherals involve a static
5.179 +// destination address.
5.180 +
5.181 +uint32_t
5.182 +Dma_jz4730_channel::encode_destination_address_increment(enum Dma_jz4730_request_type type)
5.183 +{
5.184 + switch (type)
5.185 + {
5.186 + case Dma_request_auto:
5.187 + case Dma_request_pcmcia_in:
5.188 + case Dma_request_des_in:
5.189 + case Dma_request_uart3_in:
5.190 + case Dma_request_uart2_in:
5.191 + case Dma_request_uart1_in:
5.192 + case Dma_request_uart0_in:
5.193 + case Dma_request_ssi_recv_full:
5.194 + case Dma_request_aic_recv_full:
5.195 + case Dma_request_msc_recv_full:
5.196 + case Dma_request_ost2_underflow:
5.197 + return Dma_dest_address_incr;
5.198 +
5.199 + default:
5.200 + return 0;
5.201 + }
5.202 +}
5.203 +
5.204 +// Return the closest interval length greater than or equal to the number of
5.205 +// units given encoded in the request detection interval length field of the
5.206 +// control/status register.
5.207 +
5.208 +uint32_t
5.209 +Dma_jz4730_channel::encode_req_detect_int_length(uint8_t units)
5.210 +{
5.211 + static uint8_t lengths[] = {0, 2, 4, 8, 12, 16, 20, 24, 28, 32, 48, 60, 64, 124, 128, 200};
5.212 + int i;
5.213 +
5.214 + if (!units)
5.215 + return 0;
5.216 +
5.217 + for (i = 0; i < 15; i++)
5.218 + {
5.219 + if (lengths[i++] >= units)
5.220 + break;
5.221 + }
5.222 +
5.223 + return lengths[i] << Dma_req_detect_int_length;
5.224 +}
5.225 +
5.226 +// Encode the appropriate source port width for the given request type.
5.227 +
5.228 +uint32_t
5.229 +Dma_jz4730_channel::encode_source_port_width(enum Dma_jz4730_request_type type)
5.230 +{
5.231 + switch (type)
5.232 + {
5.233 + case Dma_request_uart3_in:
5.234 + case Dma_request_uart2_in:
5.235 + case Dma_request_uart1_in:
5.236 + case Dma_request_uart0_in:
5.237 + return Dma_port_width_8_bit << Dma_source_port_width;
5.238 +
5.239 + default:
5.240 + return Dma_port_width_32_bit << Dma_source_port_width;
5.241 + }
5.242 +}
5.243 +
5.244 +// Encode the appropriate destination port width for the given request type.
5.245 +
5.246 +uint32_t
5.247 +Dma_jz4730_channel::encode_destination_port_width(enum Dma_jz4730_request_type type)
5.248 +{
5.249 + switch (type)
5.250 + {
5.251 + case Dma_request_uart3_out:
5.252 + case Dma_request_uart2_out:
5.253 + case Dma_request_uart1_out:
5.254 + case Dma_request_uart0_out:
5.255 + return Dma_port_width_8_bit << Dma_dest_port_width;
5.256 +
5.257 + default:
5.258 + return Dma_port_width_32_bit << Dma_dest_port_width;
5.259 + }
5.260 +}
5.261 +
5.262 +// Transfer data between memory locations.
5.263 +
5.264 +unsigned int
5.265 +Dma_jz4730_channel::transfer(uint32_t source, uint32_t destination,
5.266 + unsigned int count,
5.267 + enum Dma_jz4730_trans_unit_size size,
5.268 + enum Dma_jz4730_request_type type)
5.269 +{
5.270 + // Ensure an absence of address error and halt conditions globally and in this channel.
5.271 +
5.272 + if (error() || halted())
5.273 + return 0;
5.274 +
5.275 + // Ensure an absence of transaction completed and zero transfer count for this channel.
5.276 +
5.277 + if (completed() || _regs[Dma_transfer_count])
5.278 + return 0;
5.279 +
5.280 + // Disable the channel.
5.281 +
5.282 + _regs[Dma_control_status] = _regs[Dma_control_status] & ~Dma_channel_enable;
5.283 +
5.284 + // Set addresses.
5.285 +
5.286 + _regs[Dma_source] = source;
5.287 + _regs[Dma_destination] = destination;
5.288 +
5.289 + // Set transfer count to the number of units.
5.290 +
5.291 + _regs[Dma_transfer_count] = count;
5.292 +
5.293 + // Set auto-request for memory-to-memory transfers. Otherwise, set the
5.294 + // indicated request type.
5.295 +
5.296 + _regs[Dma_request_source] = type;
5.297 +
5.298 + // Set control/status fields.
5.299 + // Enable the channel (and peripheral).
5.300 +
5.301 + /* NOTE: To be considered...
5.302 + * request detection interval length (currently left as 0)
5.303 + * increments and port widths for external transfers
5.304 + * port width overriding (for AIC...)
5.305 + * transfer mode (currently left as single)
5.306 + */
5.307 +
5.308 + _regs[Dma_control_status] = encode_external_transfer(type) |
5.309 + encode_source_address_increment(type) |
5.310 + encode_destination_address_increment(type) |
5.311 + encode_source_port_width(type) |
5.312 + encode_destination_port_width(type) |
5.313 + (size << Dma_trans_unit_size) |
5.314 + (Dma_trans_mode_single << Dma_trans_mode) |
5.315 + Dma_channel_irq_enable |
5.316 + Dma_channel_enable;
5.317 +
5.318 + // An interrupt will occur upon completion, the completion flag will be set
5.319 + // and the transfer count will be zero.
5.320 +
5.321 + unsigned int remaining = count;
5.322 +
5.323 + do
5.324 + {
5.325 + if (!wait_for_irq(1000000))
5.326 + printf("status = %x\n", (uint32_t) _regs[Dma_control_status]);
5.327 +
5.328 + // Clearing the completion flag will clear the interrupt condition.
5.329 + // Any remaining units must be read before clearing the condition.
5.330 +
5.331 + else
5.332 + {
5.333 + remaining = _regs[Dma_transfer_count];
5.334 + ack_irq();
5.335 + break;
5.336 + }
5.337 + }
5.338 + while (!error() && !halted() && !completed());
5.339 +
5.340 + // Return the number of transferred units.
5.341 +
5.342 + return count - remaining;
5.343 +}
5.344 +
5.345 +// Wait indefinitely for an interrupt request, returning true if one was delivered.
5.346 +
5.347 +bool
5.348 +Dma_jz4730_channel::wait_for_irq()
5.349 +{
5.350 + return !l4_error(l4_irq_receive(_irq, L4_IPC_NEVER)) && _chip->have_interrupt(_channel);
5.351 +}
5.352 +
5.353 +// Wait up to the given timeout (in microseconds) for an interrupt request,
5.354 +// returning true if one was delivered.
5.355 +
5.356 +bool
5.357 +Dma_jz4730_channel::wait_for_irq(unsigned int timeout)
5.358 +{
5.359 + return !l4_error(l4_irq_receive(_irq, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4util_micros2l4to(timeout)))) && _chip->have_interrupt(_channel);
5.360 +}
5.361 +
5.362 +// Acknowledge an interrupt condition.
5.363 +
5.364 +void
5.365 +Dma_jz4730_channel::ack_irq()
5.366 +{
5.367 + _regs[Dma_control_status] = _regs[Dma_control_status] & ~Dma_trans_completed;
5.368 +}
5.369 +
5.370 +// Return whether a transfer has completed.
5.371 +
5.372 +bool
5.373 +Dma_jz4730_channel::completed()
5.374 +{
5.375 + return _regs[Dma_control_status] & Dma_trans_completed ? true : false;
5.376 +}
5.377 +
5.378 +// Return whether an address error condition has arisen.
5.379 +
5.380 +bool
5.381 +Dma_jz4730_channel::error()
5.382 +{
5.383 + return _regs[Dma_control_status] & Dma_address_error ? true : false;
5.384 +}
5.385 +
5.386 +// Return whether a transfer has halted.
5.387 +
5.388 +bool
5.389 +Dma_jz4730_channel::halted()
5.390 +{
5.391 + return _regs[Dma_control_status] & Dma_trans_halted ? true : false;
5.392 +}
5.393 +
5.394 +
5.395 +
5.396 +// Initialise the I2C controller.
5.397 +
5.398 +Dma_jz4730_chip::Dma_jz4730_chip(l4_addr_t start, l4_addr_t end,
5.399 + Cpm_jz4730_chip *cpm)
5.400 +: _start(start), _end(end), _cpm(cpm)
5.401 +{
5.402 + _regs = new Hw::Mmio_register_block<32>(start);
5.403 +}
5.404 +
5.405 +// Enable the peripheral.
5.406 +
5.407 +void
5.408 +Dma_jz4730_chip::enable()
5.409 +{
5.410 + // Make sure that the DMA clock is available.
5.411 +
5.412 + _cpm->start_dma();
5.413 +
5.414 + // Enable the channel.
5.415 + // NOTE: No configuration is done for channel priority mode.
5.416 +
5.417 + _regs[Dma_control] = Dma_control_enable;
5.418 + while (!(_regs[Dma_control] & Dma_control_enable));
5.419 +}
5.420 +
5.421 +// Disable the channel.
5.422 +
5.423 +void
5.424 +Dma_jz4730_chip::disable()
5.425 +{
5.426 + _regs[Dma_control] = 0;
5.427 + while (_regs[Dma_control] & Dma_control_enable);
5.428 +}
5.429 +
5.430 +// Obtain a channel object. Only one channel is supported.
5.431 +
5.432 +Dma_jz4730_channel *
5.433 +Dma_jz4730_chip::get_channel(uint8_t channel, l4_cap_idx_t irq)
5.434 +{
5.435 + if (channel < 6)
5.436 + return new Dma_jz4730_channel(this, channel, _start + 0x20 * channel, irq);
5.437 + else
5.438 + throw -L4_EINVAL;
5.439 +}
5.440 +
5.441 +// Return whether an interrupt is pending on the given channel.
5.442 +
5.443 +bool
5.444 +Dma_jz4730_chip::have_interrupt(uint8_t channel)
5.445 +{
5.446 + return _regs[Dma_irq_pending] & (1 << (Dma_irq_pending_ch0 - channel)) ? true : false;
5.447 +}
5.448 +
5.449 +
5.450 +
5.451 +// C language interface functions.
5.452 +
5.453 +void *jz4730_dma_init(l4_addr_t start, l4_addr_t end, void *cpm)
5.454 +{
5.455 + return (void *) new Dma_jz4730_chip(start, end, static_cast<Cpm_jz4730_chip *>(cpm));
5.456 +}
5.457 +
5.458 +void jz4730_dma_disable(void *dma_chip)
5.459 +{
5.460 + static_cast<Dma_jz4730_chip *>(dma_chip)->disable();
5.461 +}
5.462 +
5.463 +void jz4730_dma_enable(void *dma_chip)
5.464 +{
5.465 + static_cast<Dma_jz4730_chip *>(dma_chip)->enable();
5.466 +}
5.467 +
5.468 +void *jz4730_dma_get_channel(void *dma, uint8_t channel, l4_cap_idx_t irq)
5.469 +{
5.470 + return static_cast<Dma_jz4730_chip *>(dma)->get_channel(channel, irq);
5.471 +}
5.472 +
5.473 +void jz4730_dma_set_output_polarity(void *dma_channel, enum Dma_jz4730_ext_level polarity)
5.474 +{
5.475 + static_cast<Dma_jz4730_channel *>(dma_channel)->set_output_polarity(polarity);
5.476 +}
5.477 +
5.478 +void jz4730_dma_set_end_of_process_mode(void *dma_channel, enum Dma_jz4730_ext_level mode)
5.479 +{
5.480 + static_cast<Dma_jz4730_channel *>(dma_channel)->set_end_of_process_mode(mode);
5.481 +}
5.482 +
5.483 +void jz4730_dma_set_output_mode_cycle(void *dma_channel, enum Dma_jz4730_ext_output_mode_cycle cycle)
5.484 +{
5.485 + static_cast<Dma_jz4730_channel *>(dma_channel)->set_output_mode_cycle(cycle);
5.486 +}
5.487 +
5.488 +void jz4730_dma_set_req_detect_mode(void *dma_channel, enum Dma_jz4730_ext_req_detect_mode mode)
5.489 +{
5.490 + static_cast<Dma_jz4730_channel *>(dma_channel)->set_req_detect_mode(mode);
5.491 +}
5.492 +
5.493 +unsigned int jz4730_dma_transfer(void *dma_channel, uint32_t source,
5.494 + uint32_t destination, unsigned int count,
5.495 + enum Dma_jz4730_trans_unit_size size,
5.496 + enum Dma_jz4730_request_type type)
5.497 +{
5.498 + return static_cast<Dma_jz4730_channel *>(dma_channel)->transfer(source, destination, count, size, type);
5.499 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/pkg/landfall-examples/letux400_dma/Makefile Sun Jan 10 22:21:57 2021 +0100
6.3 @@ -0,0 +1,10 @@
6.4 +PKGDIR ?= ..
6.5 +L4DIR ?= $(PKGDIR)/../..
6.6 +
6.7 +TARGET = ex_letux400_dma
6.8 +
6.9 +SRC_CC = letux400_dma.cc
6.10 +
6.11 +REQUIRES_LIBS = l4re_c-util libdrivers-cpm libdrivers-dma libdevice-util
6.12 +
6.13 +include $(L4DIR)/mk/prog.mk
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/pkg/landfall-examples/letux400_dma/letux400_dma.cc Sun Jan 10 22:21:57 2021 +0100
7.3 @@ -0,0 +1,226 @@
7.4 +/*
7.5 + * Test DMA transfers.
7.6 + *
7.7 + * Copyright (C) 2018, 2020, 2021 Paul Boddie <paul@boddie.org.uk>
7.8 + *
7.9 + * This program is free software; you can redistribute it and/or
7.10 + * modify it under the terms of the GNU General Public License as
7.11 + * published by the Free Software Foundation; either version 2 of
7.12 + * the License, or (at your option) any later version.
7.13 + *
7.14 + * This program is distributed in the hope that it will be useful,
7.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
7.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7.17 + * GNU General Public License for more details.
7.18 + *
7.19 + * You should have received a copy of the GNU General Public License
7.20 + * along with this program; if not, write to the Free Software
7.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
7.22 + * Boston, MA 02110-1301, USA
7.23 + */
7.24 +
7.25 +#include <l4/devices/cpm-jz4730.h>
7.26 +#include <l4/devices/dma-jz4730.h>
7.27 +#include <l4/devices/memory.h>
7.28 +
7.29 +#include <l4/re/c/util/cap_alloc.h>
7.30 +#include <l4/re/c/dataspace.h>
7.31 +#include <l4/re/c/mem_alloc.h>
7.32 +#include <l4/re/c/rm.h>
7.33 +
7.34 +#include <l4/sys/factory.h>
7.35 +#include <l4/sys/icu.h>
7.36 +#include <l4/sys/irq.h>
7.37 +#include <l4/sys/rcv_endpoint.h>
7.38 +
7.39 +#include <stdio.h>
7.40 +#include <string.h>
7.41 +#include <unistd.h>
7.42 +
7.43 +
7.44 +
7.45 +/* Device and resource discovery. */
7.46 +
7.47 +static long item_in_range(long start, long end, long index)
7.48 +{
7.49 + if (start < end)
7.50 + return start + index;
7.51 + else
7.52 + return start - index;
7.53 +}
7.54 +
7.55 +
7.56 +
7.57 +int main(void)
7.58 +{
7.59 + void *cpm;
7.60 + void *dma, *dma0;
7.61 +
7.62 + /* Allocate memory to test transfers. */
7.63 +
7.64 + l4_cap_idx_t ds0_mem, ds1_mem;
7.65 + l4_size_t ds0_size = L4_PAGESIZE, ds0_psize, ds1_size = L4_PAGESIZE, ds1_psize;
7.66 + l4_addr_t ds0_addr, ds0_paddr, ds1_addr, ds1_paddr;
7.67 +
7.68 + ds0_mem = l4re_util_cap_alloc();
7.69 + ds1_mem = l4re_util_cap_alloc();
7.70 +
7.71 + if (l4_is_invalid_cap(ds0_mem) || l4_is_invalid_cap(ds1_mem))
7.72 + {
7.73 + printf("Could not allocate memory capabilities.\n");
7.74 + return 1;
7.75 + }
7.76 +
7.77 + if (l4re_ma_alloc_align(ds0_size, ds0_mem, L4RE_MA_CONTINUOUS | L4RE_MA_PINNED, 8) ||
7.78 + l4re_ma_alloc_align(ds1_size, ds1_mem, L4RE_MA_CONTINUOUS | L4RE_MA_PINNED, 8))
7.79 + {
7.80 + printf("Could not allocate memory.\n");
7.81 + return 1;
7.82 + }
7.83 +
7.84 + if (l4re_rm_attach((void **) &ds0_addr, ds0_size,
7.85 + L4RE_RM_SEARCH_ADDR | L4RE_RM_EAGER_MAP,
7.86 + ds0_mem, 0, L4_PAGESHIFT) ||
7.87 + l4re_rm_attach((void **) &ds1_addr, ds1_size,
7.88 + L4RE_RM_SEARCH_ADDR | L4RE_RM_EAGER_MAP,
7.89 + ds1_mem, 0, L4_PAGESHIFT))
7.90 + {
7.91 + printf("Could not map memory.\n");
7.92 + return 1;
7.93 + }
7.94 +
7.95 + if (l4re_ds_phys(ds0_mem, 0, &ds0_paddr, &ds0_psize) ||
7.96 + l4re_ds_phys(ds1_mem, 0, &ds1_paddr, &ds1_psize))
7.97 + {
7.98 + printf("Could not get physical addresses for memory.\n");
7.99 + return 1;
7.100 + }
7.101 +
7.102 + /* Fill the allocated memory. */
7.103 +
7.104 + memset((void *) ds0_addr, 0, ds0_size);
7.105 + memset((void *) ds1_addr, 0, ds1_size);
7.106 +
7.107 + sprintf((char *) ds0_addr, "The quick brown fox jumped over the lazy dog.\n");
7.108 +
7.109 + /* Interrupts. */
7.110 +
7.111 + l4_uint32_t dma_irq_start = 0, dma_irq_end = 0;
7.112 + l4_cap_idx_t icucap, irq0cap;
7.113 +
7.114 + /* Obtain resource details describing the interrupt for DMA channel 0. */
7.115 +
7.116 + printf("Access IRQ...\n");
7.117 +
7.118 + if (get_irq("jz4730-dma", &dma_irq_start, &dma_irq_end) < 0)
7.119 + return 1;
7.120 +
7.121 + printf("IRQ range at %d...%d.\n", dma_irq_start, dma_irq_end);
7.122 +
7.123 + /* Obtain capabilities for the interrupt controller and an interrupt. */
7.124 +
7.125 + irq0cap = l4re_util_cap_alloc();
7.126 + icucap = l4re_env_get_cap("icu");
7.127 +
7.128 + if (l4_is_invalid_cap(icucap))
7.129 + {
7.130 + printf("No 'icu' capability available in the virtual bus.\n");
7.131 + return 1;
7.132 + }
7.133 +
7.134 + if (l4_is_invalid_cap(irq0cap))
7.135 + {
7.136 + printf("Capabilities not available for interrupts.\n");
7.137 + return 1;
7.138 + }
7.139 +
7.140 + /* Create interrupt objects. */
7.141 +
7.142 + long err;
7.143 +
7.144 + err = l4_error(l4_factory_create_irq(l4re_global_env->factory, irq0cap));
7.145 +
7.146 + if (err)
7.147 + {
7.148 + printf("Could not create IRQ object: %lx\n", err);
7.149 + return 1;
7.150 + }
7.151 +
7.152 + /* Bind interrupt objects to IRQ numbers. */
7.153 +
7.154 + err = l4_error(l4_icu_bind(icucap,
7.155 + item_in_range(dma_irq_start, dma_irq_end, 0),
7.156 + irq0cap));
7.157 +
7.158 + if (err)
7.159 + {
7.160 + printf("Could not bind IRQ to the ICU: %ld\n", err);
7.161 + return 1;
7.162 + }
7.163 +
7.164 + /* Attach ourselves to the interrupt handler. */
7.165 +
7.166 + err = l4_error(l4_rcv_ep_bind_thread(irq0cap, l4re_env()->main_thread, 0));
7.167 +
7.168 + if (err)
7.169 + {
7.170 + printf("Could not attach to IRQs: %ld\n", err);
7.171 + return 1;
7.172 + }
7.173 +
7.174 + /* Peripheral memory. */
7.175 +
7.176 + l4_addr_t cpm_base = 0, cpm_base_end = 0;
7.177 + l4_addr_t dma_base = 0, dma_base_end = 0;
7.178 +
7.179 + /* Obtain resource details describing I/O memory. */
7.180 +
7.181 + printf("Access CPM...\n");
7.182 +
7.183 + if (get_memory("jz4730-cpm", &cpm_base, &cpm_base_end) < 0)
7.184 + return 1;
7.185 +
7.186 + printf("CPM at 0x%lx...0x%lx.\n", cpm_base, cpm_base_end);
7.187 +
7.188 + printf("Access DMA...\n");
7.189 +
7.190 + if (get_memory("jz4730-dma", &dma_base, &dma_base_end) < 0)
7.191 + return 1;
7.192 +
7.193 + printf("DMA at 0x%lx...0x%lx.\n", dma_base, dma_base_end);
7.194 +
7.195 + /* Obtain CPM and DMA references. */
7.196 +
7.197 + cpm = jz4730_cpm_init(cpm_base);
7.198 + dma = jz4730_dma_init(dma_base, dma_base_end, cpm);
7.199 + dma0 = jz4730_dma_get_channel(dma, 0, irq0cap);
7.200 +
7.201 + /* Enable DMA. */
7.202 +
7.203 + printf("Enable DMA...\n");
7.204 +
7.205 + jz4730_dma_enable(dma);
7.206 +
7.207 + /* Transfer data between the allocated memory regions. */
7.208 +
7.209 + printf("Transfer from %lx to %lx...\n", ds0_paddr, ds1_paddr);
7.210 +
7.211 + unsigned int ntransferred = jz4730_dma_transfer(dma0, (uint32_t) ds0_paddr,
7.212 + (uint32_t) ds1_paddr,
7.213 + L4_PAGESIZE / 4,
7.214 + Dma_trans_unit_size_32_bit,
7.215 + Dma_request_auto);
7.216 +
7.217 + printf("Transferred: %d\n", ntransferred);
7.218 + printf("Source: %s\n", (char *) ds0_addr);
7.219 + printf("Destination: %s\n", (char *) ds1_addr);
7.220 +
7.221 + /* Detach from the interrupt. */
7.222 +
7.223 + err = l4_error(l4_irq_detach(irq0cap));
7.224 +
7.225 + if (err)
7.226 + printf("Error detaching from IRQ objects: %ld\n", err);
7.227 +
7.228 + return 0;
7.229 +}