kernel: backport upstream brcm_nvram fix for NAND controller
authorRafał Miłecki <rafal@milecki.pl>
Sun, 17 Dec 2023 20:23:44 +0000 (21:23 +0100)
committerRafał Miłecki <rafal@milecki.pl>
Mon, 18 Dec 2023 07:33:04 +0000 (08:33 +0100)
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
target/linux/bcm53xx/patches-5.15/181-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch [deleted file]
target/linux/bcm53xx/patches-6.1/181-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch [deleted file]
target/linux/generic/backport-5.15/820-v6.7-0006-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/816-v6.7-0006-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch [new file with mode: 0644]

diff --git a/target/linux/bcm53xx/patches-5.15/181-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch b/target/linux/bcm53xx/patches-5.15/181-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch
deleted file mode 100644 (file)
index cceb363..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-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));
- }
diff --git a/target/linux/bcm53xx/patches-6.1/181-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch b/target/linux/bcm53xx/patches-6.1/181-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch
deleted file mode 100644 (file)
index cceb363..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-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));
- }
diff --git a/target/linux/generic/backport-5.15/820-v6.7-0006-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch b/target/linux/generic/backport-5.15/820-v6.7-0006-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch
new file mode 100644 (file)
index 0000000..d49a205
--- /dev/null
@@ -0,0 +1,261 @@
+From 1e37bf84afacd5ba17b7a13a18ca2bc78aff05c0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 15 Dec 2023 11:13:58 +0000
+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:  <Stable@vger.kernel.org>
+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>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111358.316727-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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));
+ }
diff --git a/target/linux/generic/backport-6.1/816-v6.7-0006-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch b/target/linux/generic/backport-6.1/816-v6.7-0006-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch
new file mode 100644 (file)
index 0000000..d49a205
--- /dev/null
@@ -0,0 +1,261 @@
+From 1e37bf84afacd5ba17b7a13a18ca2bc78aff05c0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 15 Dec 2023 11:13:58 +0000
+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:  <Stable@vger.kernel.org>
+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>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111358.316727-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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));
+ }