Merge branch '2022-06-09-add-support-for-nvmem-api' into next

To quote the author:
This adds support for the nvmem-cells properties cropping up in manyb
device trees. This is an easy way to load configuration, version
information, or calibration data from a non-volatile memory source. For
more information, refer to patch 6 ("misc: Add support for nvmem
cells").

For the moment I have only added some integration tests using the
ethernet addresses. This hits the main code paths (looking up nvmem
cells) but doesn't test writing. I can add a few stand-alone tests if
desired.
This commit is contained in:
Tom Rini 2022-06-09 15:20:11 -04:00
commit e5028bb227
26 changed files with 436 additions and 29 deletions

View File

@ -1091,6 +1091,13 @@ F: cmd/nvme.c
F: include/nvme.h F: include/nvme.h
F: doc/develop/driver-model/nvme.rst 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 NXP C45 TJA11XX PHY DRIVER
M: Radu Pirea <radu-nicolae.pirea@oss.nxp.com> M: Radu Pirea <radu-nicolae.pirea@oss.nxp.com>
S: Maintained S: Maintained

View File

@ -63,7 +63,6 @@
eth@10002000 { eth@10002000 {
compatible = "sandbox,eth"; compatible = "sandbox,eth";
reg = <0x10002000 0x1000>; reg = <0x10002000 0x1000>;
fake-host-hwaddr = [00 00 66 44 22 00];
}; };
host-fs { host-fs {

View File

@ -58,7 +58,6 @@
eth@10002000 { eth@10002000 {
compatible = "sandbox,eth"; compatible = "sandbox,eth";
reg = <0x0 0x10002000 0x0 0x1000>; reg = <0x0 0x10002000 0x0 0x1000>;
fake-host-hwaddr = [00 00 66 44 22 00];
}; };
i2c_0: i2c@0 { i2c_0: i2c@0 {

View File

@ -28,6 +28,9 @@
ethernet3 = &eth_3; ethernet3 = &eth_3;
ethernet4 = &dsa_eth0; ethernet4 = &dsa_eth0;
ethernet5 = &eth_5; ethernet5 = &eth_5;
ethernet6 = "/eth@10004000";
ethernet7 = &swp_1;
ethernet8 = &phy_eth0;
gpio1 = &gpio_a; gpio1 = &gpio_a;
gpio2 = &gpio_b; gpio2 = &gpio_b;
gpio3 = &gpio_c; gpio3 = &gpio_c;
@ -524,31 +527,31 @@
eth@10002000 { eth@10002000 {
compatible = "sandbox,eth"; compatible = "sandbox,eth";
reg = <0x10002000 0x1000>; reg = <0x10002000 0x1000>;
fake-host-hwaddr = [00 00 66 44 22 00];
}; };
eth_5: eth@10003000 { eth_5: eth@10003000 {
compatible = "sandbox,eth"; compatible = "sandbox,eth";
reg = <0x10003000 0x1000>; reg = <0x10003000 0x1000>;
fake-host-hwaddr = [00 00 66 44 22 11]; nvmem-cells = <&eth5_addr>;
nvmem-cell-names = "mac-address";
}; };
eth_3: sbe5 { eth_3: sbe5 {
compatible = "sandbox,eth"; compatible = "sandbox,eth";
reg = <0x10005000 0x1000>; reg = <0x10005000 0x1000>;
fake-host-hwaddr = [00 00 66 44 22 33]; nvmem-cells = <&eth3_addr>;
nvmem-cell-names = "mac-address";
}; };
eth@10004000 { eth@10004000 {
compatible = "sandbox,eth"; compatible = "sandbox,eth";
reg = <0x10004000 0x1000>; reg = <0x10004000 0x1000>;
fake-host-hwaddr = [00 00 66 44 22 22];
}; };
phy_eth0: phy-test-eth { phy_eth0: phy-test-eth {
compatible = "sandbox,eth"; compatible = "sandbox,eth";
reg = <0x10007000 0x1000>; reg = <0x10007000 0x1000>;
fake-host-hwaddr = [00 00 66 44 22 77]; mac-address = [ 02 00 11 22 33 49 ];
phy-handle = <&ethphy1>; phy-handle = <&ethphy1>;
phy-mode = "2500base-x"; phy-mode = "2500base-x";
}; };
@ -556,7 +559,8 @@
dsa_eth0: dsa-test-eth { dsa_eth0: dsa-test-eth {
compatible = "sandbox,eth"; compatible = "sandbox,eth";
reg = <0x10006000 0x1000>; reg = <0x10006000 0x1000>;
fake-host-hwaddr = [00 00 66 44 22 66]; nvmem-cells = <&eth4_addr>;
nvmem-cell-names = "mac-address";
}; };
dsa-test { dsa-test {
@ -700,6 +704,8 @@
pinctrl-0 = <&pinmux_i2c0_pins>; pinctrl-0 = <&pinmux_i2c0_pins>;
eeprom@2c { eeprom@2c {
#address-cells = <1>;
#size-cells = <1>;
reg = <0x2c>; reg = <0x2c>;
compatible = "i2c-eeprom"; compatible = "i2c-eeprom";
sandbox,emul = <&emul_eeprom>; sandbox,emul = <&emul_eeprom>;
@ -711,12 +717,22 @@
reg = <10 2>; reg = <10 2>;
}; };
}; };
eth3_addr: mac-address@24 {
reg = <24 6>;
};
}; };
rtc_0: rtc@43 { rtc_0: rtc@43 {
#address-cells = <1>;
#size-cells = <1>;
reg = <0x43>; reg = <0x43>;
compatible = "sandbox-rtc"; compatible = "sandbox-rtc";
sandbox,emul = <&emul0>; sandbox,emul = <&emul0>;
eth4_addr: mac-address@40 {
reg = <0x40 6>;
};
}; };
rtc_1: rtc@61 { rtc_1: rtc@61 {
@ -898,7 +914,13 @@
}; };
misc-test { misc-test {
#address-cells = <1>;
#size-cells = <1>;
compatible = "sandbox,misc_sandbox"; compatible = "sandbox,misc_sandbox";
eth5_addr: mac-address@10 {
reg = <0x10 6>;
};
}; };
mmc2 { mmc2 {

View File

@ -6,10 +6,6 @@ stdout=serial,vidconsole
stderr=serial,vidconsole stderr=serial,vidconsole
ethaddr=02:00:11:22:33:44 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 eth6addr=02:00:11:22:33:47
ipaddr=192.0.2.1 ipaddr=192.0.2.1

View File

@ -144,6 +144,7 @@ CONFIG_LED_GPIO=y
CONFIG_DM_MAILBOX=y CONFIG_DM_MAILBOX=y
CONFIG_SANDBOX_MBOX=y CONFIG_SANDBOX_MBOX=y
CONFIG_MISC=y CONFIG_MISC=y
CONFIG_NVMEM=y
CONFIG_CROS_EC=y CONFIG_CROS_EC=y
CONFIG_CROS_EC_I2C=y CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_LPC=y CONFIG_CROS_EC_LPC=y

View File

@ -188,6 +188,7 @@ CONFIG_LED_GPIO=y
CONFIG_DM_MAILBOX=y CONFIG_DM_MAILBOX=y
CONFIG_SANDBOX_MBOX=y CONFIG_SANDBOX_MBOX=y
CONFIG_MISC=y CONFIG_MISC=y
CONFIG_NVMEM=y
CONFIG_CROS_EC=y CONFIG_CROS_EC=y
CONFIG_CROS_EC_I2C=y CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_LPC=y CONFIG_CROS_EC_LPC=y

View File

@ -117,6 +117,7 @@ CONFIG_LED_GPIO=y
CONFIG_DM_MAILBOX=y CONFIG_DM_MAILBOX=y
CONFIG_SANDBOX_MBOX=y CONFIG_SANDBOX_MBOX=y
CONFIG_MISC=y CONFIG_MISC=y
CONFIG_NVMEM=y
CONFIG_CROS_EC=y CONFIG_CROS_EC=y
CONFIG_CROS_EC_I2C=y CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_LPC=y CONFIG_CROS_EC_LPC=y

View File

@ -144,6 +144,7 @@ CONFIG_LED_GPIO=y
CONFIG_DM_MAILBOX=y CONFIG_DM_MAILBOX=y
CONFIG_SANDBOX_MBOX=y CONFIG_SANDBOX_MBOX=y
CONFIG_MISC=y CONFIG_MISC=y
CONFIG_NVMEM=y
CONFIG_CROS_EC=y CONFIG_CROS_EC=y
CONFIG_CROS_EC_I2C=y CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_LPC=y CONFIG_CROS_EC_LPC=y

View File

@ -145,6 +145,8 @@ CONFIG_LED_GPIO=y
CONFIG_DM_MAILBOX=y CONFIG_DM_MAILBOX=y
CONFIG_SANDBOX_MBOX=y CONFIG_SANDBOX_MBOX=y
CONFIG_MISC=y CONFIG_MISC=y
CONFIG_NVMEM=y
CONFIG_SPL_NVMEM=y
CONFIG_CROS_EC=y CONFIG_CROS_EC=y
CONFIG_CROS_EC_I2C=y CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_LPC=y CONFIG_CROS_EC_LPC=y
@ -153,6 +155,7 @@ CONFIG_CROS_EC_SPI=y
CONFIG_P2SB=y CONFIG_P2SB=y
CONFIG_PWRSEQ=y CONFIG_PWRSEQ=y
CONFIG_SPL_PWRSEQ=y CONFIG_SPL_PWRSEQ=y
CONFIG_I2C_EEPROM=y
CONFIG_MMC_SANDBOX=y CONFIG_MMC_SANDBOX=y
CONFIG_SPI_FLASH_SANDBOX=y CONFIG_SPI_FLASH_SANDBOX=y
CONFIG_SPI_FLASH_ATMEL=y CONFIG_SPI_FLASH_ATMEL=y

View File

@ -14,6 +14,7 @@ U-Boot API documentation
linker_lists linker_lists
lmb lmb
logging logging
nvmem
pinctrl pinctrl
rng rng
sandbox sandbox

10
doc/api/nvmem.rst Normal file
View File

@ -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:

View File

@ -43,6 +43,22 @@ config VPL_MISC
set of generic read, write and ioctl methods may be used to set of generic read, write and ioctl methods may be used to
access the device. 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 config ALTERA_SYSID
bool "Altera Sysid support" bool "Altera Sysid support"
depends on MISC depends on MISC

View File

@ -4,6 +4,7 @@
# Wolfgang Denk, DENX Software Engineering, wd@denx.de. # Wolfgang Denk, DENX Software Engineering, wd@denx.de.
obj-$(CONFIG_$(SPL_TPL_)MISC) += misc-uclass.o 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) += cros_ec.o
obj-$(CONFIG_$(SPL_TPL_)CROS_EC_SANDBOX) += cros_ec_sandbox.o obj-$(CONFIG_$(SPL_TPL_)CROS_EC_SANDBOX) += cros_ec_sandbox.o

View File

@ -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); 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); const struct i2c_eeprom_ops *ops = device_get_ops(dev);

View File

@ -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_plat_data *plat = dev_get_plat(dev);
struct sandbox_i2c_flash *priv = dev_get_priv(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); priv->data = calloc(1, plat->size);
if (!priv->data) if (!priv->data)
return -ENOMEM; return -ENOMEM;
memcpy(&priv->data[24], mac, sizeof(mac));
return 0; return 0;
} }

