1 /*- 2 * Copyright (c) 2013 Rui Paulo <rpaulo@FreeBSD.org> 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 ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 23 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 */ 26 #include <sys/cdefs.h> 27 __FBSDID("$FreeBSD$"); 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/bus.h> 32 #include <sys/kernel.h> 33 #include <sys/module.h> 34 #include <sys/malloc.h> 35 #include <sys/rman.h> 36 #include <sys/timeet.h> 37 #include <sys/timetc.h> 38 #include <sys/watchdog.h> 39 #include <machine/bus.h> 40 #include <machine/cpu.h> 41 #include <machine/frame.h> 42 #include <machine/intr.h> 43 44 #include <dev/fdt/fdt_common.h> 45 #include <dev/ofw/openfirm.h> 46 #include <dev/ofw/ofw_bus.h> 47 #include <dev/ofw/ofw_bus_subr.h> 48 49 #include <machine/bus.h> 50 51 #include <arm/ti/ti_mbox.h> 52 #include <arm/ti/ti_prcm.h> 53 54 #include "mbox_if.h" 55 56 #ifdef DEBUG 57 #define DPRINTF(fmt, ...) do { \ 58 printf("%s: ", __func__); \ 59 printf(fmt, __VA_ARGS__); \ 60 } while (0) 61 #else 62 #define DPRINTF(fmt, ...) 63 #endif 64 65 static device_probe_t ti_mbox_probe; 66 static device_attach_t ti_mbox_attach; 67 static device_detach_t ti_mbox_detach; 68 static void ti_mbox_intr(void *); 69 static int ti_mbox_read(device_t, int, uint32_t *); 70 static int ti_mbox_write(device_t, int, uint32_t); 71 72 struct ti_mbox_softc { 73 struct mtx sc_mtx; 74 struct resource *sc_mem_res; 75 struct resource *sc_irq_res; 76 void *sc_intr; 77 bus_space_tag_t sc_bt; 78 bus_space_handle_t sc_bh; 79 }; 80 81 #define TI_MBOX_LOCK(sc) mtx_lock(&(sc)->sc_mtx) 82 #define TI_MBOX_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) 83 84 static device_method_t ti_mbox_methods[] = { 85 DEVMETHOD(device_probe, ti_mbox_probe), 86 DEVMETHOD(device_attach, ti_mbox_attach), 87 DEVMETHOD(device_detach, ti_mbox_detach), 88 89 DEVMETHOD(mbox_read, ti_mbox_read), 90 DEVMETHOD(mbox_write, ti_mbox_write), 91 92 DEVMETHOD_END 93 }; 94 95 static driver_t ti_mbox_driver = { 96 "ti_mbox", 97 ti_mbox_methods, 98 sizeof(struct ti_mbox_softc) 99 }; 100 101 static devclass_t ti_mbox_devclass; 102 103 DRIVER_MODULE(ti_mbox, simplebus, ti_mbox_driver, ti_mbox_devclass, 0, 0); 104 105 static __inline uint32_t 106 ti_mbox_reg_read(struct ti_mbox_softc *sc, uint16_t reg) 107 { 108 return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); 109 } 110 111 static __inline void 112 ti_mbox_reg_write(struct ti_mbox_softc *sc, uint16_t reg, uint32_t val) 113 { 114 bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); 115 } 116 117 static int 118 ti_mbox_probe(device_t dev) 119 { 120 121 if (!ofw_bus_status_okay(dev)) 122 return (ENXIO); 123 124 if (ofw_bus_is_compatible(dev, "ti,system-mbox")) { 125 device_set_desc(dev, "TI System Mailbox"); 126 return (BUS_PROBE_DEFAULT); 127 } 128 129 return (ENXIO); 130 } 131 132 static int 133 ti_mbox_attach(device_t dev) 134 { 135 struct ti_mbox_softc *sc; 136 int rid, delay, chan; 137 uint32_t rev, sysconfig; 138 139 if (ti_prcm_clk_enable(MAILBOX0_CLK) != 0) { 140 device_printf(dev, "could not enable MBOX clock\n"); 141 return (ENXIO); 142 } 143 sc = device_get_softc(dev); 144 rid = 0; 145 mtx_init(&sc->sc_mtx, "TI mbox", NULL, MTX_DEF); 146 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 147 RF_ACTIVE); 148 if (sc->sc_mem_res == NULL) { 149 device_printf(dev, "could not allocate memory resource\n"); 150 return (ENXIO); 151 } 152 sc->sc_bt = rman_get_bustag(sc->sc_mem_res); 153 sc->sc_bh = rman_get_bushandle(sc->sc_mem_res); 154 rid = 0; 155 sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 156 RF_ACTIVE); 157 if (sc->sc_irq_res == NULL) { 158 device_printf(dev, "could not allocate interrupt resource\n"); 159 ti_mbox_detach(dev); 160 return (ENXIO); 161 } 162 if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC, 163 NULL, ti_mbox_intr, sc, &sc->sc_intr) != 0) { 164 device_printf(dev, "unable to setup the interrupt handler\n"); 165 ti_mbox_detach(dev); 166 return (ENXIO); 167 } 168 /* 169 * Reset the controller. 170 */ 171 sysconfig = ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG); 172 DPRINTF("initial sysconfig %d\n", sysconfig); 173 sysconfig |= TI_MBOX_SYSCONFIG_SOFTRST; 174 delay = 100; 175 while (ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG) & 176 TI_MBOX_SYSCONFIG_SOFTRST) { 177 delay--; 178 DELAY(10); 179 } 180 if (delay == 0) { 181 device_printf(dev, "controller reset failed\n"); 182 ti_mbox_detach(dev); 183 return (ENXIO); 184 } 185 /* 186 * Enable smart idle mode. 187 */ 188 ti_mbox_reg_write(sc, TI_MBOX_SYSCONFIG, 189 ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG) | TI_MBOX_SYSCONFIG_SMARTIDLE); 190 rev = ti_mbox_reg_read(sc, TI_MBOX_REVISION); 191 DPRINTF("rev %d\n", rev); 192 device_printf(dev, "revision %d.%d\n", (rev >> 8) & 0x4, rev & 0x40); 193 /* 194 * Enable message interrupts. 195 */ 196 for (chan = 0; chan < 8; chan++) 197 ti_mbox_reg_write(sc, TI_MBOX_IRQENABLE_SET(chan), 1); 198 199 return (0); 200 } 201 202 static int 203 ti_mbox_detach(device_t dev) 204 { 205 struct ti_mbox_softc *sc; 206 207 sc = device_get_softc(dev); 208 if (sc->sc_intr) 209 bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr); 210 if (sc->sc_irq_res) 211 bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res), 212 sc->sc_irq_res); 213 if (sc->sc_mem_res) 214 bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res), 215 sc->sc_mem_res); 216 217 return (0); 218 } 219 220 static void 221 ti_mbox_intr(void *arg) 222 { 223 struct ti_mbox_softc *sc; 224 225 sc = arg; 226 DPRINTF("interrupt %p", sc); 227 } 228 229 static int 230 ti_mbox_read(device_t dev, int chan, uint32_t *data) 231 { 232 struct ti_mbox_softc *sc; 233 234 if (chan < 0 || chan > 7) 235 return (EINVAL); 236 sc = device_get_softc(dev); 237 238 return (ti_mbox_reg_read(sc, TI_MBOX_MESSAGE(chan))); 239 } 240 241 static int 242 ti_mbox_write(device_t dev, int chan, uint32_t data) 243 { 244 int limit = 500; 245 struct ti_mbox_softc *sc; 246 247 if (chan < 0 || chan > 7) 248 return (EINVAL); 249 sc = device_get_softc(dev); 250 TI_MBOX_LOCK(sc); 251 /* XXX implement interrupt method */ 252 while (ti_mbox_reg_read(sc, TI_MBOX_FIFOSTATUS(chan)) == 1 && 253 limit--) { 254 DELAY(10); 255 } 256 if (limit == 0) { 257 device_printf(dev, "FIFOSTAUS%d stuck\n", chan); 258 TI_MBOX_UNLOCK(sc); 259 return (EAGAIN); 260 } 261 ti_mbox_reg_write(sc, TI_MBOX_MESSAGE(chan), data); 262 263 return (0); 264 } 265