xref: /freebsd-14.2/sys/dev/extres/nvmem/nvmem.c (revision 685dc743)
1 /*-
2  * Copyright 2018 Emmanuel Vadot <[email protected]>
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/cdefs.h>
27 #include <sys/param.h>
28 #include <sys/bus.h>
29 #include <sys/kernel.h>
30 #include <sys/malloc.h>
31 #include <sys/mutex.h>
32 
33 #include <dev/fdt/fdt_common.h>
34 #include <dev/ofw/ofw_bus.h>
35 #include <dev/ofw/ofw_bus_subr.h>
36 
37 #include "nvmem.h"
38 #include "nvmem_if.h"
39 
40 static int
nvmem_get_cell_node(phandle_t node,int idx,phandle_t * cell)41 nvmem_get_cell_node(phandle_t node, int idx, phandle_t *cell)
42 {
43 	phandle_t *p_cell;
44 	phandle_t cell_node;
45 	int ncell;
46 
47 	if (!OF_hasprop(node, "nvmem-cells") ||
48 	    !OF_hasprop(node, "nvmem-cell-names"))
49 		return (ENOENT);
50 
51 	ncell = OF_getencprop_alloc_multi(node, "nvmem-cells", sizeof(*p_cell), (void **)&p_cell);
52 	if (ncell <= 0)
53 		return (ENOENT);
54 
55 	cell_node = OF_node_from_xref(p_cell[idx]);
56 	if (cell_node == p_cell[idx]) {
57 		if (bootverbose)
58 			printf("nvmem_get_node: Cannot resolve phandle %x\n",
59 			    p_cell[idx]);
60 		OF_prop_free(p_cell);
61 		return (ENOENT);
62 	}
63 
64 	OF_prop_free(p_cell);
65 	*cell = cell_node;
66 
67 	return (0);
68 }
69 
70 int
nvmem_get_cell_len(phandle_t node,const char * name)71 nvmem_get_cell_len(phandle_t node, const char *name)
72 {
73 	phandle_t cell_node;
74 	uint32_t reg[2];
75 	int rv, idx;
76 
77 	rv = ofw_bus_find_string_index(node, "nvmem-cell-names", name, &idx);
78 	if (rv != 0)
79 		return (rv);
80 
81 	rv = nvmem_get_cell_node(node, idx, &cell_node);
82 	if (rv != 0)
83 		return (rv);
84 
85 	if (OF_getencprop(cell_node, "reg", reg, sizeof(reg)) != sizeof(reg)) {
86 		if (bootverbose)
87 			printf("nvmem_get_cell_len: Cannot parse reg property of cell %s\n",
88 			    name);
89 		return (ENOENT);
90 	}
91 
92 	return (reg[1]);
93 }
94 
95 int
nvmem_read_cell_by_idx(phandle_t node,int idx,void * cell,size_t buflen)96 nvmem_read_cell_by_idx(phandle_t node, int idx, void *cell, size_t buflen)
97 {
98 	phandle_t cell_node;
99 	device_t provider;
100 	uint32_t reg[2];
101 	int rv;
102 
103 	rv = nvmem_get_cell_node(node, idx, &cell_node);
104 	if (rv != 0)
105 		return (rv);
106 
107 	/* Validate the reg property */
108 	if (OF_getencprop(cell_node, "reg", reg, sizeof(reg)) != sizeof(reg)) {
109 		if (bootverbose)
110 			printf("nvmem_get_cell_by_name: Cannot parse reg property of cell %d\n",
111 			    idx);
112 		return (ENOENT);
113 	}
114 
115 	if (buflen != reg[1])
116 		return (EINVAL);
117 
118 	provider = OF_device_from_xref(OF_xref_from_node(OF_parent(cell_node)));
119 	if (provider == NULL) {
120 		if (bootverbose)
121 			printf("nvmem_get_cell_by_idx: Cannot find the nvmem device\n");
122 		return (ENXIO);
123 	}
124 
125 	rv = NVMEM_READ(provider, reg[0], reg[1], cell);
126 	if (rv != 0) {
127 		return (rv);
128 	}
129 
130 	return (0);
131 }
132 
133 int
nvmem_read_cell_by_name(phandle_t node,const char * name,void * cell,size_t buflen)134 nvmem_read_cell_by_name(phandle_t node, const char *name, void *cell, size_t buflen)
135 {
136 	int rv, idx;
137 
138 	rv = ofw_bus_find_string_index(node, "nvmem-cell-names", name, &idx);
139 	if (rv != 0)
140 		return (rv);
141 
142 	return (nvmem_read_cell_by_idx(node, idx, cell, buflen));
143 }
144 
145 int
nvmem_write_cell_by_idx(phandle_t node,int idx,void * cell,size_t buflen)146 nvmem_write_cell_by_idx(phandle_t node, int idx, void *cell, size_t buflen)
147 {
148 	phandle_t cell_node, prov_node;
149 	device_t provider;
150 	uint32_t reg[2];
151 	int rv;
152 
153 	rv = nvmem_get_cell_node(node, idx, &cell_node);
154 	if (rv != 0)
155 		return (rv);
156 
157 	prov_node = OF_parent(cell_node);
158 	if (OF_hasprop(prov_node, "read-only"))
159 		return (ENXIO);
160 
161 	/* Validate the reg property */
162 	if (OF_getencprop(cell_node, "reg", reg, sizeof(reg)) != sizeof(reg)) {
163 		if (bootverbose)
164 			printf("nvmem_get_cell_by_idx: Cannot parse reg property of cell %d\n",
165 			    idx);
166 		return (ENXIO);
167 	}
168 
169 	if (buflen != reg[1])
170 		return (EINVAL);
171 
172 	provider = OF_device_from_xref(OF_xref_from_node(prov_node));
173 	if (provider == NULL) {
174 		if (bootverbose)
175 			printf("nvmem_get_cell_by_idx: Cannot find the nvmem device\n");
176 		return (ENXIO);
177 	}
178 
179 	rv = NVMEM_WRITE(provider, reg[0], reg[1], cell);
180 	if (rv != 0) {
181 		return (rv);
182 	}
183 
184 	return (0);
185 }
186 
187 int
nvmem_write_cell_by_name(phandle_t node,const char * name,void * cell,size_t buflen)188 nvmem_write_cell_by_name(phandle_t node, const char *name, void *cell, size_t buflen)
189 {
190 	int rv, idx;
191 
192 	rv = ofw_bus_find_string_index(node, "nvmem-cell-names", name, &idx);
193 	if (rv != 0)
194 		return (rv);
195 
196 	return (nvmem_write_cell_by_idx(node, idx, cell, buflen));
197 }
198