aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmmanuel Vadot <manu@FreeBSD.org>2018-07-31 19:08:24 +0000
committerEmmanuel Vadot <manu@FreeBSD.org>2018-07-31 19:08:24 +0000
commitcfe196fbed0284e4aaa744d53396755365f8bbaa (patch)
treee48514bac4588c4d72e1d9d5592381c6cdf337ce
parent8608db1eb6d804e7dfa68ed1b14ef8fc5375a12f (diff)
downloadsrc-cfe196fbed0284e4aaa744d53396755365f8bbaa.tar.gz
src-cfe196fbed0284e4aaa744d53396755365f8bbaa.zip
nvmem: Add nvmem interface and helpers
The nvmem interface helps provider of nvmem data to expose themselves to consumer. NVMEM is generally present on some embedded board in a form of eeprom or fuses. The nvmem api are helpers for consumer to read/write the cell data from a provider. Differential Revision: https://reviews.freebsd.org/D16419
Notes
Notes: svn path=/head/; revision=336996
-rw-r--r--share/man/man9/nvmem.9157
-rw-r--r--sys/arm/conf/GENERIC1
-rw-r--r--sys/arm64/conf/GENERIC1
-rw-r--r--sys/conf/files2
-rw-r--r--sys/dev/extres/nvmem/nvmem.c199
-rw-r--r--sys/dev/extres/nvmem/nvmem.h37
-rw-r--r--sys/dev/extres/nvmem/nvmem_if.m67
7 files changed, 464 insertions, 0 deletions
diff --git a/share/man/man9/nvmem.9 b/share/man/man9/nvmem.9
new file mode 100644
index 000000000000..281bb93ba80b
--- /dev/null
+++ b/share/man/man9/nvmem.9
@@ -0,0 +1,157 @@
+.\" Copyright (c) 2018 Emmanuel Vadot <manu@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 24, 2018
+.Dt nvmem 9
+.Os
+.Sh NAME
+.Nm nvmem
+.Nm nvmem_get_cell_len ,
+.Nm nvmem_read_cell_by_name ,
+.Nm nvmem_read_cell_by_idx ,
+.Nm nvmem_write_cell_by_name ,
+.Nm nvmem_write_cell_by_idx ,
+.Sh SYNOPSIS
+.Cd "options EXT_RESOURCES"
+.Cd "options FDT"
+.Cd "device nvmem"
+.In sys/extres/nvmem/nvmem.h
+.Ft int
+.Fn nvmem_get_cell_len "phandle_t node" "const char *name"
+.Ft int
+.Fn nvmem_read_cell_by_name "phandle_t node" "const char *name" "void *cell" "size_t buflen"
+.Ft int
+.Fn nvmem_read_cell_by_idx "phandle_t node" "int idx" "void *cell" "size_t buflen"
+.Ft int
+.Fn nvmem_write_cell_by_name "phandle_t node" "const char *name" "void *cell" "size_t buflen"
+.Ft int
+.Fn nvmem_write_cell_by_idx "phandle_t node" "int idx" "void *cell" "size_t buflen"
+.Sh DESCRIPTION
+On some embedded boards, the manufacturer stored some data on a NVMEM
+(Non-Volatile Memory), this is generally stored in some eeprom or fuses.
+.Pp
+The
+.Nm
+API consist of helpers functions for consumer and device methods for
+providers.
+.Sh FUNCTIONS
+.Bl -tag -width indent
+.It Fn nvmem_get_cell_len "phandle_t node" "const char *name"
+Get the size of the cell base on the reg property on the node.
+Return the size or ENOENT if the cell name wasn't found
+.It Fn nvmem_read_cell_by_name "phandle_t node" "const char *name" "void *cell" "size_t buflen"
+Get the cell content based on the name.
+Return 0 on sucess or ENOENT if the cell doesn't exists, ENXIO if no provider device was found,
+EINVAL if the size isn't correct.
+.It Fn nvmem_read_cell_by_idx "phandle_t node" "int idx" "void *cell" "size_t buflen"
+Get the cell content based on the id.
+Return 0 on sucess or ENOENT if the cell doesn't exists, ENXIO if no provider device was found,
+EINVAL if the size isn't correct.
+.It Fn nvmem_write_cell_by_name "phandle_t node" "const char *name" "void *cell" "size_t buflen"
+Write the cell content based on the name.
+Return 0 on sucess or ENOENT if the cell doesn't exists, ENXIO if no provider device was found,
+EINVAL if the size isn't correct.
+.It Fn nvmem_write_cell_by_idx "phandle_t node" "int idx" "void *cell" "size_t buflen"
+Write the cell content based on the id.
+Return 0 on sucess or ENOENT if the cell doesn't exists, ENXIO if no provider device was found,
+EINVAL if the size isn't correct.
+.El
+.Sh DEVICE METHODS
+.Bl -tag -width indent
+.It Fn nvmem_read "device_t dev" "uint32_t offset" "uint32_t size" "uint8_t *buffer"
+Provider device method to read a cell content.
+.It Fn nvmem_write "device_t dev" "uint32_t offset" "uint32_t size" "uint8_t *buffer"
+Provider device method to write a cell content.
+.El
+.Sh EXAMPLES
+Consider this DTS
+.Bd -literal
+/* Provider */
+eeprom: eeprom@20000 {
+ board_id: id@0 {
+ reg = <0x0 0x4>;
+ };
+};
+/* Consumer */
+device@30000 {
+ ...
+
+ nvmem-cells = <&board_id>
+ nvmem-cell-names = "boardid";
+};
+.Ed
+.Pp
+The device driver for eeprom@20000 needs to expose itself as a provider
+.Bd -literal
+#include "nvmem_if.h"
+
+int
+foo_nvmem_read(device_t dev, uint32_t offset, uint32_t size, uint8_t *buffer)
+{
+ /* Read the data */
+}
+
+int
+foo_attach(device_t dev)
+{
+ phandle_t node;
+
+ node = ofw_bus_get_node(dev);
+ ...
+ /* Registering the device so the consumers can find us */
+ OF_device_register_xref(OF_xref_from_node(node), dev);
+
+ ...
+}
+
+static device_method_t foo_methods[] = {
+ ...
+
+ /* nvmem interface */
+ DEVMETHOD(nvmem_read, foo_nvmem_read),
+
+ /* Terminate method list */
+ DEVMETHOD_END
+};
+.Ed
+.Pp
+The consumer device driver for device@30000 can now read the nvmem data
+.Bd -literal
+int
+bar_attach(device_t dev)
+{
+ phandle_t node;
+ uint32_t boardid;
+
+ ...
+ node = ofw_bus_get_node(dev);
+ nvmem_read_cell_by_name(node, "boardid", (void *)&boardid, sizeof(boardid));
+ ...
+}
+.Ed
+.Sh HISTORY
+The nvmem related function first appear in
+.Fx 12.0 .
+The nvmem interface and manual page was written by
+.An Emmanuel Vadot Aq Mt manu@FreeBSD.org .
diff --git a/sys/arm/conf/GENERIC b/sys/arm/conf/GENERIC
index 8102b89b2a53..b2c10cf34271 100644
--- a/sys/arm/conf/GENERIC
+++ b/sys/arm/conf/GENERIC
@@ -72,6 +72,7 @@ options EXT_RESOURCES
device clk
device phy
device hwreset
+device nvmem
device regulator
device syscon
diff --git a/sys/arm64/conf/GENERIC b/sys/arm64/conf/GENERIC
index fac6482472e6..f8b1c299e819 100644
--- a/sys/arm64/conf/GENERIC
+++ b/sys/arm64/conf/GENERIC
@@ -251,6 +251,7 @@ options EXT_RESOURCES
device clk
device phy
device hwreset
+device nvmem
device regulator
device syscon
diff --git a/sys/conf/files b/sys/conf/files
index 80a6a790a40f..2f0e62942e8c 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1750,6 +1750,8 @@ dev/extres/phy/phydev_if.m optional ext_resources phy fdt
dev/extres/phy/phynode_if.m optional ext_resources phy fdt
dev/extres/hwreset/hwreset.c optional ext_resources hwreset fdt
dev/extres/hwreset/hwreset_if.m optional ext_resources hwreset fdt
+dev/extres/nvmem/nvmem.c optional ext_resources nvmem fdt
+dev/extres/nvmem/nvmem_if.m optional ext_resources nvmem fdt
dev/extres/regulator/regdev_if.m optional ext_resources regulator fdt
dev/extres/regulator/regnode_if.m optional ext_resources regulator fdt
dev/extres/regulator/regulator.c optional ext_resources regulator fdt
diff --git a/sys/dev/extres/nvmem/nvmem.c b/sys/dev/extres/nvmem/nvmem.c
new file mode 100644
index 000000000000..c6f928dc3520
--- /dev/null
+++ b/sys/dev/extres/nvmem/nvmem.c
@@ -0,0 +1,199 @@
+/*-
+ * Copyright 2018 Emmanuel Vadot <manu@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "nvmem.h"
+#include "nvmem_if.h"
+
+static int
+nvmem_get_cell_node(phandle_t node, int idx, phandle_t *cell)
+{
+ phandle_t *p_cell;
+ phandle_t cell_node;
+ int ncell;
+
+ if (!OF_hasprop(node, "nvmem-cells") ||
+ !OF_hasprop(node, "nvmem-cell-names"))
+ return (ENOENT);
+
+ ncell = OF_getencprop_alloc_multi(node, "nvmem-cells", sizeof(*p_cell), (void **)&p_cell);
+ if (ncell <= 0)
+ return (ENOENT);
+
+ cell_node = OF_node_from_xref(p_cell[idx]);
+ if (cell_node == p_cell[idx]) {
+ if (bootverbose)
+ printf("nvmem_get_node: Cannot resolve phandle %x\n",
+ p_cell[idx]);
+ OF_prop_free(p_cell);
+ return (ENOENT);
+ }
+
+ OF_prop_free(p_cell);
+ *cell = cell_node;
+
+ return (0);
+}
+
+int
+nvmem_get_cell_len(phandle_t node, const char *name)
+{
+ phandle_t cell_node;
+ uint32_t reg[2];
+ int rv, idx;
+
+ rv = ofw_bus_find_string_index(node, "nvmem-cell-names", name, &idx);
+ if (rv != 0)
+ return (rv);
+
+ rv = nvmem_get_cell_node(node, idx, &cell_node);
+ if (rv != 0)
+ return (rv);
+
+ if (OF_getencprop(cell_node, "reg", reg, sizeof(reg)) != sizeof(reg)) {
+ if (bootverbose)
+ printf("nvmem_get_cell_len: Cannot parse reg property of cell %s\n",
+ name);
+ return (ENOENT);
+ }
+
+ return (reg[1]);
+}
+
+int
+nvmem_read_cell_by_idx(phandle_t node, int idx, void *cell, size_t buflen)
+{
+ phandle_t cell_node;
+ device_t provider;
+ uint32_t reg[2];
+ int rv;
+
+ rv = nvmem_get_cell_node(node, idx, &cell_node);
+ if (rv != 0)
+ return (rv);
+
+ /* Validate the reg property */
+ if (OF_getencprop(cell_node, "reg", reg, sizeof(reg)) != sizeof(reg)) {
+ if (bootverbose)
+ printf("nvmem_get_cell_by_name: Cannot parse reg property of cell %d\n",
+ idx);
+ return (ENOENT);
+ }
+
+ if (buflen != reg[1])
+ return (EINVAL);
+
+ provider = OF_device_from_xref(OF_xref_from_node(OF_parent(cell_node)));
+ if (provider == NULL) {
+ if (bootverbose)
+ printf("nvmem_get_cell_by_idx: Cannot find the nvmem device\n");
+ return (ENXIO);
+ }
+
+ rv = NVMEM_READ(provider, reg[0], reg[1], cell);
+ if (rv != 0) {
+ return (rv);
+ }
+
+ return (0);
+}
+
+int
+nvmem_read_cell_by_name(phandle_t node, const char *name, void *cell, size_t buflen)
+{
+ int rv, idx;
+
+ rv = ofw_bus_find_string_index(node, "nvmem-cell-names", name, &idx);
+ if (rv != 0)
+ return (rv);
+
+ return (nvmem_read_cell_by_idx(node, idx, cell, buflen));
+}
+
+int
+nvmem_write_cell_by_idx(phandle_t node, int idx, void *cell, size_t buflen)
+{
+ phandle_t cell_node, prov_node;
+ device_t provider;
+ uint32_t reg[2];
+ int rv;
+
+ rv = nvmem_get_cell_node(node, idx, &cell_node);
+ if (rv != 0)
+ return (rv);
+
+ prov_node = OF_parent(cell_node);
+ if (OF_hasprop(prov_node, "read-only"))
+ return (ENXIO);
+
+ /* Validate the reg property */
+ if (OF_getencprop(cell_node, "reg", reg, sizeof(reg)) != sizeof(reg)) {
+ if (bootverbose)
+ printf("nvmem_get_cell_by_idx: Cannot parse reg property of cell %d\n",
+ idx);
+ return (ENXIO);
+ }
+
+ if (buflen != reg[1])
+ return (EINVAL);
+
+ provider = OF_device_from_xref(OF_xref_from_node(prov_node));
+ if (provider == NULL) {
+ if (bootverbose)
+ printf("nvmem_get_cell_by_idx: Cannot find the nvmem device\n");
+ return (ENXIO);
+ }
+
+ rv = NVMEM_WRITE(provider, reg[0], reg[1], cell);
+ if (rv != 0) {
+ return (rv);
+ }
+
+ return (0);
+}
+
+int
+nvmem_write_cell_by_name(phandle_t node, const char *name, void *cell, size_t buflen)
+{
+ int rv, idx;
+
+ rv = ofw_bus_find_string_index(node, "nvmem-cell-names", name, &idx);
+ if (rv != 0)
+ return (rv);
+
+ return (nvmem_write_cell_by_idx(node, idx, cell, buflen));
+}
diff --git a/sys/dev/extres/nvmem/nvmem.h b/sys/dev/extres/nvmem/nvmem.h
new file mode 100644
index 000000000000..b71142315226
--- /dev/null
+++ b/sys/dev/extres/nvmem/nvmem.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright 2018 Emmanuel Vadot <manu@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_EXTRES_NVMEM_H_
+#define _DEV_EXTRES_NVMEM_H_
+
+int nvmem_get_cell_len(phandle_t node, const char *name);
+int nvmem_read_cell_by_name(phandle_t node, const char *name, void *cell, size_t buflen);
+int nvmem_read_cell_by_idx(phandle_t node, int idx, void *cell, size_t buflen);
+int nvmem_write_cell_by_name(phandle_t node, const char *name, void *cell, size_t buflen);
+int nvmem_write_cell_by_idx(phandle_t node, int idx, void *cell, size_t buflen);
+
+#endif /* _DEV_EXTRES_NVMEM_H_ */
diff --git a/sys/dev/extres/nvmem/nvmem_if.m b/sys/dev/extres/nvmem/nvmem_if.m
new file mode 100644
index 000000000000..769e07d9bc4f
--- /dev/null
+++ b/sys/dev/extres/nvmem/nvmem_if.m
@@ -0,0 +1,67 @@
+#-
+# Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+INTERFACE nvmem;
+
+#
+# Default implementations of some methods.
+#
+CODE {
+ static int
+ null_nvmem_read(device_t dev __unused, uint32_t offset __unused, uint32_t size __unused, uint8_t *buffer __unused)
+ {
+
+ return (ENXIO);
+ }
+
+ static int
+ null_nvmem_write(device_t dev __unused, uint32_t offset __unused, uint32_t size __unused, uint8_t *buffer __unused)
+ {
+
+ return (ENXIO);
+ }
+};
+
+#
+# Read
+#
+METHOD int read {
+ device_t dev;
+ uint32_t offset;
+ uint32_t size;
+ uint8_t *buffer;
+} DEFAULT null_nvmem_read;
+
+#
+# Write
+#
+METHOD int write {
+ device_t dev;
+ uint32_t offset;
+ uint32_t size;
+ uint8_t *buffer;
+} DEFAULT null_nvmem_write;