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 __FBSDID("$FreeBSD$"); 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 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 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 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 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 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 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