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