summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS7
-rw-r--r--arch/sandbox/dts/sandbox.dts1
-rw-r--r--arch/sandbox/dts/sandbox64.dts1
-rw-r--r--arch/sandbox/dts/test.dts34
-rw-r--r--board/sandbox/sandbox.env4
-rw-r--r--configs/sandbox64_defconfig1
-rw-r--r--configs/sandbox_defconfig1
-rw-r--r--configs/sandbox_flattree_defconfig1
-rw-r--r--configs/sandbox_noinst_defconfig1
-rw-r--r--configs/sandbox_spl_defconfig3
-rw-r--r--doc/api/index.rst1
-rw-r--r--doc/api/nvmem.rst10
-rw-r--r--drivers/misc/Kconfig16
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/i2c_eeprom.c3
-rw-r--r--drivers/misc/i2c_eeprom_emul.c4
-rw-r--r--drivers/misc/misc_sandbox.c3
-rw-r--r--drivers/misc/nvmem.c142
-rw-r--r--drivers/net/sandbox.c10
-rw-r--r--drivers/rtc/i2c_rtc_emul.c10
-rw-r--r--include/i2c_eeprom.h27
-rw-r--r--include/nvmem.h134
-rw-r--r--net/dsa-uclass.c6
-rw-r--r--net/eth-uclass.c13
-rw-r--r--test/dm/eth.c29
-rw-r--r--test/dm/test-fdt.c2
26 files changed, 436 insertions, 29 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 28e4d382386..4ccefb7a11c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1091,6 +1091,13 @@ F: cmd/nvme.c
F: include/nvme.h
F: doc/develop/driver-model/nvme.rst
+NVMEM
+M: Sean Anderson <seanga2@gmail.com>
+S: Maintained
+F: doc/api/nvmem.rst
+F: drivers/misc/nvmem.c
+F: include/nvmem.h
+
NXP C45 TJA11XX PHY DRIVER
M: Radu Pirea <radu-nicolae.pirea@oss.nxp.com>
S: Maintained
diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts
index 18fde1c8c6f..21f00fcab5e 100644
--- a/arch/sandbox/dts/sandbox.dts
+++ b/arch/sandbox/dts/sandbox.dts
@@ -63,7 +63,6 @@
eth@10002000 {
compatible = "sandbox,eth";
reg = <0x10002000 0x1000>;
- fake-host-hwaddr = [00 00 66 44 22 00];
};
host-fs {
diff --git a/arch/sandbox/dts/sandbox64.dts b/arch/sandbox/dts/sandbox64.dts
index ec53106af9d..3eb04570891 100644
--- a/arch/sandbox/dts/sandbox64.dts
+++ b/arch/sandbox/dts/sandbox64.dts
@@ -58,7 +58,6 @@
eth@10002000 {
compatible = "sandbox,eth";
reg = <0x0 0x10002000 0x0 0x1000>;
- fake-host-hwaddr = [00 00 66 44 22 00];
};
i2c_0: i2c@0 {
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 8f93775ff4a..e068d0c8c56 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -28,6 +28,9 @@
ethernet3 = &eth_3;
ethernet4 = &dsa_eth0;
ethernet5 = &eth_5;
+ ethernet6 = "/eth@10004000";
+ ethernet7 = &swp_1;
+ ethernet8 = &phy_eth0;
gpio1 = &gpio_a;
gpio2 = &gpio_b;
gpio3 = &gpio_c;
@@ -524,31 +527,31 @@
eth@10002000 {
compatible = "sandbox,eth";
reg = <0x10002000 0x1000>;
- fake-host-hwaddr = [00 00 66 44 22 00];
};
eth_5: eth@10003000 {
compatible = "sandbox,eth";
reg = <0x10003000 0x1000>;
- fake-host-hwaddr = [00 00 66 44 22 11];
+ nvmem-cells = <&eth5_addr>;
+ nvmem-cell-names = "mac-address";
};
eth_3: sbe5 {
compatible = "sandbox,eth";
reg = <0x10005000 0x1000>;
- fake-host-hwaddr = [00 00 66 44 22 33];
+ nvmem-cells = <&eth3_addr>;
+ nvmem-cell-names = "mac-address";
};
eth@10004000 {
compatible = "sandbox,eth";
reg = <0x10004000 0x1000>;
- fake-host-hwaddr = [00 00 66 44 22 22];
};
phy_eth0: phy-test-eth {
compatible = "sandbox,eth";
reg = <0x10007000 0x1000>;
- fake-host-hwaddr = [00 00 66 44 22 77];
+ mac-address = [ 02 00 11 22 33 49 ];
phy-handle = <&ethphy1>;
phy-mode = "2500base-x";
};
@@ -556,7 +559,8 @@
dsa_eth0: dsa-test-eth {
compatible = "sandbox,eth";
reg = <0x10006000 0x1000>;
- fake-host-hwaddr = [00 00 66 44 22 66];
+ nvmem-cells = <&eth4_addr>;
+ nvmem-cell-names = "mac-address";
};
dsa-test {
@@ -700,6 +704,8 @@
pinctrl-0 = <&pinmux_i2c0_pins>;
eeprom@2c {
+ #address-cells = <1>;
+ #size-cells = <1>;
reg = <0x2c>;
compatible = "i2c-eeprom";
sandbox,emul = <&emul_eeprom>;
@@ -711,12 +717,22 @@
reg = <10 2>;
};
};
+
+ eth3_addr: mac-address@24 {
+ reg = <24 6>;
+ };
};
rtc_0: rtc@43 {
+ #address-cells = <1>;
+ #size-cells = <1>;
reg = <0x43>;
compatible = "sandbox-rtc";
sandbox,emul = <&emul0>;
+
+ eth4_addr: mac-address@40 {
+ reg = <0x40 6>;
+ };
};
rtc_1: rtc@61 {
@@ -898,7 +914,13 @@
};
misc-test {
+ #address-cells = <1>;
+ #size-cells = <1>;
compatible = "sandbox,misc_sandbox";
+
+ eth5_addr: mac-address@10 {
+ reg = <0x10 6>;
+ };
};
mmc2 {
diff --git a/board/sandbox/sandbox.env b/board/sandbox/sandbox.env
index b4c04635a48..a2c19702d64 100644
--- a/board/sandbox/sandbox.env
+++ b/board/sandbox/sandbox.env
@@ -6,10 +6,6 @@ stdout=serial,vidconsole
stderr=serial,vidconsole
ethaddr=02:00:11:22:33:44
-eth2addr=02:00:11:22:33:48
-eth3addr=02:00:11:22:33:45
-eth4addr=02:00:11:22:33:48
-eth5addr=02:00:11:22:33:46
eth6addr=02:00:11:22:33:47
ipaddr=192.0.2.1
diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
index d7f22b39ae5..fc7e100eaaf 100644
--- a/configs/sandbox64_defconfig
+++ b/configs/sandbox64_defconfig
@@ -144,6 +144,7 @@ CONFIG_LED_GPIO=y
CONFIG_DM_MAILBOX=y
CONFIG_SANDBOX_MBOX=y
CONFIG_MISC=y
+CONFIG_NVMEM=y
CONFIG_CROS_EC=y
CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_LPC=y
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index c509a924e6b..d10cd289884 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -188,6 +188,7 @@ CONFIG_LED_GPIO=y
CONFIG_DM_MAILBOX=y
CONFIG_SANDBOX_MBOX=y
CONFIG_MISC=y
+CONFIG_NVMEM=y
CONFIG_CROS_EC=y
CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_LPC=y
diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig
index 80a4be47eba..6eb4e348a26 100644
--- a/configs/sandbox_flattree_defconfig
+++ b/configs/sandbox_flattree_defconfig
@@ -117,6 +117,7 @@ CONFIG_LED_GPIO=y
CONFIG_DM_MAILBOX=y
CONFIG_SANDBOX_MBOX=y
CONFIG_MISC=y
+CONFIG_NVMEM=y
CONFIG_CROS_EC=y
CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_LPC=y
diff --git a/configs/sandbox_noinst_defconfig b/configs/sandbox_noinst_defconfig
index c718374ed58..9ee70c29c1a 100644
--- a/configs/sandbox_noinst_defconfig
+++ b/configs/sandbox_noinst_defconfig
@@ -144,6 +144,7 @@ CONFIG_LED_GPIO=y
CONFIG_DM_MAILBOX=y
CONFIG_SANDBOX_MBOX=y
CONFIG_MISC=y
+CONFIG_NVMEM=y
CONFIG_CROS_EC=y
CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_LPC=y
diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig
index 45a6b81a3d2..ec2d26d4436 100644
--- a/configs/sandbox_spl_defconfig
+++ b/configs/sandbox_spl_defconfig
@@ -145,6 +145,8 @@ CONFIG_LED_GPIO=y
CONFIG_DM_MAILBOX=y
CONFIG_SANDBOX_MBOX=y
CONFIG_MISC=y
+CONFIG_NVMEM=y
+CONFIG_SPL_NVMEM=y
CONFIG_CROS_EC=y
CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_LPC=y
@@ -153,6 +155,7 @@ CONFIG_CROS_EC_SPI=y
CONFIG_P2SB=y
CONFIG_PWRSEQ=y
CONFIG_SPL_PWRSEQ=y
+CONFIG_I2C_EEPROM=y
CONFIG_MMC_SANDBOX=y
CONFIG_SPI_FLASH_SANDBOX=y
CONFIG_SPI_FLASH_ATMEL=y
diff --git a/doc/api/index.rst b/doc/api/index.rst
index 72fea981b72..a9338cfef9f 100644
--- a/doc/api/index.rst
+++ b/doc/api/index.rst
@@ -14,6 +14,7 @@ U-Boot API documentation
linker_lists
lmb
logging
+ nvmem
pinctrl
rng
sandbox
diff --git a/doc/api/nvmem.rst b/doc/api/nvmem.rst
new file mode 100644
index 00000000000..d9237846524
--- /dev/null
+++ b/doc/api/nvmem.rst
@@ -0,0 +1,10 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+NVMEM API
+=========
+
+.. kernel-doc:: include/nvmem.h
+ :doc: Design
+
+.. kernel-doc:: include/nvmem.h
+ :internal:
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 419ddd31c0b..f368d52c561 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -43,6 +43,22 @@ config VPL_MISC
set of generic read, write and ioctl methods may be used to
access the device.
+config NVMEM
+ bool "NVMEM support"
+ help
+ This adds support for a common interface to different types of
+ non-volatile memory. Consumers can use nvmem-cells properties to look
+ up hardware configuration data such as MAC addresses and calibration
+ settings.
+
+config SPL_NVMEM
+ bool "NVMEM support in SPL"
+ help
+ This adds support for a common interface to different types of
+ non-volatile memory. Consumers can use nvmem-cells properties to look
+ up hardware configuration data such as MAC addresses and calibration
+ settings.
+
config ALTERA_SYSID
bool "Altera Sysid support"
depends on MISC
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7a6047f64f9..6c790308937 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -4,6 +4,7 @@
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
obj-$(CONFIG_$(SPL_TPL_)MISC) += misc-uclass.o
+obj-$(CONFIG_$(SPL_TPL_)NVMEM) += nvmem.o
obj-$(CONFIG_$(SPL_TPL_)CROS_EC) += cros_ec.o
obj-$(CONFIG_$(SPL_TPL_)CROS_EC_SANDBOX) += cros_ec_sandbox.o
diff --git a/drivers/misc/i2c_eeprom.c b/drivers/misc/i2c_eeprom.c
index 89a450d0f8d..4302e180acd 100644
--- a/drivers/misc/i2c_eeprom.c
+++ b/drivers/misc/i2c_eeprom.c
@@ -33,7 +33,8 @@ int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf, int size)
return ops->read(dev, offset, buf, size);
}
-int i2c_eeprom_write(struct udevice *dev, int offset, uint8_t *buf, int size)
+int i2c_eeprom_write(struct udevice *dev, int offset, const uint8_t *buf,
+ int size)
{
const struct i2c_eeprom_ops *ops = device_get_ops(dev);
diff --git a/drivers/misc/i2c_eeprom_emul.c b/drivers/misc/i2c_eeprom_emul.c
index 85b127c406c..6f32087ede5 100644
--- a/drivers/misc/i2c_eeprom_emul.c
+++ b/drivers/misc/i2c_eeprom_emul.c
@@ -171,11 +171,15 @@ static int sandbox_i2c_eeprom_probe(struct udevice *dev)
{
struct sandbox_i2c_flash_plat_data *plat = dev_get_plat(dev);
struct sandbox_i2c_flash *priv = dev_get_priv(dev);
+ /* For eth3 */
+ const u8 mac[] = { 0x02, 0x00, 0x11, 0x22, 0x33, 0x45 };
priv->data = calloc(1, plat->size);
if (!priv->data)
return -ENOMEM;
+ memcpy(&priv->data[24], mac, sizeof(mac));
+
return 0;
}
diff --git a/drivers/misc/misc_sandbox.c b/drivers/misc/misc_sandbox.c
index 0e4292fd0aa..31cde2dbac0 100644
--- a/drivers/misc/misc_sandbox.c
+++ b/drivers/misc/misc_sandbox.c
@@ -112,8 +112,11 @@ static const struct misc_ops misc_sandbox_ops = {
int misc_sandbox_probe(struct udevice *dev)
{
struct misc_sandbox_priv *priv = dev_get_priv(dev);
+ /* For eth5 */
+ const u8 mac[] = { 0x02, 0x00, 0x11, 0x22, 0x33, 0x46 };
priv->enabled = true;
+ memcpy(&priv->mem[16], mac, sizeof(mac));
return 0;
}
diff --git a/drivers/misc/nvmem.c b/drivers/misc/nvmem.c
new file mode 100644
index 00000000000..5a2bd1f9f72
--- /dev/null
+++ b/drivers/misc/nvmem.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
+ */
+
+#include <common.h>
+#include <i2c_eeprom.h>
+#include <linker_lists.h>
+#include <misc.h>
+#include <nvmem.h>
+#include <rtc.h>
+#include <dm/device_compat.h>
+#include <dm/ofnode.h>
+#include <dm/read.h>
+#include <dm/uclass.h>
+
+int nvmem_cell_read(struct nvmem_cell *cell, void *buf, size_t size)
+{
+ dev_dbg(cell->nvmem, "%s: off=%u size=%zu\n", __func__, cell->offset, size);
+ if (size != cell->size)
+ return -EINVAL;
+
+ switch (cell->nvmem->driver->id) {
+ case UCLASS_I2C_EEPROM:
+ return i2c_eeprom_read(cell->nvmem, cell->offset, buf, size);
+ case UCLASS_MISC: {
+ int ret = misc_read(cell->nvmem, cell->offset, buf, size);
+
+ if (ret < 0)
+ return ret;
+ if (ret != size)
+ return -EIO;
+ return 0;
+ }
+ case UCLASS_RTC:
+ return dm_rtc_read(cell->nvmem, cell->offset, buf, size);
+ default:
+ return -ENOSYS;
+ }
+}
+
+int nvmem_cell_write(struct nvmem_cell *cell, const void *buf, size_t size)
+{
+ dev_dbg(cell->nvmem, "%s: off=%u size=%zu\n", __func__, cell->offset, size);
+ if (size != cell->size)
+ return -EINVAL;
+
+ switch (cell->nvmem->driver->id) {
+ case UCLASS_I2C_EEPROM:
+ return i2c_eeprom_write(cell->nvmem, cell->offset, buf, size);
+ case UCLASS_MISC: {
+ int ret = misc_write(cell->nvmem, cell->offset, buf, size);
+
+ if (ret < 0)
+ return ret;
+ if (ret != size)
+ return -EIO;
+ return 0;
+ }
+ case UCLASS_RTC:
+ return dm_rtc_write(cell->nvmem, cell->offset, buf, size);
+ default:
+ return -ENOSYS;
+ }
+}
+
+/**
+ * nvmem_get_device() - Get an nvmem device for a cell
+ * @node: ofnode of the nvmem device
+ * @cell: Cell to look up
+ *
+ * Try to find a nvmem-compatible device by going through the nvmem interfaces.
+ *
+ * Return:
+ * * 0 on success
+ * * -ENODEV if we didn't find anything
+ * * A negative error if there was a problem looking up the device
+ */
+static int nvmem_get_device(ofnode node, struct nvmem_cell *cell)
+{
+ int i, ret;
+ enum uclass_id ids[] = {
+ UCLASS_I2C_EEPROM,
+ UCLASS_MISC,
+ UCLASS_RTC,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(ids); i++) {
+ ret = uclass_get_device_by_ofnode(ids[i], node, &cell->nvmem);
+ if (!ret)
+ return 0;
+ if (ret != -ENODEV && ret != -EPFNOSUPPORT)
+ return ret;
+ }
+
+ return -ENODEV;
+}
+
+int nvmem_cell_get_by_index(struct udevice *dev, int index,
+ struct nvmem_cell *cell)
+{
+ fdt_addr_t offset;
+ fdt_size_t size = FDT_SIZE_T_NONE;
+ int ret;
+ struct ofnode_phandle_args args;
+
+ dev_dbg(dev, "%s: index=%d\n", __func__, index);
+
+ ret = dev_read_phandle_with_args(dev, "nvmem-cells", NULL, 0, index,
+ &args);
+ if (ret)
+ return ret;
+
+ ret = nvmem_get_device(ofnode_get_parent(args.node), cell);
+ if (ret)
+ return ret;
+
+ offset = ofnode_get_addr_size_index_notrans(args.node, 0, &size);
+ if (offset == FDT_ADDR_T_NONE || size == FDT_SIZE_T_NONE) {
+ dev_dbg(cell->nvmem, "missing address or size for %s\n",
+ ofnode_get_name(args.node));
+ return -EINVAL;
+ }
+
+ cell->offset = offset;
+ cell->size = size;
+ return 0;
+}
+
+int nvmem_cell_get_by_name(struct udevice *dev, const char *name,
+ struct nvmem_cell *cell)
+{
+ int index;
+
+ dev_dbg(dev, "%s, name=%s\n", __func__, name);
+
+ index = dev_read_stringlist_search(dev, "nvmem-cell-names", name);
+ if (index < 0)
+ return index;
+
+ return nvmem_cell_get_by_index(dev, index, cell);
+}
diff --git a/drivers/net/sandbox.c b/drivers/net/sandbox.c
index 37459dfa0a4..13022addb6a 100644
--- a/drivers/net/sandbox.c
+++ b/drivers/net/sandbox.c
@@ -395,9 +395,11 @@ static void sb_eth_stop(struct udevice *dev)
static int sb_eth_write_hwaddr(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_plat(dev);
+ struct eth_sandbox_priv *priv = dev_get_priv(dev);
debug("eth_sandbox %s: Write HW ADDR - %pM\n", dev->name,
pdata->enetaddr);
+ memcpy(priv->fake_host_hwaddr, pdata->enetaddr, ARP_HLEN);
return 0;
}
@@ -419,16 +421,8 @@ static int sb_eth_of_to_plat(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_plat(dev);
struct eth_sandbox_priv *priv = dev_get_priv(dev);
- const u8 *mac;
pdata->iobase = dev_read_addr(dev);
-
- mac = dev_read_u8_array_ptr(dev, "fake-host-hwaddr", ARP_HLEN);
- if (!mac) {
- printf("'fake-host-hwaddr' is missing from the DT\n");
- return -EINVAL;
- }
- memcpy(priv->fake_host_hwaddr, mac, ARP_HLEN);
priv->disabled = false;
priv->tx_handler = sb_default_handler;
diff --git a/drivers/rtc/i2c_rtc_emul.c b/drivers/rtc/i2c_rtc_emul.c
index ba418c25daf..c307d6036dd 100644
--- a/drivers/rtc/i2c_rtc_emul.c
+++ b/drivers/rtc/i2c_rtc_emul.c
@@ -203,6 +203,15 @@ static int sandbox_i2c_rtc_bind(struct udevice *dev)
return 0;
}
+static int sandbox_i2c_rtc_probe(struct udevice *dev)
+{
+ const u8 mac[] = { 0x02, 0x00, 0x11, 0x22, 0x33, 0x48 };
+ struct sandbox_i2c_rtc_plat_data *plat = dev_get_plat(dev);
+
+ memcpy(&plat->reg[0x40], mac, sizeof(mac));
+ return 0;
+}
+
static const struct udevice_id sandbox_i2c_rtc_ids[] = {
{ .compatible = "sandbox,i2c-rtc-emul" },
{ }
@@ -213,6 +222,7 @@ U_BOOT_DRIVER(sandbox_i2c_rtc_emul) = {
.id = UCLASS_I2C_EMUL,
.of_match = sandbox_i2c_rtc_ids,
.bind = sandbox_i2c_rtc_bind,
+ .probe = sandbox_i2c_rtc_probe,
.priv_auto = sizeof(struct sandbox_i2c_rtc),
.plat_auto = sizeof(struct sandbox_i2c_rtc_plat_data),
.ops = &sandbox_i2c_rtc_emul_ops,
diff --git a/include/i2c_eeprom.h b/include/i2c_eeprom.h
index 3ad565684fe..32dcb034973 100644
--- a/include/i2c_eeprom.h
+++ b/include/i2c_eeprom.h
@@ -6,6 +6,8 @@
#ifndef __I2C_EEPROM
#define __I2C_EEPROM
+struct udevice;
+
struct i2c_eeprom_ops {
int (*read)(struct udevice *dev, int offset, uint8_t *buf, int size);
int (*write)(struct udevice *dev, int offset, const uint8_t *buf,
@@ -20,6 +22,7 @@ struct i2c_eeprom {
unsigned long size;
};
+#if CONFIG_IS_ENABLED(I2C_EEPROM)
/*
* i2c_eeprom_read() - read bytes from an I2C EEPROM chip
*
@@ -42,7 +45,8 @@ int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf, int size);
*
* Return: 0 on success, -ve on failure
*/
-int i2c_eeprom_write(struct udevice *dev, int offset, uint8_t *buf, int size);
+int i2c_eeprom_write(struct udevice *dev, int offset, const uint8_t *buf,
+ int size);
/*
* i2c_eeprom_size() - get size of I2C EEPROM chip
@@ -53,4 +57,25 @@ int i2c_eeprom_write(struct udevice *dev, int offset, uint8_t *buf, int size);
*/
int i2c_eeprom_size(struct udevice *dev);
+#else /* !I2C_EEPROM */
+
+static inline int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf,
+ int size)
+{
+ return -ENOSYS;
+}
+
+static inline int i2c_eeprom_write(struct udevice *dev, int offset,
+ const uint8_t *buf, int size)
+{
+ return -ENOSYS;
+}
+
+static inline int i2c_eeprom_size(struct udevice *dev)
+{
+ return -ENOSYS;
+}
+
+#endif /* I2C_EEPROM */
+
#endif
diff --git a/include/nvmem.h b/include/nvmem.h
new file mode 100644
index 00000000000..822e698bdd4
--- /dev/null
+++ b/include/nvmem.h
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2022 Sean Anderson <sean.anderson@seco.com>
+ */
+
+#ifndef NVMEM_H
+#define NVMEM_H
+
+/**
+ * DOC: Design
+ *
+ * The NVMEM subsystem is a "meta-uclass" in that it abstracts over several
+ * different uclasses all with read/write APIs. One approach to implementing
+ * this could be to add a new sub-device for each nvmem-style device of
+ * UCLASS_NVMEM. This subsystem has taken the approach of using the existing
+ * access methods (i2c_eeprom_write, misc_write, etc.) directly. This has the
+ * advantage of not requiring an extra device/driver, saving on binary size and
+ * runtime memory usage. On the other hand, it is not idiomatic. Similar
+ * efforts should generally use a new uclass.
+ */
+
+/**
+ * struct nvmem_cell - One datum within non-volatile memory
+ * @nvmem: The backing storage device
+ * @offset: The offset of the cell from the start of @nvmem
+ * @size: The size of the cell, in bytes
+ */
+struct nvmem_cell {
+ struct udevice *nvmem;
+ unsigned int offset;
+ size_t size;
+};
+
+struct udevice;
+
+#if CONFIG_IS_ENABLED(NVMEM)
+
+/**
+ * nvmem_cell_read() - Read the value of an nvmem cell
+ * @cell: The nvmem cell to read
+ * @buf: The buffer to read into
+ * @size: The size of @buf
+ *
+ * Return:
+ * * 0 on success
+ * * -EINVAL if @buf is not the same size as @cell.
+ * * -ENOSYS if CONFIG_NVMEM is disabled
+ * * A negative error if there was a problem reading the underlying storage
+ */
+int nvmem_cell_read(struct nvmem_cell *cell, void *buf, size_t size);
+
+/**
+ * nvmem_cell_write() - Write a value to an nvmem cell
+ * @cell: The nvmem cell to write
+ * @buf: The buffer to write from
+ * @size: The size of @buf
+ *
+ * Return:
+ * * 0 on success
+ * * -EINVAL if @buf is not the same size as @cell
+ * * -ENOSYS if @cell is read-only, or if CONFIG_NVMEM is disabled
+ * * A negative error if there was a problem writing the underlying storage
+ */
+int nvmem_cell_write(struct nvmem_cell *cell, const void *buf, size_t size);
+
+/**
+ * nvmem_cell_get_by_index() - Get an nvmem cell from a given device and index
+ * @dev: The device that uses the nvmem cell
+ * @index: The index of the cell in nvmem-cells
+ * @cell: The cell to initialize
+ *
+ * Look up the nvmem cell referenced by the phandle at @index in nvmem-cells in
+ * @dev.
+ *
+ * Return:
+ * * 0 on success
+ * * -EINVAL if the regs property is missing, empty, or undersized
+ * * -ENODEV if the nvmem device is missing or unimplemented
+ * * -ENOSYS if CONFIG_NVMEM is disabled
+ * * A negative error if there was a problem reading nvmem-cells or getting the
+ * device
+ */
+int nvmem_cell_get_by_index(struct udevice *dev, int index,
+ struct nvmem_cell *cell);
+
+/**
+ * nvmem_cell_get_by_name() - Get an nvmem cell from a given device and name
+ * @dev: The device that uses the nvmem cell
+ * @name: The name of the nvmem cell
+ * @cell: The cell to initialize
+ *
+ * Look up the nvmem cell referenced by @name in the nvmem-cell-names property
+ * of @dev.
+ *
+ * Return:
+ * * 0 on success
+ * * -EINVAL if the regs property is missing, empty, or undersized
+ * * -ENODEV if the nvmem device is missing or unimplemented
+ * * -ENODATA if @name is not in nvmem-cell-names
+ * * -ENOSYS if CONFIG_NVMEM is disabled
+ * * A negative error if there was a problem reading nvmem-cell-names,
+ * nvmem-cells, or getting the device
+ */
+int nvmem_cell_get_by_name(struct udevice *dev, const char *name,
+ struct nvmem_cell *cell);
+
+#else /* CONFIG_NVMEM */
+
+static inline int nvmem_cell_read(struct nvmem_cell *cell, void *buf, int size)
+{
+ return -ENOSYS;
+}
+
+static inline int nvmem_cell_write(struct nvmem_cell *cell, const void *buf,
+ int size)
+{
+ return -ENOSYS;
+}
+
+static inline int nvmem_cell_get_by_index(struct udevice *dev, int index,
+ struct nvmem_cell *cell)
+{
+ return -ENOSYS;
+}
+
+static inline int nvmem_cell_get_by_name(struct udevice *dev, const char *name,
+ struct nvmem_cell *cell)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_NVMEM */
+
+#endif /* NVMEM_H */
diff --git a/net/dsa-uclass.c b/net/dsa-uclass.c
index 9ff55a02fb2..3bf4351c847 100644
--- a/net/dsa-uclass.c
+++ b/net/dsa-uclass.c
@@ -477,8 +477,10 @@ static int dsa_pre_probe(struct udevice *dev)
return -ENODEV;
}
- uclass_find_device_by_ofnode(UCLASS_ETH, pdata->master_node,
- &priv->master_dev);
+ err = uclass_get_device_by_ofnode(UCLASS_ETH, pdata->master_node,
+ &priv->master_dev);
+ if (err)
+ return err;
/* Simulate a probing event for the CPU port */
if (ops->port_probe) {
diff --git a/net/eth-uclass.c b/net/eth-uclass.c
index bcefc54ded8..0f6b45b002c 100644
--- a/net/eth-uclass.c
+++ b/net/eth-uclass.c
@@ -14,6 +14,7 @@
#include <env.h>
#include <log.h>
#include <net.h>
+#include <nvmem.h>
#include <asm/global_data.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
@@ -507,17 +508,21 @@ static bool eth_dev_get_mac_address(struct udevice *dev, u8 mac[ARP_HLEN])
{
#if CONFIG_IS_ENABLED(OF_CONTROL)
const uint8_t *p;
+ struct nvmem_cell mac_cell;
p = dev_read_u8_array_ptr(dev, "mac-address", ARP_HLEN);
if (!p)
p = dev_read_u8_array_ptr(dev, "local-mac-address", ARP_HLEN);
- if (!p)
- return false;
+ if (p) {
+ memcpy(mac, p, ARP_HLEN);
+ return true;
+ }
- memcpy(mac, p, ARP_HLEN);
+ if (nvmem_cell_get_by_name(dev, "mac-address", &mac_cell))
+ return false;
- return true;
+ return !nvmem_cell_read(&mac_cell, mac, ARP_HLEN);
#else
return false;
#endif
diff --git a/test/dm/eth.c b/test/dm/eth.c
index e4ee6956106..5437f9ea4a0 100644
--- a/test/dm/eth.c
+++ b/test/dm/eth.c
@@ -147,6 +147,35 @@ static int dm_test_eth_act(struct unit_test_state *uts)
}
DM_TEST(dm_test_eth_act, UT_TESTF_SCAN_FDT);
+/* Ensure that all addresses are loaded properly */
+static int dm_test_ethaddr(struct unit_test_state *uts)
+{
+ static const char *const addr[] = {
+ "02:00:11:22:33:44",
+ "02:00:11:22:33:48", /* dsa slave */
+ "02:00:11:22:33:45",
+ "02:00:11:22:33:48", /* dsa master */
+ "02:00:11:22:33:46",
+ "02:00:11:22:33:47",
+ "02:00:11:22:33:48", /* dsa slave */
+ "02:00:11:22:33:49",
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(addr); i++) {
+ char addrname[10];
+
+ if (i)
+ snprintf(addrname, sizeof(addrname), "eth%daddr", i + 1);
+ else
+ strcpy(addrname, "ethaddr");
+ ut_asserteq_str(addr[i], env_get(addrname));
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_ethaddr, UT_TESTF_SCAN_FDT);
+
/* The asserts include a return on fail; cleanup in the caller */
static int _dm_test_eth_rotate1(struct unit_test_state *uts)
{
diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c
index e1de066226c..f9e81747595 100644
--- a/test/dm/test-fdt.c
+++ b/test/dm/test-fdt.c
@@ -184,7 +184,7 @@ static int dm_test_alias_highest_id(struct unit_test_state *uts)
int ret;
ret = dev_read_alias_highest_id("ethernet");
- ut_asserteq(5, ret);
+ ut_asserteq(8, ret);
ret = dev_read_alias_highest_id("gpio");
ut_asserteq(3, ret);