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