xref: /freebsd/sys/dev/nvmem/nvmem.c (revision e51b3d8e)
1 /*-
2  * Copyright 2018 Emmanuel Vadot <manu@FreeBSD.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/param.h>
27 #include <sys/bus.h>
28 #include <sys/kernel.h>
29 #include <sys/malloc.h>
30 #include <sys/mutex.h>
31 
32 #include <dev/fdt/fdt_common.h>
33 #include <dev/ofw/ofw_bus.h>
34 #include <dev/ofw/ofw_bus_subr.h>
35 
36 #include "nvmem.h"
37 #include "nvmem_if.h"
38 
39 static int
nvmem_get_cell_node(phandle_t node,int idx,phandle_t * cell)40 nvmem_get_cell_node(phandle_t node, int idx, phandle_t *cell)
41 {
42 	phandle_t *p_cell;
43 	phandle_t cell_node;
44 	int ncell;
45 
46 	if (!OF_hasprop(node, "nvmem-cells") ||
47 	    !OF_hasprop(node, "nvmem-cell-names"))
48 		return (ENOENT);
49 
50 	ncell = OF_getencprop_alloc_multi(node, "nvmem-cells", sizeof(*p_cell), (void **)&p_cell);
51 	if (ncell <= 0)
52 		return (ENOENT);
53 
54 	cell_node = OF_node_from_xref(p_cell[idx]);
55 	if (cell_node == p_cell[idx]) {
56 		if (bootverbose)
57 			printf("nvmem_get_node: Cannot resolve phandle %x\n",
58 			    p_cell[idx]);
59 		OF_prop_free(p_cell);
60 		return (ENOENT);
61 	}
62 
63 	OF_prop_free(p_cell);
64 	*cell = cell_node;
65 
66 	return (0);
67 }
68 
69 int
nvmem_get_cell_len(phandle_t node,const char * name)70 nvmem_get_cell_len(phandle_t node, const char *name)
71 {
72 	phandle_t cell_node;
73 	uint32_t reg[2];
74 	int rv, idx;
75 
76 	rv = ofw_bus_find_string_index(node, "nvmem-cell-names", name, &idx);
77 	if (rv != 0)
78 		return (rv);
79 
80 	rv = nvmem_get_cell_node(node, idx, &cell_node);
81 	if (rv != 0)
82 		return (rv);
83 
84 	if (OF_getencprop(cell_node, "reg", reg, sizeof(reg)) != sizeof(reg)) {
85 		if (bootverbose)
86 			printf("nvmem_get_cell_len: Cannot parse reg property of cell %s\n",
87 			    name);
88 		return (ENOENT);
89 	}
90 
91 	return (reg[1]);
92 }
93 
94 int
nvmem_read_cell_by_idx(phandle_t node,int idx,void * cell,size_t buflen)95 nvmem_read_cell_by_idx(phandle_t node, int idx, void *cell, size_t buflen)
96 {
97 	phandle_t cell_node;
98 	device_t provider;
99 	uint32_t reg[2];
100 	int rv;
101 
102 	rv = nvmem_get_cell_node(node, idx, &cell_node);
103 	if (rv != 0)
104 		return (rv);
105 
106 	/* Validate the reg property */
107 	if (OF_getencprop(cell_node, "reg", reg, sizeof(reg)) != sizeof(reg)) {
108 		if (bootverbose)
109 			printf("nvmem_get_cell_by_name: Cannot parse reg property of cell %d\n",
110 			    idx);
111 		return (ENOENT);
112 	}
113 
114 	if (buflen != reg[1])
115 		return (EINVAL);
116 
117 	provider = OF_device_from_xref(OF_xref_from_node(OF_parent(cell_node)));
118 	if (provider == NULL) {
119 		if (bootverbose)
120 			printf("nvmem_get_cell_by_idx: Cannot find the nvmem device\n");
121 		return (ENXIO);
122 	}
123 
124 	rv = NVMEM_READ(provider, reg[0], reg[1], cell);
125 	if (rv != 0) {
126 		return (rv);
127 	}
128 
129 	return (0);
130 }
131 
132 int
nvmem_read_cell_by_name(phandle_t node,const char * name,void * cell,size_t buflen)133 nvmem_read_cell_by_name(phandle_t node, const char *name, void *cell, size_t buflen)
134 {
135 	int rv, idx;
136 
137 	rv = ofw_bus_find_string_index(node, "nvmem-cell-names", name, &idx);
138 	if (rv != 0)
139 		return (rv);
140 
141 	return (nvmem_read_cell_by_idx(node, idx, cell, buflen));
142 }
143 
144 int
nvmem_write_cell_by_idx(phandle_t node,int idx,void * cell,size_t buflen)145 nvmem_write_cell_by_idx(phandle_t node, int idx, void *cell, size_t buflen)
146 {
147 	phandle_t cell_node, prov_node;
148 	device_t provider;
149 	uint32_t reg[2];
150 	int rv;
151 
152 	rv = nvmem_get_cell_node(node, idx, &cell_node);
153 	if (rv != 0)
154 		return (rv);
155 
156 	prov_node = OF_parent(cell_node);
157 	if (OF_hasprop(prov_node, "read-only"))
158 		return (ENXIO);
159 
160 	/* Validate the reg property */
161 	if (OF_getencprop(cell_node, "reg", reg, sizeof(reg)) != sizeof(reg)) {
162 		if (bootverbose)
163 			printf("nvmem_get_cell_by_idx: Cannot parse reg property of cell %d\n",
164 			    idx);
165 		return (ENXIO);
166 	}
167 
168 	if (buflen != reg[1])
169 		return (EINVAL);
170 
171 	provider = OF_device_from_xref(OF_xref_from_node(prov_node));
172 	if (provider == NULL) {
173 		if (bootverbose)
174 			printf("nvmem_get_cell_by_idx: Cannot find the nvmem device\n");
175 		return (ENXIO);
176 	}
177 
178 	rv = NVMEM_WRITE(provider, reg[0], reg[1], cell);
179 	if (rv != 0) {
180 		return (rv);
181 	}
182 
183 	return (0);
184 }
185 
186 int
nvmem_write_cell_by_name(phandle_t node,const char * name,void * cell,size_t buflen)187 nvmem_write_cell_by_name(phandle_t node, const char *name, void *cell, size_t buflen)
188 {
189 	int rv, idx;
190 
191 	rv = ofw_bus_find_string_index(node, "nvmem-cell-names", name, &idx);
192 	if (rv != 0)
193 		return (rv);
194 
195 	return (nvmem_write_cell_by_idx(node, idx, cell, buflen));
196 }
197