summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/misc/Kconfig16
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/nvmem.c142
3 files changed, 159 insertions, 0 deletions
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/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);
+}