Index: head/share/man/man9/nvmem.9 =================================================================== --- head/share/man/man9/nvmem.9 +++ head/share/man/man9/nvmem.9 @@ -0,0 +1,157 @@ +.\" Copyright (c) 2018 Emmanuel Vadot +.\" +.\" 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 . Index: head/sys/arm/conf/GENERIC =================================================================== --- head/sys/arm/conf/GENERIC +++ head/sys/arm/conf/GENERIC @@ -72,6 +72,7 @@ device clk device phy device hwreset +device nvmem device regulator device syscon Index: head/sys/arm64/conf/GENERIC =================================================================== --- head/sys/arm64/conf/GENERIC +++ head/sys/arm64/conf/GENERIC @@ -251,6 +251,7 @@ device clk device phy device hwreset +device nvmem device regulator device syscon Index: head/sys/conf/files =================================================================== --- head/sys/conf/files +++ head/sys/conf/files @@ -1750,6 +1750,8 @@ 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 Index: head/sys/dev/extres/nvmem/nvmem.h =================================================================== --- head/sys/dev/extres/nvmem/nvmem.h +++ head/sys/dev/extres/nvmem/nvmem.h @@ -0,0 +1,37 @@ +/*- + * Copyright 2018 Emmanuel Vadot + * + * 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_ */ Index: head/sys/dev/extres/nvmem/nvmem.c =================================================================== --- head/sys/dev/extres/nvmem/nvmem.c +++ head/sys/dev/extres/nvmem/nvmem.c @@ -0,0 +1,199 @@ +/*- + * Copyright 2018 Emmanuel Vadot + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include +#include + +#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)); +} Index: head/sys/dev/extres/nvmem/nvmem_if.m =================================================================== --- head/sys/dev/extres/nvmem/nvmem_if.m +++ head/sys/dev/extres/nvmem/nvmem_if.m @@ -0,0 +1,67 @@ +#- +# Copyright (c) 2018 Emmanuel Vadot +# +# 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;