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