1 /* 2 * A dataspace server exposing regions of a MMC/SD card. 3 * 4 * Copyright (C) 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/cpm-generic.h> 23 #include <l4/devices/dma-generic.h> 24 #include <l4/devices/gpio-generic.h> 25 #include <l4/devices/memory.h> 26 #include <l4/devices/msc-generic.h> 27 28 #include <l4/re/c/util/cap_alloc.h> 29 #include <l4/sys/debugger.h> 30 #include <l4/sys/err.h> 31 #include <l4/sys/factory.h> 32 #include <l4/sys/icu.h> 33 #include <l4/sys/ipc.h> 34 #include <l4/sys/irq.h> 35 36 #include <ipc/thread.h> 37 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 42 #include <mem/memory_incremental.h> 43 #include <fsserver/page_queue_shared.h> 44 #include <fsserver/pages.h> 45 #include <resource/resource_server.h> 46 #include <systypes/env.h> 47 48 #include "msc_region_opener.h" 49 #include "msc_region_operations.h" 50 51 52 53 /* Common configuration. */ 54 55 static l4_cap_idx_t icucap; 56 57 58 59 /* Device and resource discovery. */ 60 61 static long item_in_range(long start, long end, long index) 62 { 63 if (start < end) 64 return start + index; 65 else if (start > end) 66 return start - index; 67 else 68 return start; 69 } 70 71 72 73 /* Common functions. */ 74 75 static int init_irq(int num, l4_cap_idx_t irq, l4_uint32_t start, l4_uint32_t end) 76 { 77 /* Create interrupt object. */ 78 79 long err = l4_error(l4_factory_create_irq(l4re_global_env->factory, irq)); 80 81 if (err) 82 { 83 printf("Could not create IRQ object: %ld\n", err); 84 return 1; 85 } 86 87 /* Bind interrupt objects to IRQ numbers. */ 88 89 err = l4_error(l4_icu_bind(icucap, 90 item_in_range(start, end, num), 91 irq)); 92 93 if (err) 94 { 95 printf("Could not bind IRQ to the ICU: %ld\n", err); 96 return 1; 97 } 98 99 return 0; 100 } 101 102 103 104 /* Peripheral resources. */ 105 106 static Cpm_chip *cpm; 107 static Dma_chip *dma; 108 static Hw::Gpio_chip *gpio; 109 static Msc_chip *msc; 110 111 /* Obtain an abstraction for the memory card. */ 112 113 static MscRegionOperations *get_msc_region_operations(const char *machine, 114 int msc_channel_num, 115 int dma_channel_num, 116 int card) 117 { 118 char resource[strlen(machine) + 8]; 119 l4_addr_t cpm_base = 0, cpm_base_end = 0; 120 l4_addr_t dma_base = 0, dma_base_end = 0; 121 l4_addr_t gpio_base = 0, gpio_base_end = 0; 122 l4_addr_t msc_base = 0, msc_base_end = 0; 123 l4_addr_t msc_phys_base = 0, msc_phys_base_end = 0; 124 l4_uint32_t dma_irq_start = 0, dma_irq_end = 0; 125 l4_uint32_t msc_irq_start = 0, msc_irq_end = 0; 126 127 icucap = l4re_env_get_cap("icu"); 128 129 /* Obtain resource details describing I/O memory. */ 130 131 sprintf(resource, "%s-cpm", machine); 132 133 if (get_memory(resource, &cpm_base, &cpm_base_end) < 0) 134 return NULL; 135 136 cpm = new_cpm_chip(machine, cpm_base); 137 138 sprintf(resource, "%s-dma", machine); 139 140 if (get_memory(resource, &dma_base, &dma_base_end) < 0) 141 return NULL; 142 143 dma = new_dma_chip(machine, dma_base, dma_base_end, cpm); 144 145 if (get_irq(resource, &dma_irq_start, &dma_irq_end) < 0) 146 return NULL; 147 148 l4_cap_idx_t dma_irq = l4re_util_cap_alloc(); 149 150 if (init_irq(0, dma_irq, dma_irq_start, dma_irq_end)) 151 return NULL; 152 153 dma->enable(); 154 155 sprintf(resource, "%s-gpio", machine); 156 157 if (get_memory(resource, &gpio_base, &gpio_base_end) < 0) 158 return NULL; 159 160 sprintf(resource, "%s-msc", machine); 161 162 if (get_memory_complete(resource, &msc_base, &msc_base_end, 163 &msc_phys_base, &msc_phys_base_end) < 0) 164 return NULL; 165 166 msc = new_msc_chip(machine, msc_phys_base, msc_base, msc_base_end, cpm); 167 168 if (get_irq(resource, &msc_irq_start, &msc_irq_end) < 0) 169 return NULL; 170 171 l4_cap_idx_t msc_irq = l4re_util_cap_alloc(); 172 173 if (init_irq(msc_channel_num, msc_irq, msc_irq_start, msc_irq_end)) 174 return NULL; 175 176 Dma_channel *dma_channel = dma->get_channel(dma_channel_num, dma_irq); 177 Msc_channel *msc_channel = msc->get_channel(msc_channel_num, msc_irq, dma_channel); 178 179 /* Configure the memory card peripheral clock. 180 NOTE: This needs to be parameterised. */ 181 182 enum Clock_identifiers clocks[] = {Clock_msc0, Clock_msc1, Clock_msc2}; 183 184 /* Configure the GPIO pins. 185 NOTE: This needs to be parameterised and to use a more abstract interface. */ 186 187 if (!strcmp(machine, "jz4780")) 188 { 189 cpm->set_frequency(clocks[msc_channel_num], 50000000); 190 gpio = new_gpio_chip(machine, gpio_base, 4); 191 gpio->config_pad(20, Hw::Gpio_chip::Function_alt, 0); 192 gpio->config_pad(21, Hw::Gpio_chip::Function_alt, 0); 193 gpio->config_pad(22, Hw::Gpio_chip::Function_alt, 0); 194 gpio->config_pad(23, Hw::Gpio_chip::Function_alt, 0); 195 gpio->config_pad(28, Hw::Gpio_chip::Function_alt, 0); 196 gpio->config_pad(29, Hw::Gpio_chip::Function_alt, 0); 197 } 198 else if (!strcmp(machine, "x1600")) 199 { 200 cpm->set_frequency(clocks[msc_channel_num], 25000000); 201 gpio = new_gpio_chip(machine, gpio_base, 2); 202 gpio->config_pad(17, Hw::Gpio_chip::Function_alt, 1); 203 gpio->config_pad(18, Hw::Gpio_chip::Function_alt, 1); 204 gpio->config_pad(19, Hw::Gpio_chip::Function_alt, 1); 205 gpio->config_pad(20, Hw::Gpio_chip::Function_alt, 1); 206 gpio->config_pad(21, Hw::Gpio_chip::Function_alt, 1); 207 gpio->config_pad(22, Hw::Gpio_chip::Function_alt, 1); 208 } 209 210 /* Attempt to enable the memory card peripheral. */ 211 212 msc_channel->enable(); 213 214 if (card >= msc_channel->num_cards()) 215 return NULL; 216 217 struct dma_region region; 218 219 if (get_dma_region(512, 12, ®ion)) 220 return NULL; 221 222 return new MscRegionOperations(msc_channel, card, region); 223 } 224 225 226 227 /* Default number of pages for files. */ 228 229 const unsigned int MEMORY_PAGES = 20; 230 231 232 233 /* Server program. */ 234 235 int main(int argc, char *argv[]) 236 { 237 l4_debugger_set_object_name(l4re_env()->main_thread, "block_server"); 238 long err; 239 240 /* Introduce concurrency control. */ 241 242 err = ipc_thread_init(); 243 244 if (err) 245 { 246 printf("Initialisation error: %s\n", l4sys_errtostr(err)); 247 return 1; 248 } 249 250 if (argc < 5) 251 { 252 printf("Need the following:\n\n" \ 253 "A machine indicator such as jz4780 or x1600.\n" \ 254 "An MSC channel/peripheral number.\n" \ 255 "A DMA channel number.\n" \ 256 "A card number.\n\n" \ 257 "A number of memory pages can be indicated for the use of the " \ 258 "server.\n\n" \ 259 "A named capability from the environment can be specified.\n"); 260 return 1; 261 } 262 263 char *machine = argv[1]; 264 int msc_channel_num = atoi(argv[2]); 265 int dma_channel_num = atoi(argv[3]); 266 int card = atoi(argv[4]); 267 268 unsigned int memory_pages = MEMORY_PAGES; 269 270 if (argc > 5) 271 memory_pages = atoi(argv[5]); 272 273 const char *server_name = (argc > 6) ? argv[6] : ENV_FILESYSTEM_SERVER_NAME; 274 275 /* Obtain a DMA space for associating allocated memory with physical 276 addresses. */ 277 278 l4_cap_idx_t dma_space; 279 280 err = get_dma_space(&dma_space); 281 282 if (err) 283 { 284 printf("Could not obtain DMA space: %s\n", l4sys_errtostr(err)); 285 return 1; 286 } 287 288 MscRegionOperations *ops = get_msc_region_operations(machine, 289 msc_channel_num, 290 dma_channel_num, 291 card); 292 293 if (ops == NULL) 294 { 295 printf("Could not access memory card peripheral.\n"); 296 return 1; 297 } 298 299 /* Some memory plus infrastructure. */ 300 301 MemoryIncremental mem(memory_pages, PAGE_SIZE, dma_space); 302 PageQueueShared queue; 303 Pages pages(&mem, &queue); 304 ResourceRegistry registry(&pages); 305 MscRegionOpener opener(®istry, ops); 306 307 /* Register a server associating it with the given object. */ 308 309 ResourceServer server(&opener); 310 server.bind(server_name); 311 312 printf("Starting server using %d pages...\n", memory_pages); 313 server.start(); 314 315 return 0; 316 } 317 318 // vim: tabstop=2 expandtab shiftwidth=2