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/cpm-jz4730.h> 23 #include <l4/devices/dma-jz4730.h> 24 #include <l4/devices/memory.h> 25 26 #include <l4/re/c/util/cap_alloc.h> 27 #include <l4/re/c/dataspace.h> 28 #include <l4/re/c/dma_space.h> 29 #include <l4/re/c/mem_alloc.h> 30 #include <l4/re/c/rm.h> 31 #include <l4/re/protocols.h> 32 33 #include <l4/sys/err.h> 34 #include <l4/sys/factory.h> 35 #include <l4/sys/icu.h> 36 #include <l4/sys/irq.h> 37 #include <l4/sys/rcv_endpoint.h> 38 39 #include <l4/vbus/vbus.h> 40 41 #include <stdio.h> 42 #include <string.h> 43 #include <unistd.h> 44 45 46 47 /* Device and resource discovery. */ 48 49 static long item_in_range(long start, long end, long index) 50 { 51 if (start < end) 52 return start + index; 53 else 54 return start - index; 55 } 56 57 58 59 int main(void) 60 { 61 long err; 62 void *cpm; 63 void *dmac, *dma0; 64 l4_cap_idx_t dma, vbus; 65 66 dma = l4re_util_cap_alloc(); 67 vbus = l4re_env_get_cap("vbus"); 68 69 if (l4_is_invalid_cap(dma)) 70 { 71 printf("Could not allocate DMA capability.\n"); 72 return 1; 73 } 74 75 /* Create the DMA space. */ 76 77 err = l4_error(l4_factory_create(l4re_env()->mem_alloc, L4RE_PROTO_DMA_SPACE, dma)); 78 79 if (err) 80 { 81 printf("Could not create DMA space: %s\n", l4sys_errtostr(err)); 82 return 1; 83 } 84 85 l4vbus_device_handle_t device = L4VBUS_NULL; 86 l4vbus_resource_t dma_resource; 87 88 if (!find_resource(&device, &dma_resource, L4VBUS_RESOURCE_DMA_DOMAIN)) 89 { 90 printf("Could not find DMA domain.\n"); 91 return 1; 92 } 93 94 err = l4vbus_assign_dma_domain(vbus, dma_resource.start, 95 L4VBUS_DMAD_BIND | L4VBUS_DMAD_L4RE_DMA_SPACE, 96 dma); 97 98 if (err) 99 { 100 printf("Could not assign DMA space: %s\n", l4sys_errtostr(err)); 101 return 1; 102 } 103 104 /* Allocate memory to test transfers. */ 105 106 l4_cap_idx_t ds0_mem, ds1_mem; 107 l4_size_t ds0_size = L4_PAGESIZE, ds0_psize, ds1_size = L4_PAGESIZE, ds1_psize; 108 l4_addr_t ds0_addr, ds1_addr; 109 l4re_dma_space_dma_addr_t ds0_paddr, ds1_paddr; 110 111 ds0_mem = l4re_util_cap_alloc(); 112 ds1_mem = l4re_util_cap_alloc(); 113 114 if (l4_is_invalid_cap(ds0_mem) || l4_is_invalid_cap(ds1_mem)) 115 { 116 printf("Could not allocate memory capabilities.\n"); 117 return 1; 118 } 119 120 if (l4re_ma_alloc_align(ds0_size, ds0_mem, L4RE_MA_CONTINUOUS | L4RE_MA_PINNED, 8) || 121 l4re_ma_alloc_align(ds1_size, ds1_mem, L4RE_MA_CONTINUOUS | L4RE_MA_PINNED, 8)) 122 { 123 printf("Could not allocate memory.\n"); 124 return 1; 125 } 126 127 if (l4re_rm_attach((void **) &ds0_addr, ds0_size, 128 L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_EAGER_MAP | L4RE_RM_F_RW, 129 ds0_mem, 0, L4_PAGESHIFT) || 130 l4re_rm_attach((void **) &ds1_addr, ds1_size, 131 L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_EAGER_MAP | L4RE_RM_F_RW, 132 ds1_mem, 0, L4_PAGESHIFT)) 133 { 134 printf("Could not map memory.\n"); 135 return 1; 136 } 137 138 err = l4re_dma_space_map(dma, ds0_mem | L4_CAP_FPAGE_RW, 0, &ds0_psize, 0, 139 L4RE_DMA_SPACE_BIDIRECTIONAL, &ds0_paddr) || 140 l4re_dma_space_map(dma, ds1_mem | L4_CAP_FPAGE_RW, 0, &ds1_psize, 0, 141 L4RE_DMA_SPACE_BIDIRECTIONAL, &ds1_paddr); 142 143 if (err) 144 { 145 printf("Could not get physical addresses for memory: %s\n", l4sys_errtostr(err)); 146 return 1; 147 } 148 149 /* Fill the allocated memory. */ 150 151 memset((void *) ds0_addr, 0, ds0_size); 152 memset((void *) ds1_addr, 0, ds1_size); 153 154 sprintf((char *) ds0_addr, "The quick brown fox jumped over the lazy dog.\n"); 155 156 /* Interrupts. */ 157 158 l4_uint32_t dma_irq_start = 0, dma_irq_end = 0; 159 l4_cap_idx_t icucap, irq0cap; 160 161 /* Obtain resource details describing the interrupt for DMA channel 0. */ 162 163 printf("Access IRQ...\n"); 164 165 if (get_irq("jz4730-dma", &dma_irq_start, &dma_irq_end) < 0) 166 return 1; 167 168 printf("IRQ range at %d...%d.\n", dma_irq_start, dma_irq_end); 169 170 /* Obtain capabilities for the interrupt controller and an interrupt. */ 171 172 irq0cap = l4re_util_cap_alloc(); 173 icucap = l4re_env_get_cap("icu"); 174 175 if (l4_is_invalid_cap(icucap)) 176 { 177 printf("No 'icu' capability available in the virtual bus.\n"); 178 return 1; 179 } 180 181 if (l4_is_invalid_cap(irq0cap)) 182 { 183 printf("Capabilities not available for interrupts.\n"); 184 return 1; 185 } 186 187 /* Create interrupt objects. */ 188 189 err = l4_error(l4_factory_create_irq(l4re_global_env->factory, irq0cap)); 190 191 if (err) 192 { 193 printf("Could not create IRQ object: %lx\n", err); 194 return 1; 195 } 196 197 /* Bind interrupt objects to IRQ numbers. */ 198 199 err = l4_error(l4_icu_bind(icucap, 200 item_in_range(dma_irq_start, dma_irq_end, 0), 201 irq0cap)); 202 203 if (err) 204 { 205 printf("Could not bind IRQ to the ICU: %ld\n", err); 206 return 1; 207 } 208 209 /* Attach ourselves to the interrupt handler. */ 210 211 err = l4_error(l4_rcv_ep_bind_thread(irq0cap, l4re_env()->main_thread, 0)); 212 213 if (err) 214 { 215 printf("Could not attach to IRQs: %ld\n", err); 216 return 1; 217 } 218 219 /* Peripheral memory. */ 220 221 l4_addr_t cpm_base = 0, cpm_base_end = 0; 222 l4_addr_t dma_base = 0, dma_base_end = 0; 223 224 /* Obtain resource details describing I/O memory. */ 225 226 printf("Access CPM...\n"); 227 228 if (get_memory("jz4730-cpm", &cpm_base, &cpm_base_end) < 0) 229 return 1; 230 231 printf("CPM at 0x%lx...0x%lx.\n", cpm_base, cpm_base_end); 232 233 printf("Access DMA...\n"); 234 235 if (get_memory("jz4730-dma", &dma_base, &dma_base_end) < 0) 236 return 1; 237 238 printf("DMA at 0x%lx...0x%lx.\n", dma_base, dma_base_end); 239 240 /* Obtain CPM and DMA references. */ 241 242 cpm = jz4730_cpm_init(cpm_base); 243 dmac = jz4730_dma_init(dma_base, dma_base_end, cpm); 244 dma0 = jz4730_dma_get_channel(dmac, 0, irq0cap); 245 246 /* Enable DMA. */ 247 248 printf("Enable DMA...\n"); 249 250 jz4730_dma_enable(dmac); 251 252 /* Transfer data between the allocated memory regions. */ 253 254 printf("Transfer from %llx to %llx...\n", ds0_paddr, ds1_paddr); 255 256 unsigned int ntransferred = jz4730_dma_transfer(dma0, (uint32_t) ds0_paddr, 257 (uint32_t) ds1_paddr, 258 L4_PAGESIZE / 4, 259 Dma_trans_unit_size_32_bit, 260 Dma_request_auto); 261 262 printf("Transferred: %d\n", ntransferred); 263 printf("Source: %s\n", (char *) ds0_addr); 264 printf("Destination: %s\n", (char *) ds1_addr); 265 266 /* Detach from the interrupt. */ 267 268 err = l4_error(l4_irq_detach(irq0cap)); 269 270 if (err) 271 printf("Error detaching from IRQ objects: %ld\n", err); 272 273 return 0; 274 }