1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl> 4 */ 5 6 #include <linux/bcm47xx_nvram.h> 7 #include <linux/etherdevice.h> 8 #include <linux/hex.h> 9 #include <linux/if_ether.h> 10 #include <linux/io.h> 11 #include <linux/mod_devicetable.h> 12 #include <linux/module.h> 13 #include <linux/nvmem-consumer.h> 14 #include <linux/nvmem-provider.h> 15 #include <linux/of.h> 16 #include <linux/platform_device.h> 17 #include <linux/slab.h> 18 19 #define NVRAM_MAGIC "FLSH" 20 21 /** 22 * struct brcm_nvram - driver state internal struct 23 * 24 * @dev: NVMEM device pointer 25 * @nvmem_size: Size of the whole space available for NVRAM 26 * @data: NVRAM data copy stored to avoid poking underlying flash controller 27 * @data_len: NVRAM data size 28 * @padding_byte: Padding value used to fill remaining space 29 * @cells: Array of discovered NVMEM cells 30 * @ncells: Number of elements in cells 31 */ 32 struct brcm_nvram { 33 struct device *dev; 34 size_t nvmem_size; 35 uint8_t *data; 36 size_t data_len; 37 uint8_t padding_byte; 38 struct nvmem_cell_info *cells; 39 int ncells; 40 }; 41 42 struct brcm_nvram_header { 43 char magic[4]; 44 __le32 len; 45 __le32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */ 46 __le32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */ 47 __le32 config_ncdl; /* ncdl values for memc */ 48 }; 49 50 static int brcm_nvram_read(void *context, unsigned int offset, void *val, 51 size_t bytes) 52 { 53 struct brcm_nvram *priv = context; 54 size_t to_copy; 55 56 if (offset + bytes > priv->data_len) 57 to_copy = max_t(ssize_t, (ssize_t)priv->data_len - offset, 0); 58 else 59 to_copy = bytes; 60 61 memcpy(val, priv->data + offset, to_copy); 62 63 memset((uint8_t *)val + to_copy, priv->padding_byte, bytes - to_copy); 64 65 return 0; 66 } 67 68 static int brcm_nvram_copy_data(struct brcm_nvram *priv, struct platform_device *pdev) 69 { 70 struct resource *res; 71 void __iomem *base; 72 73 base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 74 if (IS_ERR(base)) 75 return PTR_ERR(base); 76 77 priv->nvmem_size = resource_size(res); 78 79 priv->padding_byte = readb(base + priv->nvmem_size - 1); 80 for (priv->data_len = priv->nvmem_size; 81 priv->data_len; 82 priv->data_len--) { 83 if (readb(base + priv->data_len - 1) != priv->padding_byte) 84 break; 85 } 86 WARN(priv->data_len > SZ_128K, "Unexpected (big) NVRAM size: %zu B\n", priv->data_len); 87 88 priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL); 89 if (!priv->data) 90 return -ENOMEM; 91 92 memcpy_fromio(priv->data, base, priv->data_len); 93 94 bcm47xx_nvram_init_from_iomem(base, priv->data_len); 95 96 return 0; 97 } 98 99 static int brcm_nvram_read_post_process_macaddr(void *context, const char *id, int index, 100 unsigned int offset, void *buf, size_t bytes) 101 { 102 u8 mac[ETH_ALEN]; 103 104 if (bytes != MAC_ADDR_STR_LEN) 105 return -EINVAL; 106 107 if (!mac_pton(buf, mac)) 108 return -EINVAL; 109 110 if (index) 111 eth_addr_add(mac, index); 112 113 ether_addr_copy(buf, mac); 114 115 return 0; 116 } 117 118 static int brcm_nvram_add_cells(struct brcm_nvram *priv, uint8_t *data, 119 size_t len) 120 { 121 struct device *dev = priv->dev; 122 char *var, *value; 123 uint8_t tmp; 124 int idx; 125 int err = 0; 126 127 tmp = priv->data[len - 1]; 128 priv->data[len - 1] = '\0'; 129 130 priv->ncells = 0; 131 for (var = data + sizeof(struct brcm_nvram_header); 132 var < (char *)data + len && *var; 133 var += strlen(var) + 1) { 134 priv->ncells++; 135 } 136 137 priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL); 138 if (!priv->cells) { 139 err = -ENOMEM; 140 goto out; 141 } 142 143 for (var = data + sizeof(struct brcm_nvram_header), idx = 0; 144 var < (char *)data + len && *var; 145 var = value + strlen(value) + 1, idx++) { 146 char *eq, *name; 147 148 eq = strchr(var, '='); 149 if (!eq) 150 break; 151 *eq = '\0'; 152 name = devm_kstrdup(dev, var, GFP_KERNEL); 153 *eq = '='; 154 if (!name) { 155 err = -ENOMEM; 156 goto out; 157 } 158 value = eq + 1; 159 160 priv->cells[idx].name = name; 161 priv->cells[idx].offset = value - (char *)data; 162 priv->cells[idx].bytes = strlen(value); 163 priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name); 164 if (!strcmp(name, "et0macaddr") || 165 !strcmp(name, "et1macaddr") || 166 !strcmp(name, "et2macaddr")) { 167 priv->cells[idx].raw_len = strlen(value); 168 priv->cells[idx].bytes = ETH_ALEN; 169 priv->cells[idx].read_post_process = brcm_nvram_read_post_process_macaddr; 170 } 171 } 172 173 out: 174 priv->data[len - 1] = tmp; 175 return err; 176 } 177 178 static int brcm_nvram_parse(struct brcm_nvram *priv) 179 { 180 struct brcm_nvram_header *header = (struct brcm_nvram_header *)priv->data; 181 struct device *dev = priv->dev; 182 size_t len; 183 int err; 184 185 if (memcmp(header->magic, NVRAM_MAGIC, 4)) { 186 dev_err(dev, "Invalid NVRAM magic\n"); 187 return -EINVAL; 188 } 189 190 len = le32_to_cpu(header->len); 191 if (len > priv->nvmem_size) { 192 dev_err(dev, "NVRAM length (%zd) exceeds mapped size (%zd)\n", len, 193 priv->nvmem_size); 194 return -EINVAL; 195 } 196 197 err = brcm_nvram_add_cells(priv, priv->data, len); 198 if (err) 199 dev_err(dev, "Failed to add cells: %d\n", err); 200 201 return 0; 202 } 203 204 static int brcm_nvram_probe(struct platform_device *pdev) 205 { 206 struct nvmem_config config = { 207 .name = "brcm-nvram", 208 .reg_read = brcm_nvram_read, 209 }; 210 struct device *dev = &pdev->dev; 211 struct brcm_nvram *priv; 212 int err; 213 214 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 215 if (!priv) 216 return -ENOMEM; 217 priv->dev = dev; 218 219 err = brcm_nvram_copy_data(priv, pdev); 220 if (err) 221 return err; 222 223 err = brcm_nvram_parse(priv); 224 if (err) 225 return err; 226 227 config.dev = dev; 228 config.cells = priv->cells; 229 config.ncells = priv->ncells; 230 config.priv = priv; 231 config.size = priv->nvmem_size; 232 233 return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config)); 234 } 235 236 static const struct of_device_id brcm_nvram_of_match_table[] = { 237 { .compatible = "brcm,nvram", }, 238 {}, 239 }; 240 241 static struct platform_driver brcm_nvram_driver = { 242 .probe = brcm_nvram_probe, 243 .driver = { 244 .name = "brcm_nvram", 245 .of_match_table = brcm_nvram_of_match_table, 246 }, 247 }; 248 249 static int __init brcm_nvram_init(void) 250 { 251 return platform_driver_register(&brcm_nvram_driver); 252 } 253 254 subsys_initcall_sync(brcm_nvram_init); 255 256 MODULE_AUTHOR("Rafał Miłecki"); 257 MODULE_DESCRIPTION("Broadcom I/O-mapped NVRAM support driver"); 258 MODULE_LICENSE("GPL"); 259 MODULE_DEVICE_TABLE(of, brcm_nvram_of_match_table); 260