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 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 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 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 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 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 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