bcm53xx: add the latest fix version of brcm_nvram
It was just sent for upstream.
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
(cherry picked from commit 943bd3c9f6)
			
			
This commit is contained in:
		@@ -0,0 +1,256 @@
 | 
				
			|||||||
 | 
					From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
 | 
				
			||||||
 | 
					Date: Thu, 14 Sep 2023 07:59:09 +0200
 | 
				
			||||||
 | 
					Subject: [PATCH] nvmem: brcm_nvram: store a copy of NVRAM content
 | 
				
			||||||
 | 
					MIME-Version: 1.0
 | 
				
			||||||
 | 
					Content-Type: text/plain; charset=UTF-8
 | 
				
			||||||
 | 
					Content-Transfer-Encoding: 8bit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This driver uses MMIO access for reading NVRAM from a flash device.
 | 
				
			||||||
 | 
					Underneath there is a flash controller that reads data and provides
 | 
				
			||||||
 | 
					mapping window.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Using MMIO interface affects controller configuration and may break real
 | 
				
			||||||
 | 
					controller driver. It was reported by multiple users of devices with
 | 
				
			||||||
 | 
					NVRAM stored on NAND.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Modify driver to read & cache NVRAM content during init and use that
 | 
				
			||||||
 | 
					copy to provide NVMEM data when requested. On NAND flashes due to their
 | 
				
			||||||
 | 
					alignment NVRAM partitions can be quite big (1 MiB and more) while
 | 
				
			||||||
 | 
					actual NVRAM content stays quite small (usually 16 to 32 KiB). To avoid
 | 
				
			||||||
 | 
					allocating so much memory check for actual data length.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Link: https://lore.kernel.org/linux-mtd/CACna6rwf3_9QVjYcM+847biTX=K0EoWXuXcSMkJO1Vy_5vmVqA@mail.gmail.com/
 | 
				
			||||||
 | 
					Fixes: 3fef9ed0627a ("nvmem: brcm_nvram: new driver exposing Broadcom's NVRAM")
 | 
				
			||||||
 | 
					Cc: Arınç ÜNAL <arinc.unal@arinc9.com>
 | 
				
			||||||
 | 
					Cc: Florian Fainelli <florian.fainelli@broadcom.com>
 | 
				
			||||||
 | 
					Cc: Scott Branden <scott.branden@broadcom.com>
 | 
				
			||||||
 | 
					Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
 | 
				
			||||||
 | 
					Acked-by: Arınç ÜNAL <arinc.unal@arinc9.com>
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					 drivers/nvmem/brcm_nvram.c | 134 ++++++++++++++++++++++++++-----------
 | 
				
			||||||
 | 
					 1 file changed, 94 insertions(+), 40 deletions(-)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- a/drivers/nvmem/brcm_nvram.c
 | 
				
			||||||
 | 
					+++ b/drivers/nvmem/brcm_nvram.c
 | 
				
			||||||
 | 
					@@ -17,9 +17,23 @@
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 #define NVRAM_MAGIC			"FLSH"
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+/**
 | 
				
			||||||
 | 
					+ * struct brcm_nvram - driver state internal struct
 | 
				
			||||||
 | 
					+ *
 | 
				
			||||||
 | 
					+ * @dev:		NVMEM device pointer
 | 
				
			||||||
 | 
					+ * @nvmem_size:		Size of the whole space available for NVRAM
 | 
				
			||||||
 | 
					+ * @data:		NVRAM data copy stored to avoid poking underlaying flash controller
 | 
				
			||||||
 | 
					+ * @data_len:		NVRAM data size
 | 
				
			||||||
 | 
					+ * @padding_byte:	Padding value used to fill remaining space
 | 
				
			||||||
 | 
					+ * @cells:		Array of discovered NVMEM cells
 | 
				
			||||||
 | 
					+ * @ncells:		Number of elements in cells
 | 
				
			||||||
 | 
					+ */
 | 
				
			||||||
 | 
					 struct brcm_nvram {
 | 
				
			||||||
 | 
					 	struct device *dev;
 | 
				
			||||||
 | 
					-	void __iomem *base;
 | 
				
			||||||
 | 
					+	size_t nvmem_size;
 | 
				
			||||||
 | 
					+	uint8_t *data;
 | 
				
			||||||
 | 
					+	size_t data_len;
 | 
				
			||||||
 | 
					+	uint8_t padding_byte;
 | 
				
			||||||
 | 
					 	struct nvmem_cell_info *cells;
 | 
				
			||||||
 | 
					 	int ncells;
 | 
				
			||||||
 | 
					 };
 | 
				
			||||||
 | 
					@@ -36,10 +50,47 @@ static int brcm_nvram_read(void *context
 | 
				
			||||||
 | 
					 			   size_t bytes)
 | 
				
			||||||
 | 
					 {
 | 
				
			||||||
 | 
					 	struct brcm_nvram *priv = context;
 | 
				
			||||||
 | 
					-	u8 *dst = val;
 | 
				
			||||||
 | 
					+	size_t to_copy;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	if (offset + bytes > priv->data_len)
 | 
				
			||||||
 | 
					+		to_copy = max_t(ssize_t, (ssize_t)priv->data_len - offset, 0);
 | 
				
			||||||
 | 
					+	else
 | 
				
			||||||
 | 
					+		to_copy = bytes;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	memcpy(val, priv->data + offset, to_copy);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	memset((uint8_t *)val + to_copy, priv->padding_byte, bytes - to_copy);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	return 0;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static int brcm_nvram_copy_data(struct brcm_nvram *priv, struct platform_device *pdev)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	struct resource *res;
 | 
				
			||||||
 | 
					+	void __iomem *base;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
 | 
				
			||||||
 | 
					+	if (IS_ERR(base))
 | 
				
			||||||
 | 
					+		return PTR_ERR(base);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	priv->nvmem_size = resource_size(res);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	priv->padding_byte = readb(base + priv->nvmem_size - 1);
 | 
				
			||||||
 | 
					+	for (priv->data_len = priv->nvmem_size;
 | 
				
			||||||
 | 
					+	     priv->data_len;
 | 
				
			||||||
 | 
					+	     priv->data_len--) {
 | 
				
			||||||
 | 
					+		if (readb(base + priv->data_len - 1) != priv->padding_byte)
 | 
				
			||||||
 | 
					+			break;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+	WARN(priv->data_len > SZ_128K, "Unexpected (big) NVRAM size: %zu B\n", priv->data_len);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-	while (bytes--)
 | 
				
			||||||
 | 
					-		*dst++ = readb(priv->base + offset++);
 | 
				
			||||||
 | 
					+	priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL);
 | 
				
			||||||
 | 
					+	if (!priv->data)
 | 
				
			||||||
 | 
					+		return -ENOMEM;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	memcpy_fromio(priv->data, base, priv->data_len);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	bcm47xx_nvram_init_from_iomem(base, priv->data_len);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 	return 0;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					@@ -67,8 +118,13 @@ static int brcm_nvram_add_cells(struct b
 | 
				
			||||||
 | 
					 				size_t len)
 | 
				
			||||||
 | 
					 {
 | 
				
			||||||
 | 
					 	struct device *dev = priv->dev;
 | 
				
			||||||
 | 
					-	char *var, *value, *eq;
 | 
				
			||||||
 | 
					+	char *var, *value;
 | 
				
			||||||
 | 
					+	uint8_t tmp;
 | 
				
			||||||
 | 
					 	int idx;
 | 
				
			||||||
 | 
					+	int err = 0;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	tmp = priv->data[len - 1];
 | 
				
			||||||
 | 
					+	priv->data[len - 1] = '\0';
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 	priv->ncells = 0;
 | 
				
			||||||
 | 
					 	for (var = data + sizeof(struct brcm_nvram_header);
 | 
				
			||||||
 | 
					@@ -78,67 +134,68 @@ static int brcm_nvram_add_cells(struct b
 | 
				
			||||||
 | 
					 	}
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 	priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL);
 | 
				
			||||||
 | 
					-	if (!priv->cells)
 | 
				
			||||||
 | 
					-		return -ENOMEM;
 | 
				
			||||||
 | 
					+	if (!priv->cells) {
 | 
				
			||||||
 | 
					+		err = -ENOMEM;
 | 
				
			||||||
 | 
					+		goto out;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 	for (var = data + sizeof(struct brcm_nvram_header), idx = 0;
 | 
				
			||||||
 | 
					 	     var < (char *)data + len && *var;
 | 
				
			||||||
 | 
					 	     var = value + strlen(value) + 1, idx++) {
 | 
				
			||||||
 | 
					+		char *eq, *name;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					 		eq = strchr(var, '=');
 | 
				
			||||||
 | 
					 		if (!eq)
 | 
				
			||||||
 | 
					 			break;
 | 
				
			||||||
 | 
					 		*eq = '\0';
 | 
				
			||||||
 | 
					+		name = devm_kstrdup(dev, var, GFP_KERNEL);
 | 
				
			||||||
 | 
					+		*eq = '=';
 | 
				
			||||||
 | 
					+		if (!name) {
 | 
				
			||||||
 | 
					+			err = -ENOMEM;
 | 
				
			||||||
 | 
					+			goto out;
 | 
				
			||||||
 | 
					+		}
 | 
				
			||||||
 | 
					 		value = eq + 1;
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-		priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL);
 | 
				
			||||||
 | 
					-		if (!priv->cells[idx].name)
 | 
				
			||||||
 | 
					-			return -ENOMEM;
 | 
				
			||||||
 | 
					+		priv->cells[idx].name = name;
 | 
				
			||||||
 | 
					 		priv->cells[idx].offset = value - (char *)data;
 | 
				
			||||||
 | 
					 		priv->cells[idx].bytes = strlen(value);
 | 
				
			||||||
 | 
					 		priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name);
 | 
				
			||||||
 | 
					-		if (!strcmp(var, "et0macaddr") ||
 | 
				
			||||||
 | 
					-		    !strcmp(var, "et1macaddr") ||
 | 
				
			||||||
 | 
					-		    !strcmp(var, "et2macaddr")) {
 | 
				
			||||||
 | 
					+		if (!strcmp(name, "et0macaddr") ||
 | 
				
			||||||
 | 
					+		    !strcmp(name, "et1macaddr") ||
 | 
				
			||||||
 | 
					+		    !strcmp(name, "et2macaddr")) {
 | 
				
			||||||
 | 
					 			priv->cells[idx].raw_len = strlen(value);
 | 
				
			||||||
 | 
					 			priv->cells[idx].bytes = ETH_ALEN;
 | 
				
			||||||
 | 
					 			priv->cells[idx].read_post_process = brcm_nvram_read_post_process_macaddr;
 | 
				
			||||||
 | 
					 		}
 | 
				
			||||||
 | 
					 	}
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-	return 0;
 | 
				
			||||||
 | 
					+out:
 | 
				
			||||||
 | 
					+	priv->data[len - 1] = tmp;
 | 
				
			||||||
 | 
					+	return err;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 static int brcm_nvram_parse(struct brcm_nvram *priv)
 | 
				
			||||||
 | 
					 {
 | 
				
			||||||
 | 
					+	struct brcm_nvram_header *header = (struct brcm_nvram_header *)priv->data;
 | 
				
			||||||
 | 
					 	struct device *dev = priv->dev;
 | 
				
			||||||
 | 
					-	struct brcm_nvram_header header;
 | 
				
			||||||
 | 
					-	uint8_t *data;
 | 
				
			||||||
 | 
					 	size_t len;
 | 
				
			||||||
 | 
					 	int err;
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-	memcpy_fromio(&header, priv->base, sizeof(header));
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					-	if (memcmp(header.magic, NVRAM_MAGIC, 4)) {
 | 
				
			||||||
 | 
					+	if (memcmp(header->magic, NVRAM_MAGIC, 4)) {
 | 
				
			||||||
 | 
					 		dev_err(dev, "Invalid NVRAM magic\n");
 | 
				
			||||||
 | 
					 		return -EINVAL;
 | 
				
			||||||
 | 
					 	}
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-	len = le32_to_cpu(header.len);
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					-	data = kzalloc(len, GFP_KERNEL);
 | 
				
			||||||
 | 
					-	if (!data)
 | 
				
			||||||
 | 
					-		return -ENOMEM;
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					-	memcpy_fromio(data, priv->base, len);
 | 
				
			||||||
 | 
					-	data[len - 1] = '\0';
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					-	err = brcm_nvram_add_cells(priv, data, len);
 | 
				
			||||||
 | 
					-	if (err) {
 | 
				
			||||||
 | 
					-		dev_err(dev, "Failed to add cells: %d\n", err);
 | 
				
			||||||
 | 
					-		return err;
 | 
				
			||||||
 | 
					+	len = le32_to_cpu(header->len);
 | 
				
			||||||
 | 
					+	if (len > priv->nvmem_size) {
 | 
				
			||||||
 | 
					+		dev_err(dev, "NVRAM length (%zd) exceeds mapped size (%zd)\n", len,
 | 
				
			||||||
 | 
					+			priv->nvmem_size);
 | 
				
			||||||
 | 
					+		return -EINVAL;
 | 
				
			||||||
 | 
					 	}
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-	kfree(data);
 | 
				
			||||||
 | 
					+	err = brcm_nvram_add_cells(priv, priv->data, len);
 | 
				
			||||||
 | 
					+	if (err)
 | 
				
			||||||
 | 
					+		dev_err(dev, "Failed to add cells: %d\n", err);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 	return 0;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					@@ -150,7 +207,6 @@ static int brcm_nvram_probe(struct platf
 | 
				
			||||||
 | 
					 		.reg_read = brcm_nvram_read,
 | 
				
			||||||
 | 
					 	};
 | 
				
			||||||
 | 
					 	struct device *dev = &pdev->dev;
 | 
				
			||||||
 | 
					-	struct resource *res;
 | 
				
			||||||
 | 
					 	struct brcm_nvram *priv;
 | 
				
			||||||
 | 
					 	int err;
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					@@ -159,21 +215,19 @@ static int brcm_nvram_probe(struct platf
 | 
				
			||||||
 | 
					 		return -ENOMEM;
 | 
				
			||||||
 | 
					 	priv->dev = dev;
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-	priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
 | 
				
			||||||
 | 
					-	if (IS_ERR(priv->base))
 | 
				
			||||||
 | 
					-		return PTR_ERR(priv->base);
 | 
				
			||||||
 | 
					+	err = brcm_nvram_copy_data(priv, pdev);
 | 
				
			||||||
 | 
					+	if (err)
 | 
				
			||||||
 | 
					+		return err;
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 	err = brcm_nvram_parse(priv);
 | 
				
			||||||
 | 
					 	if (err)
 | 
				
			||||||
 | 
					 		return err;
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-	bcm47xx_nvram_init_from_iomem(priv->base, resource_size(res));
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					 	config.dev = dev;
 | 
				
			||||||
 | 
					 	config.cells = priv->cells;
 | 
				
			||||||
 | 
					 	config.ncells = priv->ncells;
 | 
				
			||||||
 | 
					 	config.priv = priv;
 | 
				
			||||||
 | 
					-	config.size = resource_size(res);
 | 
				
			||||||
 | 
					+	config.size = priv->nvmem_size;
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 	return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
		Reference in New Issue
	
	Block a user