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