Landfall

pkg/landfall-examples/letux400_dma/letux400_dma.cc

147:3d05f2d33524
17 months ago Paul Boddie Tidied and slightly improved the instructions. l4re-git-distribution
     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 }