13fef9ed0SRafał Miłecki // SPDX-License-Identifier: GPL-2.0-only 23fef9ed0SRafał Miłecki /* 33fef9ed0SRafał Miłecki * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl> 43fef9ed0SRafał Miłecki */ 53fef9ed0SRafał Miłecki 63fef9ed0SRafał Miłecki #include <linux/io.h> 73fef9ed0SRafał Miłecki #include <linux/mod_devicetable.h> 83fef9ed0SRafał Miłecki #include <linux/module.h> 96e977eaaSRafał Miłecki #include <linux/nvmem-consumer.h> 103fef9ed0SRafał Miłecki #include <linux/nvmem-provider.h> 11*207775f7SRafał Miłecki #include <linux/of.h> 123fef9ed0SRafał Miłecki #include <linux/platform_device.h> 136e977eaaSRafał Miłecki #include <linux/slab.h> 146e977eaaSRafał Miłecki 156e977eaaSRafał Miłecki #define NVRAM_MAGIC "FLSH" 163fef9ed0SRafał Miłecki 173fef9ed0SRafał Miłecki struct brcm_nvram { 183fef9ed0SRafał Miłecki struct device *dev; 193fef9ed0SRafał Miłecki void __iomem *base; 206e977eaaSRafał Miłecki struct nvmem_cell_info *cells; 216e977eaaSRafał Miłecki int ncells; 226e977eaaSRafał Miłecki }; 236e977eaaSRafał Miłecki 246e977eaaSRafał Miłecki struct brcm_nvram_header { 256e977eaaSRafał Miłecki char magic[4]; 266e977eaaSRafał Miłecki __le32 len; 276e977eaaSRafał Miłecki __le32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */ 286e977eaaSRafał Miłecki __le32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */ 296e977eaaSRafał Miłecki __le32 config_ncdl; /* ncdl values for memc */ 303fef9ed0SRafał Miłecki }; 313fef9ed0SRafał Miłecki 323fef9ed0SRafał Miłecki static int brcm_nvram_read(void *context, unsigned int offset, void *val, 333fef9ed0SRafał Miłecki size_t bytes) 343fef9ed0SRafał Miłecki { 353fef9ed0SRafał Miłecki struct brcm_nvram *priv = context; 363fef9ed0SRafał Miłecki u8 *dst = val; 373fef9ed0SRafał Miłecki 383fef9ed0SRafał Miłecki while (bytes--) 393fef9ed0SRafał Miłecki *dst++ = readb(priv->base + offset++); 403fef9ed0SRafał Miłecki 413fef9ed0SRafał Miłecki return 0; 423fef9ed0SRafał Miłecki } 433fef9ed0SRafał Miłecki 446e977eaaSRafał Miłecki static int brcm_nvram_add_cells(struct brcm_nvram *priv, uint8_t *data, 456e977eaaSRafał Miłecki size_t len) 466e977eaaSRafał Miłecki { 476e977eaaSRafał Miłecki struct device *dev = priv->dev; 486e977eaaSRafał Miłecki char *var, *value, *eq; 496e977eaaSRafał Miłecki int idx; 506e977eaaSRafał Miłecki 516e977eaaSRafał Miłecki priv->ncells = 0; 526e977eaaSRafał Miłecki for (var = data + sizeof(struct brcm_nvram_header); 536e977eaaSRafał Miłecki var < (char *)data + len && *var; 546e977eaaSRafał Miłecki var += strlen(var) + 1) { 556e977eaaSRafał Miłecki priv->ncells++; 566e977eaaSRafał Miłecki } 576e977eaaSRafał Miłecki 586e977eaaSRafał Miłecki priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL); 596e977eaaSRafał Miłecki if (!priv->cells) 606e977eaaSRafał Miłecki return -ENOMEM; 616e977eaaSRafał Miłecki 626e977eaaSRafał Miłecki for (var = data + sizeof(struct brcm_nvram_header), idx = 0; 636e977eaaSRafał Miłecki var < (char *)data + len && *var; 646e977eaaSRafał Miłecki var = value + strlen(value) + 1, idx++) { 656e977eaaSRafał Miłecki eq = strchr(var, '='); 666e977eaaSRafał Miłecki if (!eq) 676e977eaaSRafał Miłecki break; 686e977eaaSRafał Miłecki *eq = '\0'; 696e977eaaSRafał Miłecki value = eq + 1; 706e977eaaSRafał Miłecki 716e977eaaSRafał Miłecki priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL); 726e977eaaSRafał Miłecki if (!priv->cells[idx].name) 736e977eaaSRafał Miłecki return -ENOMEM; 746e977eaaSRafał Miłecki priv->cells[idx].offset = value - (char *)data; 756e977eaaSRafał Miłecki priv->cells[idx].bytes = strlen(value); 76*207775f7SRafał Miłecki priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name); 776e977eaaSRafał Miłecki } 786e977eaaSRafał Miłecki 796e977eaaSRafał Miłecki return 0; 806e977eaaSRafał Miłecki } 816e977eaaSRafał Miłecki 826e977eaaSRafał Miłecki static int brcm_nvram_parse(struct brcm_nvram *priv) 836e977eaaSRafał Miłecki { 846e977eaaSRafał Miłecki struct device *dev = priv->dev; 856e977eaaSRafał Miłecki struct brcm_nvram_header header; 866e977eaaSRafał Miłecki uint8_t *data; 876e977eaaSRafał Miłecki size_t len; 886e977eaaSRafał Miłecki int err; 896e977eaaSRafał Miłecki 906e977eaaSRafał Miłecki memcpy_fromio(&header, priv->base, sizeof(header)); 916e977eaaSRafał Miłecki 926e977eaaSRafał Miłecki if (memcmp(header.magic, NVRAM_MAGIC, 4)) { 936e977eaaSRafał Miłecki dev_err(dev, "Invalid NVRAM magic\n"); 946e977eaaSRafał Miłecki return -EINVAL; 956e977eaaSRafał Miłecki } 966e977eaaSRafał Miłecki 976e977eaaSRafał Miłecki len = le32_to_cpu(header.len); 986e977eaaSRafał Miłecki 996e977eaaSRafał Miłecki data = kcalloc(1, len, GFP_KERNEL); 1006e977eaaSRafał Miłecki memcpy_fromio(data, priv->base, len); 1016e977eaaSRafał Miłecki data[len - 1] = '\0'; 1026e977eaaSRafał Miłecki 1036e977eaaSRafał Miłecki err = brcm_nvram_add_cells(priv, data, len); 1046e977eaaSRafał Miłecki if (err) { 1056e977eaaSRafał Miłecki dev_err(dev, "Failed to add cells: %d\n", err); 1066e977eaaSRafał Miłecki return err; 1076e977eaaSRafał Miłecki } 1086e977eaaSRafał Miłecki 1096e977eaaSRafał Miłecki kfree(data); 1106e977eaaSRafał Miłecki 1116e977eaaSRafał Miłecki return 0; 1126e977eaaSRafał Miłecki } 1136e977eaaSRafał Miłecki 1143fef9ed0SRafał Miłecki static int brcm_nvram_probe(struct platform_device *pdev) 1153fef9ed0SRafał Miłecki { 1163fef9ed0SRafał Miłecki struct nvmem_config config = { 1173fef9ed0SRafał Miłecki .name = "brcm-nvram", 1183fef9ed0SRafał Miłecki .reg_read = brcm_nvram_read, 1193fef9ed0SRafał Miłecki }; 1203fef9ed0SRafał Miłecki struct device *dev = &pdev->dev; 1213fef9ed0SRafał Miłecki struct resource *res; 1223fef9ed0SRafał Miłecki struct brcm_nvram *priv; 1236e977eaaSRafał Miłecki int err; 1243fef9ed0SRafał Miłecki 1253fef9ed0SRafał Miłecki priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 1263fef9ed0SRafał Miłecki if (!priv) 1273fef9ed0SRafał Miłecki return -ENOMEM; 1283fef9ed0SRafał Miłecki priv->dev = dev; 1293fef9ed0SRafał Miłecki 1303fef9ed0SRafał Miłecki res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1313fef9ed0SRafał Miłecki priv->base = devm_ioremap_resource(dev, res); 1323fef9ed0SRafał Miłecki if (IS_ERR(priv->base)) 1333fef9ed0SRafał Miłecki return PTR_ERR(priv->base); 1343fef9ed0SRafał Miłecki 1356e977eaaSRafał Miłecki err = brcm_nvram_parse(priv); 1366e977eaaSRafał Miłecki if (err) 1376e977eaaSRafał Miłecki return err; 1386e977eaaSRafał Miłecki 1393fef9ed0SRafał Miłecki config.dev = dev; 1406e977eaaSRafał Miłecki config.cells = priv->cells; 1416e977eaaSRafał Miłecki config.ncells = priv->ncells; 1423fef9ed0SRafał Miłecki config.priv = priv; 1433fef9ed0SRafał Miłecki config.size = resource_size(res); 1443fef9ed0SRafał Miłecki 1453fef9ed0SRafał Miłecki return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config)); 1463fef9ed0SRafał Miłecki } 1473fef9ed0SRafał Miłecki 1483fef9ed0SRafał Miłecki static const struct of_device_id brcm_nvram_of_match_table[] = { 1493fef9ed0SRafał Miłecki { .compatible = "brcm,nvram", }, 1503fef9ed0SRafał Miłecki {}, 1513fef9ed0SRafał Miłecki }; 1523fef9ed0SRafał Miłecki 1533fef9ed0SRafał Miłecki static struct platform_driver brcm_nvram_driver = { 1543fef9ed0SRafał Miłecki .probe = brcm_nvram_probe, 1553fef9ed0SRafał Miłecki .driver = { 1563fef9ed0SRafał Miłecki .name = "brcm_nvram", 1573fef9ed0SRafał Miłecki .of_match_table = brcm_nvram_of_match_table, 1583fef9ed0SRafał Miłecki }, 1593fef9ed0SRafał Miłecki }; 1603fef9ed0SRafał Miłecki 1613fef9ed0SRafał Miłecki static int __init brcm_nvram_init(void) 1623fef9ed0SRafał Miłecki { 1633fef9ed0SRafał Miłecki return platform_driver_register(&brcm_nvram_driver); 1643fef9ed0SRafał Miłecki } 1653fef9ed0SRafał Miłecki 1663fef9ed0SRafał Miłecki subsys_initcall_sync(brcm_nvram_init); 1673fef9ed0SRafał Miłecki 1683fef9ed0SRafał Miłecki MODULE_AUTHOR("Rafał Miłecki"); 1693fef9ed0SRafał Miłecki MODULE_LICENSE("GPL"); 1703fef9ed0SRafał Miłecki MODULE_DEVICE_TABLE(of, brcm_nvram_of_match_table); 171