1 /* $NetBSD: flash_mtdparts.c,v 1.3 2021/08/07 16:19:10 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: flash_mtdparts.c,v 1.3 2021/08/07 16:19:10 thorpej Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/device.h> 35 #include <sys/systm.h> 36 #include <sys/kmem.h> 37 38 #include <dev/flash/flash.h> 39 40 extern int flash_print(void *, const char *); 41 42 static void 43 flash_attach_partition(struct flash_interface *flash_if, device_t parent, 44 struct flash_partition *part) 45 { 46 struct flash_attach_args faa; 47 48 faa.flash_if = flash_if; 49 faa.partinfo = *part; 50 51 config_found(parent, &faa, flash_print, 52 CFARGS(.iattr = "flashbus")); 53 } 54 55 static flash_size_t 56 flash_parse_size(char *partdef, char **ep) 57 { 58 flash_size_t size; 59 60 /* Parse the size parameter */ 61 size = strtoul(partdef, ep, 10); 62 if (partdef == *ep) 63 return 0; 64 65 switch (**ep) { 66 case 'G': 67 case 'g': 68 size <<= 10; 69 /* FALLTHROUGH */ 70 case 'M': 71 case 'm': 72 size <<= 10; 73 /* FALLTHROUGH */ 74 case 'K': 75 case 'k': 76 size <<= 10; 77 (*ep)++; 78 break; 79 } 80 81 return size; 82 } 83 84 static int 85 flash_parse_partdef(flash_size_t flash_size, char *partdef, flash_off_t *offset, 86 struct flash_partition *ppart) 87 { 88 struct flash_partition part; 89 90 /* Get the partition size */ 91 if (*partdef == '-') { 92 /* Use the remaining space */ 93 part.part_size = 0; 94 partdef++; 95 } else { 96 part.part_size = flash_parse_size(partdef, &partdef); 97 if (part.part_size == 0) 98 return EINVAL; 99 } 100 101 if (*partdef == '@') { 102 /* Explicit offset */ 103 partdef++; 104 part.part_offset = flash_parse_size(partdef, &partdef); 105 } else { 106 /* Offset is the end of the previous partition */ 107 part.part_offset = *offset; 108 } 109 110 /* Calculate partition size for "all remaining space" parts */ 111 if (part.part_size == 0) 112 part.part_size = flash_size - part.part_offset; 113 114 if (*partdef == '(') { 115 /* Partition name */ 116 partdef++; 117 part.part_name = partdef; 118 partdef = strchr(partdef, ')'); 119 if (partdef == NULL) 120 return EINVAL; 121 *partdef = '\0'; 122 partdef++; 123 } 124 125 part.part_flags = 0; 126 if (strncmp(partdef, "ro", 2) == 0) { 127 part.part_flags |= FLASH_PART_READONLY; 128 partdef += 2; 129 } 130 131 *ppart = part; 132 133 *offset = part.part_offset + part.part_size; 134 135 return 0; 136 } 137 138 static void 139 flash_parse_mtddef(struct flash_interface *flash_if, device_t parent, 140 flash_size_t flash_size, char *mtddef) 141 { 142 struct flash_partition part; 143 char *partdef = mtddef, *nextdef; 144 flash_off_t offset = 0; 145 int error; 146 147 while (partdef && offset < flash_size) { 148 /* Find the end */ 149 nextdef = strchr(partdef, ','); 150 if (nextdef == NULL) 151 nextdef = strchr(partdef, ' '); 152 if (nextdef) 153 *nextdef++ = '\0'; 154 155 error = flash_parse_partdef(flash_size, partdef, &offset, 156 &part); 157 if (error) { 158 aprint_error_dev(parent, "bad partition def '%s'\n", 159 partdef); 160 return; 161 } 162 163 flash_attach_partition(flash_if, parent, &part); 164 165 partdef = nextdef; 166 } 167 } 168 169 /* 170 * Attach partitions to a given parent device node that match the supplied 171 * device id. The cmdline follows the following format: 172 * 173 * mtdparts=<mtddef>[;<mtddef] 174 * <mtddef> := <mtd-id>:<partdef>[,<partdef>] 175 * <partdef> := <size>[@offset][<name>][ro] 176 * <mtd-id> := unique id used in mapping driver/device (number of flash bank) 177 * <size> := memsize OR "-" to denote all remaining space 178 * <name> := '(' NAME ')' 179 */ 180 void 181 flash_attach_mtdparts(struct flash_interface *flash_if, device_t parent, 182 flash_size_t flash_size, const char *mtd_id, const char *cmdline) 183 { 184 char *mtddef; 185 size_t mtddeflen; 186 187 /* Find the definition for our mtd id */ 188 const char *s = strstr(cmdline, mtd_id); 189 if (s == NULL || s[strlen(mtd_id)] != ':') 190 return; 191 192 mtddef = kmem_strdupsize(s + strlen(mtd_id) + 1, &mtddeflen, KM_SLEEP); 193 194 flash_parse_mtddef(flash_if, parent, flash_size, mtddef); 195 196 kmem_free(mtddef, mtddeflen); 197 } 198