1258209Srpaulo/*- 2258209Srpaulo * Copyright (c) 2013 Rui Paulo <rpaulo@FreeBSD.org> 3258209Srpaulo * All rights reserved. 4258209Srpaulo * 5258209Srpaulo * Redistribution and use in source and binary forms, with or without 6258209Srpaulo * modification, are permitted provided that the following conditions 7258209Srpaulo * are met: 8258209Srpaulo * 1. Redistributions of source code must retain the above copyright 9258209Srpaulo * notice, this list of conditions and the following disclaimer. 10258209Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11258209Srpaulo * notice, this list of conditions and the following disclaimer in the 12258209Srpaulo * documentation and/or other materials provided with the distribution. 13258209Srpaulo * 14258209Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15258209Srpaulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16258209Srpaulo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17258209Srpaulo * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 18258209Srpaulo * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19258209Srpaulo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20258209Srpaulo * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21258209Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22258209Srpaulo * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 23258209Srpaulo * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24258209Srpaulo * POSSIBILITY OF SUCH DAMAGE. 25258209Srpaulo */ 26258209Srpaulo#include <sys/cdefs.h> 27258209Srpaulo__FBSDID("$FreeBSD: releng/11.0/sys/arm/ti/ti_mbox.c 283276 2015-05-22 03:16:18Z gonzo $"); 28258209Srpaulo 29258209Srpaulo#include <sys/param.h> 30258209Srpaulo#include <sys/systm.h> 31258209Srpaulo#include <sys/bus.h> 32258209Srpaulo#include <sys/kernel.h> 33258209Srpaulo#include <sys/module.h> 34258209Srpaulo#include <sys/malloc.h> 35258209Srpaulo#include <sys/rman.h> 36258209Srpaulo#include <sys/timeet.h> 37258209Srpaulo#include <sys/timetc.h> 38258209Srpaulo#include <sys/watchdog.h> 39258209Srpaulo#include <machine/bus.h> 40258209Srpaulo#include <machine/cpu.h> 41258209Srpaulo#include <machine/frame.h> 42258209Srpaulo#include <machine/intr.h> 43258209Srpaulo 44258209Srpaulo#include <dev/fdt/fdt_common.h> 45258209Srpaulo#include <dev/ofw/openfirm.h> 46258209Srpaulo#include <dev/ofw/ofw_bus.h> 47258209Srpaulo#include <dev/ofw/ofw_bus_subr.h> 48258209Srpaulo 49258209Srpaulo#include <machine/bus.h> 50258209Srpaulo 51258209Srpaulo#include <arm/ti/ti_mbox.h> 52258209Srpaulo#include <arm/ti/ti_prcm.h> 53258209Srpaulo 54258209Srpaulo#include "mbox_if.h" 55258209Srpaulo 56258209Srpaulo#ifdef DEBUG 57258209Srpaulo#define DPRINTF(fmt, ...) do { \ 58258209Srpaulo printf("%s: ", __func__); \ 59258209Srpaulo printf(fmt, __VA_ARGS__); \ 60258209Srpaulo} while (0) 61258209Srpaulo#else 62258209Srpaulo#define DPRINTF(fmt, ...) 63258209Srpaulo#endif 64258209Srpaulo 65258209Srpaulostatic device_probe_t ti_mbox_probe; 66258209Srpaulostatic device_attach_t ti_mbox_attach; 67258209Srpaulostatic device_detach_t ti_mbox_detach; 68258209Srpaulostatic void ti_mbox_intr(void *); 69258209Srpaulostatic int ti_mbox_read(device_t, int, uint32_t *); 70258209Srpaulostatic int ti_mbox_write(device_t, int, uint32_t); 71258209Srpaulo 72258209Srpaulostruct ti_mbox_softc { 73258209Srpaulo struct mtx sc_mtx; 74258209Srpaulo struct resource *sc_mem_res; 75258209Srpaulo struct resource *sc_irq_res; 76258209Srpaulo void *sc_intr; 77258209Srpaulo bus_space_tag_t sc_bt; 78258209Srpaulo bus_space_handle_t sc_bh; 79258209Srpaulo}; 80258209Srpaulo 81258209Srpaulo#define TI_MBOX_LOCK(sc) mtx_lock(&(sc)->sc_mtx) 82258209Srpaulo#define TI_MBOX_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) 83258209Srpaulo 84258209Srpaulostatic device_method_t ti_mbox_methods[] = { 85258209Srpaulo DEVMETHOD(device_probe, ti_mbox_probe), 86258209Srpaulo DEVMETHOD(device_attach, ti_mbox_attach), 87258209Srpaulo DEVMETHOD(device_detach, ti_mbox_detach), 88258209Srpaulo 89258209Srpaulo DEVMETHOD(mbox_read, ti_mbox_read), 90258209Srpaulo DEVMETHOD(mbox_write, ti_mbox_write), 91258209Srpaulo 92258209Srpaulo DEVMETHOD_END 93258209Srpaulo}; 94258209Srpaulo 95258209Srpaulostatic driver_t ti_mbox_driver = { 96258209Srpaulo "ti_mbox", 97258209Srpaulo ti_mbox_methods, 98258209Srpaulo sizeof(struct ti_mbox_softc) 99258209Srpaulo}; 100258209Srpaulo 101258209Srpaulostatic devclass_t ti_mbox_devclass; 102258209Srpaulo 103258209SrpauloDRIVER_MODULE(ti_mbox, simplebus, ti_mbox_driver, ti_mbox_devclass, 0, 0); 104258209Srpaulo 105258209Srpaulostatic __inline uint32_t 106258209Srpauloti_mbox_reg_read(struct ti_mbox_softc *sc, uint16_t reg) 107258209Srpaulo{ 108258209Srpaulo return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); 109258209Srpaulo} 110258209Srpaulo 111258209Srpaulostatic __inline void 112258209Srpauloti_mbox_reg_write(struct ti_mbox_softc *sc, uint16_t reg, uint32_t val) 113258209Srpaulo{ 114258209Srpaulo bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); 115258209Srpaulo} 116258209Srpaulo 117258209Srpaulostatic int 118258209Srpauloti_mbox_probe(device_t dev) 119258209Srpaulo{ 120261410Sian 121261410Sian if (!ofw_bus_status_okay(dev)) 122261410Sian return (ENXIO); 123261410Sian 124283276Sgonzo if (ofw_bus_is_compatible(dev, "ti,omap4-mailbox")) { 125258209Srpaulo device_set_desc(dev, "TI System Mailbox"); 126258209Srpaulo return (BUS_PROBE_DEFAULT); 127258209Srpaulo } 128258209Srpaulo 129258209Srpaulo return (ENXIO); 130258209Srpaulo} 131258209Srpaulo 132258209Srpaulostatic int 133258209Srpauloti_mbox_attach(device_t dev) 134258209Srpaulo{ 135258209Srpaulo struct ti_mbox_softc *sc; 136258209Srpaulo int rid, delay, chan; 137258209Srpaulo uint32_t rev, sysconfig; 138258209Srpaulo 139258209Srpaulo if (ti_prcm_clk_enable(MAILBOX0_CLK) != 0) { 140258209Srpaulo device_printf(dev, "could not enable MBOX clock\n"); 141258209Srpaulo return (ENXIO); 142258209Srpaulo } 143258209Srpaulo sc = device_get_softc(dev); 144258209Srpaulo rid = 0; 145261211Sjmg mtx_init(&sc->sc_mtx, "TI mbox", NULL, MTX_DEF); 146258209Srpaulo sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 147258209Srpaulo RF_ACTIVE); 148258209Srpaulo if (sc->sc_mem_res == NULL) { 149258209Srpaulo device_printf(dev, "could not allocate memory resource\n"); 150258209Srpaulo return (ENXIO); 151258209Srpaulo } 152258209Srpaulo sc->sc_bt = rman_get_bustag(sc->sc_mem_res); 153258209Srpaulo sc->sc_bh = rman_get_bushandle(sc->sc_mem_res); 154258209Srpaulo rid = 0; 155258209Srpaulo sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 156258209Srpaulo RF_ACTIVE); 157258209Srpaulo if (sc->sc_irq_res == NULL) { 158258209Srpaulo device_printf(dev, "could not allocate interrupt resource\n"); 159258209Srpaulo ti_mbox_detach(dev); 160258209Srpaulo return (ENXIO); 161258209Srpaulo } 162258209Srpaulo if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC, 163258209Srpaulo NULL, ti_mbox_intr, sc, &sc->sc_intr) != 0) { 164258209Srpaulo device_printf(dev, "unable to setup the interrupt handler\n"); 165258209Srpaulo ti_mbox_detach(dev); 166258209Srpaulo return (ENXIO); 167258209Srpaulo } 168258209Srpaulo /* 169258209Srpaulo * Reset the controller. 170258209Srpaulo */ 171258209Srpaulo sysconfig = ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG); 172258209Srpaulo DPRINTF("initial sysconfig %d\n", sysconfig); 173258209Srpaulo sysconfig |= TI_MBOX_SYSCONFIG_SOFTRST; 174258209Srpaulo delay = 100; 175258209Srpaulo while (ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG) & 176258209Srpaulo TI_MBOX_SYSCONFIG_SOFTRST) { 177258209Srpaulo delay--; 178258209Srpaulo DELAY(10); 179258209Srpaulo } 180258209Srpaulo if (delay == 0) { 181258209Srpaulo device_printf(dev, "controller reset failed\n"); 182258209Srpaulo ti_mbox_detach(dev); 183258209Srpaulo return (ENXIO); 184258209Srpaulo } 185258209Srpaulo /* 186258209Srpaulo * Enable smart idle mode. 187258209Srpaulo */ 188258209Srpaulo ti_mbox_reg_write(sc, TI_MBOX_SYSCONFIG, 189258209Srpaulo ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG) | TI_MBOX_SYSCONFIG_SMARTIDLE); 190258209Srpaulo rev = ti_mbox_reg_read(sc, TI_MBOX_REVISION); 191258209Srpaulo DPRINTF("rev %d\n", rev); 192258209Srpaulo device_printf(dev, "revision %d.%d\n", (rev >> 8) & 0x4, rev & 0x40); 193258209Srpaulo /* 194258209Srpaulo * Enable message interrupts. 195258209Srpaulo */ 196258209Srpaulo for (chan = 0; chan < 8; chan++) 197258209Srpaulo ti_mbox_reg_write(sc, TI_MBOX_IRQENABLE_SET(chan), 1); 198258209Srpaulo 199258209Srpaulo return (0); 200258209Srpaulo} 201258209Srpaulo 202258209Srpaulostatic int 203258209Srpauloti_mbox_detach(device_t dev) 204258209Srpaulo{ 205258209Srpaulo struct ti_mbox_softc *sc; 206258209Srpaulo 207258209Srpaulo sc = device_get_softc(dev); 208258209Srpaulo if (sc->sc_intr) 209258209Srpaulo bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr); 210258209Srpaulo if (sc->sc_irq_res) 211258209Srpaulo bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res), 212258209Srpaulo sc->sc_irq_res); 213258209Srpaulo if (sc->sc_mem_res) 214258209Srpaulo bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res), 215258209Srpaulo sc->sc_mem_res); 216258209Srpaulo 217258209Srpaulo return (0); 218258209Srpaulo} 219258209Srpaulo 220258209Srpaulostatic void 221258209Srpauloti_mbox_intr(void *arg) 222258209Srpaulo{ 223258209Srpaulo struct ti_mbox_softc *sc; 224258209Srpaulo 225258209Srpaulo sc = arg; 226258209Srpaulo DPRINTF("interrupt %p", sc); 227258209Srpaulo} 228258209Srpaulo 229258209Srpaulostatic int 230258209Srpauloti_mbox_read(device_t dev, int chan, uint32_t *data) 231258209Srpaulo{ 232258209Srpaulo struct ti_mbox_softc *sc; 233258209Srpaulo 234258209Srpaulo if (chan < 0 || chan > 7) 235258209Srpaulo return (EINVAL); 236258209Srpaulo sc = device_get_softc(dev); 237258209Srpaulo 238258209Srpaulo return (ti_mbox_reg_read(sc, TI_MBOX_MESSAGE(chan))); 239258209Srpaulo} 240258209Srpaulo 241258209Srpaulostatic int 242258209Srpauloti_mbox_write(device_t dev, int chan, uint32_t data) 243258209Srpaulo{ 244258209Srpaulo int limit = 500; 245258209Srpaulo struct ti_mbox_softc *sc; 246258209Srpaulo 247258209Srpaulo if (chan < 0 || chan > 7) 248258209Srpaulo return (EINVAL); 249258209Srpaulo sc = device_get_softc(dev); 250258209Srpaulo TI_MBOX_LOCK(sc); 251258209Srpaulo /* XXX implement interrupt method */ 252258209Srpaulo while (ti_mbox_reg_read(sc, TI_MBOX_FIFOSTATUS(chan)) == 1 && 253258209Srpaulo limit--) { 254258209Srpaulo DELAY(10); 255258209Srpaulo } 256258209Srpaulo if (limit == 0) { 257258209Srpaulo device_printf(dev, "FIFOSTAUS%d stuck\n", chan); 258258209Srpaulo TI_MBOX_UNLOCK(sc); 259258209Srpaulo return (EAGAIN); 260258209Srpaulo } 261258209Srpaulo ti_mbox_reg_write(sc, TI_MBOX_MESSAGE(chan), data); 262258209Srpaulo 263258209Srpaulo return (0); 264258209Srpaulo} 265