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