1 /* 2 * Test DMA transfers. 3 * 4 * Copyright (C) 2018, 2020, 2021, 2023, 2024 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 struct dma_region ds0_region, ds1_region; 64 65 err = get_dma_region(L4_PAGESIZE, 8, &ds0_region); 66 67 if (err) 68 { 69 printf("Could not allocate region #0.\n"); 70 return 1; 71 } 72 73 err = get_dma_region(L4_PAGESIZE, 8, &ds1_region); 74 75 if (err) 76 { 77 printf("Could not allocate region #1.\n"); 78 return 1; 79 } 80 81 /* Fill the allocated memory. */ 82 83 memset((void *) ds0_region.vaddr, 0, ds0_region.size); 84 memset((void *) ds1_region.vaddr, 0, ds1_region.size); 85 86 sprintf((char *) ds0_region.vaddr, "The quick brown fox jumped over the lazy dog.\n"); 87 88 /* Interrupts. */ 89 90 l4_uint32_t dma_irq_start = 0, dma_irq_end = 0; 91 l4_cap_idx_t icucap, irq0cap; 92 93 /* Obtain resource details describing the interrupt for DMA channel 0. */ 94 95 printf("Access IRQ...\n"); 96 97 if (get_irq("jz4730-dma", &dma_irq_start, &dma_irq_end) < 0) 98 return 1; 99 100 printf("IRQ range at %d...%d.\n", dma_irq_start, dma_irq_end); 101 102 /* Obtain capabilities for the interrupt controller and an interrupt. */ 103 104 irq0cap = l4re_util_cap_alloc(); 105 icucap = l4re_env_get_cap("icu"); 106 107 if (l4_is_invalid_cap(icucap)) 108 { 109 printf("No 'icu' capability available in the virtual bus.\n"); 110 return 1; 111 } 112 113 if (l4_is_invalid_cap(irq0cap)) 114 { 115 printf("Capabilities not available for interrupts.\n"); 116 return 1; 117 } 118 119 /* Create interrupt objects. */ 120 121 err = l4_error(l4_factory_create_irq(l4re_global_env->factory, irq0cap)); 122 123 if (err) 124 { 125 printf("Could not create IRQ object: %lx\n", err); 126 return 1; 127 } 128 129 /* Bind interrupt objects to IRQ numbers. */ 130 131 err = l4_error(l4_icu_bind(icucap, 132 item_in_range(dma_irq_start, dma_irq_end, 0), 133 irq0cap)); 134 135 if (err) 136 { 137 printf("Could not bind IRQ to the ICU: %ld\n", err); 138 return 1; 139 } 140 141 /* Attach ourselves to the interrupt handler. */ 142 143 err = l4_error(l4_rcv_ep_bind_thread(irq0cap, l4re_env()->main_thread, 0)); 144 145 if (err) 146 { 147 printf("Could not attach to IRQs: %ld\n", err); 148 return 1; 149 } 150 151 /* Peripheral memory. */ 152 153 l4_addr_t cpm_base = 0, cpm_base_end = 0; 154 l4_addr_t dma_base = 0, dma_base_end = 0; 155 156 /* Obtain resource details describing I/O memory. */ 157 158 printf("Access CPM...\n"); 159 160 if (get_memory("jz4730-cpm", &cpm_base, &cpm_base_end) < 0) 161 return 1; 162 163 printf("CPM at 0x%lx...0x%lx.\n", cpm_base, cpm_base_end); 164 165 printf("Access DMA...\n"); 166 167 if (get_memory("jz4730-dma", &dma_base, &dma_base_end) < 0) 168 return 1; 169 170 printf("DMA at 0x%lx...0x%lx.\n", dma_base, dma_base_end); 171 172 /* Obtain CPM and DMA references. */ 173 174 cpm = jz4730_cpm_init(cpm_base); 175 dmac = jz4730_dma_init(dma_base, dma_base_end, cpm); 176 dma0 = jz4730_dma_get_channel(dmac, 0, irq0cap); 177 178 /* Enable DMA. */ 179 180 printf("Enable DMA...\n"); 181 182 jz4730_dma_enable(dmac); 183 184 /* Transfer data between the allocated memory regions. */ 185 186 printf("Transfer from %llx to %llx...\n", ds0_region.paddr, ds1_region.paddr); 187 188 unsigned int count = L4_PAGESIZE / 4; 189 190 unsigned int to_transfer = jz4730_dma_transfer(dma0, (uint32_t) ds0_region.paddr, 191 (uint32_t) ds1_region.paddr, 192 count, 193 1, 1, 194 4, 4, 195 4, 196 Dma_request_auto); 197 198 unsigned int transferred = to_transfer ? count - jz4730_dma_wait(dma0) : 0; 199 200 printf("Transferred: %d\n", transferred); 201 printf("Source: %s\n", (char *) ds0_region.vaddr); 202 printf("Destination: %s\n", (char *) ds1_region.vaddr); 203 204 /* Detach from the interrupt. */ 205 206 err = l4_error(l4_irq_detach(irq0cap)); 207 208 if (err) 209 printf("Error detaching from IRQ objects: %ld\n", err); 210 211 return 0; 212 }