1 /* 2 * Test DMA transfers. 3 * 4 * Copyright (C) 2018, 2020, 2021, 2023 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/devices/dma.h> 23 #include <l4/devices/cpm-jz4730.h> 24 #include <l4/devices/dma-jz4730.h> 25 #include <l4/devices/memory.h> 26 27 #include <l4/re/c/util/cap_alloc.h> 28 #include <l4/re/c/dataspace.h> 29 #include <l4/re/c/rm.h> 30 31 #include <l4/sys/err.h> 32 #include <l4/sys/factory.h> 33 #include <l4/sys/icu.h> 34 #include <l4/sys/irq.h> 35 #include <l4/sys/rcv_endpoint.h> 36 37 #include <stdio.h> 38 #include <string.h> 39 #include <unistd.h> 40 41 42 43 /* Device and resource discovery. */ 44 45 static long item_in_range(long start, long end, long index) 46 { 47 if (start < end) 48 return start + index; 49 else 50 return start - index; 51 } 52 53 54 55 int main(void) 56 { 57 long err; 58 void *cpm; 59 void *dmac, *dma0; 60 61 /* Allocate memory to test transfers. */ 62 63 l4_cap_idx_t ds0_mem, ds1_mem; 64 l4_size_t ds0_size = L4_PAGESIZE, ds1_size = L4_PAGESIZE; 65 l4_addr_t ds0_addr, ds1_addr; 66 l4re_dma_space_dma_addr_t ds0_paddr, ds1_paddr; 67 68 err = get_dma_region(ds0_size, 8, &ds0_addr, &ds0_paddr, &ds0_mem); 69 70 if (err) 71 { 72 printf("Could not allocate region #0.\n"); 73 return 1; 74 } 75 76 err = get_dma_region(ds1_size, 8, &ds1_addr, &ds1_paddr, &ds1_mem); 77 78 if (err) 79 { 80 printf("Could not allocate region #1.\n"); 81 return 1; 82 } 83 84 /* Fill the allocated memory. */ 85 86 memset((void *) ds0_addr, 0, ds0_size); 87 memset((void *) ds1_addr, 0, ds1_size); 88 89 sprintf((char *) ds0_addr, "The quick brown fox jumped over the lazy dog.\n"); 90 91 /* Interrupts. */ 92 93 l4_uint32_t dma_irq_start = 0, dma_irq_end = 0; 94 l4_cap_idx_t icucap, irq0cap; 95 96 /* Obtain resource details describing the interrupt for DMA channel 0. */ 97 98 printf("Access IRQ...\n"); 99 100 if (get_irq("jz4730-dma", &dma_irq_start, &dma_irq_end) < 0) 101 return 1; 102 103 printf("IRQ range at %d...%d.\n", dma_irq_start, dma_irq_end); 104 105 /* Obtain capabilities for the interrupt controller and an interrupt. */ 106 107 irq0cap = l4re_util_cap_alloc(); 108 icucap = l4re_env_get_cap("icu"); 109 110 if (l4_is_invalid_cap(icucap)) 111 { 112 printf("No 'icu' capability available in the virtual bus.\n"); 113 return 1; 114 } 115 116 if (l4_is_invalid_cap(irq0cap)) 117 { 118 printf("Capabilities not available for interrupts.\n"); 119 return 1; 120 } 121 122 /* Create interrupt objects. */ 123 124 err = l4_error(l4_factory_create_irq(l4re_global_env->factory, irq0cap)); 125 126 if (err) 127 { 128 printf("Could not create IRQ object: %lx\n", err); 129 return 1; 130 } 131 132 /* Bind interrupt objects to IRQ numbers. */ 133 134 err = l4_error(l4_icu_bind(icucap, 135 item_in_range(dma_irq_start, dma_irq_end, 0), 136 irq0cap)); 137 138 if (err) 139 { 140 printf("Could not bind IRQ to the ICU: %ld\n", err); 141 return 1; 142 } 143 144 /* Attach ourselves to the interrupt handler. */ 145 146 err = l4_error(l4_rcv_ep_bind_thread(irq0cap, l4re_env()->main_thread, 0)); 147 148 if (err) 149 { 150 printf("Could not attach to IRQs: %ld\n", err); 151 return 1; 152 } 153 154 /* Peripheral memory. */ 155 156 l4_addr_t cpm_base = 0, cpm_base_end = 0; 157 l4_addr_t dma_base = 0, dma_base_end = 0; 158 159 /* Obtain resource details describing I/O memory. */ 160 161 printf("Access CPM...\n"); 162 163 if (get_memory("jz4730-cpm", &cpm_base, &cpm_base_end) < 0) 164 return 1; 165 166 printf("CPM at 0x%lx...0x%lx.\n", cpm_base, cpm_base_end); 167 168 printf("Access DMA...\n"); 169 170 if (get_memory("jz4730-dma", &dma_base, &dma_base_end) < 0) 171 return 1; 172 173 printf("DMA at 0x%lx...0x%lx.\n", dma_base, dma_base_end); 174 175 /* Obtain CPM and DMA references. */ 176 177 cpm = jz4730_cpm_init(cpm_base); 178 dmac = jz4730_dma_init(dma_base, dma_base_end, cpm); 179 dma0 = jz4730_dma_get_channel(dmac, 0, irq0cap); 180 181 /* Enable DMA. */ 182 183 printf("Enable DMA...\n"); 184 185 jz4730_dma_enable(dmac); 186 187 /* Transfer data between the allocated memory regions. */ 188 189 printf("Transfer from %llx to %llx...\n", ds0_paddr, ds1_paddr); 190 191 unsigned int count = L4_PAGESIZE / 4; 192 193 unsigned int to_transfer = jz4730_dma_transfer(dma0, (uint32_t) ds0_paddr, 194 (uint32_t) ds1_paddr, 195 count, 196 1, 1, 197 4, 4, 198 4, 199 Dma_request_auto); 200 201 unsigned int transferred = to_transfer ? count - jz4730_dma_wait(dma0) : 0; 202 203 printf("Transferred: %d\n", transferred); 204 printf("Source: %s\n", (char *) ds0_addr); 205 printf("Destination: %s\n", (char *) ds1_addr); 206 207 /* Detach from the interrupt. */ 208 209 err = l4_error(l4_irq_detach(irq0cap)); 210 211 if (err) 212 printf("Error detaching from IRQ objects: %ld\n", err); 213 214 return 0; 215 }