1 /*- 2 * Copyright (c) 2014 Steven Lawrance <stl@koffein.net> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 /* 31 * Access to the Freescale i.MX6 On-Chip One-Time-Programmable Memory 32 */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/kernel.h> 37 #include <sys/module.h> 38 #include <sys/bus.h> 39 #include <sys/rman.h> 40 41 #include <dev/ofw/ofw_bus.h> 42 #include <dev/ofw/ofw_bus_subr.h> 43 44 #include <machine/bus.h> 45 46 #include <arm/freescale/fsl_ocotpreg.h> 47 #include <arm/freescale/fsl_ocotpvar.h> 48 49 /* 50 * Find the physical address and size of the ocotp registers and devmap them, 51 * returning a pointer to the virtual address of the base. 52 * 53 * XXX This is temporary until we've worked out all the details of controlling 54 * the load order of devices. In an ideal world this device would be up and 55 * running before anything that needs it. When we're at a point to make that 56 * happen, this little block of code, and the few lines in fsl_ocotp_read_4() 57 * that refer to it can be deleted. 58 */ 59 #include <vm/vm.h> 60 #include <vm/pmap.h> 61 #include <dev/fdt/fdt_common.h> 62 #include <machine/devmap.h> 63 64 static uint32_t *ocotp_regs; 65 static vm_size_t ocotp_size; 66 67 static void 68 fsl_ocotp_devmap(void) 69 { 70 phandle_t child, root; 71 u_long base, size; 72 73 if ((root = OF_finddevice("/")) == 0) 74 goto fatal; 75 if ((child = fdt_depth_search_compatible(root, "fsl,imx6q-ocotp", 0)) == 0) 76 goto fatal; 77 if (fdt_regsize(child, &base, &size) != 0) 78 goto fatal; 79 80 ocotp_size = (vm_size_t)size; 81 82 if ((ocotp_regs = pmap_mapdev((vm_offset_t)base, ocotp_size)) == NULL) 83 goto fatal; 84 85 return; 86 fatal: 87 panic("cannot find/map the ocotp registers"); 88 } 89 /* XXX end of temporary code */ 90 91 struct ocotp_softc { 92 device_t dev; 93 struct resource *mem_res; 94 }; 95 96 static struct ocotp_softc *ocotp_sc; 97 98 static inline uint32_t 99 RD4(struct ocotp_softc *sc, bus_size_t off) 100 { 101 102 return (bus_read_4(sc->mem_res, off)); 103 } 104 105 static int 106 ocotp_detach(device_t dev) 107 { 108 109 /* The ocotp registers are always accessible. */ 110 return (EBUSY); 111 } 112 113 static int 114 ocotp_attach(device_t dev) 115 { 116 struct ocotp_softc *sc; 117 int err, rid; 118 119 sc = device_get_softc(dev); 120 sc->dev = dev; 121 122 /* Allocate bus_space resources. */ 123 rid = 0; 124 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 125 RF_ACTIVE); 126 if (sc->mem_res == NULL) { 127 device_printf(dev, "Cannot allocate memory resources\n"); 128 err = ENXIO; 129 goto out; 130 } 131 132 ocotp_sc = sc; 133 134 /* We're done with the temporary mapping now. */ 135 if (ocotp_regs != NULL) 136 pmap_unmapdev((vm_offset_t)ocotp_regs, ocotp_size); 137 138 err = 0; 139 140 out: 141 if (err != 0) 142 ocotp_detach(dev); 143 144 return (err); 145 } 146 147 static int 148 ocotp_probe(device_t dev) 149 { 150 151 if (!ofw_bus_status_okay(dev)) 152 return (ENXIO); 153 154 if (ofw_bus_is_compatible(dev, "fsl,imx6q-ocotp") == 0) 155 return (ENXIO); 156 157 device_set_desc(dev, 158 "Freescale On-Chip One-Time-Programmable Memory"); 159 160 return (BUS_PROBE_DEFAULT); 161 } 162 163 uint32_t 164 fsl_ocotp_read_4(bus_size_t off) 165 { 166 167 if (off > FSL_OCOTP_LAST_REG) 168 panic("fsl_ocotp_read_4: offset out of range"); 169 170 /* If we have a softcontext use the regular bus_space read. */ 171 if (ocotp_sc != NULL) 172 return (RD4(ocotp_sc, off)); 173 174 /* 175 * Otherwise establish a tempory device mapping if necessary, and read 176 * the device without any help from bus_space. 177 * 178 * XXX Eventually the code from there down can be deleted. 179 */ 180 if (ocotp_regs == NULL) 181 fsl_ocotp_devmap(); 182 183 return (ocotp_regs[off / 4]); 184 } 185 186 static device_method_t ocotp_methods[] = { 187 /* Device interface */ 188 DEVMETHOD(device_probe, ocotp_probe), 189 DEVMETHOD(device_attach, ocotp_attach), 190 DEVMETHOD(device_detach, ocotp_detach), 191 192 DEVMETHOD_END 193 }; 194 195 static driver_t ocotp_driver = { 196 "ocotp", 197 ocotp_methods, 198 sizeof(struct ocotp_softc) 199 }; 200 201 static devclass_t ocotp_devclass; 202 203 EARLY_DRIVER_MODULE(ocotp, simplebus, ocotp_driver, ocotp_devclass, 0, 0, 204 BUS_PASS_CPU + BUS_PASS_ORDER_FIRST); 205 206