View File

@ -112,8 +112,11 @@ static const struct misc_ops misc_sandbox_ops = {
int misc_sandbox_probe(struct udevice *dev) int misc_sandbox_probe(struct udevice *dev)
{ {
struct misc_sandbox_priv *priv = dev_get_priv(dev); struct misc_sandbox_priv *priv = dev_get_priv(dev);
/* For eth5 */
const u8 mac[] = { 0x02, 0x00, 0x11, 0x22, 0x33, 0x46 };
priv->enabled = true; priv->enabled = true;
memcpy(&priv->mem[16], mac, sizeof(mac));
return 0; return 0;
} }

142
drivers/misc/nvmem.c Normal file
View File

@ -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);
}

View File

@ -395,9 +395,11 @@ static void sb_eth_stop(struct udevice *dev)
static int sb_eth_write_hwaddr(struct udevice *dev) static int sb_eth_write_hwaddr(struct udevice *dev)
{ {
struct eth_pdata *pdata = dev_get_plat(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, debug("eth_sandbox %s: Write HW ADDR - %pM\n", dev->name,
pdata->enetaddr); pdata->enetaddr);
memcpy(priv->fake_host_hwaddr, pdata->enetaddr, ARP_HLEN);
return 0; 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_pdata *pdata = dev_get_plat(dev);
struct eth_sandbox_priv *priv = dev_get_priv(dev); struct eth_sandbox_priv *priv = dev_get_priv(dev);
const u8 *mac;
pdata->iobase = dev_read_addr(dev); 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->disabled = false;
priv->tx_handler = sb_default_handler; priv->tx_handler = sb_default_handler;

View File

@ -203,6 +203,15 @@ static int sandbox_i2c_rtc_bind(struct udevice *dev)
return 0; 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[] = { static const struct udevice_id sandbox_i2c_rtc_ids[] = {
{ .compatible = "sandbox,i2c-rtc-emul" }, { .compatible = "sandbox,i2c-rtc-emul" },
{ } { }
@ -213,6 +222,7 @@ U_BOOT_DRIVER(sandbox_i2c_rtc_emul) = {
.id = UCLASS_I2C_EMUL, .id = UCLASS_I2C_EMUL,
.of_match = sandbox_i2c_rtc_ids, .of_match = sandbox_i2c_rtc_ids,
.bind = sandbox_i2c_rtc_bind, .bind = sandbox_i2c_rtc_bind,
.probe = sandbox_i2c_rtc_probe,
.priv_auto = sizeof(struct sandbox_i2c_rtc), .priv_auto = sizeof(struct sandbox_i2c_rtc),
.plat_auto = sizeof(struct sandbox_i2c_rtc_plat_data), .plat_auto = sizeof(struct sandbox_i2c_rtc_plat_data),
.ops = &sandbox_i2c_rtc_emul_ops, .ops = &sandbox_i2c_rtc_emul_ops,

View File

@ -6,6 +6,8 @@
#ifndef __I2C_EEPROM #ifndef __I2C_EEPROM
#define __I2C_EEPROM #define __I2C_EEPROM
struct udevice;
struct i2c_eeprom_ops { struct i2c_eeprom_ops {
int (*read)(struct udevice *dev, int offset, uint8_t *buf, int size); int (*read)(struct udevice *dev, int offset, uint8_t *buf, int size);
int (*write)(struct udevice *dev, int offset, const uint8_t *buf, int (*write)(struct udevice *dev, int offset, const uint8_t *buf,
@ -20,6 +22,7 @@ struct i2c_eeprom {
unsigned long size; unsigned long size;
}; };
#if CONFIG_IS_ENABLED(I2C_EEPROM)
/* /*
* i2c_eeprom_read() - read bytes from an I2C EEPROM chip * 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 * 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 * 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); 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 #endif

134
include/nvmem.h Normal file
View File

@ -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 */

View File

@ -477,8 +477,10 @@ static int dsa_pre_probe(struct udevice *dev)
return -ENODEV; return -ENODEV;
} }
uclass_find_device_by_ofnode(UCLASS_ETH, pdata->master_node, err = uclass_get_device_by_ofnode(UCLASS_ETH, pdata->master_node,
&priv->master_dev); &priv->master_dev);
if (err)
return err;
/* Simulate a probing event for the CPU port */ /* Simulate a probing event for the CPU port */
if (ops->port_probe) { if (ops->port_probe) {

View File

@ -14,6 +14,7 @@
#include <env.h> #include <env.h>
#include <log.h> #include <log.h>
#include <net.h> #include <net.h>
#include <nvmem.h>
#include <asm/global_data.h> #include <asm/global_data.h>
#include <dm/device-internal.h> #include <dm/device-internal.h>
#include <dm/uclass-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) #if CONFIG_IS_ENABLED(OF_CONTROL)
const uint8_t *p; const uint8_t *p;
struct nvmem_cell mac_cell;
p = dev_read_u8_array_ptr(dev, "mac-address", ARP_HLEN); p = dev_read_u8_array_ptr(dev, "mac-address", ARP_HLEN);
if (!p) if (!p)
p = dev_read_u8_array_ptr(dev, "local-mac-address", ARP_HLEN); p = dev_read_u8_array_ptr(dev, "local-mac-address", ARP_HLEN);
if (!p) if (p) {
memcpy(mac, p, ARP_HLEN);
return true;
}
if (nvmem_cell_get_by_name(dev, "mac-address", &mac_cell))
return false; return false;
memcpy(mac, p, ARP_HLEN); return !nvmem_cell_read(&mac_cell, mac, ARP_HLEN);
return true;
#else #else
return false; return false;
#endif #endif

View File

@ -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); 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 */ /* The asserts include a return on fail; cleanup in the caller */
static int _dm_test_eth_rotate1(struct unit_test_state *uts) static int _dm_test_eth_rotate1(struct unit_test_state *uts)
{ {

View File

@ -184,7 +184,7 @@ static int dm_test_alias_highest_id(struct unit_test_state *uts)
int ret; int ret;
ret = dev_read_alias_highest_id("ethernet"); ret = dev_read_alias_highest_id("ethernet");
ut_asserteq(5, ret); ut_asserteq(8, ret);
ret = dev_read_alias_highest_id("gpio"); ret = dev_read_alias_highest_id("gpio");
ut_asserteq(3, ret); ut_asserteq(3, ret);