Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_bsc.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_bsc.c (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_bsc.c (revision 322724) @@ -1,507 +1,514 @@ /*- * Copyright (c) 2001 Tsubai Masanari. * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2013 Luiz Otavio O Souza * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" +static struct ofw_compat_data compat_data[] = { + {"broadcom,bcm2835-bsc", 1}, + {"brcm,bcm2708-i2c", 1}, + {"brcm,bcm2835-i2c", 1}, + {NULL, 0} +}; + static void bcm_bsc_intr(void *); static int bcm_bsc_detach(device_t); static void bcm_bsc_modifyreg(struct bcm_bsc_softc *sc, uint32_t off, uint32_t mask, uint32_t value) { uint32_t reg; mtx_assert(&sc->sc_mtx, MA_OWNED); reg = BCM_BSC_READ(sc, off); reg &= ~mask; reg |= value; BCM_BSC_WRITE(sc, off, reg); } static int bcm_bsc_clock_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); BCM_BSC_UNLOCK(sc); clk &= 0xffff; if (clk == 0) clk = 32768; clk = BCM_BSC_CORE_CLK / clk; return (sysctl_handle_int(oidp, &clk, 0, req)); } static int bcm_bsc_clkt_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clkt; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); clkt = BCM_BSC_READ(sc, BCM_BSC_CLKT); BCM_BSC_UNLOCK(sc); clkt &= 0xffff; error = sysctl_handle_int(oidp, &clkt, sizeof(clkt), req); if (error != 0 || req->newptr == NULL) return (error); BCM_BSC_LOCK(sc); BCM_BSC_WRITE(sc, BCM_BSC_CLKT, clkt & 0xffff); BCM_BSC_UNLOCK(sc); return (0); } static int bcm_bsc_fall_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk, reg; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); reg = BCM_BSC_READ(sc, BCM_BSC_DELAY); BCM_BSC_UNLOCK(sc); reg >>= 16; error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); clk = BCM_BSC_CORE_CLK / clk; if (reg > clk / 2) reg = clk / 2 - 1; bcm_bsc_modifyreg(sc, BCM_BSC_DELAY, 0xffff0000, reg << 16); BCM_BSC_UNLOCK(sc); return (0); } static int bcm_bsc_rise_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk, reg; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); reg = BCM_BSC_READ(sc, BCM_BSC_DELAY); BCM_BSC_UNLOCK(sc); reg &= 0xffff; error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); clk = BCM_BSC_CORE_CLK / clk; if (reg > clk / 2) reg = clk / 2 - 1; bcm_bsc_modifyreg(sc, BCM_BSC_DELAY, 0xffff, reg); BCM_BSC_UNLOCK(sc); return (0); } static void bcm_bsc_sysctl_init(struct bcm_bsc_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid *tree_node; struct sysctl_oid_list *tree; /* * Add system sysctl tree/handlers. */ ctx = device_get_sysctl_ctx(sc->sc_dev); tree_node = device_get_sysctl_tree(sc->sc_dev); tree = SYSCTL_CHILDREN(tree_node); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "frequency", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_clock_proc, "IU", "I2C BUS clock frequency"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clock_stretch", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_clkt_proc, "IU", "I2C BUS clock stretch timeout"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "fall_edge_delay", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_fall_proc, "IU", "I2C BUS falling edge delay"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "rise_edge_delay", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_rise_proc, "IU", "I2C BUS rising edge delay"); } static void bcm_bsc_reset(struct bcm_bsc_softc *sc) { /* Enable the BSC Controller, disable interrupts. */ BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN); /* Clear pending interrupts. */ BCM_BSC_WRITE(sc, BCM_BSC_STATUS, BCM_BSC_STATUS_CLKT | BCM_BSC_STATUS_ERR | BCM_BSC_STATUS_DONE); /* Clear the FIFO. */ bcm_bsc_modifyreg(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_CLEAR0, BCM_BSC_CTRL_CLEAR0); } static int bcm_bsc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-bsc")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2708/2835 BSC controller"); return (BUS_PROBE_DEFAULT); } static int bcm_bsc_attach(device_t dev) { struct bcm_bsc_softc *sc; unsigned long start; device_t gpio; int i, rid; sc = device_get_softc(dev); sc->sc_dev = dev; rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); /* Check the unit we are attaching by its base address. */ start = rman_get_start(sc->sc_mem_res); for (i = 0; i < nitems(bcm_bsc_pins); i++) { - if (bcm_bsc_pins[i].start == start) + if (bcm_bsc_pins[i].start == (start & BCM_BSC_BASE_MASK)) break; } if (i == nitems(bcm_bsc_pins)) { device_printf(dev, "only bsc0 and bsc1 are supported\n"); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (ENXIO); } /* * Configure the GPIO pins to ALT0 function to enable BSC control * over the pins. */ gpio = devclass_get_device(devclass_find("gpio"), 0); if (!gpio) { device_printf(dev, "cannot find gpio0\n"); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (ENXIO); } bcm_gpio_set_alternate(gpio, bcm_bsc_pins[i].sda, BCM_GPIO_ALT0); bcm_gpio_set_alternate(gpio, bcm_bsc_pins[i].scl, BCM_GPIO_ALT0); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (!sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate interrupt\n"); return (ENXIO); } /* Hook up our interrupt handler. */ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, bcm_bsc_intr, sc, &sc->sc_intrhand)) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot setup the interrupt handler\n"); return (ENXIO); } mtx_init(&sc->sc_mtx, "bcm_bsc", NULL, MTX_DEF); bcm_bsc_sysctl_init(sc); /* Enable the BSC controller. Flush the FIFO. */ BCM_BSC_LOCK(sc); bcm_bsc_reset(sc); BCM_BSC_UNLOCK(sc); sc->sc_iicbus = device_add_child(dev, "iicbus", -1); if (sc->sc_iicbus == NULL) { bcm_bsc_detach(dev); return (ENXIO); } return (bus_generic_attach(dev)); } static int bcm_bsc_detach(device_t dev) { struct bcm_bsc_softc *sc; bus_generic_detach(dev); sc = device_get_softc(dev); mtx_destroy(&sc->sc_mtx); if (sc->sc_intrhand) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (0); } static void bcm_bsc_intr(void *arg) { struct bcm_bsc_softc *sc; uint32_t status; sc = (struct bcm_bsc_softc *)arg; BCM_BSC_LOCK(sc); /* The I2C interrupt is shared among all the BSC controllers. */ if ((sc->sc_flags & BCM_I2C_BUSY) == 0) { BCM_BSC_UNLOCK(sc); return; } status = BCM_BSC_READ(sc, BCM_BSC_STATUS); /* Check for errors. */ if (status & (BCM_BSC_STATUS_CLKT | BCM_BSC_STATUS_ERR)) { /* Disable interrupts. */ bcm_bsc_reset(sc); sc->sc_flags |= BCM_I2C_ERROR; wakeup(sc->sc_dev); BCM_BSC_UNLOCK(sc); return; } if (sc->sc_flags & BCM_I2C_READ) { while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_RXD)) { *sc->sc_data++ = BCM_BSC_READ(sc, BCM_BSC_DATA); sc->sc_resid--; status = BCM_BSC_READ(sc, BCM_BSC_STATUS); } } else { while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_TXD)) { BCM_BSC_WRITE(sc, BCM_BSC_DATA, *sc->sc_data++); sc->sc_resid--; status = BCM_BSC_READ(sc, BCM_BSC_STATUS); } } if (status & BCM_BSC_STATUS_DONE) { /* Disable interrupts. */ bcm_bsc_reset(sc); wakeup(sc->sc_dev); } BCM_BSC_UNLOCK(sc); } static int bcm_bsc_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct bcm_bsc_softc *sc; uint32_t intr, read, status; int i, err; sc = device_get_softc(dev); BCM_BSC_LOCK(sc); /* If the controller is busy wait until it is available. */ while (sc->sc_flags & BCM_I2C_BUSY) mtx_sleep(dev, &sc->sc_mtx, 0, "bscbusw", 0); /* Now we have control over the BSC controller. */ sc->sc_flags = BCM_I2C_BUSY; /* Clear the FIFO and the pending interrupts. */ bcm_bsc_reset(sc); err = 0; for (i = 0; i < nmsgs; i++) { /* Write the slave address. */ BCM_BSC_WRITE(sc, BCM_BSC_SLAVE, msgs[i].slave >> 1); /* Write the data length. */ BCM_BSC_WRITE(sc, BCM_BSC_DLEN, msgs[i].len); sc->sc_data = msgs[i].buf; sc->sc_resid = msgs[i].len; if ((msgs[i].flags & IIC_M_RD) == 0) { /* Fill up the TX FIFO. */ status = BCM_BSC_READ(sc, BCM_BSC_STATUS); while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_TXD)) { BCM_BSC_WRITE(sc, BCM_BSC_DATA, *sc->sc_data); sc->sc_data++; sc->sc_resid--; status = BCM_BSC_READ(sc, BCM_BSC_STATUS); } read = 0; intr = BCM_BSC_CTRL_INTT; sc->sc_flags &= ~BCM_I2C_READ; } else { sc->sc_flags |= BCM_I2C_READ; read = BCM_BSC_CTRL_READ; intr = BCM_BSC_CTRL_INTR; } intr |= BCM_BSC_CTRL_INTD; /* Start the transfer. */ BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN | BCM_BSC_CTRL_ST | read | intr); /* Wait for the transaction to complete. */ err = mtx_sleep(dev, &sc->sc_mtx, 0, "bsciow", hz); /* Check for errors. */ if (err == 0 && (sc->sc_flags & BCM_I2C_ERROR)) err = EIO; if (err != 0) break; } /* Clean the controller flags. */ sc->sc_flags = 0; /* Wake up the threads waiting for bus. */ wakeup(dev); BCM_BSC_UNLOCK(sc); return (err); } static int bcm_bsc_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { struct bcm_bsc_softc *sc; uint32_t busfreq; sc = device_get_softc(dev); BCM_BSC_LOCK(sc); bcm_bsc_reset(sc); if (sc->sc_iicbus == NULL) busfreq = 100000; else busfreq = IICBUS_GET_FREQUENCY(sc->sc_iicbus, speed); BCM_BSC_WRITE(sc, BCM_BSC_CLOCK, BCM_BSC_CORE_CLK / busfreq); BCM_BSC_UNLOCK(sc); return (IIC_ENOADDR); } static phandle_t bcm_bsc_get_node(device_t bus, device_t dev) { /* We only have one child, the I2C bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t bcm_bsc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_bsc_probe), DEVMETHOD(device_attach, bcm_bsc_attach), DEVMETHOD(device_detach, bcm_bsc_detach), /* iicbus interface */ DEVMETHOD(iicbus_reset, bcm_bsc_iicbus_reset), DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_transfer, bcm_bsc_transfer), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, bcm_bsc_get_node), DEVMETHOD_END }; static devclass_t bcm_bsc_devclass; static driver_t bcm_bsc_driver = { "iichb", bcm_bsc_methods, sizeof(struct bcm_bsc_softc), }; DRIVER_MODULE(iicbus, bcm2835_bsc, iicbus_driver, iicbus_devclass, 0, 0); DRIVER_MODULE(bcm2835_bsc, simplebus, bcm_bsc_driver, bcm_bsc_devclass, 0, 0); Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h (revision 322724) @@ -1,70 +1,71 @@ /*- * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2013 Luiz Otavio O Souza * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _BCM2835_BSCVAR_H #define _BCM2835_BSCVAR_H struct { uint32_t sda; uint32_t scl; unsigned long start; } bcm_bsc_pins[] = { - { 0, 1, 0x20205000 }, /* BSC0 GPIO pins and base address. */ - { 2, 3, 0x20804000 } /* BSC1 GPIO pins and base address. */ + { 0, 1, 0x205000 }, /* BSC0 GPIO pins and base address. */ + { 2, 3, 0x804000 } /* BSC1 GPIO pins and base address. */ }; +#define BCM_BSC_BASE_MASK 0x00ffffff struct bcm_bsc_softc { device_t sc_dev; device_t sc_iicbus; struct mtx sc_mtx; struct resource * sc_mem_res; struct resource * sc_irq_res; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; uint16_t sc_resid; uint8_t *sc_data; uint8_t sc_flags; void * sc_intrhand; }; #define BCM_I2C_BUSY 0x01 #define BCM_I2C_READ 0x02 #define BCM_I2C_ERROR 0x04 #define BCM_BSC_WRITE(_sc, _off, _val) \ bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val) #define BCM_BSC_READ(_sc, _off) \ bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off) #define BCM_BSC_LOCK(_sc) \ mtx_lock(&(_sc)->sc_mtx) #define BCM_BSC_UNLOCK(_sc) \ mtx_unlock(&(_sc)->sc_mtx) #endif /* _BCM2835_BSCVAR_H_ */ Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_common.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_common.c (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_common.c (revision 322724) @@ -1,75 +1,83 @@ /*- * Copyright (C) 2008-2011 MARVELL INTERNATIONAL LTD. * All rights reserved. * * Developed by Semihalf. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of MARVELL nor the names of contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "opt_global.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include struct fdt_fixup_entry fdt_fixup_table[] = { { NULL, NULL } }; static int fdt_intc_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, int *pol) { - if (!fdt_is_compatible(node, "broadcom,bcm2835-armctrl-ic")) - return (ENXIO); - - *interrupt = fdt32_to_cpu(intr[0]); - *trig = INTR_TRIGGER_CONFORM; - *pol = INTR_POLARITY_CONFORM; - - return (0); + if (fdt_is_compatible(node, "broadcom,bcm2835-armctrl-ic") || + fdt_is_compatible(node, "brcm,bcm2836-armctrl-ic")) { + *interrupt = fdt32_to_cpu(intr[0]); + *trig = INTR_TRIGGER_CONFORM; + *pol = INTR_POLARITY_CONFORM; + return (0); + } +#ifdef SOC_BCM2836 + if (fdt_is_compatible(node, "brcm,bcm2836-l1-intc")) { + *interrupt = fdt32_to_cpu(intr[0]) + 72; + *trig = INTR_TRIGGER_CONFORM; + *pol = INTR_POLARITY_CONFORM; + return (0); + } +#endif + return (ENXIO); } fdt_pic_decode_t fdt_pic_table[] = { &fdt_intc_decode_ic, NULL }; Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c (revision 322724) @@ -1,1817 +1,1613 @@ /*- * Copyright (C) 2013-2014 Daisuke Aoyama * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include + +#include +#include + #include #include #include #include "cpufreq_if.h" #include "mbox_if.h" #ifdef DEBUG #define DPRINTF(fmt, ...) do { \ printf("%s:%u: ", __func__, __LINE__); \ printf(fmt, ##__VA_ARGS__); \ } while (0) #else #define DPRINTF(fmt, ...) #endif #define HZ2MHZ(freq) ((freq) / (1000 * 1000)) #define MHZ2HZ(freq) ((freq) * (1000 * 1000)) #define OFFSET2MVOLT(val) (1200 + ((val) * 25)) #define MVOLT2OFFSET(val) (((val) - 1200) / 25) #define DEFAULT_ARM_FREQUENCY 700 #define DEFAULT_CORE_FREQUENCY 250 #define DEFAULT_SDRAM_FREQUENCY 400 #define DEFAULT_LOWEST_FREQ 300 #define TRANSITION_LATENCY 1000 #define MIN_OVER_VOLTAGE -16 #define MAX_OVER_VOLTAGE 6 #define MSG_ERROR -999999999 #define MHZSTEP 100 #define HZSTEP (MHZ2HZ(MHZSTEP)) #define TZ_ZEROC 2732 #define VC_LOCK(sc) do { \ sema_wait(&vc_sema); \ } while (0) #define VC_UNLOCK(sc) do { \ sema_post(&vc_sema); \ } while (0) /* ARM->VC mailbox property semaphore */ static struct sema vc_sema; static struct sysctl_ctx_list bcm2835_sysctl_ctx; struct bcm2835_cpufreq_softc { device_t dev; int arm_max_freq; int arm_min_freq; int core_max_freq; int core_min_freq; int sdram_max_freq; int sdram_min_freq; int max_voltage_core; int min_voltage_core; /* the values written in mbox */ int voltage_core; int voltage_sdram; int voltage_sdram_c; int voltage_sdram_i; int voltage_sdram_p; int turbo_mode; - /* mbox buffer (physical address) */ - bus_dma_tag_t dma_tag; - bus_dmamap_t dma_map; - bus_size_t dma_size; - void *dma_buf; - bus_addr_t dma_phys; - /* initial hook for waiting mbox intr */ struct intr_config_hook init_hook; }; +static struct ofw_compat_data compat_data[] = { + { "broadcom,bcm2835-vc", 1 }, + { "broadcom,bcm2708-vc", 1 }, + { "brcm,bcm2709", 1 }, + { NULL, 0 } +}; + static int cpufreq_verbose = 0; TUNABLE_INT("hw.bcm2835.cpufreq.verbose", &cpufreq_verbose); static int cpufreq_lowest_freq = DEFAULT_LOWEST_FREQ; TUNABLE_INT("hw.bcm2835.cpufreq.lowest_freq", &cpufreq_lowest_freq); #ifdef PROP_DEBUG static void bcm2835_dump(const void *data, int len) { const uint8_t *p = (const uint8_t*)data; int i; printf("dump @ %p:\n", data); for (i = 0; i < len; i++) { printf("%2.2x ", p[i]); if ((i % 4) == 3) printf(" "); if ((i % 16) == 15) printf("\n"); } printf("\n"); } #endif static int -bcm2835_mbox_call_prop(struct bcm2835_cpufreq_softc *sc) -{ - struct bcm2835_mbox_hdr *msg = (struct bcm2835_mbox_hdr *)sc->dma_buf; - struct bcm2835_mbox_tag_hdr *tag, *last; - uint8_t *up; - device_t mbox; - size_t hdr_size; - int idx; - int err; - - /* - * For multiple calls, locking is not here. The caller must have - * VC semaphore. - */ - - /* get mbox device */ - mbox = devclass_get_device(devclass_find("mbox"), 0); - if (mbox == NULL) { - device_printf(sc->dev, "can't find mbox\n"); - return (-1); - } - - /* go mailbox property */ -#ifdef PROP_DEBUG - bcm2835_dump(msg, 64); -#endif - bus_dmamap_sync(sc->dma_tag, sc->dma_map, - BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); - MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_PROP, (uint32_t)sc->dma_phys); - MBOX_READ(mbox, BCM2835_MBOX_CHAN_PROP, &err); - bus_dmamap_sync(sc->dma_tag, sc->dma_map, BUS_DMASYNC_POSTREAD); -#ifdef PROP_DEBUG - bcm2835_dump(msg, 64); -#endif - - /* check response code */ - if (msg->code != BCM2835_MBOX_CODE_RESP_SUCCESS) { - device_printf(sc->dev, "mbox response error\n"); - return (-1); - } - - /* tag = first tag */ - up = (uint8_t *)msg; - hdr_size = sizeof(struct bcm2835_mbox_hdr); - tag = (struct bcm2835_mbox_tag_hdr *)(up + hdr_size); - /* last = end of buffer specified by header */ - last = (struct bcm2835_mbox_tag_hdr *)(up + msg->buf_size); - - /* loop unitl end tag (=0x0) */ - hdr_size = sizeof(struct bcm2835_mbox_tag_hdr); - for (idx = 0; tag->tag != 0; idx++) { - if ((tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE) == 0) { - device_printf(sc->dev, "tag%d response error\n", idx); - return (-1); - } - /* clear response bit */ - tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; - - /* get next tag */ - up = (uint8_t *)tag; - tag = (struct bcm2835_mbox_tag_hdr *)(up + hdr_size + - tag->val_buf_size); - - /* check buffer size of header */ - if (tag > last) { - device_printf(sc->dev, "mbox buffer size error\n"); - return (-1); - } - } - - return (0); -} - -static int bcm2835_cpufreq_get_clock_rate(struct bcm2835_cpufreq_softc *sc, uint32_t clock_id) { - struct msg_get_clock_rate *msg; + struct msg_get_clock_rate msg; int rate; int err; /* * Get clock rate * Tag: 0x00030002 * Request: * Length: 4 * Value: * u32: clock id * Response: * Length: 8 * Value: * u32: clock id * u32: rate (in Hz) */ - /* using DMA buffer for VC */ - msg = (struct msg_get_clock_rate *)sc->dma_buf; - if (sizeof(*msg) > sc->dma_size) { - device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", - sizeof(*msg), sc->dma_size); - return (MSG_ERROR); - } - /* setup single tag buffer */ - memset(msg, 0, sizeof(*msg)); - msg->hdr.buf_size = sizeof(*msg); - msg->hdr.code = BCM2835_MBOX_CODE_REQ; - msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE; - msg->tag_hdr.val_buf_size = sizeof(msg->body); - msg->tag_hdr.val_len = sizeof(msg->body.req); - msg->body.req.clock_id = clock_id; - msg->end_tag = 0; + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE; + msg.tag_hdr.val_buf_size = sizeof(msg.body); + msg.tag_hdr.val_len = sizeof(msg.body.req); + msg.body.req.clock_id = clock_id; + msg.end_tag = 0; /* call mailbox property */ - err = bcm2835_mbox_call_prop(sc); + err = bcm2835_mbox_property(&msg, sizeof(msg)); if (err) { device_printf(sc->dev, "can't get clock rate (id=%u)\n", clock_id); return (MSG_ERROR); } /* result (Hz) */ - rate = (int)msg->body.resp.rate_hz; + rate = (int)msg.body.resp.rate_hz; DPRINTF("clock = %d(Hz)\n", rate); return (rate); } static int bcm2835_cpufreq_get_max_clock_rate(struct bcm2835_cpufreq_softc *sc, uint32_t clock_id) { - struct msg_get_max_clock_rate *msg; + struct msg_get_max_clock_rate msg; int rate; int err; /* * Get max clock rate * Tag: 0x00030004 * Request: * Length: 4 * Value: * u32: clock id * Response: * Length: 8 * Value: * u32: clock id * u32: rate (in Hz) */ - /* using DMA buffer for VC */ - msg = (struct msg_get_max_clock_rate *)sc->dma_buf; - if (sizeof(*msg) > sc->dma_size) { - device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", - sizeof(*msg), sc->dma_size); - return (MSG_ERROR); - } - /* setup single tag buffer */ - memset(msg, 0, sizeof(*msg)); - msg->hdr.buf_size = sizeof(*msg); - msg->hdr.code = BCM2835_MBOX_CODE_REQ; - msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MAX_CLOCK_RATE; - msg->tag_hdr.val_buf_size = sizeof(msg->body); - msg->tag_hdr.val_len = sizeof(msg->body.req); - msg->body.req.clock_id = clock_id; - msg->end_tag = 0; + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_MAX_CLOCK_RATE; + msg.tag_hdr.val_buf_size = sizeof(msg.body); + msg.tag_hdr.val_len = sizeof(msg.body.req); + msg.body.req.clock_id = clock_id; + msg.end_tag = 0; /* call mailbox property */ - err = bcm2835_mbox_call_prop(sc); + err = bcm2835_mbox_property(&msg, sizeof(msg)); if (err) { device_printf(sc->dev, "can't get max clock rate (id=%u)\n", clock_id); return (MSG_ERROR); } /* result (Hz) */ - rate = (int)msg->body.resp.rate_hz; + rate = (int)msg.body.resp.rate_hz; DPRINTF("clock = %d(Hz)\n", rate); return (rate); } static int bcm2835_cpufreq_get_min_clock_rate(struct bcm2835_cpufreq_softc *sc, uint32_t clock_id) { - struct msg_get_min_clock_rate *msg; + struct msg_get_min_clock_rate msg; int rate; int err; /* * Get min clock rate * Tag: 0x00030007 * Request: * Length: 4 * Value: * u32: clock id * Response: * Length: 8 * Value: * u32: clock id * u32: rate (in Hz) */ - /* using DMA buffer for VC */ - msg = (struct msg_get_min_clock_rate *)sc->dma_buf; - if (sizeof(*msg) > sc->dma_size) { - device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", - sizeof(*msg), sc->dma_size); - return (MSG_ERROR); - } - /* setup single tag buffer */ - memset(msg, 0, sizeof(*msg)); - msg->hdr.buf_size = sizeof(*msg); - msg->hdr.code = BCM2835_MBOX_CODE_REQ; - msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MIN_CLOCK_RATE; - msg->tag_hdr.val_buf_size = sizeof(msg->body); - msg->tag_hdr.val_len = sizeof(msg->body.req); - msg->body.req.clock_id = clock_id; - msg->end_tag = 0; + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_MIN_CLOCK_RATE; + msg.tag_hdr.val_buf_size = sizeof(msg.body); + msg.tag_hdr.val_len = sizeof(msg.body.req); + msg.body.req.clock_id = clock_id; + msg.end_tag = 0; /* call mailbox property */ - err = bcm2835_mbox_call_prop(sc); + err = bcm2835_mbox_property(&msg, sizeof(msg)); if (err) { device_printf(sc->dev, "can't get min clock rate (id=%u)\n", clock_id); return (MSG_ERROR); } /* result (Hz) */ - rate = (int)msg->body.resp.rate_hz; + rate = (int)msg.body.resp.rate_hz; DPRINTF("clock = %d(Hz)\n", rate); return (rate); } static int bcm2835_cpufreq_set_clock_rate(struct bcm2835_cpufreq_softc *sc, uint32_t clock_id, uint32_t rate_hz) { - struct msg_set_clock_rate *msg; + struct msg_set_clock_rate msg; int rate; int err; /* * Set clock rate * Tag: 0x00038002 * Request: * Length: 8 * Value: * u32: clock id * u32: rate (in Hz) * Response: * Length: 8 * Value: * u32: clock id * u32: rate (in Hz) */ - /* using DMA buffer for VC */ - msg = (struct msg_set_clock_rate *)sc->dma_buf; - if (sizeof(*msg) > sc->dma_size) { - device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", - sizeof(*msg), sc->dma_size); - return (MSG_ERROR); - } - /* setup single tag buffer */ - memset(msg, 0, sizeof(*msg)); - msg->hdr.buf_size = sizeof(*msg); - msg->hdr.code = BCM2835_MBOX_CODE_REQ; - msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_CLOCK_RATE; - msg->tag_hdr.val_buf_size = sizeof(msg->body); - msg->tag_hdr.val_len = sizeof(msg->body.req); - msg->body.req.clock_id = clock_id; - msg->body.req.rate_hz = rate_hz; - msg->end_tag = 0; + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_CLOCK_RATE; + msg.tag_hdr.val_buf_size = sizeof(msg.body); + msg.tag_hdr.val_len = sizeof(msg.body.req); + msg.body.req.clock_id = clock_id; + msg.body.req.rate_hz = rate_hz; + msg.end_tag = 0; /* call mailbox property */ - err = bcm2835_mbox_call_prop(sc); + err = bcm2835_mbox_property(&msg, sizeof(msg)); if (err) { device_printf(sc->dev, "can't set clock rate (id=%u)\n", clock_id); return (MSG_ERROR); } /* workaround for core clock */ if (clock_id == BCM2835_MBOX_CLOCK_ID_CORE) { /* for safety (may change voltage without changing clock) */ DELAY(TRANSITION_LATENCY); /* * XXX: the core clock is unable to change at once, * to change certainly, write it twice now. */ /* setup single tag buffer */ - memset(msg, 0, sizeof(*msg)); - msg->hdr.buf_size = sizeof(*msg); - msg->hdr.code = BCM2835_MBOX_CODE_REQ; - msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_CLOCK_RATE; - msg->tag_hdr.val_buf_size = sizeof(msg->body); - msg->tag_hdr.val_len = sizeof(msg->body.req); - msg->body.req.clock_id = clock_id; - msg->body.req.rate_hz = rate_hz; - msg->end_tag = 0; + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_CLOCK_RATE; + msg.tag_hdr.val_buf_size = sizeof(msg.body); + msg.tag_hdr.val_len = sizeof(msg.body.req); + msg.body.req.clock_id = clock_id; + msg.body.req.rate_hz = rate_hz; + msg.end_tag = 0; /* call mailbox property */ - err = bcm2835_mbox_call_prop(sc); + err = bcm2835_mbox_property(&msg, sizeof(msg)); if (err) { device_printf(sc->dev, "can't set clock rate (id=%u)\n", clock_id); return (MSG_ERROR); } } /* result (Hz) */ - rate = (int)msg->body.resp.rate_hz; + rate = (int)msg.body.resp.rate_hz; DPRINTF("clock = %d(Hz)\n", rate); return (rate); } static int bcm2835_cpufreq_get_turbo(struct bcm2835_cpufreq_softc *sc) { - struct msg_get_turbo *msg; + struct msg_get_turbo msg; int level; int err; /* * Get turbo * Tag: 0x00030009 * Request: * Length: 4 * Value: * u32: id * Response: * Length: 8 * Value: * u32: id * u32: level */ - /* using DMA buffer for VC */ - msg = (struct msg_get_turbo *)sc->dma_buf; - if (sizeof(*msg) > sc->dma_size) { - device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", - sizeof(*msg), sc->dma_size); - return (MSG_ERROR); - } - /* setup single tag buffer */ - memset(msg, 0, sizeof(*msg)); - msg->hdr.buf_size = sizeof(*msg); - msg->hdr.code = BCM2835_MBOX_CODE_REQ; - msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_TURBO; - msg->tag_hdr.val_buf_size = sizeof(msg->body); - msg->tag_hdr.val_len = sizeof(msg->body.req); - msg->body.req.id = 0; - msg->end_tag = 0; + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_TURBO; + msg.tag_hdr.val_buf_size = sizeof(msg.body); + msg.tag_hdr.val_len = sizeof(msg.body.req); + msg.body.req.id = 0; + msg.end_tag = 0; /* call mailbox property */ - err = bcm2835_mbox_call_prop(sc); + err = bcm2835_mbox_property(&msg, sizeof(msg)); if (err) { device_printf(sc->dev, "can't get turbo\n"); return (MSG_ERROR); } /* result 0=non-turbo, 1=turbo */ - level = (int)msg->body.resp.level; + level = (int)msg.body.resp.level; DPRINTF("level = %d\n", level); return (level); } static int bcm2835_cpufreq_set_turbo(struct bcm2835_cpufreq_softc *sc, uint32_t level) { - struct msg_set_turbo *msg; + struct msg_set_turbo msg; int value; int err; /* * Set turbo * Tag: 0x00038009 * Request: * Length: 8 * Value: * u32: id * u32: level * Response: * Length: 8 * Value: * u32: id * u32: level */ - /* using DMA buffer for VC */ - msg = (struct msg_set_turbo *)sc->dma_buf; - if (sizeof(*msg) > sc->dma_size) { - device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", - sizeof(*msg), sc->dma_size); - return (MSG_ERROR); - } - /* replace unknown value to OFF */ if (level != BCM2835_MBOX_TURBO_ON && level != BCM2835_MBOX_TURBO_OFF) level = BCM2835_MBOX_TURBO_OFF; /* setup single tag buffer */ - memset(msg, 0, sizeof(*msg)); - msg->hdr.buf_size = sizeof(*msg); - msg->hdr.code = BCM2835_MBOX_CODE_REQ; - msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_TURBO; - msg->tag_hdr.val_buf_size = sizeof(msg->body); - msg->tag_hdr.val_len = sizeof(msg->body.req); - msg->body.req.id = 0; - msg->body.req.level = level; - msg->end_tag = 0; + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_TURBO; + msg.tag_hdr.val_buf_size = sizeof(msg.body); + msg.tag_hdr.val_len = sizeof(msg.body.req); + msg.body.req.id = 0; + msg.body.req.level = level; + msg.end_tag = 0; /* call mailbox property */ - err = bcm2835_mbox_call_prop(sc); + err = bcm2835_mbox_property(&msg, sizeof(msg)); if (err) { device_printf(sc->dev, "can't set turbo\n"); return (MSG_ERROR); } /* result 0=non-turbo, 1=turbo */ - value = (int)msg->body.resp.level; + value = (int)msg.body.resp.level; DPRINTF("level = %d\n", value); return (value); } static int bcm2835_cpufreq_get_voltage(struct bcm2835_cpufreq_softc *sc, uint32_t voltage_id) { - struct msg_get_voltage *msg; + struct msg_get_voltage msg; int value; int err; /* * Get voltage * Tag: 0x00030003 * Request: * Length: 4 * Value: * u32: voltage id * Response: * Length: 8 * Value: * u32: voltage id * u32: value (offset from 1.2V in units of 0.025V) */ - /* using DMA buffer for VC */ - msg = (struct msg_get_voltage *)sc->dma_buf; - if (sizeof(*msg) > sc->dma_size) { - device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", - sizeof(*msg), sc->dma_size); - return (MSG_ERROR); - } - /* setup single tag buffer */ - memset(msg, 0, sizeof(*msg)); - msg->hdr.buf_size = sizeof(*msg); - msg->hdr.code = BCM2835_MBOX_CODE_REQ; - msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_VOLTAGE; - msg->tag_hdr.val_buf_size = sizeof(msg->body); - msg->tag_hdr.val_len = sizeof(msg->body.req); - msg->body.req.voltage_id = voltage_id; - msg->end_tag = 0; + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_VOLTAGE; + msg.tag_hdr.val_buf_size = sizeof(msg.body); + msg.tag_hdr.val_len = sizeof(msg.body.req); + msg.body.req.voltage_id = voltage_id; + msg.end_tag = 0; /* call mailbox property */ - err = bcm2835_mbox_call_prop(sc); + err = bcm2835_mbox_property(&msg, sizeof(msg)); if (err) { device_printf(sc->dev, "can't get voltage\n"); return (MSG_ERROR); } /* result (offset from 1.2V) */ - value = (int)msg->body.resp.value; + value = (int)msg.body.resp.value; DPRINTF("value = %d\n", value); return (value); } static int bcm2835_cpufreq_get_max_voltage(struct bcm2835_cpufreq_softc *sc, uint32_t voltage_id) { - struct msg_get_max_voltage *msg; + struct msg_get_max_voltage msg; int value; int err; /* * Get voltage * Tag: 0x00030005 * Request: * Length: 4 * Value: * u32: voltage id * Response: * Length: 8 * Value: * u32: voltage id * u32: value (offset from 1.2V in units of 0.025V) */ - /* using DMA buffer for VC */ - msg = (struct msg_get_max_voltage *)sc->dma_buf; - if (sizeof(*msg) > sc->dma_size) { - device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", - sizeof(*msg), sc->dma_size); - return (MSG_ERROR); - } - /* setup single tag buffer */ - memset(msg, 0, sizeof(*msg)); - msg->hdr.buf_size = sizeof(*msg); - msg->hdr.code = BCM2835_MBOX_CODE_REQ; - msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MAX_VOLTAGE; - msg->tag_hdr.val_buf_size = sizeof(msg->body); - msg->tag_hdr.val_len = sizeof(msg->body.req); - msg->body.req.voltage_id = voltage_id; - msg->end_tag = 0; + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_MAX_VOLTAGE; + msg.tag_hdr.val_buf_size = sizeof(msg.body); + msg.tag_hdr.val_len = sizeof(msg.body.req); + msg.body.req.voltage_id = voltage_id; + msg.end_tag = 0; /* call mailbox property */ - err = bcm2835_mbox_call_prop(sc); + err = bcm2835_mbox_property(&msg, sizeof(msg)); if (err) { device_printf(sc->dev, "can't get max voltage\n"); return (MSG_ERROR); } /* result (offset from 1.2V) */ - value = (int)msg->body.resp.value; + value = (int)msg.body.resp.value; DPRINTF("value = %d\n", value); return (value); } static int bcm2835_cpufreq_get_min_voltage(struct bcm2835_cpufreq_softc *sc, uint32_t voltage_id) { - struct msg_get_min_voltage *msg; + struct msg_get_min_voltage msg; int value; int err; /* * Get voltage * Tag: 0x00030008 * Request: * Length: 4 * Value: * u32: voltage id * Response: * Length: 8 * Value: * u32: voltage id * u32: value (offset from 1.2V in units of 0.025V) */ - /* using DMA buffer for VC */ - msg = (struct msg_get_min_voltage *)sc->dma_buf; - if (sizeof(*msg) > sc->dma_size) { - device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", - sizeof(*msg), sc->dma_size); - return (MSG_ERROR); - } - /* setup single tag buffer */ - memset(msg, 0, sizeof(*msg)); - msg->hdr.buf_size = sizeof(*msg); - msg->hdr.code = BCM2835_MBOX_CODE_REQ; - msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MIN_VOLTAGE; - msg->tag_hdr.val_buf_size = sizeof(msg->body); - msg->tag_hdr.val_len = sizeof(msg->body.req); - msg->body.req.voltage_id = voltage_id; - msg->end_tag = 0; + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_MIN_VOLTAGE; + msg.tag_hdr.val_buf_size = sizeof(msg.body); + msg.tag_hdr.val_len = sizeof(msg.body.req); + msg.body.req.voltage_id = voltage_id; + msg.end_tag = 0; /* call mailbox property */ - err = bcm2835_mbox_call_prop(sc); + err = bcm2835_mbox_property(&msg, sizeof(msg)); if (err) { device_printf(sc->dev, "can't get min voltage\n"); return (MSG_ERROR); } /* result (offset from 1.2V) */ - value = (int)msg->body.resp.value; + value = (int)msg.body.resp.value; DPRINTF("value = %d\n", value); return (value); } static int bcm2835_cpufreq_set_voltage(struct bcm2835_cpufreq_softc *sc, uint32_t voltage_id, int32_t value) { - struct msg_set_voltage *msg; + struct msg_set_voltage msg; int err; /* * Set voltage * Tag: 0x00038003 * Request: * Length: 4 * Value: * u32: voltage id * u32: value (offset from 1.2V in units of 0.025V) * Response: * Length: 8 * Value: * u32: voltage id * u32: value (offset from 1.2V in units of 0.025V) */ /* * over_voltage: * 0 (1.2 V). Values above 6 are only allowed when force_turbo or * current_limit_override are specified (which set the warranty bit). */ if (value > MAX_OVER_VOLTAGE || value < MIN_OVER_VOLTAGE) { /* currently not supported */ device_printf(sc->dev, "not supported voltage: %d\n", value); return (MSG_ERROR); } - /* using DMA buffer for VC */ - msg = (struct msg_set_voltage *)sc->dma_buf; - if (sizeof(*msg) > sc->dma_size) { - device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", - sizeof(*msg), sc->dma_size); - return (MSG_ERROR); - } - /* setup single tag buffer */ - memset(msg, 0, sizeof(*msg)); - msg->hdr.buf_size = sizeof(*msg); - msg->hdr.code = BCM2835_MBOX_CODE_REQ; - msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_VOLTAGE; - msg->tag_hdr.val_buf_size = sizeof(msg->body); - msg->tag_hdr.val_len = sizeof(msg->body.req); - msg->body.req.voltage_id = voltage_id; - msg->body.req.value = (uint32_t)value; - msg->end_tag = 0; + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_VOLTAGE; + msg.tag_hdr.val_buf_size = sizeof(msg.body); + msg.tag_hdr.val_len = sizeof(msg.body.req); + msg.body.req.voltage_id = voltage_id; + msg.body.req.value = (uint32_t)value; + msg.end_tag = 0; /* call mailbox property */ - err = bcm2835_mbox_call_prop(sc); + err = bcm2835_mbox_property(&msg, sizeof(msg)); if (err) { device_printf(sc->dev, "can't set voltage\n"); return (MSG_ERROR); } /* result (offset from 1.2V) */ - value = (int)msg->body.resp.value; + value = (int)msg.body.resp.value; DPRINTF("value = %d\n", value); return (value); } static int bcm2835_cpufreq_get_temperature(struct bcm2835_cpufreq_softc *sc) { - struct msg_get_temperature *msg; + struct msg_get_temperature msg; int value; int err; /* * Get temperature * Tag: 0x00030006 * Request: * Length: 4 * Value: * u32: temperature id * Response: * Length: 8 * Value: * u32: temperature id * u32: value */ - /* using DMA buffer for VC */ - msg = (struct msg_get_temperature *)sc->dma_buf; - if (sizeof(*msg) > sc->dma_size) { - device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", - sizeof(*msg), sc->dma_size); - return (MSG_ERROR); - } - /* setup single tag buffer */ - memset(msg, 0, sizeof(*msg)); - msg->hdr.buf_size = sizeof(*msg); - msg->hdr.code = BCM2835_MBOX_CODE_REQ; - msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_TEMPERATURE; - msg->tag_hdr.val_buf_size = sizeof(msg->body); - msg->tag_hdr.val_len = sizeof(msg->body.req); - msg->body.req.temperature_id = 0; - msg->end_tag = 0; + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_TEMPERATURE; + msg.tag_hdr.val_buf_size = sizeof(msg.body); + msg.tag_hdr.val_len = sizeof(msg.body.req); + msg.body.req.temperature_id = 0; + msg.end_tag = 0; /* call mailbox property */ - err = bcm2835_mbox_call_prop(sc); + err = bcm2835_mbox_property(&msg, sizeof(msg)); if (err) { device_printf(sc->dev, "can't get temperature\n"); return (MSG_ERROR); } /* result (temperature of degree C) */ - value = (int)msg->body.resp.value; + value = (int)msg.body.resp.value; DPRINTF("value = %d\n", value); return (value); } static int sysctl_bcm2835_cpufreq_arm_freq(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ VC_LOCK(sc); err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, val); VC_UNLOCK(sc); if (err == MSG_ERROR) { device_printf(sc->dev, "set clock arm_freq error\n"); return (EIO); } DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_core_freq(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ VC_LOCK(sc); err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, val); if (err == MSG_ERROR) { VC_UNLOCK(sc); device_printf(sc->dev, "set clock core_freq error\n"); return (EIO); } VC_UNLOCK(sc); DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_sdram_freq(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ VC_LOCK(sc); err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, val); VC_UNLOCK(sc); if (err == MSG_ERROR) { device_printf(sc->dev, "set clock sdram_freq error\n"); return (EIO); } DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_turbo(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_turbo(sc); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ if (val > 0) sc->turbo_mode = BCM2835_MBOX_TURBO_ON; else sc->turbo_mode = BCM2835_MBOX_TURBO_OFF; VC_LOCK(sc); err = bcm2835_cpufreq_set_turbo(sc, sc->turbo_mode); VC_UNLOCK(sc); if (err == MSG_ERROR) { device_printf(sc->dev, "set turbo error\n"); return (EIO); } DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_voltage_core(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) return (EINVAL); sc->voltage_core = val; VC_LOCK(sc); err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE, sc->voltage_core); VC_UNLOCK(sc); if (err == MSG_ERROR) { device_printf(sc->dev, "set voltage core error\n"); return (EIO); } DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_voltage_sdram_c(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) return (EINVAL); sc->voltage_sdram_c = val; VC_LOCK(sc); err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C, sc->voltage_sdram_c); VC_UNLOCK(sc); if (err == MSG_ERROR) { device_printf(sc->dev, "set voltage sdram_c error\n"); return (EIO); } DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_voltage_sdram_i(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) return (EINVAL); sc->voltage_sdram_i = val; VC_LOCK(sc); err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I, sc->voltage_sdram_i); VC_UNLOCK(sc); if (err == MSG_ERROR) { device_printf(sc->dev, "set voltage sdram_i error\n"); return (EIO); } DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_voltage_sdram_p(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) return (EINVAL); sc->voltage_sdram_p = val; VC_LOCK(sc); err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P, sc->voltage_sdram_p); VC_UNLOCK(sc); if (err == MSG_ERROR) { device_printf(sc->dev, "set voltage sdram_p error\n"); return (EIO); } DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_voltage_sdram(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* multiple write only */ if (!req->newptr) return (EINVAL); val = 0; err = sysctl_handle_int(oidp, &val, 0, req); if (err) return (err); /* write request */ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) return (EINVAL); sc->voltage_sdram = val; VC_LOCK(sc); err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C, val); if (err == MSG_ERROR) { VC_UNLOCK(sc); device_printf(sc->dev, "set voltage sdram_c error\n"); return (EIO); } err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I, val); if (err == MSG_ERROR) { VC_UNLOCK(sc); device_printf(sc->dev, "set voltage sdram_i error\n"); return (EIO); } err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P, val); if (err == MSG_ERROR) { VC_UNLOCK(sc); device_printf(sc->dev, "set voltage sdram_p error\n"); return (EIO); } VC_UNLOCK(sc); DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_temperature(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_temperature(sc); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ return (EINVAL); } static int sysctl_bcm2835_devcpu_temperature(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_temperature(sc); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); /* 1/1000 celsius (raw) to 1/10 kelvin */ val = val / 100 + TZ_ZEROC; err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ return (EINVAL); } static void bcm2835_cpufreq_init(void *arg) { struct bcm2835_cpufreq_softc *sc = arg; struct sysctl_ctx_list *ctx; device_t cpu; int arm_freq, core_freq, sdram_freq; int arm_max_freq, arm_min_freq, core_max_freq, core_min_freq; int sdram_max_freq, sdram_min_freq; int voltage_core, voltage_sdram_c, voltage_sdram_i, voltage_sdram_p; int max_voltage_core, min_voltage_core; int max_voltage_sdram_c, min_voltage_sdram_c; int max_voltage_sdram_i, min_voltage_sdram_i; int max_voltage_sdram_p, min_voltage_sdram_p; int turbo, temperature; VC_LOCK(sc); /* current clock */ arm_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); core_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); sdram_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM); /* max/min clock */ arm_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); arm_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); core_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); core_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); sdram_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM); sdram_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM); /* turbo mode */ turbo = bcm2835_cpufreq_get_turbo(sc); if (turbo > 0) sc->turbo_mode = BCM2835_MBOX_TURBO_ON; else sc->turbo_mode = BCM2835_MBOX_TURBO_OFF; /* voltage */ voltage_core = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE); voltage_sdram_c = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); voltage_sdram_i = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); voltage_sdram_p = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); /* current values (offset from 1.2V) */ sc->voltage_core = voltage_core; sc->voltage_sdram = voltage_sdram_c; sc->voltage_sdram_c = voltage_sdram_c; sc->voltage_sdram_i = voltage_sdram_i; sc->voltage_sdram_p = voltage_sdram_p; /* max/min voltage */ max_voltage_core = bcm2835_cpufreq_get_max_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE); min_voltage_core = bcm2835_cpufreq_get_min_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE); max_voltage_sdram_c = bcm2835_cpufreq_get_max_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); max_voltage_sdram_i = bcm2835_cpufreq_get_max_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); max_voltage_sdram_p = bcm2835_cpufreq_get_max_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); min_voltage_sdram_c = bcm2835_cpufreq_get_min_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); min_voltage_sdram_i = bcm2835_cpufreq_get_min_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); min_voltage_sdram_p = bcm2835_cpufreq_get_min_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); /* temperature */ temperature = bcm2835_cpufreq_get_temperature(sc); /* show result */ if (cpufreq_verbose || bootverbose) { device_printf(sc->dev, "Boot settings:\n"); device_printf(sc->dev, "current ARM %dMHz, Core %dMHz, SDRAM %dMHz, Turbo %s\n", HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq), (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) ? "ON" : "OFF"); device_printf(sc->dev, "max/min ARM %d/%dMHz, Core %d/%dMHz, SDRAM %d/%dMHz\n", HZ2MHZ(arm_max_freq), HZ2MHZ(arm_min_freq), HZ2MHZ(core_max_freq), HZ2MHZ(core_min_freq), HZ2MHZ(sdram_max_freq), HZ2MHZ(sdram_min_freq)); device_printf(sc->dev, "current Core %dmV, SDRAM_C %dmV, SDRAM_I %dmV, " "SDRAM_P %dmV\n", OFFSET2MVOLT(voltage_core), OFFSET2MVOLT(voltage_sdram_c), OFFSET2MVOLT(voltage_sdram_i), OFFSET2MVOLT(voltage_sdram_p)); device_printf(sc->dev, "max/min Core %d/%dmV, SDRAM_C %d/%dmV, SDRAM_I %d/%dmV, " "SDRAM_P %d/%dmV\n", OFFSET2MVOLT(max_voltage_core), OFFSET2MVOLT(min_voltage_core), OFFSET2MVOLT(max_voltage_sdram_c), OFFSET2MVOLT(min_voltage_sdram_c), OFFSET2MVOLT(max_voltage_sdram_i), OFFSET2MVOLT(min_voltage_sdram_i), OFFSET2MVOLT(max_voltage_sdram_p), OFFSET2MVOLT(min_voltage_sdram_p)); device_printf(sc->dev, "Temperature %d.%dC\n", (temperature / 1000), (temperature % 1000) / 100); } else { /* !cpufreq_verbose && !bootverbose */ device_printf(sc->dev, "ARM %dMHz, Core %dMHz, SDRAM %dMHz, Turbo %s\n", HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq), (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) ? "ON" : "OFF"); } /* keep in softc (MHz/mV) */ sc->arm_max_freq = HZ2MHZ(arm_max_freq); sc->arm_min_freq = HZ2MHZ(arm_min_freq); sc->core_max_freq = HZ2MHZ(core_max_freq); sc->core_min_freq = HZ2MHZ(core_min_freq); sc->sdram_max_freq = HZ2MHZ(sdram_max_freq); sc->sdram_min_freq = HZ2MHZ(sdram_min_freq); sc->max_voltage_core = OFFSET2MVOLT(max_voltage_core); sc->min_voltage_core = OFFSET2MVOLT(min_voltage_core); /* if turbo is on, set to max values */ if (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) { bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, arm_max_freq); DELAY(TRANSITION_LATENCY); bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, core_max_freq); DELAY(TRANSITION_LATENCY); bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, sdram_max_freq); DELAY(TRANSITION_LATENCY); } else { bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, arm_min_freq); DELAY(TRANSITION_LATENCY); bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, core_min_freq); DELAY(TRANSITION_LATENCY); bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, sdram_min_freq); DELAY(TRANSITION_LATENCY); } VC_UNLOCK(sc); /* add human readable temperature to dev.cpu node */ cpu = device_get_parent(sc->dev); if (cpu != NULL) { ctx = device_get_sysctl_ctx(cpu); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0, sysctl_bcm2835_devcpu_temperature, "IK", "Current SoC temperature"); } /* release this hook (continue boot) */ config_intrhook_disestablish(&sc->init_hook); } static void bcm2835_cpufreq_identify(driver_t *driver, device_t parent) { + const struct ofw_compat_data *compat; + phandle_t root; + root = OF_finddevice("/"); + for (compat = compat_data; compat->ocd_str != NULL; compat++) + if (fdt_is_compatible(root, compat->ocd_str)) + break; + + if (compat->ocd_data == 0) + return; + DPRINTF("driver=%p, parent=%p\n", driver, parent); if (device_find_child(parent, "bcm2835_cpufreq", -1) != NULL) return; if (BUS_ADD_CHILD(parent, 0, "bcm2835_cpufreq", -1) == NULL) device_printf(parent, "add child failed\n"); } static int bcm2835_cpufreq_probe(device_t dev) { device_set_desc(dev, "CPU Frequency Control"); return (0); } -static void -bcm2835_cpufreq_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) -{ - bus_addr_t *addr; - - if (err) - return; - addr = (bus_addr_t *)arg; - *addr = PHYS_TO_VCBUS(segs[0].ds_addr); -} - static int bcm2835_cpufreq_attach(device_t dev) { struct bcm2835_cpufreq_softc *sc; struct sysctl_oid *oid; - int err; /* set self dev */ sc = device_get_softc(dev); sc->dev = dev; /* initial values */ sc->arm_max_freq = -1; sc->arm_min_freq = -1; sc->core_max_freq = -1; sc->core_min_freq = -1; sc->sdram_max_freq = -1; sc->sdram_min_freq = -1; sc->max_voltage_core = 0; sc->min_voltage_core = 0; - /* create VC mbox buffer */ - sc->dma_size = PAGE_SIZE; - err = bus_dma_tag_create( - bus_get_dma_tag(sc->dev), - PAGE_SIZE, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - sc->dma_size, 1, /* maxsize, nsegments */ - sc->dma_size, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->dma_tag); - if (err) { - device_printf(dev, "can't create DMA tag\n"); - return (ENXIO); - } - - err = bus_dmamem_alloc(sc->dma_tag, (void **)&sc->dma_buf, 0, - &sc->dma_map); - if (err) { - bus_dma_tag_destroy(sc->dma_tag); - device_printf(dev, "can't allocate dmamem\n"); - return (ENXIO); - } - - err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->dma_buf, - sc->dma_size, bcm2835_cpufreq_cb, &sc->dma_phys, 0); - if (err) { - bus_dmamem_free(sc->dma_tag, sc->dma_buf, sc->dma_map); - bus_dma_tag_destroy(sc->dma_tag); - device_printf(dev, "can't load DMA map\n"); - return (ENXIO); - } - /* OK, ready to use VC buffer */ - /* setup sysctl at first device */ if (device_get_unit(dev) == 0) { sysctl_ctx_init(&bcm2835_sysctl_ctx); /* create node for hw.cpufreq */ oid = SYSCTL_ADD_NODE(&bcm2835_sysctl_ctx, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "cpufreq", CTLFLAG_RD, NULL, ""); /* Frequency (Hz) */ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "arm_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_arm_freq, "IU", "ARM frequency (Hz)"); SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "core_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_core_freq, "IU", "Core frequency (Hz)"); SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "sdram_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_sdram_freq, "IU", "SDRAM frequency (Hz)"); /* Turbo state */ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "turbo", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_turbo, "IU", "Disables dynamic clocking"); /* Voltage (offset from 1.2V in units of 0.025V) */ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "voltage_core", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_voltage_core, "I", "ARM/GPU core voltage" "(offset from 1.2V in units of 0.025V)"); SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "voltage_sdram", CTLTYPE_INT | CTLFLAG_WR, sc, 0, sysctl_bcm2835_cpufreq_voltage_sdram, "I", "SDRAM voltage (offset from 1.2V in units of 0.025V)"); /* Voltage individual SDRAM */ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "voltage_sdram_c", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_voltage_sdram_c, "I", "SDRAM controller voltage" "(offset from 1.2V in units of 0.025V)"); SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "voltage_sdram_i", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_voltage_sdram_i, "I", "SDRAM I/O voltage (offset from 1.2V in units of 0.025V)"); SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "voltage_sdram_p", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_voltage_sdram_p, "I", "SDRAM phy voltage (offset from 1.2V in units of 0.025V)"); /* Temperature */ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0, sysctl_bcm2835_cpufreq_temperature, "I", "SoC temperature (thousandths of a degree C)"); } /* ARM->VC lock */ sema_init(&vc_sema, 1, "vcsema"); /* register callback for using mbox when interrupts are enabled */ sc->init_hook.ich_func = bcm2835_cpufreq_init; sc->init_hook.ich_arg = sc; if (config_intrhook_establish(&sc->init_hook) != 0) { - bus_dmamap_unload(sc->dma_tag, sc->dma_map); - bus_dmamem_free(sc->dma_tag, sc->dma_buf, sc->dma_map); - bus_dma_tag_destroy(sc->dma_tag); device_printf(dev, "config_intrhook_establish failed\n"); return (ENOMEM); } /* this device is controlled by cpufreq(4) */ cpufreq_register(dev); return (0); } static int bcm2835_cpufreq_detach(device_t dev) { struct bcm2835_cpufreq_softc *sc; sc = device_get_softc(dev); sema_destroy(&vc_sema); - - if (sc->dma_phys != 0) - bus_dmamap_unload(sc->dma_tag, sc->dma_map); - if (sc->dma_buf != NULL) - bus_dmamem_free(sc->dma_tag, sc->dma_buf, sc->dma_map); - if (sc->dma_tag != NULL) - bus_dma_tag_destroy(sc->dma_tag); return (cpufreq_unregister(dev)); } static int bcm2835_cpufreq_set(device_t dev, const struct cf_setting *cf) { struct bcm2835_cpufreq_softc *sc; uint32_t rate_hz, rem; int cur_freq, resp_freq, arm_freq, min_freq, core_freq; if (cf == NULL || cf->freq < 0) return (EINVAL); sc = device_get_softc(dev); /* setting clock (Hz) */ rate_hz = (uint32_t)MHZ2HZ(cf->freq); rem = rate_hz % HZSTEP; rate_hz -= rem; if (rate_hz == 0) return (EINVAL); /* adjust min freq */ min_freq = sc->arm_min_freq; if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON) if (min_freq > cpufreq_lowest_freq) min_freq = cpufreq_lowest_freq; if (rate_hz < MHZ2HZ(min_freq) || rate_hz > MHZ2HZ(sc->arm_max_freq)) return (EINVAL); /* set new value and verify it */ VC_LOCK(sc); cur_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); resp_freq = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, rate_hz); DELAY(TRANSITION_LATENCY); arm_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); /* * if non-turbo and lower than or equal min_freq, * clock down core and sdram to default first. */ if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON) { core_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); if (rate_hz > MHZ2HZ(sc->arm_min_freq)) { bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, MHZ2HZ(sc->core_max_freq)); DELAY(TRANSITION_LATENCY); bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, MHZ2HZ(sc->sdram_max_freq)); DELAY(TRANSITION_LATENCY); } else { if (sc->core_min_freq < DEFAULT_CORE_FREQUENCY && core_freq > DEFAULT_CORE_FREQUENCY) { /* first, down to 250, then down to min */ DELAY(TRANSITION_LATENCY); bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, MHZ2HZ(DEFAULT_CORE_FREQUENCY)); DELAY(TRANSITION_LATENCY); /* reset core voltage */ bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE, 0); DELAY(TRANSITION_LATENCY); } bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, MHZ2HZ(sc->core_min_freq)); DELAY(TRANSITION_LATENCY); bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, MHZ2HZ(sc->sdram_min_freq)); DELAY(TRANSITION_LATENCY); } } VC_UNLOCK(sc); if (resp_freq < 0 || arm_freq < 0 || resp_freq != arm_freq) { device_printf(dev, "wrong freq\n"); return (EIO); } DPRINTF("cpufreq: %d -> %d\n", cur_freq, arm_freq); return (0); } static int bcm2835_cpufreq_get(device_t dev, struct cf_setting *cf) { struct bcm2835_cpufreq_softc *sc; int arm_freq; if (cf == NULL) return (EINVAL); sc = device_get_softc(dev); memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf)); cf->dev = NULL; /* get cuurent value */ VC_LOCK(sc); arm_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); VC_UNLOCK(sc); if (arm_freq < 0) { device_printf(dev, "can't get clock\n"); return (EINVAL); } /* CPU clock in MHz or 100ths of a percent. */ cf->freq = HZ2MHZ(arm_freq); /* Voltage in mV. */ cf->volts = CPUFREQ_VAL_UNKNOWN; /* Power consumed in mW. */ cf->power = CPUFREQ_VAL_UNKNOWN; /* Transition latency in us. */ cf->lat = TRANSITION_LATENCY; /* Driver providing this setting. */ cf->dev = dev; return (0); } static int bcm2835_cpufreq_make_freq_list(device_t dev, struct cf_setting *sets, int *count) { struct bcm2835_cpufreq_softc *sc; int freq, min_freq, volts, rem; int idx; sc = device_get_softc(dev); freq = sc->arm_max_freq; min_freq = sc->arm_min_freq; /* adjust head freq to STEP */ rem = freq % MHZSTEP; freq -= rem; if (freq < min_freq) freq = min_freq; /* if non-turbo, add extra low freq */ if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON) if (min_freq > cpufreq_lowest_freq) min_freq = cpufreq_lowest_freq; /* from freq to min_freq */ for (idx = 0; idx < *count && freq >= min_freq; idx++) { if (freq > sc->arm_min_freq) volts = sc->max_voltage_core; else volts = sc->min_voltage_core; sets[idx].freq = freq; sets[idx].volts = volts; sets[idx].lat = TRANSITION_LATENCY; sets[idx].dev = dev; freq -= MHZSTEP; } *count = ++idx; return (0); } static int bcm2835_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count) { struct bcm2835_cpufreq_softc *sc; if (sets == NULL || count == NULL) return (EINVAL); sc = device_get_softc(dev); if (sc->arm_min_freq < 0 || sc->arm_max_freq < 0) { printf("device is not configured\n"); return (EINVAL); } /* fill data with unknown value */ memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count)); /* create new array up to count */ bcm2835_cpufreq_make_freq_list(dev, sets, count); return (0); } static int bcm2835_cpufreq_type(device_t dev, int *type) { if (type == NULL) return (EINVAL); *type = CPUFREQ_TYPE_ABSOLUTE; return (0); } static device_method_t bcm2835_cpufreq_methods[] = { /* Device interface */ DEVMETHOD(device_identify, bcm2835_cpufreq_identify), DEVMETHOD(device_probe, bcm2835_cpufreq_probe), DEVMETHOD(device_attach, bcm2835_cpufreq_attach), DEVMETHOD(device_detach, bcm2835_cpufreq_detach), /* cpufreq interface */ DEVMETHOD(cpufreq_drv_set, bcm2835_cpufreq_set), DEVMETHOD(cpufreq_drv_get, bcm2835_cpufreq_get), DEVMETHOD(cpufreq_drv_settings, bcm2835_cpufreq_settings), DEVMETHOD(cpufreq_drv_type, bcm2835_cpufreq_type), DEVMETHOD_END }; static devclass_t bcm2835_cpufreq_devclass; static driver_t bcm2835_cpufreq_driver = { "bcm2835_cpufreq", bcm2835_cpufreq_methods, sizeof(struct bcm2835_cpufreq_softc), }; DRIVER_MODULE(bcm2835_cpufreq, cpu, bcm2835_cpufreq_driver, bcm2835_cpufreq_devclass, 0, 0); Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_dma.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_dma.c (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_dma.c (revision 322724) @@ -1,731 +1,772 @@ /* * Copyright (c) 2013 Daisuke Aoyama * Copyright (c) 2013 Oleksandr Tymoshenko * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bcm2835_dma.h" #include "bcm2835_vcbus.h" #define MAX_REG 9 /* private flags */ #define BCM_DMA_CH_USED 0x00000001 #define BCM_DMA_CH_FREE 0x40000000 #define BCM_DMA_CH_UNMAP 0x80000000 /* Register Map (4.2.1.2) */ #define BCM_DMA_CS(n) (0x100*(n) + 0x00) #define CS_ACTIVE (1 << 0) #define CS_END (1 << 1) #define CS_INT (1 << 2) #define CS_DREQ (1 << 3) #define CS_ISPAUSED (1 << 4) #define CS_ISHELD (1 << 5) #define CS_ISWAIT (1 << 6) #define CS_ERR (1 << 8) #define CS_WAITWRT (1 << 28) #define CS_DISDBG (1 << 29) #define CS_ABORT (1 << 30) #define CS_RESET (1U << 31) #define BCM_DMA_CBADDR(n) (0x100*(n) + 0x04) #define BCM_DMA_INFO(n) (0x100*(n) + 0x08) #define INFO_INT_EN (1 << 0) #define INFO_TDMODE (1 << 1) #define INFO_WAIT_RESP (1 << 3) #define INFO_D_INC (1 << 4) #define INFO_D_WIDTH (1 << 5) #define INFO_D_DREQ (1 << 6) #define INFO_S_INC (1 << 8) #define INFO_S_WIDTH (1 << 9) #define INFO_S_DREQ (1 << 10) #define INFO_WAITS_SHIFT (21) #define INFO_PERMAP_SHIFT (16) #define INFO_PERMAP_MASK (0x1f << INFO_PERMAP_SHIFT) #define BCM_DMA_SRC(n) (0x100*(n) + 0x0C) #define BCM_DMA_DST(n) (0x100*(n) + 0x10) #define BCM_DMA_LEN(n) (0x100*(n) + 0x14) #define BCM_DMA_STRIDE(n) (0x100*(n) + 0x18) #define BCM_DMA_CBNEXT(n) (0x100*(n) + 0x1C) #define BCM_DMA_DEBUG(n) (0x100*(n) + 0x20) #define DEBUG_ERROR_MASK (7) #define BCM_DMA_INT_STATUS 0xfe0 #define BCM_DMA_ENABLE 0xff0 /* relative offset from BCM_VC_DMA0_BASE (p.39) */ #define BCM_DMA_CH(n) (0x100*(n)) +/* channels used by GPU */ +#define BCM_DMA_CH_BULK 0 +#define BCM_DMA_CH_FAST1 2 +#define BCM_DMA_CH_FAST2 3 + +#define BCM_DMA_CH_GPU_MASK ((1 << BCM_DMA_CH_BULK) | \ + (1 << BCM_DMA_CH_FAST1) | \ + (1 << BCM_DMA_CH_FAST2)) + /* DMA Control Block - 256bit aligned (p.40) */ struct bcm_dma_cb { uint32_t info; /* Transfer Information */ uint32_t src; /* Source Address */ uint32_t dst; /* Destination Address */ uint32_t len; /* Transfer Length */ uint32_t stride; /* 2D Mode Stride */ uint32_t next; /* Next Control Block Address */ uint32_t rsvd1; /* Reserved */ uint32_t rsvd2; /* Reserved */ }; #ifdef DEBUG static void bcm_dma_cb_dump(struct bcm_dma_cb *cb); static void bcm_dma_reg_dump(int ch); #endif /* DMA channel private info */ struct bcm_dma_ch { int ch; uint32_t flags; struct bcm_dma_cb * cb; uint32_t vc_cb; bus_dmamap_t dma_map; void (*intr_func)(int, void *); void * intr_arg; }; struct bcm_dma_softc { device_t sc_dev; struct mtx sc_mtx; struct resource * sc_mem; struct resource * sc_irq[BCM_DMA_CH_MAX]; void * sc_intrhand[BCM_DMA_CH_MAX]; struct bcm_dma_ch sc_dma_ch[BCM_DMA_CH_MAX]; bus_dma_tag_t sc_dma_tag; }; static struct bcm_dma_softc *bcm_dma_sc = NULL; +static uint32_t bcm_dma_channel_mask; +static struct ofw_compat_data compat_data[] = { + {"broadcom,bcm2835-dma", 1}, + {"brcm,bcm2835-dma", 1}, + {NULL, 0} +}; + static void bcm_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) { bus_addr_t *addr; if (err) return; addr = (bus_addr_t*)arg; *addr = PHYS_TO_VCBUS(segs[0].ds_addr); } static void bcm_dma_reset(device_t dev, int ch) { struct bcm_dma_softc *sc = device_get_softc(dev); struct bcm_dma_cb *cb; uint32_t cs; int count; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return; cs = bus_read_4(sc->sc_mem, BCM_DMA_CS(ch)); if (cs & CS_ACTIVE) { /* pause current task */ bus_write_4(sc->sc_mem, BCM_DMA_CS(ch), 0); count = 1000; do { cs = bus_read_4(sc->sc_mem, BCM_DMA_CS(ch)); } while (!(cs & CS_ISPAUSED) && (count-- > 0)); if (!(cs & CS_ISPAUSED)) { device_printf(dev, "Can't abort DMA transfer at channel %d\n", ch); } bus_write_4(sc->sc_mem, BCM_DMA_CBNEXT(ch), 0); /* Complete everything, clear interrupt */ bus_write_4(sc->sc_mem, BCM_DMA_CS(ch), CS_ABORT | CS_INT | CS_END| CS_ACTIVE); } /* clear control blocks */ bus_write_4(sc->sc_mem, BCM_DMA_CBADDR(ch), 0); bus_write_4(sc->sc_mem, BCM_DMA_CBNEXT(ch), 0); /* Reset control block */ cb = sc->sc_dma_ch[ch].cb; bzero(cb, sizeof(*cb)); cb->info = INFO_WAIT_RESP; } static int bcm_dma_init(device_t dev) { struct bcm_dma_softc *sc = device_get_softc(dev); - uint32_t mask; + uint32_t reg; struct bcm_dma_ch *ch; void *cb_virt; vm_paddr_t cb_phys; int err; int i; - /* disable and clear interrupt status */ - bus_write_4(sc->sc_mem, BCM_DMA_ENABLE, 0); - bus_write_4(sc->sc_mem, BCM_DMA_INT_STATUS, 0); + /* + * Only channels set in bcm_dma_channel_mask can be controlled by us. + * The others are out of our control as well as the corresponding bits + * in both BCM_DMA_ENABLE and BCM_DMA_INT_STATUS global registers. As + * these registers are RW ones, there is no safe way how to write only + * the bits which can be controlled by us. + * + * Fortunately, after reset, all channels are enabled in BCM_DMA_ENABLE + * register and all statuses are cleared in BCM_DMA_INT_STATUS one. + * Not touching these registers is a trade off between correct + * initialization which does not count on anything and not messing up + * something we have no control over. + */ + reg = bus_read_4(sc->sc_mem, BCM_DMA_ENABLE); + if ((reg & bcm_dma_channel_mask) != bcm_dma_channel_mask) + device_printf(dev, "channels are not enabled\n"); + reg = bus_read_4(sc->sc_mem, BCM_DMA_INT_STATUS); + if ((reg & bcm_dma_channel_mask) != 0) + device_printf(dev, "statuses are not cleared\n"); /* Allocate DMA chunks control blocks */ /* p.40 of spec - control block should be 32-bit aligned */ err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, sizeof(struct bcm_dma_cb), 1, sizeof(struct bcm_dma_cb), BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_dma_tag); if (err) { - device_printf(dev, "failed allocate DMA tag"); + device_printf(dev, "failed allocate DMA tag\n"); return (err); } /* setup initial settings */ for (i = 0; i < BCM_DMA_CH_MAX; i++) { ch = &sc->sc_dma_ch[i]; + bzero(ch, sizeof(struct bcm_dma_ch)); + ch->ch = i; + ch->flags = BCM_DMA_CH_UNMAP; + + if ((bcm_dma_channel_mask & (1 << i)) == 0) + continue; + err = bus_dmamem_alloc(sc->sc_dma_tag, &cb_virt, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &ch->dma_map); if (err) { device_printf(dev, "cannot allocate DMA memory\n"); break; } /* * Least alignment for busdma-allocated stuff is cache * line size, so just make sure nothing stupid happend * and we got properly aligned address */ if ((uintptr_t)cb_virt & 0x1f) { device_printf(dev, "DMA address is not 32-bytes aligned: %p\n", (void*)cb_virt); break; } err = bus_dmamap_load(sc->sc_dma_tag, ch->dma_map, cb_virt, sizeof(struct bcm_dma_cb), bcm_dmamap_cb, &cb_phys, BUS_DMA_WAITOK); if (err) { device_printf(dev, "cannot load DMA memory\n"); break; } - bzero(ch, sizeof(struct bcm_dma_ch)); - ch->ch = i; ch->cb = cb_virt; ch->vc_cb = cb_phys; - ch->intr_func = NULL; - ch->intr_arg = NULL; - ch->flags = BCM_DMA_CH_UNMAP; - + ch->flags = BCM_DMA_CH_FREE; ch->cb->info = INFO_WAIT_RESP; /* reset DMA engine */ - bcm_dma_reset(dev, i); + bus_write_4(sc->sc_mem, BCM_DMA_CS(i), CS_RESET); } - /* now use DMA2/DMA3 only */ - sc->sc_dma_ch[2].flags = BCM_DMA_CH_FREE; - sc->sc_dma_ch[3].flags = BCM_DMA_CH_FREE; - - /* enable DMAs */ - mask = 0; - - for (i = 0; i < BCM_DMA_CH_MAX; i++) - if (sc->sc_dma_ch[i].flags & BCM_DMA_CH_FREE) - mask |= (1 << i); - - bus_write_4(sc->sc_mem, BCM_DMA_ENABLE, mask); - return (0); } /* * Allocate DMA channel for further use, returns channel # or * BCM_DMA_CH_INVALID */ int bcm_dma_allocate(int req_ch) { struct bcm_dma_softc *sc = bcm_dma_sc; int ch = BCM_DMA_CH_INVALID; int i; if (req_ch >= BCM_DMA_CH_MAX) return (BCM_DMA_CH_INVALID); /* Auto(req_ch < 0) or CH specified */ mtx_lock(&sc->sc_mtx); if (req_ch < 0) { for (i = 0; i < BCM_DMA_CH_MAX; i++) { if (sc->sc_dma_ch[i].flags & BCM_DMA_CH_FREE) { ch = i; sc->sc_dma_ch[ch].flags &= ~BCM_DMA_CH_FREE; sc->sc_dma_ch[ch].flags |= BCM_DMA_CH_USED; break; } } } else { if (sc->sc_dma_ch[req_ch].flags & BCM_DMA_CH_FREE) { ch = req_ch; sc->sc_dma_ch[ch].flags &= ~BCM_DMA_CH_FREE; sc->sc_dma_ch[ch].flags |= BCM_DMA_CH_USED; } } mtx_unlock(&sc->sc_mtx); return (ch); } /* * Frees allocated channel. Returns 0 on success, -1 otherwise */ int bcm_dma_free(int ch) { struct bcm_dma_softc *sc = bcm_dma_sc; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return (-1); mtx_lock(&sc->sc_mtx); if (sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED) { sc->sc_dma_ch[ch].flags |= BCM_DMA_CH_FREE; sc->sc_dma_ch[ch].flags &= ~BCM_DMA_CH_USED; sc->sc_dma_ch[ch].intr_func = NULL; sc->sc_dma_ch[ch].intr_arg = NULL; /* reset DMA engine */ bcm_dma_reset(sc->sc_dev, ch); } mtx_unlock(&sc->sc_mtx); return (0); } /* * Assign handler function for channel interrupt * Returns 0 on success, -1 otherwise */ int bcm_dma_setup_intr(int ch, void (*func)(int, void *), void *arg) { struct bcm_dma_softc *sc = bcm_dma_sc; struct bcm_dma_cb *cb; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return (-1); if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) return (-1); sc->sc_dma_ch[ch].intr_func = func; sc->sc_dma_ch[ch].intr_arg = arg; cb = sc->sc_dma_ch[ch].cb; cb->info |= INFO_INT_EN; return (0); } /* * Setup DMA source parameters * ch - channel number * dreq - hardware DREQ # or BCM_DMA_DREQ_NONE if * source is physical memory * inc_addr - BCM_DMA_INC_ADDR if source address * should be increased after each access or * BCM_DMA_SAME_ADDR if address should remain * the same * width - size of read operation, BCM_DMA_32BIT * for 32bit bursts, BCM_DMA_128BIT for 128 bits * * Returns 0 on success, -1 otherwise */ int bcm_dma_setup_src(int ch, int dreq, int inc_addr, int width) { struct bcm_dma_softc *sc = bcm_dma_sc; uint32_t info; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return (-1); if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) return (-1); info = sc->sc_dma_ch[ch].cb->info; info &= ~INFO_PERMAP_MASK; info |= (dreq << INFO_PERMAP_SHIFT) & INFO_PERMAP_MASK; if (dreq) info |= INFO_S_DREQ; else info &= ~INFO_S_DREQ; if (width == BCM_DMA_128BIT) info |= INFO_S_WIDTH; else info &= ~INFO_S_WIDTH; if (inc_addr == BCM_DMA_INC_ADDR) info |= INFO_S_INC; else info &= ~INFO_S_INC; sc->sc_dma_ch[ch].cb->info = info; return (0); } /* * Setup DMA destination parameters * ch - channel number * dreq - hardware DREQ # or BCM_DMA_DREQ_NONE if * destination is physical memory * inc_addr - BCM_DMA_INC_ADDR if source address * should be increased after each access or * BCM_DMA_SAME_ADDR if address should remain * the same * width - size of write operation, BCM_DMA_32BIT * for 32bit bursts, BCM_DMA_128BIT for 128 bits * * Returns 0 on success, -1 otherwise */ int bcm_dma_setup_dst(int ch, int dreq, int inc_addr, int width) { struct bcm_dma_softc *sc = bcm_dma_sc; uint32_t info; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return (-1); if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) return (-1); info = sc->sc_dma_ch[ch].cb->info; info &= ~INFO_PERMAP_MASK; info |= (dreq << INFO_PERMAP_SHIFT) & INFO_PERMAP_MASK; if (dreq) info |= INFO_D_DREQ; else info &= ~INFO_D_DREQ; if (width == BCM_DMA_128BIT) info |= INFO_D_WIDTH; else info &= ~INFO_D_WIDTH; if (inc_addr == BCM_DMA_INC_ADDR) info |= INFO_D_INC; else info &= ~INFO_D_INC; sc->sc_dma_ch[ch].cb->info = info; return (0); } #ifdef DEBUG void bcm_dma_cb_dump(struct bcm_dma_cb *cb) { printf("DMA CB "); printf("INFO: %8.8x ", cb->info); printf("SRC: %8.8x ", cb->src); printf("DST: %8.8x ", cb->dst); printf("LEN: %8.8x ", cb->len); printf("\n"); printf("STRIDE: %8.8x ", cb->stride); printf("NEXT: %8.8x ", cb->next); printf("RSVD1: %8.8x ", cb->rsvd1); printf("RSVD2: %8.8x ", cb->rsvd2); printf("\n"); } void bcm_dma_reg_dump(int ch) { struct bcm_dma_softc *sc = bcm_dma_sc; int i; uint32_t reg; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return; printf("DMA%d: ", ch); for (i = 0; i < MAX_REG; i++) { reg = bus_read_4(sc->sc_mem, BCM_DMA_CH(ch) + i*4); printf("%8.8x ", reg); } printf("\n"); } #endif /* * Start DMA transaction * ch - channel number * src, dst - source and destination address in * ARM physical memory address space. * len - amount of bytes to be transfered * * Returns 0 on success, -1 otherwise */ int bcm_dma_start(int ch, vm_paddr_t src, vm_paddr_t dst, int len) { struct bcm_dma_softc *sc = bcm_dma_sc; struct bcm_dma_cb *cb; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return (-1); if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) return (-1); cb = sc->sc_dma_ch[ch].cb; if (BCM2835_ARM_IS_IO(src)) cb->src = IO_TO_VCBUS(src); else cb->src = PHYS_TO_VCBUS(src); if (BCM2835_ARM_IS_IO(dst)) cb->dst = IO_TO_VCBUS(dst); else cb->dst = PHYS_TO_VCBUS(dst); cb->len = len; bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_ch[ch].dma_map, BUS_DMASYNC_PREWRITE); bus_write_4(sc->sc_mem, BCM_DMA_CBADDR(ch), sc->sc_dma_ch[ch].vc_cb); bus_write_4(sc->sc_mem, BCM_DMA_CS(ch), CS_ACTIVE); #ifdef DEBUG bcm_dma_cb_dump(sc->sc_dma_ch[ch].cb); bcm_dma_reg_dump(ch); #endif return (0); } /* * Get length requested for DMA transaction * ch - channel number * * Returns size of transaction, 0 if channel is invalid */ uint32_t bcm_dma_length(int ch) { struct bcm_dma_softc *sc = bcm_dma_sc; struct bcm_dma_cb *cb; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return (0); if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) return (0); cb = sc->sc_dma_ch[ch].cb; return (cb->len); } static void bcm_dma_intr(void *arg) { struct bcm_dma_softc *sc = bcm_dma_sc; struct bcm_dma_ch *ch = (struct bcm_dma_ch *)arg; uint32_t cs, debug; /* my interrupt? */ cs = bus_read_4(sc->sc_mem, BCM_DMA_CS(ch->ch)); - if (!(cs & (CS_INT | CS_ERR))) + if (!(cs & (CS_INT | CS_ERR))) { + device_printf(sc->sc_dev, + "unexpected DMA intr CH=%d, CS=%x\n", ch->ch, cs); return; + } /* running? */ if (!(ch->flags & BCM_DMA_CH_USED)) { device_printf(sc->sc_dev, "unused DMA intr CH=%d, CS=%x\n", ch->ch, cs); return; } if (cs & CS_ERR) { debug = bus_read_4(sc->sc_mem, BCM_DMA_DEBUG(ch->ch)); device_printf(sc->sc_dev, "DMA error %d on CH%d\n", debug & DEBUG_ERROR_MASK, ch->ch); bus_write_4(sc->sc_mem, BCM_DMA_DEBUG(ch->ch), debug & DEBUG_ERROR_MASK); bcm_dma_reset(sc->sc_dev, ch->ch); } if (cs & CS_INT) { /* acknowledge interrupt */ bus_write_4(sc->sc_mem, BCM_DMA_CS(ch->ch), CS_INT | CS_END); /* Prepare for possible access to len field */ bus_dmamap_sync(sc->sc_dma_tag, ch->dma_map, BUS_DMASYNC_POSTWRITE); /* save callback function and argument */ if (ch->intr_func) ch->intr_func(ch->ch, ch->intr_arg); } } static int bcm_dma_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-dma")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2835 DMA Controller"); return (BUS_PROBE_DEFAULT); } static int bcm_dma_attach(device_t dev) { struct bcm_dma_softc *sc = device_get_softc(dev); + phandle_t node; int rid, err = 0; int i; sc->sc_dev = dev; if (bcm_dma_sc) return (ENXIO); for (i = 0; i < BCM_DMA_CH_MAX; i++) { sc->sc_irq[i] = NULL; sc->sc_intrhand[i] = NULL; } + /* Get DMA channel mask. */ + node = ofw_bus_get_node(sc->sc_dev); + if (OF_getencprop(node, "brcm,dma-channel-mask", &bcm_dma_channel_mask, + sizeof(bcm_dma_channel_mask)) == -1 && + OF_getencprop(node, "broadcom,channels", &bcm_dma_channel_mask, + sizeof(bcm_dma_channel_mask)) == -1) { + device_printf(dev, "could not get channel mask property\n"); + return (ENXIO); + } + + /* Mask out channels used by GPU. */ + bcm_dma_channel_mask &= ~BCM_DMA_CH_GPU_MASK; + /* DMA0 - DMA14 */ rid = 0; sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } /* IRQ DMA0 - DMA11 XXX NOT USE DMA12(spurious?) */ for (rid = 0; rid < BCM_DMA_CH_MAX; rid++) { + if ((bcm_dma_channel_mask & (1 << rid)) == 0) + continue; + sc->sc_irq[rid] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_irq[rid] == NULL) { device_printf(dev, "cannot allocate interrupt\n"); err = ENXIO; goto fail; } if (bus_setup_intr(dev, sc->sc_irq[rid], INTR_TYPE_MISC | INTR_MPSAFE, NULL, bcm_dma_intr, &sc->sc_dma_ch[rid], &sc->sc_intrhand[rid])) { device_printf(dev, "cannot setup interrupt handler\n"); err = ENXIO; goto fail; } } mtx_init(&sc->sc_mtx, "bcmdma", "bcmdma", MTX_DEF); bcm_dma_sc = sc; err = bcm_dma_init(dev); if (err) goto fail; return (err); fail: if (sc->sc_mem) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem); for (i = 0; i < BCM_DMA_CH_MAX; i++) { if (sc->sc_intrhand[i]) bus_teardown_intr(dev, sc->sc_irq[i], sc->sc_intrhand[i]); if (sc->sc_irq[i]) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq[i]); } return (err); } static device_method_t bcm_dma_methods[] = { DEVMETHOD(device_probe, bcm_dma_probe), DEVMETHOD(device_attach, bcm_dma_attach), { 0, 0 } }; static driver_t bcm_dma_driver = { "bcm_dma", bcm_dma_methods, sizeof(struct bcm_dma_softc), }; static devclass_t bcm_dma_devclass; DRIVER_MODULE(bcm_dma, simplebus, bcm_dma_driver, bcm_dma_devclass, 0, 0); MODULE_VERSION(bcm_dma, 1); Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_dma.h =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_dma.h (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_dma.h (revision 322724) @@ -1,62 +1,60 @@ /* * Copyright (c) 2013 Daisuke Aoyama * Copyright (c) 2013 Oleksandr Tymoshenko * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _BCM2835_DMA_H_ #define _BCM2835_DMA_H_ #define BCM_DMA_BLOCK_SIZE 512 /* DMA0-DMA15 but DMA15 is special */ #define BCM_DMA_CH_MAX 12 /* request CH for any nubmer */ #define BCM_DMA_CH_INVALID (-1) #define BCM_DMA_CH_ANY (-1) -#define BCM_DMA_CH_FAST1 (2) -#define BCM_DMA_CH_FAST2 (3) /* Peripheral DREQ Signals (4.2.1.3) */ #define BCM_DMA_DREQ_NONE 0 #define BCM_DMA_DREQ_EMMC 11 #define BCM_DMA_DREQ_SDHOST 13 #define BCM_DMA_SAME_ADDR 0 #define BCM_DMA_INC_ADDR 1 #define BCM_DMA_32BIT 0 #define BCM_DMA_128BIT 1 int bcm_dma_allocate(int req_ch); int bcm_dma_free(int ch); int bcm_dma_setup_intr(int ch, void (*func)(int, void *), void *arg); int bcm_dma_setup_src(int ch, int dreq, int inc_addr, int width); int bcm_dma_setup_dst(int ch, int dreq, int inc_addr, int width); int bcm_dma_start(int ch, vm_paddr_t src, vm_paddr_t dst, int len); uint32_t bcm_dma_length(int ch); #endif /* _BCM2835_DMA_H_ */ Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_fb.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_fb.c (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_fb.c (revision 322724) @@ -1,998 +1,870 @@ /*- * Copyright (c) 2012 Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include -#include #include -#include -#include +#include +#include +#include #include -#include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include -#include -#include -#include -#include -#include - +#include #include #include #include - -#include #include -#include -#include +#include #include "mbox_if.h" -#define BCMFB_FONT_HEIGHT 16 - struct argb { uint8_t a; uint8_t r; uint8_t g; uint8_t b; }; static struct argb bcmfb_palette[16] = { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0xaa}, {0x00, 0x00, 0xaa, 0x00}, {0x00, 0x00, 0xaa, 0xaa}, {0x00, 0xaa, 0x00, 0x00}, {0x00, 0xaa, 0x00, 0xaa}, {0x00, 0xaa, 0x55, 0x00}, {0x00, 0xaa, 0xaa, 0xaa}, {0x00, 0x55, 0x55, 0x55}, {0x00, 0x55, 0x55, 0xff}, {0x00, 0x55, 0xff, 0x55}, {0x00, 0x55, 0xff, 0xff}, {0x00, 0xff, 0x55, 0x55}, {0x00, 0xff, 0x55, 0xff}, {0x00, 0xff, 0xff, 0x55}, {0x00, 0xff, 0xff, 0xff} }; /* mouse pointer from dev/syscons/scgfbrndr.c */ static u_char mouse_pointer[16] = { 0x00, 0x40, 0x60, 0x70, 0x78, 0x7c, 0x7e, 0x68, 0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00 }; -#define FB_WIDTH 640 -#define FB_HEIGHT 480 -#define FB_DEPTH 24 +#define BCMFB_FONT_HEIGHT 16 +#define BCMFB_FONT_WIDTH 8 +#define FB_WIDTH 640 +#define FB_HEIGHT 480 +#define FB_DEPTH 24 -struct bcm_fb_config { - uint32_t xres; - uint32_t yres; - uint32_t vxres; - uint32_t vyres; - uint32_t pitch; - uint32_t bpp; - uint32_t xoffset; - uint32_t yoffset; - /* Filled by videocore */ - uint32_t base; - uint32_t screen_size; -}; - struct bcmsc_softc { - device_t dev; - struct cdev * cdev; - struct mtx mtx; - bus_dma_tag_t dma_tag; - bus_dmamap_t dma_map; - struct bcm_fb_config* fb_config; - bus_addr_t fb_config_phys; - struct intr_config_hook init_hook; - -}; - -struct video_adapter_softc { /* Videoadpater part */ video_adapter_t va; - int console; intptr_t fb_addr; intptr_t fb_paddr; unsigned int fb_size; unsigned int height; unsigned int width; unsigned int depth; unsigned int stride; unsigned int xmargin; unsigned int ymargin; unsigned char *font; + int fbswap; int initialized; }; -static struct bcmsc_softc *bcmsc_softc; -static struct video_adapter_softc va_softc; +static struct bcmsc_softc bcmsc; -#define bcm_fb_lock(_sc) mtx_lock(&(_sc)->mtx) -#define bcm_fb_unlock(_sc) mtx_unlock(&(_sc)->mtx) -#define bcm_fb_lock_assert(sc) mtx_assert(&(_sc)->mtx, MA_OWNED) +static struct ofw_compat_data compat_data[] = { + {"broadcom,bcm2835-fb", 1}, + {"brcm,bcm2708-fb", 1}, + {NULL, 0} +}; static int bcm_fb_probe(device_t); static int bcm_fb_attach(device_t); -static void bcm_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err); static void bcmfb_update_margins(video_adapter_t *adp); static int bcmfb_configure(int); -static void -bcm_fb_init(void *arg) -{ - struct bcmsc_softc *sc = arg; - struct video_adapter_softc *va_sc = &va_softc; - int err; - volatile struct bcm_fb_config* fb_config = sc->fb_config; - phandle_t node; - pcell_t cell; - device_t mbox; - - node = ofw_bus_get_node(sc->dev); - - fb_config->xres = 0; - fb_config->yres = 0; - fb_config->bpp = 0; - - if ((OF_getprop(node, "broadcom,width", &cell, sizeof(cell))) > 0) - fb_config->xres = (int)fdt32_to_cpu(cell); - if (fb_config->xres == 0) - fb_config->xres = FB_WIDTH; - - if ((OF_getprop(node, "broadcom,height", &cell, sizeof(cell))) > 0) - fb_config->yres = (uint32_t)fdt32_to_cpu(cell); - if (fb_config->yres == 0) - fb_config->yres = FB_HEIGHT; - - if ((OF_getprop(node, "broadcom,depth", &cell, sizeof(cell))) > 0) - fb_config->bpp = (uint32_t)fdt32_to_cpu(cell); - if (fb_config->bpp == 0) - fb_config->bpp = FB_DEPTH; - - fb_config->vxres = 0; - fb_config->vyres = 0; - fb_config->xoffset = 0; - fb_config->yoffset = 0; - fb_config->base = 0; - fb_config->pitch = 0; - fb_config->screen_size = 0; - - bus_dmamap_sync(sc->dma_tag, sc->dma_map, - BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); - - mbox = devclass_get_device(devclass_find("mbox"), 0); - if (mbox) { - MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_FB, sc->fb_config_phys); - MBOX_READ(mbox, BCM2835_MBOX_CHAN_FB, &err); - } - bus_dmamap_sync(sc->dma_tag, sc->dma_map, - BUS_DMASYNC_POSTREAD); - - if (fb_config->base != 0) { - device_printf(sc->dev, "%dx%d(%dx%d@%d,%d) %dbpp\n", - fb_config->xres, fb_config->yres, - fb_config->vxres, fb_config->vyres, - fb_config->xoffset, fb_config->yoffset, - fb_config->bpp); - - - device_printf(sc->dev, "pitch %d, base 0x%08x, screen_size %d\n", - fb_config->pitch, fb_config->base, - fb_config->screen_size); - - va_sc->fb_addr = (intptr_t)pmap_mapdev(fb_config->base, fb_config->screen_size); - va_sc->fb_paddr = fb_config->base; - va_sc->fb_size = fb_config->screen_size; - va_sc->depth = fb_config->bpp; - va_sc->stride = fb_config->pitch; - - va_sc->width = fb_config->xres; - va_sc->height = fb_config->yres; - bcmfb_update_margins(&va_sc->va); - } - else { - device_printf(sc->dev, "Failed to set framebuffer info\n"); - return; - } - - config_intrhook_disestablish(&sc->init_hook); -} - static int bcm_fb_probe(device_t dev) { - int error = 0; + int error; - if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-fb")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2835 framebuffer device"); - error = sc_probe_unit(device_get_unit(dev), device_get_flags(dev) | SC_AUTODETECT_KBD); if (error != 0) return (error); - return (BUS_PROBE_DEFAULT); } static int bcm_fb_attach(device_t dev) { - struct bcmsc_softc *sc = device_get_softc(dev); - int dma_size = sizeof(struct bcm_fb_config); - int err; + struct bcm2835_fb_config fb; + struct bcmsc_softc *sc; - if (bcmsc_softc) + sc = (struct bcmsc_softc *)vid_get_adapter(vid_find_adapter( + "bcmfb", 0)); + if (sc != NULL) + device_set_softc(dev, sc); + else + sc = device_get_softc(dev); + + memset(&fb, 0, sizeof(fb)); + if (bcm2835_mbox_fb_get_w_h(&fb) != 0) return (ENXIO); + fb.bpp = FB_DEPTH; + fb.vxres = fb.xres; + fb.vyres = fb.yres; + fb.xoffset = fb.yoffset = 0; + if (bcm2835_mbox_fb_init(&fb) != 0) + return (ENXIO); - bcmsc_softc = sc; + sc->fb_addr = (intptr_t)pmap_mapdev(fb.base, fb.size); + sc->fb_paddr = fb.base; + sc->fb_size = fb.size; + sc->depth = fb.bpp; + sc->stride = fb.pitch; + sc->width = fb.xres; + sc->height = fb.yres; + bcmfb_update_margins(&sc->va); - sc->dev = dev; - mtx_init(&sc->mtx, "bcm2835fb", "fb", MTX_DEF); - - err = bus_dma_tag_create( - bus_get_dma_tag(sc->dev), - PAGE_SIZE, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - dma_size, 1, /* maxsize, nsegments */ - dma_size, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->dma_tag); - - err = bus_dmamem_alloc(sc->dma_tag, (void **)&sc->fb_config, - 0, &sc->dma_map); - if (err) { - device_printf(dev, "cannot allocate framebuffer\n"); - goto fail; - } - - err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->fb_config, - dma_size, bcm_fb_dmamap_cb, &sc->fb_config_phys, BUS_DMA_NOWAIT); - - if (err) { - device_printf(dev, "cannot load DMA map\n"); - goto fail; - } - - err = (sc_attach_unit(device_get_unit(dev), - device_get_flags(dev) | SC_AUTODETECT_KBD)); - - if (err) { + if (sc_attach_unit(device_get_unit(dev), + device_get_flags(dev) | SC_AUTODETECT_KBD) != 0) { device_printf(dev, "failed to attach syscons\n"); - goto fail; + return (ENXIO); } - /* - * We have to wait until interrupts are enabled. - * Mailbox relies on it to get data from VideoCore - */ - sc->init_hook.ich_func = bcm_fb_init; - sc->init_hook.ich_arg = sc; + device_printf(dev, "%dx%d(%dx%d@%d,%d) %dbpp\n", fb.xres, fb.yres, + fb.vxres, fb.vyres, fb.xoffset, fb.yoffset, fb.bpp); + device_printf(dev, + "fbswap: %d, pitch %d, base 0x%08x, screen_size %d\n", + sc->fbswap, fb.pitch, fb.base, fb.size); - if (config_intrhook_establish(&sc->init_hook) != 0) { - device_printf(dev, "failed to establish intrhook\n"); - return (ENOMEM); - } - return (0); - -fail: - return (ENXIO); } - -static void -bcm_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) -{ - bus_addr_t *addr; - - if (err) - return; - - addr = (bus_addr_t*)arg; - *addr = PHYS_TO_VCBUS(segs[0].ds_addr); -} - static device_method_t bcm_fb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_fb_probe), DEVMETHOD(device_attach, bcm_fb_attach), { 0, 0 } }; static devclass_t bcm_fb_devclass; static driver_t bcm_fb_driver = { "fb", bcm_fb_methods, sizeof(struct bcmsc_softc), }; DRIVER_MODULE(bcm2835fb, ofwbus, bcm_fb_driver, bcm_fb_devclass, 0, 0); +DRIVER_MODULE(bcm2835fb, simplebus, bcm_fb_driver, bcm_fb_devclass, 0, 0); /* * Video driver routines and glue. */ static vi_probe_t bcmfb_probe; static vi_init_t bcmfb_init; static vi_get_info_t bcmfb_get_info; static vi_query_mode_t bcmfb_query_mode; static vi_set_mode_t bcmfb_set_mode; static vi_save_font_t bcmfb_save_font; static vi_load_font_t bcmfb_load_font; static vi_show_font_t bcmfb_show_font; static vi_save_palette_t bcmfb_save_palette; static vi_load_palette_t bcmfb_load_palette; static vi_set_border_t bcmfb_set_border; static vi_save_state_t bcmfb_save_state; static vi_load_state_t bcmfb_load_state; static vi_set_win_org_t bcmfb_set_win_org; static vi_read_hw_cursor_t bcmfb_read_hw_cursor; static vi_set_hw_cursor_t bcmfb_set_hw_cursor; static vi_set_hw_cursor_shape_t bcmfb_set_hw_cursor_shape; static vi_blank_display_t bcmfb_blank_display; static vi_mmap_t bcmfb_mmap; static vi_ioctl_t bcmfb_ioctl; static vi_clear_t bcmfb_clear; static vi_fill_rect_t bcmfb_fill_rect; static vi_bitblt_t bcmfb_bitblt; static vi_diag_t bcmfb_diag; static vi_save_cursor_palette_t bcmfb_save_cursor_palette; static vi_load_cursor_palette_t bcmfb_load_cursor_palette; static vi_copy_t bcmfb_copy; static vi_putp_t bcmfb_putp; static vi_putc_t bcmfb_putc; static vi_puts_t bcmfb_puts; static vi_putm_t bcmfb_putm; static video_switch_t bcmfbvidsw = { .probe = bcmfb_probe, .init = bcmfb_init, .get_info = bcmfb_get_info, .query_mode = bcmfb_query_mode, .set_mode = bcmfb_set_mode, .save_font = bcmfb_save_font, .load_font = bcmfb_load_font, .show_font = bcmfb_show_font, .save_palette = bcmfb_save_palette, .load_palette = bcmfb_load_palette, .set_border = bcmfb_set_border, .save_state = bcmfb_save_state, .load_state = bcmfb_load_state, .set_win_org = bcmfb_set_win_org, .read_hw_cursor = bcmfb_read_hw_cursor, .set_hw_cursor = bcmfb_set_hw_cursor, .set_hw_cursor_shape = bcmfb_set_hw_cursor_shape, .blank_display = bcmfb_blank_display, .mmap = bcmfb_mmap, .ioctl = bcmfb_ioctl, .clear = bcmfb_clear, .fill_rect = bcmfb_fill_rect, .bitblt = bcmfb_bitblt, .diag = bcmfb_diag, .save_cursor_palette = bcmfb_save_cursor_palette, .load_cursor_palette = bcmfb_load_cursor_palette, .copy = bcmfb_copy, .putp = bcmfb_putp, .putc = bcmfb_putc, .puts = bcmfb_puts, .putm = bcmfb_putm, }; VIDEO_DRIVER(bcmfb, bcmfbvidsw, bcmfb_configure); static vr_init_t bcmrend_init; static vr_clear_t bcmrend_clear; static vr_draw_border_t bcmrend_draw_border; static vr_draw_t bcmrend_draw; static vr_set_cursor_t bcmrend_set_cursor; static vr_draw_cursor_t bcmrend_draw_cursor; static vr_blink_cursor_t bcmrend_blink_cursor; static vr_set_mouse_t bcmrend_set_mouse; static vr_draw_mouse_t bcmrend_draw_mouse; /* * We use our own renderer; this is because we must emulate a hardware * cursor. */ static sc_rndr_sw_t bcmrend = { bcmrend_init, bcmrend_clear, bcmrend_draw_border, bcmrend_draw, bcmrend_set_cursor, bcmrend_draw_cursor, bcmrend_blink_cursor, bcmrend_set_mouse, bcmrend_draw_mouse }; RENDERER(bcmfb, 0, bcmrend, gfb_set); RENDERER_MODULE(bcmfb, gfb_set); static void bcmrend_init(scr_stat* scp) { } static void bcmrend_clear(scr_stat* scp, int c, int attr) { } static void bcmrend_draw_border(scr_stat* scp, int color) { } static void bcmrend_draw(scr_stat* scp, int from, int count, int flip) { video_adapter_t* adp = scp->sc->adp; int i, c, a; if (!flip) { /* Normal printing */ vidd_puts(adp, from, (uint16_t*)sc_vtb_pointer(&scp->vtb, from), count); } else { /* This is for selections and such: invert the color attribute */ for (i = count; i-- > 0; ++from) { c = sc_vtb_getc(&scp->vtb, from); a = sc_vtb_geta(&scp->vtb, from) >> 8; vidd_putc(adp, from, c, (a >> 4) | ((a & 0xf) << 4)); } } } static void bcmrend_set_cursor(scr_stat* scp, int base, int height, int blink) { } static void bcmrend_draw_cursor(scr_stat* scp, int off, int blink, int on, int flip) { - video_adapter_t* adp = scp->sc->adp; - struct video_adapter_softc *sc; - int row, col; + int bytes, col, i, j, row; + struct bcmsc_softc *sc; uint8_t *addr; - int i, j, bytes; + video_adapter_t *adp; - sc = (struct video_adapter_softc *)adp; + adp = scp->sc->adp; + sc = (struct bcmsc_softc *)adp; if (scp->curs_attr.height <= 0) return; if (sc->fb_addr == 0) return; if (off >= adp->va_info.vi_width * adp->va_info.vi_height) return; /* calculate the coordinates in the video buffer */ row = (off / adp->va_info.vi_width) * adp->va_info.vi_cheight; col = (off % adp->va_info.vi_width) * adp->va_info.vi_cwidth; addr = (uint8_t *)sc->fb_addr + (row + sc->ymargin)*(sc->stride) + (sc->depth/8) * (col + sc->xmargin); - bytes = sc->depth/8; - + bytes = sc->depth / 8; /* our cursor consists of simply inverting the char under it */ for (i = 0; i < adp->va_info.vi_cheight; i++) { for (j = 0; j < adp->va_info.vi_cwidth; j++) { switch (sc->depth) { case 32: case 24: addr[bytes*j + 2] ^= 0xff; /* FALLTHROUGH */ case 16: addr[bytes*j + 1] ^= 0xff; addr[bytes*j] ^= 0xff; break; default: break; } } addr += sc->stride; } } static void bcmrend_blink_cursor(scr_stat* scp, int at, int flip) { } static void bcmrend_set_mouse(scr_stat* scp) { } static void bcmrend_draw_mouse(scr_stat* scp, int x, int y, int on) { vidd_putm(scp->sc->adp, x, y, mouse_pointer, 0xffffffff, 16, 8); } static uint16_t bcmfb_static_window[ROW*COL]; extern u_char dflt_font_16[]; /* * Update videoadapter settings after changing resolution */ static void bcmfb_update_margins(video_adapter_t *adp) { - struct video_adapter_softc *sc; + struct bcmsc_softc *sc; video_info_t *vi; - sc = (struct video_adapter_softc *)adp; + sc = (struct bcmsc_softc *)adp; vi = &adp->va_info; sc->xmargin = (sc->width - (vi->vi_width * vi->vi_cwidth)) / 2; - sc->ymargin = (sc->height - (vi->vi_height * vi->vi_cheight))/2; + sc->ymargin = (sc->height - (vi->vi_height * vi->vi_cheight)) / 2; } static int bcmfb_configure(int flags) { - struct video_adapter_softc *va_sc; - - va_sc = &va_softc; - phandle_t display, root; + char bootargs[2048], *n, *p, *v; pcell_t cell; + phandle_t chosen, display, root; + struct bcmsc_softc *sc; - if (va_sc->initialized) + sc = &bcmsc; + if (sc->initialized) return (0); - va_sc->width = 0; - va_sc->height = 0; + sc->width = 0; + sc->height = 0; /* * It seems there is no way to let syscons framework know * that framebuffer resolution has changed. So just try - * to fetch data from FDT and go with defaults if failed + * to fetch data from FDT bootargs, FDT display data and + * finally go with defaults if everything else has failed. */ + chosen = OF_finddevice("/chosen"); + if (chosen != 0 && + OF_getprop(chosen, "bootargs", &bootargs, sizeof(bootargs)) > 0) { + p = bootargs; + while ((v = strsep(&p, " ")) != NULL) { + if (*v == '\0') + continue; + n = strsep(&v, "="); + if (strcmp(n, "bcm2708_fb.fbwidth") == 0 && v != NULL) + sc->width = (unsigned int)strtol(v, NULL, 0); + else if (strcmp(n, "bcm2708_fb.fbheight") == 0 && + v != NULL) + sc->height = (unsigned int)strtol(v, NULL, 0); + else if (strcmp(n, "bcm2708_fb.fbswap") == 0 && + v != NULL) + if (*v == '1') + sc->fbswap = 1; + } + } + root = OF_finddevice("/"); if ((root != 0) && (display = fdt_find_compatible(root, "broadcom,bcm2835-fb", 1))) { - if ((OF_getprop(display, "broadcom,width", - &cell, sizeof(cell))) > 0) - va_sc->width = (int)fdt32_to_cpu(cell); + if (sc->width == 0) { + if ((OF_getprop(display, "broadcom,width", + &cell, sizeof(cell))) > 0) + sc->width = (int)fdt32_to_cpu(cell); + } - if ((OF_getprop(display, "broadcom,height", - &cell, sizeof(cell))) > 0) - va_sc->height = (int)fdt32_to_cpu(cell); + if (sc->height == 0) { + if ((OF_getprop(display, "broadcom,height", + &cell, sizeof(cell))) > 0) + sc->height = (int)fdt32_to_cpu(cell); + } } - if (va_sc->width == 0) - va_sc->width = FB_WIDTH; - if (va_sc->height == 0) - va_sc->height = FB_HEIGHT; + if (sc->width == 0) + sc->width = FB_WIDTH; + if (sc->height == 0) + sc->height = FB_HEIGHT; - bcmfb_init(0, &va_sc->va, 0); + bcmfb_init(0, &sc->va, 0); + sc->initialized = 1; - va_sc->initialized = 1; - return (0); } static int bcmfb_probe(int unit, video_adapter_t **adp, void *arg, int flags) { return (0); } static int bcmfb_init(int unit, video_adapter_t *adp, int flags) { - struct video_adapter_softc *sc; + struct bcmsc_softc *sc; video_info_t *vi; - sc = (struct video_adapter_softc *)adp; + sc = (struct bcmsc_softc *)adp; vi = &adp->va_info; vid_init_struct(adp, "bcmfb", -1, unit); sc->font = dflt_font_16; vi->vi_cheight = BCMFB_FONT_HEIGHT; - vi->vi_cwidth = 8; + vi->vi_cwidth = BCMFB_FONT_WIDTH; + vi->vi_width = sc->width / vi->vi_cwidth; + vi->vi_height = sc->height / vi->vi_cheight; - vi->vi_width = sc->width/8; - vi->vi_height = sc->height/vi->vi_cheight; - /* * Clamp width/height to syscons maximums */ if (vi->vi_width > COL) vi->vi_width = COL; if (vi->vi_height > ROW) vi->vi_height = ROW; sc->xmargin = (sc->width - (vi->vi_width * vi->vi_cwidth)) / 2; - sc->ymargin = (sc->height - (vi->vi_height * vi->vi_cheight))/2; + sc->ymargin = (sc->height - (vi->vi_height * vi->vi_cheight)) / 2; - adp->va_window = (vm_offset_t) bcmfb_static_window; adp->va_flags |= V_ADP_FONT /* | V_ADP_COLOR | V_ADP_MODECHANGE */; vid_register(&sc->va); return (0); } static int bcmfb_get_info(video_adapter_t *adp, int mode, video_info_t *info) { bcopy(&adp->va_info, info, sizeof(*info)); return (0); } static int bcmfb_query_mode(video_adapter_t *adp, video_info_t *info) { return (0); } static int bcmfb_set_mode(video_adapter_t *adp, int mode) { return (0); } static int bcmfb_save_font(video_adapter_t *adp, int page, int size, int width, u_char *data, int c, int count) { return (0); } static int bcmfb_load_font(video_adapter_t *adp, int page, int size, int width, u_char *data, int c, int count) { - struct video_adapter_softc *sc = (struct video_adapter_softc *)adp; + struct bcmsc_softc *sc; + sc = (struct bcmsc_softc *)adp; sc->font = data; return (0); } static int bcmfb_show_font(video_adapter_t *adp, int page) { return (0); } static int bcmfb_save_palette(video_adapter_t *adp, u_char *palette) { return (0); } static int bcmfb_load_palette(video_adapter_t *adp, u_char *palette) { return (0); } static int bcmfb_set_border(video_adapter_t *adp, int border) { return (bcmfb_blank_display(adp, border)); } static int bcmfb_save_state(video_adapter_t *adp, void *p, size_t size) { return (0); } static int bcmfb_load_state(video_adapter_t *adp, void *p) { return (0); } static int bcmfb_set_win_org(video_adapter_t *adp, off_t offset) { return (0); } static int bcmfb_read_hw_cursor(video_adapter_t *adp, int *col, int *row) { *col = *row = 0; return (0); } static int bcmfb_set_hw_cursor(video_adapter_t *adp, int col, int row) { return (0); } static int bcmfb_set_hw_cursor_shape(video_adapter_t *adp, int base, int height, int celsize, int blink) { return (0); } static int bcmfb_blank_display(video_adapter_t *adp, int mode) { - struct video_adapter_softc *sc; + struct bcmsc_softc *sc; - sc = (struct video_adapter_softc *)adp; + sc = (struct bcmsc_softc *)adp; if (sc && sc->fb_addr) memset((void*)sc->fb_addr, 0, sc->fb_size); return (0); } static int bcmfb_mmap(video_adapter_t *adp, vm_ooffset_t offset, vm_paddr_t *paddr, int prot, vm_memattr_t *memattr) { - struct video_adapter_softc *sc; + struct bcmsc_softc *sc; - sc = (struct video_adapter_softc *)adp; + sc = (struct bcmsc_softc *)adp; /* * This might be a legacy VGA mem request: if so, just point it at the * framebuffer, since it shouldn't be touched */ if (offset < sc->stride*sc->height) { *paddr = sc->fb_paddr + offset; return (0); } return (EINVAL); } static int bcmfb_ioctl(video_adapter_t *adp, u_long cmd, caddr_t data) { - struct video_adapter_softc *sc; + struct bcmsc_softc *sc; struct fbtype *fb; - sc = (struct video_adapter_softc *)adp; + sc = (struct bcmsc_softc *)adp; switch (cmd) { case FBIOGTYPE: fb = (struct fbtype *)data; fb->fb_type = FBTYPE_PCIMISC; fb->fb_height = sc->height; fb->fb_width = sc->width; fb->fb_depth = sc->depth; if (sc->depth <= 1 || sc->depth > 8) fb->fb_cmsize = 0; else fb->fb_cmsize = 1 << sc->depth; fb->fb_size = sc->fb_size; break; default: return (fb_commonioctl(adp, cmd, data)); } return (0); } static int bcmfb_clear(video_adapter_t *adp) { return (bcmfb_blank_display(adp, 0)); } static int bcmfb_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy) { return (0); } static int bcmfb_bitblt(video_adapter_t *adp, ...) { return (0); } static int bcmfb_diag(video_adapter_t *adp, int level) { return (0); } static int bcmfb_save_cursor_palette(video_adapter_t *adp, u_char *palette) { return (0); } static int bcmfb_load_cursor_palette(video_adapter_t *adp, u_char *palette) { return (0); } static int bcmfb_copy(video_adapter_t *adp, vm_offset_t src, vm_offset_t dst, int n) { return (0); } static int bcmfb_putp(video_adapter_t *adp, vm_offset_t off, uint32_t p, uint32_t a, int size, int bpp, int bit_ltor, int byte_ltor) { return (0); } static int bcmfb_putc(video_adapter_t *adp, vm_offset_t off, uint8_t c, uint8_t a) { - struct video_adapter_softc *sc; - int row; - int col; - int i, j, k; - uint8_t *addr; + int bytes, col, i, j, k, row; + struct bcmsc_softc *sc; u_char *p; - uint8_t fg, bg, color; + uint8_t *addr, fg, bg, color; uint16_t rgb; - sc = (struct video_adapter_softc *)adp; + sc = (struct bcmsc_softc *)adp; if (sc->fb_addr == 0) return (0); row = (off / adp->va_info.vi_width) * adp->va_info.vi_cheight; col = (off % adp->va_info.vi_width) * adp->va_info.vi_cwidth; p = sc->font + c*BCMFB_FONT_HEIGHT; addr = (uint8_t *)sc->fb_addr + (row + sc->ymargin)*(sc->stride) + (sc->depth/8) * (col + sc->xmargin); fg = a & 0xf ; bg = (a >> 4) & 0xf; + bytes = sc->depth / 8; for (i = 0; i < BCMFB_FONT_HEIGHT; i++) { for (j = 0, k = 7; j < 8; j++, k--) { if ((p[i] & (1 << k)) == 0) color = bg; else color = fg; switch (sc->depth) { case 32: - addr[4*j+0] = bcmfb_palette[color].r; - addr[4*j+1] = bcmfb_palette[color].g; - addr[4*j+2] = bcmfb_palette[color].b; - addr[4*j+3] = bcmfb_palette[color].a; - break; case 24: - addr[3*j] = bcmfb_palette[color].r; - addr[3*j+1] = bcmfb_palette[color].g; - addr[3*j+2] = bcmfb_palette[color].b; + if (sc->fbswap) { + addr[bytes * j + 0] = + bcmfb_palette[color].b; + addr[bytes * j + 1] = + bcmfb_palette[color].g; + addr[bytes * j + 2] = + bcmfb_palette[color].r; + } else { + addr[bytes * j + 0] = + bcmfb_palette[color].r; + addr[bytes * j + 1] = + bcmfb_palette[color].g; + addr[bytes * j + 2] = + bcmfb_palette[color].b; + } + if (sc->depth == 32) + addr[bytes * j + 3] = + bcmfb_palette[color].a; break; case 16: rgb = (bcmfb_palette[color].r >> 3) << 11; rgb |= (bcmfb_palette[color].g >> 2) << 5; rgb |= (bcmfb_palette[color].b >> 3); - addr[2*j] = rgb & 0xff; - addr[2*j + 1] = (rgb >> 8) & 0xff; + addr[bytes * j] = rgb & 0xff; + addr[bytes * j + 1] = (rgb >> 8) & 0xff; default: /* Not supported yet */ break; } } addr += (sc->stride); } return (0); } static int bcmfb_puts(video_adapter_t *adp, vm_offset_t off, u_int16_t *s, int len) { int i; for (i = 0; i < len; i++) bcmfb_putc(adp, off + i, s[i] & 0xff, (s[i] & 0xff00) >> 8); return (0); } static int bcmfb_putm(video_adapter_t *adp, int x, int y, uint8_t *pixel_image, uint32_t pixel_mask, int size, int width) { return (0); } /* * Define a stub keyboard driver in case one hasn't been * compiled into the kernel */ #include #include static int dummy_kbd_configure(int flags); keyboard_switch_t bcmdummysw; static int dummy_kbd_configure(int flags) { return (0); } KEYBOARD_DRIVER(bcmdummy, bcmdummysw, dummy_kbd_configure); Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_fbd.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_fbd.c (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_fbd.c (revision 322724) @@ -1,310 +1,277 @@ /*- * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2012, 2013 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Oleksandr Rybalko * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include -#include -#include +#include #include -#include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include -#include -#include -#include -#include -#include -#include - #include #include #include #include #include +#include -#include -#include +#include #include "fb_if.h" #include "mbox_if.h" -#define FB_WIDTH 640 -#define FB_HEIGHT 480 -#define FB_DEPTH 24 +#define FB_DEPTH 24 -struct bcm_fb_config { - uint32_t xres; - uint32_t yres; - uint32_t vxres; - uint32_t vyres; - uint32_t pitch; - uint32_t bpp; - uint32_t xoffset; - uint32_t yoffset; - /* Filled by videocore */ - uint32_t base; - uint32_t screen_size; +struct bcmsc_softc { + struct fb_info info; + int fbswap; + struct bcm2835_fb_config fb; + device_t dev; }; -struct bcmsc_softc { - device_t dev; - struct fb_info *info; - bus_dma_tag_t dma_tag; - bus_dmamap_t dma_map; - struct bcm_fb_config* fb_config; - bus_addr_t fb_config_phys; - struct intr_config_hook init_hook; +static struct ofw_compat_data compat_data[] = { + {"broadcom,bcm2835-fb", 1}, + {"brcm,bcm2708-fb", 1}, + {NULL, 0} }; static int bcm_fb_probe(device_t); static int bcm_fb_attach(device_t); -static void bcm_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, - int err); -static void -bcm_fb_init(void *arg) +static int +bcm_fb_init(struct bcmsc_softc *sc, struct bcm2835_fb_config *fb) { - volatile struct bcm_fb_config *fb_config; - struct bcmsc_softc *sc; - struct fb_info *info; - phandle_t node; - pcell_t cell; - device_t mbox; - device_t fbd; - int err = 0; + int err; - sc = arg; - fb_config = sc->fb_config; - node = ofw_bus_get_node(sc->dev); + err = 0; - fb_config->xres = 0; - fb_config->yres = 0; - fb_config->bpp = 0; - fb_config->vxres = 0; - fb_config->vyres = 0; - fb_config->xoffset = 0; - fb_config->yoffset = 0; - fb_config->base = 0; - fb_config->pitch = 0; - fb_config->screen_size = 0; + memset(fb, 0, sizeof(*fb)); + if (bcm2835_mbox_fb_get_w_h(fb) != 0) + return (ENXIO); + fb->bpp = FB_DEPTH; - if ((OF_getprop(node, "broadcom,width", &cell, sizeof(cell))) > 0) - fb_config->xres = (int)fdt32_to_cpu(cell); - if (fb_config->xres == 0) - fb_config->xres = FB_WIDTH; + fb->vxres = fb->xres; + fb->vyres = fb->yres; + fb->xoffset = fb->yoffset = 0; - if ((OF_getprop(node, "broadcom,height", &cell, sizeof(cell))) > 0) - fb_config->yres = (uint32_t)fdt32_to_cpu(cell); - if (fb_config->yres == 0) - fb_config->yres = FB_HEIGHT; + if ((err = bcm2835_mbox_fb_init(fb)) != 0) { + device_printf(sc->dev, "bcm2835_mbox_fb_init failed, err=%d\n", err); + return (ENXIO); + } - if ((OF_getprop(node, "broadcom,depth", &cell, sizeof(cell))) > 0) - fb_config->bpp = (uint32_t)fdt32_to_cpu(cell); - if (fb_config->bpp == 0) - fb_config->bpp = FB_DEPTH; + return (0); +} - bus_dmamap_sync(sc->dma_tag, sc->dma_map, - BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); +static int +bcm_fb_setup_fbd(struct bcmsc_softc *sc) +{ + struct bcm2835_fb_config fb; + device_t fbd; + int err; - mbox = devclass_get_device(devclass_find("mbox"), 0); - if (mbox) { - MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_FB, sc->fb_config_phys); - MBOX_READ(mbox, BCM2835_MBOX_CHAN_FB, &err); + err = bcm_fb_init(sc, &fb); + if (err) + return (err); + + memset(&sc->info, 0, sizeof(sc->info)); + sc->info.fb_name = device_get_nameunit(sc->dev); + + sc->info.fb_vbase = (intptr_t)pmap_mapdev(fb.base, fb.size); + sc->info.fb_pbase = fb.base; + sc->info.fb_size = fb.size; + sc->info.fb_bpp = sc->info.fb_depth = fb.bpp; + sc->info.fb_stride = fb.pitch; + sc->info.fb_width = fb.xres; + sc->info.fb_height = fb.yres; +#ifdef VM_MEMATTR_WRITE_COMBINING + sc->info.fb_flags = FB_FLAG_MEMATTR; + sc->info.fb_memattr = VM_MEMATTR_WRITE_COMBINING; +#endif + + if (sc->fbswap) { + switch (sc->info.fb_bpp) { + case 24: + vt_generate_cons_palette(sc->info.fb_cmap, + COLOR_FORMAT_RGB, 0xff, 0, 0xff, 8, 0xff, 16); + sc->info.fb_cmsize = 16; + break; + case 32: + vt_generate_cons_palette(sc->info.fb_cmap, + COLOR_FORMAT_RGB, 0xff, 16, 0xff, 8, 0xff, 0); + sc->info.fb_cmsize = 16; + break; + } } - bus_dmamap_sync(sc->dma_tag, sc->dma_map, - BUS_DMASYNC_POSTREAD); - if (fb_config->base != 0) { - device_printf(sc->dev, "%dx%d(%dx%d@%d,%d) %dbpp\n", - fb_config->xres, fb_config->yres, - fb_config->vxres, fb_config->vyres, - fb_config->xoffset, fb_config->yoffset, - fb_config->bpp); + fbd = device_add_child(sc->dev, "fbd", device_get_unit(sc->dev)); + if (fbd == NULL) { + device_printf(sc->dev, "Failed to add fbd child\n"); + pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size); + return (ENXIO); + } else if (device_probe_and_attach(fbd) != 0) { + device_printf(sc->dev, "Failed to attach fbd device\n"); + device_delete_child(sc->dev, fbd); + pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size); + return (ENXIO); + } - device_printf(sc->dev, "pitch %d, base 0x%08x, screen_size %d\n", - fb_config->pitch, fb_config->base, - fb_config->screen_size); + device_printf(sc->dev, "%dx%d(%dx%d@%d,%d) %dbpp\n", fb.xres, fb.yres, + fb.vxres, fb.vyres, fb.xoffset, fb.yoffset, fb.bpp); + device_printf(sc->dev, + "fbswap: %d, pitch %d, base 0x%08x, screen_size %d\n", + sc->fbswap, fb.pitch, fb.base, fb.size); - info = malloc(sizeof(struct fb_info), M_DEVBUF, - M_WAITOK | M_ZERO); - info->fb_name = device_get_nameunit(sc->dev); - info->fb_vbase = (intptr_t)pmap_mapdev(fb_config->base, - fb_config->screen_size); - info->fb_pbase = fb_config->base; - info->fb_size = fb_config->screen_size; - info->fb_bpp = info->fb_depth = fb_config->bpp; - info->fb_stride = fb_config->pitch; - info->fb_width = fb_config->xres; - info->fb_height = fb_config->yres; + return (0); +} - sc->info = info; +static int +bcm_fb_resync_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct bcmsc_softc *sc = arg1; + struct bcm2835_fb_config fb; + int val; + int err; - fbd = device_add_child(sc->dev, "fbd", - device_get_unit(sc->dev)); - if (fbd == NULL) { - device_printf(sc->dev, "Failed to add fbd child\n"); - return; - } - if (device_probe_and_attach(fbd) != 0) { - device_printf(sc->dev, "Failed to attach fbd device\n"); - return; - } - } else { - device_printf(sc->dev, "Failed to set framebuffer info\n"); - return; - } + val = 0; + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || !req->newptr) /* error || read request */ + return (err); - config_intrhook_disestablish(&sc->init_hook); + bcm_fb_init(sc, &fb); + + return (0); } +static void +bcm_fb_sysctl_init(struct bcmsc_softc *sc) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid *tree_node; + struct sysctl_oid_list *tree; + + /* + * Add system sysctl tree/handlers. + */ + ctx = device_get_sysctl_ctx(sc->dev); + tree_node = device_get_sysctl_tree(sc->dev); + tree = SYSCTL_CHILDREN(tree_node); + SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "resync", + CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), + bcm_fb_resync_sysctl, "IU", "Set to resync framebuffer with VC"); +} + static int bcm_fb_probe(device_t dev) { - if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-fb")) + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2835 VT framebuffer driver"); return (BUS_PROBE_DEFAULT); } static int bcm_fb_attach(device_t dev) { - struct bcmsc_softc *sc = device_get_softc(dev); - int dma_size = sizeof(struct bcm_fb_config); + char bootargs[2048], *n, *p, *v; int err; + phandle_t chosen; + struct bcmsc_softc *sc; + sc = device_get_softc(dev); sc->dev = dev; - err = bus_dma_tag_create( - bus_get_dma_tag(sc->dev), - PAGE_SIZE, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - dma_size, 1, /* maxsize, nsegments */ - dma_size, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->dma_tag); + /* Newer firmware versions needs an inverted color palette. */ + sc->fbswap = 0; + chosen = OF_finddevice("/chosen"); + if (chosen != 0 && + OF_getprop(chosen, "bootargs", &bootargs, sizeof(bootargs)) > 0) { + p = bootargs; + while ((v = strsep(&p, " ")) != NULL) { + if (*v == '\0') + continue; + n = strsep(&v, "="); + if (strcmp(n, "bcm2708_fb.fbswap") == 0 && v != NULL) + if (*v == '1') + sc->fbswap = 1; + } + } - err = bus_dmamem_alloc(sc->dma_tag, (void **)&sc->fb_config, 0, - &sc->dma_map); - if (err) { - device_printf(dev, "cannot allocate framebuffer\n"); - goto fail; - } + bcm_fb_sysctl_init(sc); - err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->fb_config, - dma_size, bcm_fb_dmamap_cb, &sc->fb_config_phys, BUS_DMA_NOWAIT); + err = bcm_fb_setup_fbd(sc); + if (err) + return (err); - if (err) { - device_printf(dev, "cannot load DMA map\n"); - goto fail; - } - - /* - * We have to wait until interrupts are enabled. - * Mailbox relies on it to get data from VideoCore - */ - sc->init_hook.ich_func = bcm_fb_init; - sc->init_hook.ich_arg = sc; - - if (config_intrhook_establish(&sc->init_hook) != 0) { - device_printf(dev, "failed to establish intrhook\n"); - return (ENOMEM); - } - return (0); - -fail: - return (ENXIO); } -static void -bcm_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) -{ - bus_addr_t *addr; - - if (err) - return; - - addr = (bus_addr_t*)arg; - *addr = PHYS_TO_VCBUS(segs[0].ds_addr); -} - static struct fb_info * bcm_fb_helper_getinfo(device_t dev) { struct bcmsc_softc *sc; sc = device_get_softc(dev); - return (sc->info); + return (&sc->info); } static device_method_t bcm_fb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_fb_probe), DEVMETHOD(device_attach, bcm_fb_attach), /* Framebuffer service methods */ DEVMETHOD(fb_getinfo, bcm_fb_helper_getinfo), DEVMETHOD_END }; static devclass_t bcm_fb_devclass; static driver_t bcm_fb_driver = { "fb", bcm_fb_methods, sizeof(struct bcmsc_softc), }; DRIVER_MODULE(bcm2835fb, ofwbus, bcm_fb_driver, bcm_fb_devclass, 0, 0); +DRIVER_MODULE(bcm2835fb, simplebus, bcm_fb_driver, bcm_fb_devclass, 0, 0); Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_gpio.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_gpio.c (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_gpio.c (revision 322724) @@ -1,794 +1,786 @@ /*- * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2012 Luiz Otavio O Souza. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #ifdef DEBUG #define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) #else #define dprintf(fmt, args...) #endif #define BCM_GPIO_PINS 54 #define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) struct bcm_gpio_sysctl { struct bcm_gpio_softc *sc; uint32_t pin; }; struct bcm_gpio_softc { device_t sc_dev; struct mtx sc_mtx; struct resource * sc_mem_res; struct resource * sc_irq_res; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; void * sc_intrhand; int sc_gpio_npins; int sc_ro_npins; int sc_ro_pins[BCM_GPIO_PINS]; struct gpio_pin sc_gpio_pins[BCM_GPIO_PINS]; struct bcm_gpio_sysctl sc_sysctl[BCM_GPIO_PINS]; }; enum bcm_gpio_pud { BCM_GPIO_NONE, BCM_GPIO_PULLDOWN, BCM_GPIO_PULLUP, }; #define BCM_GPIO_LOCK(_sc) mtx_lock(&_sc->sc_mtx) #define BCM_GPIO_UNLOCK(_sc) mtx_unlock(&_sc->sc_mtx) #define BCM_GPIO_LOCK_ASSERT(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED) #define BCM_GPIO_GPFSEL(_bank) 0x00 + _bank * 4 #define BCM_GPIO_GPSET(_bank) 0x1c + _bank * 4 #define BCM_GPIO_GPCLR(_bank) 0x28 + _bank * 4 #define BCM_GPIO_GPLEV(_bank) 0x34 + _bank * 4 #define BCM_GPIO_GPPUD(_bank) 0x94 #define BCM_GPIO_GPPUDCLK(_bank) 0x98 + _bank * 4 #define BCM_GPIO_WRITE(_sc, _off, _val) \ bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) #define BCM_GPIO_READ(_sc, _off) \ bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off) +static struct ofw_compat_data compat_data[] = { + {"broadcom,bcm2835-gpio", 1}, + {"brcm,bcm2835-gpio", 1}, + {NULL, 0} +}; + static int bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin) { int i; for (i = 0; i < sc->sc_ro_npins; i++) if (pin == sc->sc_ro_pins[i]) return (1); return (0); } static uint32_t bcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin) { uint32_t bank, func, offset; /* Five banks, 10 pins per bank, 3 bits per pin. */ bank = pin / 10; offset = (pin - bank * 10) * 3; BCM_GPIO_LOCK(sc); func = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7; BCM_GPIO_UNLOCK(sc); return (func); } static void bcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize) { switch (nfunc) { case BCM_GPIO_INPUT: strncpy(buf, "input", bufsize); break; case BCM_GPIO_OUTPUT: strncpy(buf, "output", bufsize); break; case BCM_GPIO_ALT0: strncpy(buf, "alt0", bufsize); break; case BCM_GPIO_ALT1: strncpy(buf, "alt1", bufsize); break; case BCM_GPIO_ALT2: strncpy(buf, "alt2", bufsize); break; case BCM_GPIO_ALT3: strncpy(buf, "alt3", bufsize); break; case BCM_GPIO_ALT4: strncpy(buf, "alt4", bufsize); break; case BCM_GPIO_ALT5: strncpy(buf, "alt5", bufsize); break; default: strncpy(buf, "invalid", bufsize); } } static int bcm_gpio_str_func(char *func, uint32_t *nfunc) { if (strcasecmp(func, "input") == 0) *nfunc = BCM_GPIO_INPUT; else if (strcasecmp(func, "output") == 0) *nfunc = BCM_GPIO_OUTPUT; else if (strcasecmp(func, "alt0") == 0) *nfunc = BCM_GPIO_ALT0; else if (strcasecmp(func, "alt1") == 0) *nfunc = BCM_GPIO_ALT1; else if (strcasecmp(func, "alt2") == 0) *nfunc = BCM_GPIO_ALT2; else if (strcasecmp(func, "alt3") == 0) *nfunc = BCM_GPIO_ALT3; else if (strcasecmp(func, "alt4") == 0) *nfunc = BCM_GPIO_ALT4; else if (strcasecmp(func, "alt5") == 0) *nfunc = BCM_GPIO_ALT5; else return (-1); return (0); } static uint32_t bcm_gpio_func_flag(uint32_t nfunc) { switch (nfunc) { case BCM_GPIO_INPUT: return (GPIO_PIN_INPUT); case BCM_GPIO_OUTPUT: return (GPIO_PIN_OUTPUT); } return (0); } static void bcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f) { uint32_t bank, data, offset; /* Must be called with lock held. */ BCM_GPIO_LOCK_ASSERT(sc); /* Five banks, 10 pins per bank, 3 bits per pin. */ bank = pin / 10; offset = (pin - bank * 10) * 3; data = BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)); data &= ~(7 << offset); data |= (f << offset); BCM_GPIO_WRITE(sc, BCM_GPIO_GPFSEL(bank), data); } static void bcm_gpio_set_pud(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t state) { uint32_t bank, offset; /* Must be called with lock held. */ BCM_GPIO_LOCK_ASSERT(sc); bank = pin / 32; offset = pin - 32 * bank; BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state); DELAY(10); BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), (1 << offset)); DELAY(10); BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0); BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0); } void bcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc) { struct bcm_gpio_softc *sc; int i; sc = device_get_softc(dev); BCM_GPIO_LOCK(sc); /* Disable pull-up or pull-down on pin. */ bcm_gpio_set_pud(sc, pin, BCM_GPIO_NONE); /* And now set the pin function. */ bcm_gpio_set_function(sc, pin, nfunc); /* Update the pin flags. */ for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i < sc->sc_gpio_npins) sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc); BCM_GPIO_UNLOCK(sc); } static void bcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { BCM_GPIO_LOCK(sc); /* * Manage input/output. */ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; bcm_gpio_set_function(sc, pin->gp_pin, BCM_GPIO_OUTPUT); } else { pin->gp_flags |= GPIO_PIN_INPUT; bcm_gpio_set_function(sc, pin->gp_pin, BCM_GPIO_INPUT); } } /* Manage Pull-up/pull-down. */ pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN); if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) { if (flags & GPIO_PIN_PULLUP) { pin->gp_flags |= GPIO_PIN_PULLUP; bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLUP); } else { pin->gp_flags |= GPIO_PIN_PULLDOWN; bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLDOWN); } } else bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE); BCM_GPIO_UNLOCK(sc); } static int bcm_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = BCM_GPIO_PINS - 1; return (0); } static int bcm_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct bcm_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); BCM_GPIO_LOCK(sc); *caps = sc->sc_gpio_pins[i].gp_caps; BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct bcm_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); BCM_GPIO_LOCK(sc); *flags = sc->sc_gpio_pins[i].gp_flags; BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct bcm_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); BCM_GPIO_LOCK(sc); memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct bcm_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); /* We never touch on read-only/reserved pins. */ if (bcm_gpio_pin_is_ro(sc, pin)) return (EINVAL); bcm_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); return (0); } static int bcm_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct bcm_gpio_softc *sc = device_get_softc(dev); uint32_t bank, offset; int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); /* We never write to read-only/reserved pins. */ if (bcm_gpio_pin_is_ro(sc, pin)) return (EINVAL); bank = pin / 32; offset = pin - 32 * bank; BCM_GPIO_LOCK(sc); if (value) BCM_GPIO_WRITE(sc, BCM_GPIO_GPSET(bank), (1 << offset)); else BCM_GPIO_WRITE(sc, BCM_GPIO_GPCLR(bank), (1 << offset)); BCM_GPIO_UNLOCK(sc); return (0); } static int bcm_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct bcm_gpio_softc *sc = device_get_softc(dev); uint32_t bank, offset, reg_data; int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); bank = pin / 32; offset = pin - 32 * bank; BCM_GPIO_LOCK(sc); reg_data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); BCM_GPIO_UNLOCK(sc); *val = (reg_data & (1 << offset)) ? 1 : 0; return (0); } static int bcm_gpio_pin_toggle(device_t dev, uint32_t pin) { struct bcm_gpio_softc *sc = device_get_softc(dev); uint32_t bank, data, offset; int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); /* We never write to read-only/reserved pins. */ if (bcm_gpio_pin_is_ro(sc, pin)) return (EINVAL); bank = pin / 32; offset = pin - 32 * bank; BCM_GPIO_LOCK(sc); data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); if (data & (1 << offset)) BCM_GPIO_WRITE(sc, BCM_GPIO_GPCLR(bank), (1 << offset)); else BCM_GPIO_WRITE(sc, BCM_GPIO_GPSET(bank), (1 << offset)); BCM_GPIO_UNLOCK(sc); return (0); } static int -bcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc) -{ - int i, len; - pcell_t pins[BCM_GPIO_PINS]; - phandle_t gpio; - - /* Find the gpio node to start. */ - gpio = ofw_bus_get_node(sc->sc_dev); - - len = OF_getproplen(gpio, "broadcom,read-only"); - if (len < 0 || len > sizeof(pins)) - return (-1); - - if (OF_getprop(gpio, "broadcom,read-only", &pins, len) < 0) - return (-1); - - sc->sc_ro_npins = len / sizeof(pcell_t); - - device_printf(sc->sc_dev, "read-only pins: "); - for (i = 0; i < sc->sc_ro_npins; i++) { - sc->sc_ro_pins[i] = fdt32_to_cpu(pins[i]); - if (i > 0) - printf(","); - printf("%d", sc->sc_ro_pins[i]); - } - if (i > 0) - printf("."); - printf("\n"); - - return (0); -} - -static int bcm_gpio_func_proc(SYSCTL_HANDLER_ARGS) { char buf[16]; struct bcm_gpio_softc *sc; struct bcm_gpio_sysctl *sc_sysctl; uint32_t nfunc; int error; sc_sysctl = arg1; sc = sc_sysctl->sc; /* Get the current pin function. */ nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin); bcm_gpio_func_str(nfunc, buf, sizeof(buf)); error = sysctl_handle_string(oidp, buf, sizeof(buf), req); if (error != 0 || req->newptr == NULL) return (error); - + /* Ignore changes on read-only pins. */ + if (bcm_gpio_pin_is_ro(sc, sc_sysctl->pin)) + return (0); /* Parse the user supplied string and check for a valid pin function. */ if (bcm_gpio_str_func(buf, &nfunc) != 0) return (EINVAL); /* Update the pin alternate function. */ bcm_gpio_set_alternate(sc->sc_dev, sc_sysctl->pin, nfunc); return (0); } static void bcm_gpio_sysctl_init(struct bcm_gpio_softc *sc) { char pinbuf[3]; struct bcm_gpio_sysctl *sc_sysctl; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree_node, *pin_node, *pinN_node; struct sysctl_oid_list *tree, *pin_tree, *pinN_tree; int i; /* * Add per-pin sysctl tree/handlers. */ ctx = device_get_sysctl_ctx(sc->sc_dev); tree_node = device_get_sysctl_tree(sc->sc_dev); tree = SYSCTL_CHILDREN(tree_node); pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin", CTLFLAG_RD, NULL, "GPIO Pins"); pin_tree = SYSCTL_CHILDREN(pin_node); for (i = 0; i < sc->sc_gpio_npins; i++) { snprintf(pinbuf, sizeof(pinbuf), "%d", i); pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf, CTLFLAG_RD, NULL, "GPIO Pin"); pinN_tree = SYSCTL_CHILDREN(pinN_node); sc->sc_sysctl[i].sc = sc; sc_sysctl = &sc->sc_sysctl[i]; sc_sysctl->sc = sc; sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin; SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function", CTLFLAG_RW | CTLTYPE_STRING, sc_sysctl, sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc, "A", "Pin Function"); } } static int -bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc) +bcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc, phandle_t node, + const char *propname, const char *label) { - int i, j, len, npins; - pcell_t pins[BCM_GPIO_PINS]; - phandle_t gpio, node, reserved; - char name[32]; + int i, need_comma, npins, range_start, range_stop; + pcell_t *pins; - /* Get read-only pins. */ - if (bcm_gpio_get_ro_pins(sc) != 0) + /* Get the property data. */ + npins = OF_getencprop_alloc(node, propname, sizeof(*pins), + (void **)&pins); + if (npins < 0) return (-1); + if (npins == 0) { + free(pins, M_OFWPROP); + return (0); + } + for (i = 0; i < npins; i++) + sc->sc_ro_pins[i + sc->sc_ro_npins] = pins[i]; + sc->sc_ro_npins += npins; + need_comma = 0; + device_printf(sc->sc_dev, "%s pins: ", label); + range_start = range_stop = pins[0]; + for (i = 1; i < npins; i++) { + if (pins[i] != range_stop + 1) { + if (need_comma) + printf(","); + if (range_start != range_stop) + printf("%d-%d", range_start, range_stop); + else + printf("%d", range_start); + range_start = range_stop = pins[i]; + need_comma = 1; + } else + range_stop++; + } + if (need_comma) + printf(","); + if (range_start != range_stop) + printf("%d-%d.\n", range_start, range_stop); + else + printf("%d.\n", range_start); + free(pins, M_OFWPROP); - /* Find the gpio/reserved pins node to start. */ + return (0); +} + +static int +bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc) +{ + char *name; + phandle_t gpio, node, reserved; + ssize_t len; + + /* Get read-only pins if they're provided */ gpio = ofw_bus_get_node(sc->sc_dev); - node = OF_child(gpio); - - /* - * Find reserved node - */ + if (bcm_gpio_get_ro_pins(sc, gpio, "broadcom,read-only", + "read-only") != 0) + return (0); + /* Traverse the GPIO subnodes to find the reserved pins node. */ reserved = 0; + node = OF_child(gpio); while ((node != 0) && (reserved == 0)) { - len = OF_getprop(node, "name", name, - sizeof(name) - 1); - name[len] = 0; + len = OF_getprop_alloc(node, "name", 1, (void **)&name); + if (len == -1) + return (-1); if (strcmp(name, "reserved") == 0) reserved = node; + free(name, M_OFWPROP); node = OF_peer(node); } - if (reserved == 0) return (-1); - /* Get the reserved pins. */ - len = OF_getproplen(reserved, "broadcom,pins"); - if (len < 0 || len > sizeof(pins)) + if (bcm_gpio_get_ro_pins(sc, reserved, "broadcom,pins", + "reserved") != 0) return (-1); - if (OF_getprop(reserved, "broadcom,pins", &pins, len) < 0) - return (-1); - - npins = len / sizeof(pcell_t); - - j = 0; - device_printf(sc->sc_dev, "reserved pins: "); - for (i = 0; i < npins; i++) { - if (i > 0) - printf(","); - printf("%d", fdt32_to_cpu(pins[i])); - /* Some pins maybe already on the list of read-only pins. */ - if (bcm_gpio_pin_is_ro(sc, fdt32_to_cpu(pins[i]))) - continue; - sc->sc_ro_pins[j++ + sc->sc_ro_npins] = fdt32_to_cpu(pins[i]); - } - sc->sc_ro_npins += j; - if (i > 0) - printf("."); - printf("\n"); - return (0); } static int bcm_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-gpio")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2708/2835 GPIO controller"); return (BUS_PROBE_DEFAULT); } static int bcm_gpio_attach(device_t dev) { struct bcm_gpio_softc *sc = device_get_softc(dev); uint32_t func; int i, j, rid; phandle_t gpio; sc->sc_dev = dev; mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_DEF); rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate interrupt\n"); return (ENXIO); } /* Find our node. */ gpio = ofw_bus_get_node(sc->sc_dev); if (!OF_hasprop(gpio, "gpio-controller")) /* Node is not a GPIO controller. */ goto fail; /* * Find the read-only pins. These are pins we never touch or bad * things could happen. */ if (bcm_gpio_get_reserved_pins(sc) == -1) goto fail; /* Initialize the software controlled pins. */ for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) { - if (bcm_gpio_pin_is_ro(sc, j)) - continue; snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, "pin %d", j); func = bcm_gpio_get_function(sc, j); sc->sc_gpio_pins[i].gp_pin = j; sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS; sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func); i++; } sc->sc_gpio_npins = i; bcm_gpio_sysctl_init(sc); device_add_child(dev, "gpioc", -1); device_add_child(dev, "gpiobus", -1); return (bus_generic_attach(dev)); fail: if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (ENXIO); } static int bcm_gpio_detach(device_t dev) { return (EBUSY); } static phandle_t bcm_gpio_get_node(device_t bus, device_t dev) { /* We only have one child, the GPIO bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t bcm_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_gpio_probe), DEVMETHOD(device_attach, bcm_gpio_attach), DEVMETHOD(device_detach, bcm_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_pin_max, bcm_gpio_pin_max), DEVMETHOD(gpio_pin_getname, bcm_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, bcm_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, bcm_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, bcm_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, bcm_gpio_pin_get), DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node), DEVMETHOD_END }; static devclass_t bcm_gpio_devclass; static driver_t bcm_gpio_driver = { "gpio", bcm_gpio_methods, sizeof(struct bcm_gpio_softc), }; DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0); Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_intr.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_intr.c (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_intr.c (revision 322724) @@ -1,207 +1,242 @@ /*- * Copyright (c) 2012 Damjan Marion * All rights reserved. * * Based on OMAP3 INTC code by Ben Gray * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include +#ifdef SOC_BCM2836 +#include +#endif + #define INTC_PENDING_BASIC 0x00 #define INTC_PENDING_BANK1 0x04 #define INTC_PENDING_BANK2 0x08 #define INTC_FIQ_CONTROL 0x0C #define INTC_ENABLE_BANK1 0x10 #define INTC_ENABLE_BANK2 0x14 #define INTC_ENABLE_BASIC 0x18 #define INTC_DISABLE_BANK1 0x1C #define INTC_DISABLE_BANK2 0x20 #define INTC_DISABLE_BASIC 0x24 #define BANK1_START 8 #define BANK1_END (BANK1_START + 32 - 1) #define BANK2_START (BANK1_START + 32) #define BANK2_END (BANK2_START + 32 - 1) +#define BANK3_START (BANK2_START + 32) +#define BANK3_END (BANK3_START + 32 - 1) #define IS_IRQ_BASIC(n) (((n) >= 0) && ((n) < BANK1_START)) #define IS_IRQ_BANK1(n) (((n) >= BANK1_START) && ((n) <= BANK1_END)) #define IS_IRQ_BANK2(n) (((n) >= BANK2_START) && ((n) <= BANK2_END)) +#define ID_IRQ_BCM2836(n) (((n) >= BANK3_START) && ((n) <= BANK3_END)) #define IRQ_BANK1(n) ((n) - BANK1_START) #define IRQ_BANK2(n) ((n) - BANK2_START) #ifdef DEBUG #define dprintf(fmt, args...) printf(fmt, ##args) #else #define dprintf(fmt, args...) #endif struct bcm_intc_softc { device_t sc_dev; struct resource * intc_res; bus_space_tag_t intc_bst; bus_space_handle_t intc_bsh; }; static struct bcm_intc_softc *bcm_intc_sc = NULL; -#define intc_read_4(reg) \ - bus_space_read_4(bcm_intc_sc->intc_bst, bcm_intc_sc->intc_bsh, reg) -#define intc_write_4(reg, val) \ - bus_space_write_4(bcm_intc_sc->intc_bst, bcm_intc_sc->intc_bsh, reg, val) +#define intc_read_4(_sc, reg) \ + bus_space_read_4((_sc)->intc_bst, (_sc)->intc_bsh, (reg)) +#define intc_write_4(_sc, reg, val) \ + bus_space_write_4((_sc)->intc_bst, (_sc)->intc_bsh, (reg), (val)) static int bcm_intc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-armctrl-ic")) + if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-armctrl-ic") && + !ofw_bus_is_compatible(dev, "brcm,bcm2836-armctrl-ic")) return (ENXIO); device_set_desc(dev, "BCM2835 Interrupt Controller"); return (BUS_PROBE_DEFAULT); } static int bcm_intc_attach(device_t dev) { struct bcm_intc_softc *sc = device_get_softc(dev); int rid = 0; sc->sc_dev = dev; if (bcm_intc_sc) return (ENXIO); sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->intc_res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } sc->intc_bst = rman_get_bustag(sc->intc_res); sc->intc_bsh = rman_get_bushandle(sc->intc_res); bcm_intc_sc = sc; return (0); } static device_method_t bcm_intc_methods[] = { DEVMETHOD(device_probe, bcm_intc_probe), DEVMETHOD(device_attach, bcm_intc_attach), { 0, 0 } }; static driver_t bcm_intc_driver = { "intc", bcm_intc_methods, sizeof(struct bcm_intc_softc), }; static devclass_t bcm_intc_devclass; -DRIVER_MODULE(intc, simplebus, bcm_intc_driver, bcm_intc_devclass, 0, 0); +EARLY_DRIVER_MODULE(intc, simplebus, bcm_intc_driver, bcm_intc_devclass, + 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); int arm_get_next_irq(int last_irq) { + struct bcm_intc_softc *sc = bcm_intc_sc; uint32_t pending; int32_t irq = last_irq + 1; +#ifdef SOC_BCM2836 + int ret; +#endif /* Sanity check */ if (irq < 0) irq = 0; - + +#ifdef SOC_BCM2836 + if ((ret = bcm2836_get_next_irq(irq)) >= 0) + return (ret + BANK3_START); +#endif + /* TODO: should we mask last_irq? */ - pending = intc_read_4(INTC_PENDING_BASIC); - while (irq < BANK1_START) { - if (pending & (1 << irq)) - return irq; - irq++; + if (irq < BANK1_START) { + pending = intc_read_4(sc, INTC_PENDING_BASIC); + if ((pending & 0xFF) == 0) { + irq = BANK1_START; /* skip to next bank */ + } else do { + if (pending & (1 << irq)) + return irq; + irq++; + } while (irq < BANK1_START); } - - pending = intc_read_4(INTC_PENDING_BANK1); - while (irq < BANK2_START) { - if (pending & (1 << IRQ_BANK1(irq))) - return irq; - irq++; + if (irq < BANK2_START) { + pending = intc_read_4(sc, INTC_PENDING_BANK1); + if (pending == 0) { + irq = BANK2_START; /* skip to next bank */ + } else do { + if (pending & (1 << IRQ_BANK1(irq))) + return irq; + irq++; + } while (irq < BANK2_START); } - - pending = intc_read_4(INTC_PENDING_BANK2); - while (irq <= BANK2_END) { - if (pending & (1 << IRQ_BANK2(irq))) - return irq; - irq++; + if (irq < BANK3_START) { + pending = intc_read_4(sc, INTC_PENDING_BANK2); + if (pending != 0) do { + if (pending & (1 << IRQ_BANK2(irq))) + return irq; + irq++; + } while (irq < BANK3_START); } - return (-1); } void arm_mask_irq(uintptr_t nb) { + struct bcm_intc_softc *sc = bcm_intc_sc; dprintf("%s: %d\n", __func__, nb); if (IS_IRQ_BASIC(nb)) - intc_write_4(INTC_DISABLE_BASIC, (1 << nb)); + intc_write_4(sc, INTC_DISABLE_BASIC, (1 << nb)); else if (IS_IRQ_BANK1(nb)) - intc_write_4(INTC_DISABLE_BANK1, (1 << IRQ_BANK1(nb))); + intc_write_4(sc, INTC_DISABLE_BANK1, (1 << IRQ_BANK1(nb))); else if (IS_IRQ_BANK2(nb)) - intc_write_4(INTC_DISABLE_BANK2, (1 << IRQ_BANK2(nb))); + intc_write_4(sc, INTC_DISABLE_BANK2, (1 << IRQ_BANK2(nb))); +#ifdef SOC_BCM2836 + else if (ID_IRQ_BCM2836(nb)) + bcm2836_mask_irq(nb - BANK3_START); +#endif else printf("arm_mask_irq: Invalid IRQ number: %d\n", nb); } void arm_unmask_irq(uintptr_t nb) { + struct bcm_intc_softc *sc = bcm_intc_sc; dprintf("%s: %d\n", __func__, nb); if (IS_IRQ_BASIC(nb)) - intc_write_4(INTC_ENABLE_BASIC, (1 << nb)); + intc_write_4(sc, INTC_ENABLE_BASIC, (1 << nb)); else if (IS_IRQ_BANK1(nb)) - intc_write_4(INTC_ENABLE_BANK1, (1 << IRQ_BANK1(nb))); + intc_write_4(sc, INTC_ENABLE_BANK1, (1 << IRQ_BANK1(nb))); else if (IS_IRQ_BANK2(nb)) - intc_write_4(INTC_ENABLE_BANK2, (1 << IRQ_BANK2(nb))); + intc_write_4(sc, INTC_ENABLE_BANK2, (1 << IRQ_BANK2(nb))); +#ifdef SOC_BCM2836 + else if (ID_IRQ_BCM2836(nb)) + bcm2836_unmask_irq(nb - BANK3_START); +#endif else printf("arm_mask_irq: Invalid IRQ number: %d\n", nb); } Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_machdep.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_machdep.c (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_machdep.c (revision 322724) @@ -1,132 +1,144 @@ /*- * Copyright (c) 2012 Oleksandr Tymoshenko. * Copyright (c) 1994-1998 Mark Brinicombe. * Copyright (c) 1994 Brini. * All rights reserved. * * This code is derived from software written for Brini by Mark Brinicombe * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Brini. * 4. The name of the company nor the name of the author may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: FreeBSD: //depot/projects/arm/src/sys/arm/at91/kb920x_machdep.c, rev 45 */ #include "opt_ddb.h" #include "opt_platform.h" #include "opt_global.h" #include __FBSDID("$FreeBSD$"); #define _ARM32_BUS_DMA_PRIVATE #include #include #include #include #include #include #include #include #include #include vm_offset_t initarm_lastaddr(void) { return (arm_devmap_lastaddr()); } void initarm_early_init(void) { } void initarm_gpio_init(void) { } void initarm_late_init(void) { phandle_t system; pcell_t cells[2]; int len; system = OF_finddevice("/system"); if (system != 0) { len = OF_getprop(system, "linux,serial", &cells, sizeof(cells)); if (len > 0) board_set_serial(fdt64_to_cpu(*((uint64_t *)cells))); len = OF_getprop(system, "linux,revision", &cells, sizeof(cells)); if (len > 0) board_set_revision(fdt32_to_cpu(*((uint32_t *)cells))); } } +#ifdef SOC_BCM2835 /* * Set up static device mappings. * All on-chip peripherals exist in a 16MB range starting at 0x20000000. * Map the entire range using 1MB section mappings. */ int initarm_devmap_init(void) { arm_devmap_add_entry(0x20000000, 0x01000000); return (0); } +#endif + +#ifdef SOC_BCM2836 +static int +initarm_devmap_init(void) +{ + + arm_devmap_add_entry(0x3f000000, 0x01000000); + return (0); +} +#endif struct arm32_dma_range * bus_dma_get_range(void) { return (NULL); } int bus_dma_get_range_nb(void) { return (0); } void cpu_reset() { bcmwd_watchdog_reset(); while (1); } Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_mbox.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_mbox.c (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_mbox.c (revision 322724) @@ -1,243 +1,536 @@ /*- * Copyright (c) 2012 Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include +#include #include -#include #include #include #include #include +#include +#include #include "mbox_if.h" #define REG_READ 0x00 #define REG_POL 0x10 #define REG_SENDER 0x14 #define REG_STATUS 0x18 #define STATUS_FULL 0x80000000 #define STATUS_EMPTY 0x40000000 #define REG_CONFIG 0x1C #define CONFIG_DATA_IRQ 0x00000001 #define REG_WRITE 0x20 /* This is Mailbox 1 address */ #define MBOX_MSG(chan, data) (((data) & ~0xf) | ((chan) & 0xf)) #define MBOX_CHAN(msg) ((msg) & 0xf) #define MBOX_DATA(msg) ((msg) & ~0xf) #define MBOX_LOCK(sc) do { \ mtx_lock(&(sc)->lock); \ } while(0) #define MBOX_UNLOCK(sc) do { \ mtx_unlock(&(sc)->lock); \ } while(0) #ifdef DEBUG #define dprintf(fmt, args...) printf(fmt, ##args) #else #define dprintf(fmt, args...) #endif struct bcm_mbox_softc { struct mtx lock; struct resource * mem_res; struct resource * irq_res; void* intr_hl; bus_space_tag_t bst; bus_space_handle_t bsh; int msg[BCM2835_MBOX_CHANS]; - struct sema sema[BCM2835_MBOX_CHANS]; + int have_message[BCM2835_MBOX_CHANS]; + struct sx property_chan_lock; }; #define mbox_read_4(sc, reg) \ bus_space_read_4((sc)->bst, (sc)->bsh, reg) #define mbox_write_4(sc, reg, val) \ bus_space_write_4((sc)->bst, (sc)->bsh, reg, val) +static struct ofw_compat_data compat_data[] = { + {"broadcom,bcm2835-mbox", 1}, + {"brcm,bcm2835-mbox", 1}, + {NULL, 0} +}; + +static int +bcm_mbox_read_msg(struct bcm_mbox_softc *sc, int *ochan) +{ + uint32_t data; + uint32_t msg; + int chan; + + msg = mbox_read_4(sc, REG_READ); + dprintf("bcm_mbox_intr: raw data %08x\n", msg); + chan = MBOX_CHAN(msg); + data = MBOX_DATA(msg); + if (sc->msg[chan]) { + printf("bcm_mbox_intr: channel %d oveflow\n", chan); + return (1); + } + dprintf("bcm_mbox_intr: chan %d, data %08x\n", chan, data); + sc->msg[chan] = msg; + + if (ochan != NULL) + *ochan = chan; + + return (0); +} + static void bcm_mbox_intr(void *arg) { struct bcm_mbox_softc *sc = arg; int chan; - uint32_t data; - uint32_t msg; - while (!(mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY)) { - msg = mbox_read_4(sc, REG_READ); - dprintf("bcm_mbox_intr: raw data %08x\n", msg); - chan = MBOX_CHAN(msg); - data = MBOX_DATA(msg); - if (sc->msg[chan]) { - printf("bcm_mbox_intr: channel %d oveflow\n", chan); - continue; + MBOX_LOCK(sc); + while (!(mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY)) + if (bcm_mbox_read_msg(sc, &chan) == 0) { + sc->have_message[chan] = 1; + wakeup(&sc->have_message[chan]); } - dprintf("bcm_mbox_intr: chan %d, data %08x\n", chan, data); - sc->msg[chan] = msg; - sema_post(&sc->sema[chan]); - } + MBOX_UNLOCK(sc); } static int bcm_mbox_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (ofw_bus_is_compatible(dev, "broadcom,bcm2835-mbox")) { - device_set_desc(dev, "BCM2835 VideoCore Mailbox"); - return(BUS_PROBE_DEFAULT); - } + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); - return (ENXIO); + device_set_desc(dev, "BCM2835 VideoCore Mailbox"); + + return (BUS_PROBE_DEFAULT); } static int bcm_mbox_attach(device_t dev) { struct bcm_mbox_softc *sc = device_get_softc(dev); int i; int rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->mem_res); sc->bsh = rman_get_bushandle(sc->mem_res); rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "could not allocate interrupt resource\n"); return (ENXIO); } /* Setup and enable the timer */ if (bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE | INTR_TYPE_MISC, NULL, bcm_mbox_intr, sc, &sc->intr_hl) != 0) { bus_release_resource(dev, SYS_RES_IRQ, rid, sc->irq_res); device_printf(dev, "Unable to setup the clock irq handler.\n"); return (ENXIO); } mtx_init(&sc->lock, "vcio mbox", NULL, MTX_DEF); for (i = 0; i < BCM2835_MBOX_CHANS; i++) { sc->msg[i] = 0; - sema_init(&sc->sema[i], 0, "mbox"); + sc->have_message[i] = 0; } + sx_init(&sc->property_chan_lock, "mboxprop"); + /* Read all pending messages */ while ((mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY) == 0) (void)mbox_read_4(sc, REG_READ); mbox_write_4(sc, REG_CONFIG, CONFIG_DATA_IRQ); return (0); } /* * Mailbox API */ static int bcm_mbox_write(device_t dev, int chan, uint32_t data) { int limit = 1000; struct bcm_mbox_softc *sc = device_get_softc(dev); dprintf("bcm_mbox_write: chan %d, data %08x\n", chan, data); MBOX_LOCK(sc); + sc->have_message[chan] = 0; while ((mbox_read_4(sc, REG_STATUS) & STATUS_FULL) && --limit) DELAY(5); if (limit == 0) { printf("bcm_mbox_write: STATUS_FULL stuck"); MBOX_UNLOCK(sc); return (EAGAIN); } mbox_write_4(sc, REG_WRITE, MBOX_MSG(chan, data)); MBOX_UNLOCK(sc); return (0); } static int bcm_mbox_read(device_t dev, int chan, uint32_t *data) { struct bcm_mbox_softc *sc = device_get_softc(dev); + int err, read_chan; dprintf("bcm_mbox_read: chan %d\n", chan); + + err = 0; MBOX_LOCK(sc); - while (sema_trywait(&sc->sema[chan]) == 0) { - /* do not unlock sc while waiting for the mbox */ - if (sema_timedwait(&sc->sema[chan], 10*hz) == 0) - break; - printf("timeout sema for chan %d\n", chan); + if (!cold) { + if (sc->have_message[chan] == 0) { + if (mtx_sleep(&sc->have_message[chan], &sc->lock, 0, + "mbox", 10*hz) != 0) { + device_printf(dev, "timeout waiting for message on chan %d\n", chan); + err = ETIMEDOUT; + } + } + } else { + do { + /* Wait for a message */ + while ((mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY)) + ; + /* Read the message */ + if (bcm_mbox_read_msg(sc, &read_chan) != 0) { + err = EINVAL; + goto out; + } + } while (read_chan != chan); } /* * get data from intr handler, the same channel is never coming * because of holding sc lock. */ *data = MBOX_DATA(sc->msg[chan]); sc->msg[chan] = 0; + sc->have_message[chan] = 0; +out: MBOX_UNLOCK(sc); dprintf("bcm_mbox_read: chan %d, data %08x\n", chan, *data); - return (0); + return (err); } static device_method_t bcm_mbox_methods[] = { DEVMETHOD(device_probe, bcm_mbox_probe), DEVMETHOD(device_attach, bcm_mbox_attach), DEVMETHOD(mbox_read, bcm_mbox_read), DEVMETHOD(mbox_write, bcm_mbox_write), DEVMETHOD_END }; static driver_t bcm_mbox_driver = { "mbox", bcm_mbox_methods, sizeof(struct bcm_mbox_softc), }; static devclass_t bcm_mbox_devclass; DRIVER_MODULE(mbox, simplebus, bcm_mbox_driver, bcm_mbox_devclass, 0, 0); + +static void +bcm2835_mbox_dma_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) +{ + bus_addr_t *addr; + + if (err) + return; + addr = (bus_addr_t *)arg; + *addr = PHYS_TO_VCBUS(segs[0].ds_addr); +} + +static void * +bcm2835_mbox_init_dma(device_t dev, size_t len, bus_dma_tag_t *tag, + bus_dmamap_t *map, bus_addr_t *phys) +{ + void *buf; + int err; + + err = bus_dma_tag_create(bus_get_dma_tag(dev), 16, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + len, 1, len, 0, NULL, NULL, tag); + if (err != 0) { + device_printf(dev, "can't create DMA tag\n"); + return (NULL); + } + + err = bus_dmamem_alloc(*tag, &buf, 0, map); + if (err != 0) { + bus_dma_tag_destroy(*tag); + device_printf(dev, "can't allocate dmamem\n"); + return (NULL); + } + + err = bus_dmamap_load(*tag, *map, buf, len, bcm2835_mbox_dma_cb, + phys, 0); + if (err != 0) { + bus_dmamem_free(*tag, buf, *map); + bus_dma_tag_destroy(*tag); + device_printf(dev, "can't load DMA map\n"); + return (NULL); + } + + return (buf); +} + +static int +bcm2835_mbox_err(device_t dev, bus_addr_t msg_phys, uint32_t resp_phys, + struct bcm2835_mbox_hdr *msg, size_t len) +{ + int idx; + struct bcm2835_mbox_tag_hdr *tag; + uint8_t *last; + + if ((uint32_t)msg_phys != resp_phys) { + device_printf(dev, "response channel mismatch\n"); + return (EIO); + } + if (msg->code != BCM2835_MBOX_CODE_RESP_SUCCESS) { + device_printf(dev, "mbox response error\n"); + return (EIO); + } + + /* Loop until the end tag. */ + tag = (struct bcm2835_mbox_tag_hdr *)(msg + 1); + last = (uint8_t *)msg + len; + for (idx = 0; tag->tag != 0; idx++) { + if ((tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE) == 0) { + device_printf(dev, "tag %d response error\n", idx); + return (EIO); + } + /* Clear the response bit. */ + tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; + + /* Next tag. */ + tag = (struct bcm2835_mbox_tag_hdr *)((uint8_t *)tag + + sizeof(*tag) + tag->val_buf_size); + + if ((uint8_t *)tag > last) { + device_printf(dev, "mbox buffer size error\n"); + return (EIO); + } + } + + return (0); +} + +int +bcm2835_mbox_property(void *msg, size_t msg_size) +{ + struct bcm_mbox_softc *sc; + struct msg_set_power_state *buf; + bus_dma_tag_t msg_tag; + bus_dmamap_t msg_map; + bus_addr_t msg_phys; + uint32_t reg; + device_t mbox; + int err; + + /* get mbox device */ + mbox = devclass_get_device(devclass_find("mbox"), 0); + if (mbox == NULL) + return (ENXIO); + + sc = device_get_softc(mbox); + sx_xlock(&sc->property_chan_lock); + + /* Allocate memory for the message */ + buf = bcm2835_mbox_init_dma(mbox, msg_size, &msg_tag, &msg_map, + &msg_phys); + if (buf == NULL) { + err = ENOMEM; + goto out; + } + + memcpy(buf, msg, msg_size); + + bus_dmamap_sync(msg_tag, msg_map, + BUS_DMASYNC_PREWRITE); + + MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_PROP, (uint32_t)msg_phys); + MBOX_READ(mbox, BCM2835_MBOX_CHAN_PROP, ®); + + bus_dmamap_sync(msg_tag, msg_map, + BUS_DMASYNC_PREREAD); + + memcpy(msg, buf, msg_size); + + err = bcm2835_mbox_err(mbox, msg_phys, reg, + (struct bcm2835_mbox_hdr *)msg, msg_size); + + bus_dmamap_unload(msg_tag, msg_map); + bus_dmamem_free(msg_tag, buf, msg_map); + bus_dma_tag_destroy(msg_tag); +out: + sx_xunlock(&sc->property_chan_lock); + return (err); +} + +int +bcm2835_mbox_set_power_state(uint32_t device_id, boolean_t on) +{ + struct msg_set_power_state msg; + int err; + + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_POWER_STATE; + msg.tag_hdr.val_buf_size = sizeof(msg.body); + msg.tag_hdr.val_len = sizeof(msg.body.req); + msg.body.req.device_id = device_id; + msg.body.req.state = (on ? BCM2835_MBOX_POWER_ON : 0) | + BCM2835_MBOX_POWER_WAIT; + msg.end_tag = 0; + + err = bcm2835_mbox_property(&msg, sizeof(msg)); + + return (err); +} + +int +bcm2835_mbox_get_clock_rate(uint32_t clock_id, uint32_t *hz) +{ + struct msg_get_clock_rate msg; + int err; + + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE; + msg.tag_hdr.val_buf_size = sizeof(msg.body); + msg.tag_hdr.val_len = sizeof(msg.body.req); + msg.body.req.clock_id = clock_id; + msg.end_tag = 0; + + err = bcm2835_mbox_property(&msg, sizeof(msg)); + *hz = msg.body.resp.rate_hz; + + return (err); +} + +int +bcm2835_mbox_fb_get_w_h(struct bcm2835_fb_config *fb) +{ + int err; + struct msg_fb_get_w_h msg; + + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + BCM2835_MBOX_INIT_TAG(&msg.physical_w_h, GET_PHYSICAL_W_H); + msg.physical_w_h.tag_hdr.val_len = 0; + msg.end_tag = 0; + + err = bcm2835_mbox_property(&msg, sizeof(msg)); + if (err == 0) { + fb->xres = msg.physical_w_h.body.resp.width; + fb->yres = msg.physical_w_h.body.resp.height; + } + + return (err); +} + +int +bcm2835_mbox_fb_init(struct bcm2835_fb_config *fb) +{ + int err; + struct msg_fb_setup msg; + + memset(&msg, 0, sizeof(msg)); + msg.hdr.buf_size = sizeof(msg); + msg.hdr.code = BCM2835_MBOX_CODE_REQ; + BCM2835_MBOX_INIT_TAG(&msg.physical_w_h, SET_PHYSICAL_W_H); + msg.physical_w_h.body.req.width = fb->xres; + msg.physical_w_h.body.req.height = fb->yres; + BCM2835_MBOX_INIT_TAG(&msg.virtual_w_h, SET_VIRTUAL_W_H); + msg.virtual_w_h.body.req.width = fb->vxres; + msg.virtual_w_h.body.req.height = fb->vyres; + BCM2835_MBOX_INIT_TAG(&msg.offset, SET_VIRTUAL_OFFSET); + msg.offset.body.req.x = fb->xoffset; + msg.offset.body.req.y = fb->yoffset; + BCM2835_MBOX_INIT_TAG(&msg.depth, SET_DEPTH); + msg.depth.body.req.bpp = fb->bpp; + BCM2835_MBOX_INIT_TAG(&msg.alpha, SET_ALPHA_MODE); + msg.alpha.body.req.alpha = BCM2835_MBOX_ALPHA_MODE_IGNORED; + BCM2835_MBOX_INIT_TAG(&msg.buffer, ALLOCATE_BUFFER); + msg.buffer.body.req.alignment = PAGE_SIZE; + BCM2835_MBOX_INIT_TAG(&msg.pitch, GET_PITCH); + msg.end_tag = 0; + + err = bcm2835_mbox_property(&msg, sizeof(msg)); + if (err == 0) { + fb->xres = msg.physical_w_h.body.resp.width; + fb->yres = msg.physical_w_h.body.resp.height; + fb->vxres = msg.virtual_w_h.body.resp.width; + fb->vyres = msg.virtual_w_h.body.resp.height; + fb->xoffset = msg.offset.body.resp.x; + fb->yoffset = msg.offset.body.resp.y; + fb->pitch = msg.pitch.body.resp.pitch; + fb->base = VCBUS_TO_PHYS(msg.buffer.body.resp.fb_address); + fb->size = msg.buffer.body.resp.fb_size; + } + + return (err); +} Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h (revision 322724) @@ -1,273 +1,478 @@ /*- * Copyright (C) 2013-2014 Daisuke Aoyama * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _BCM2835_MBOX_PROP_H_ #define _BCM2835_MBOX_PROP_H_ #include #include /* * Mailbox property interface: * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ #define BCM2835_MBOX_CODE_REQ 0 #define BCM2835_MBOX_CODE_RESP_SUCCESS 0x80000000 #define BCM2835_MBOX_CODE_RESP_ERROR 0x80000001 #define BCM2835_MBOX_TAG_VAL_LEN_RESPONSE 0x80000000 struct bcm2835_mbox_hdr { uint32_t buf_size; uint32_t code; }; struct bcm2835_mbox_tag_hdr { uint32_t tag; uint32_t val_buf_size; uint32_t val_len; }; +#define BCM2835_MBOX_INIT_TAG(tag_, tagid_) do { \ + (tag_)->tag_hdr.tag = BCM2835_MBOX_TAG_##tagid_; \ + (tag_)->tag_hdr.val_buf_size = sizeof((tag_)->body); \ + (tag_)->tag_hdr.val_len = sizeof((tag_)->body.req); \ +} while (0) + +#define BCM2835_MBOX_POWER_ID_EMMC 0x00000000 +#define BCM2835_MBOX_POWER_ID_UART0 0x00000001 +#define BCM2835_MBOX_POWER_ID_UART1 0x00000002 +#define BCM2835_MBOX_POWER_ID_USB_HCD 0x00000003 +#define BCM2835_MBOX_POWER_ID_I2C0 0x00000004 +#define BCM2835_MBOX_POWER_ID_I2C1 0x00000005 +#define BCM2835_MBOX_POWER_ID_I2C2 0x00000006 +#define BCM2835_MBOX_POWER_ID_SPI 0x00000007 +#define BCM2835_MBOX_POWER_ID_CCP2TX 0x00000008 + +#define BCM2835_MBOX_POWER_ON (1 << 0) +#define BCM2835_MBOX_POWER_WAIT (1 << 1) + +#define BCM2835_MBOX_TAG_GET_POWER_STATE 0x00020001 +#define BCM2835_MBOX_TAG_SET_POWER_STATE 0x00028001 + +struct msg_get_power_state { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t device_id; + } req; + struct { + uint32_t device_id; + uint32_t state; + } resp; + } body; + uint32_t end_tag; +}; + +struct msg_set_power_state { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t device_id; + uint32_t state; + } req; + struct { + uint32_t device_id; + uint32_t state; + } resp; + } body; + uint32_t end_tag; +}; + +/* Sets the power state for a given device */ +int bcm2835_mbox_set_power_state(uint32_t, boolean_t); + #define BCM2835_MBOX_CLOCK_ID_EMMC 0x00000001 #define BCM2835_MBOX_CLOCK_ID_UART 0x00000002 #define BCM2835_MBOX_CLOCK_ID_ARM 0x00000003 #define BCM2835_MBOX_CLOCK_ID_CORE 0x00000004 #define BCM2835_MBOX_CLOCK_ID_V3D 0x00000005 #define BCM2835_MBOX_CLOCK_ID_H264 0x00000006 #define BCM2835_MBOX_CLOCK_ID_ISP 0x00000007 #define BCM2835_MBOX_CLOCK_ID_SDRAM 0x00000008 #define BCM2835_MBOX_CLOCK_ID_PIXEL 0x00000009 #define BCM2835_MBOX_CLOCK_ID_PWM 0x0000000a #define BCM2835_MBOX_TAG_GET_CLOCK_RATE 0x00030002 #define BCM2835_MBOX_TAG_SET_CLOCK_RATE 0x00038002 #define BCM2835_MBOX_TAG_GET_MAX_CLOCK_RATE 0x00030004 #define BCM2835_MBOX_TAG_GET_MIN_CLOCK_RATE 0x00030007 struct msg_get_clock_rate { struct bcm2835_mbox_hdr hdr; struct bcm2835_mbox_tag_hdr tag_hdr; union { struct { uint32_t clock_id; } req; struct { uint32_t clock_id; uint32_t rate_hz; } resp; } body; uint32_t end_tag; }; struct msg_set_clock_rate { struct bcm2835_mbox_hdr hdr; struct bcm2835_mbox_tag_hdr tag_hdr; union { struct { uint32_t clock_id; uint32_t rate_hz; } req; struct { uint32_t clock_id; uint32_t rate_hz; } resp; } body; uint32_t end_tag; }; struct msg_get_max_clock_rate { struct bcm2835_mbox_hdr hdr; struct bcm2835_mbox_tag_hdr tag_hdr; union { struct { uint32_t clock_id; } req; struct { uint32_t clock_id; uint32_t rate_hz; } resp; } body; uint32_t end_tag; }; struct msg_get_min_clock_rate { struct bcm2835_mbox_hdr hdr; struct bcm2835_mbox_tag_hdr tag_hdr; union { struct { uint32_t clock_id; } req; struct { uint32_t clock_id; uint32_t rate_hz; } resp; } body; uint32_t end_tag; }; +int bcm2835_mbox_get_clock_rate(uint32_t, uint32_t *); + #define BCM2835_MBOX_TURBO_ON 1 #define BCM2835_MBOX_TURBO_OFF 0 #define BCM2835_MBOX_TAG_GET_TURBO 0x00030009 #define BCM2835_MBOX_TAG_SET_TURBO 0x00038009 struct msg_get_turbo { struct bcm2835_mbox_hdr hdr; struct bcm2835_mbox_tag_hdr tag_hdr; union { struct { uint32_t id; } req; struct { uint32_t id; uint32_t level; } resp; } body; uint32_t end_tag; }; struct msg_set_turbo { struct bcm2835_mbox_hdr hdr; struct bcm2835_mbox_tag_hdr tag_hdr; union { struct { uint32_t id; uint32_t level; } req; struct { uint32_t id; uint32_t level; } resp; } body; uint32_t end_tag; }; #define BCM2835_MBOX_VOLTAGE_ID_CORE 0x00000001 #define BCM2835_MBOX_VOLTAGE_ID_SDRAM_C 0x00000002 #define BCM2835_MBOX_VOLTAGE_ID_SDRAM_P 0x00000003 #define BCM2835_MBOX_VOLTAGE_ID_SDRAM_I 0x00000004 #define BCM2835_MBOX_TAG_GET_VOLTAGE 0x00030003 #define BCM2835_MBOX_TAG_SET_VOLTAGE 0x00038003 #define BCM2835_MBOX_TAG_GET_MAX_VOLTAGE 0x00030005 #define BCM2835_MBOX_TAG_GET_MIN_VOLTAGE 0x00030008 struct msg_get_voltage { struct bcm2835_mbox_hdr hdr; struct bcm2835_mbox_tag_hdr tag_hdr; union { struct { uint32_t voltage_id; } req; struct { uint32_t voltage_id; uint32_t value; } resp; } body; uint32_t end_tag; }; struct msg_set_voltage { struct bcm2835_mbox_hdr hdr; struct bcm2835_mbox_tag_hdr tag_hdr; union { struct { uint32_t voltage_id; uint32_t value; } req; struct { uint32_t voltage_id; uint32_t value; } resp; } body; uint32_t end_tag; }; struct msg_get_max_voltage { struct bcm2835_mbox_hdr hdr; struct bcm2835_mbox_tag_hdr tag_hdr; union { struct { uint32_t voltage_id; } req; struct { uint32_t voltage_id; uint32_t value; } resp; } body; uint32_t end_tag; }; struct msg_get_min_voltage { struct bcm2835_mbox_hdr hdr; struct bcm2835_mbox_tag_hdr tag_hdr; union { struct { uint32_t voltage_id; } req; struct { uint32_t voltage_id; uint32_t value; } resp; } body; uint32_t end_tag; }; #define BCM2835_MBOX_TAG_GET_TEMPERATURE 0x00030006 #define BCM2835_MBOX_TAG_GET_MAX_TEMPERATURE 0x0003000a struct msg_get_temperature { struct bcm2835_mbox_hdr hdr; struct bcm2835_mbox_tag_hdr tag_hdr; union { struct { uint32_t temperature_id; } req; struct { uint32_t temperature_id; uint32_t value; } resp; } body; uint32_t end_tag; }; struct msg_get_max_temperature { struct bcm2835_mbox_hdr hdr; struct bcm2835_mbox_tag_hdr tag_hdr; union { struct { uint32_t temperature_id; } req; struct { uint32_t temperature_id; uint32_t value; } resp; } body; uint32_t end_tag; }; + +#define BCM2835_MBOX_TAG_GET_PHYSICAL_W_H 0x00040003 +#define BCM2835_MBOX_TAG_SET_PHYSICAL_W_H 0x00048003 +#define BCM2835_MBOX_TAG_GET_VIRTUAL_W_H 0x00040004 +#define BCM2835_MBOX_TAG_SET_VIRTUAL_W_H 0x00048004 + +struct bcm2835_mbox_tag_fb_w_h { + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t width; + uint32_t height; + } req; + struct { + uint32_t width; + uint32_t height; + } resp; + } body; +}; + +#define BCM2835_MBOX_TAG_GET_DEPTH 0x00040005 +#define BCM2835_MBOX_TAG_SET_DEPTH 0x00048005 + +struct bcm2835_mbox_tag_depth { + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t bpp; + } req; + struct { + uint32_t bpp; + } resp; + } body; +}; + +#define BCM2835_MBOX_TAG_GET_ALPHA_MODE 0x00040007 +#define BCM2835_MBOX_TAG_SET_ALPHA_MODE 0x00048007 + +#define BCM2835_MBOX_ALPHA_MODE_0_OPAQUE 0 +#define BCM2835_MBOX_ALPHA_MODE_0_TRANSPARENT 1 +#define BCM2835_MBOX_ALPHA_MODE_IGNORED 2 + +struct bcm2835_mbox_tag_alpha_mode { + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t alpha; + } req; + struct { + uint32_t alpha; + } resp; + } body; +}; + +#define BCM2835_MBOX_TAG_GET_VIRTUAL_OFFSET 0x00040009 +#define BCM2835_MBOX_TAG_SET_VIRTUAL_OFFSET 0x00048009 + +struct bcm2835_mbox_tag_virtual_offset { + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t x; + uint32_t y; + } req; + struct { + uint32_t x; + uint32_t y; + } resp; + } body; +}; + +#define BCM2835_MBOX_TAG_GET_PITCH 0x00040008 + +struct bcm2835_mbox_tag_pitch { + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + } req; + struct { + uint32_t pitch; + } resp; + } body; +}; + +#define BCM2835_MBOX_TAG_ALLOCATE_BUFFER 0x00040001 + +struct bcm2835_mbox_tag_allocate_buffer { + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + uint32_t alignment; + } req; + struct { + uint32_t fb_address; + uint32_t fb_size; + } resp; + } body; +}; + +#define BCM2835_MBOX_TAG_RELEASE_BUFFER 0x00048001 + +struct bcm2835_mbox_tag_release_buffer { + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + } req; + struct { + } resp; + } body; +}; + +struct bcm2835_fb_config { + uint32_t xres; + uint32_t yres; + uint32_t vxres; + uint32_t vyres; + uint32_t xoffset; + uint32_t yoffset; + uint32_t bpp; + uint32_t pitch; + uint32_t base; + uint32_t size; +}; + +struct msg_fb_get_w_h { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_fb_w_h physical_w_h; + uint32_t end_tag; +}; + +int bcm2835_mbox_fb_get_w_h(struct bcm2835_fb_config *); + +struct msg_fb_setup { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_fb_w_h physical_w_h; + struct bcm2835_mbox_tag_fb_w_h virtual_w_h; + struct bcm2835_mbox_tag_virtual_offset offset; + struct bcm2835_mbox_tag_depth depth; + struct bcm2835_mbox_tag_alpha_mode alpha; + struct bcm2835_mbox_tag_allocate_buffer buffer; + struct bcm2835_mbox_tag_pitch pitch; + uint32_t end_tag; +}; + +int bcm2835_mbox_fb_init(struct bcm2835_fb_config *); + +int bcm2835_mbox_property(void *, size_t); #endif /* _BCM2835_MBOX_PROP_H_ */ Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c (revision 322724) @@ -1,677 +1,682 @@ /*- * Copyright (c) 2012 Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmcbr_if.h" #include "sdhci_if.h" #include "bcm2835_dma.h" +#include #include "bcm2835_vcbus.h" #define BCM2835_DEFAULT_SDHCI_FREQ 50 #define BCM_SDHCI_BUFFER_SIZE 512 #define NUM_DMA_SEGS 2 #ifdef DEBUG #define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) #else #define dprintf(fmt, args...) #endif static int bcm2835_sdhci_hs = 1; static int bcm2835_sdhci_pio_mode = 0; +static struct ofw_compat_data compat_data[] = { + {"broadcom,bcm2835-sdhci", 1}, + {"brcm,bcm2835-mmc", 1}, + {NULL, 0} +}; + TUNABLE_INT("hw.bcm2835.sdhci.hs", &bcm2835_sdhci_hs); TUNABLE_INT("hw.bcm2835.sdhci.pio_mode", &bcm2835_sdhci_pio_mode); struct bcm_sdhci_softc { device_t sc_dev; - struct mtx sc_mtx; struct resource * sc_mem_res; struct resource * sc_irq_res; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; void * sc_intrhand; struct mmc_request * sc_req; - struct mmc_data * sc_data; - uint32_t sc_flags; -#define LPC_SD_FLAGS_IGNORECRC (1 << 0) - int sc_xfer_direction; -#define DIRECTION_READ 0 -#define DIRECTION_WRITE 1 - int sc_xfer_done; - int sc_bus_busy; struct sdhci_slot sc_slot; - int sc_dma_inuse; int sc_dma_ch; bus_dma_tag_t sc_dma_tag; bus_dmamap_t sc_dma_map; vm_paddr_t sc_sdhci_buffer_phys; uint32_t cmd_and_mode; bus_addr_t dmamap_seg_addrs[NUM_DMA_SEGS]; bus_size_t dmamap_seg_sizes[NUM_DMA_SEGS]; int dmamap_seg_count; int dmamap_seg_index; int dmamap_status; }; static int bcm_sdhci_probe(device_t); static int bcm_sdhci_attach(device_t); static int bcm_sdhci_detach(device_t); static void bcm_sdhci_intr(void *); static int bcm_sdhci_get_ro(device_t, device_t); static void bcm_sdhci_dma_intr(int ch, void *arg); -#define bcm_sdhci_lock(_sc) \ - mtx_lock(&_sc->sc_mtx); -#define bcm_sdhci_unlock(_sc) \ - mtx_unlock(&_sc->sc_mtx); - static void bcm_sdhci_dmacb(void *arg, bus_dma_segment_t *segs, int nseg, int err) { struct bcm_sdhci_softc *sc = arg; int i; sc->dmamap_status = err; sc->dmamap_seg_count = nseg; /* Note nseg is guaranteed to be zero if err is non-zero. */ for (i = 0; i < nseg; i++) { sc->dmamap_seg_addrs[i] = segs[i].ds_addr; sc->dmamap_seg_sizes[i] = segs[i].ds_len; } } static int bcm_sdhci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-sdhci")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Broadcom 2708 SDHCI controller"); + return (BUS_PROBE_DEFAULT); } static int bcm_sdhci_attach(device_t dev) { struct bcm_sdhci_softc *sc = device_get_softc(dev); int rid, err; phandle_t node; pcell_t cell; - int default_freq; + u_int default_freq; sc->sc_dev = dev; sc->sc_req = NULL; - err = 0; - default_freq = BCM2835_DEFAULT_SDHCI_FREQ; - node = ofw_bus_get_node(sc->sc_dev); - if ((OF_getprop(node, "clock-frequency", &cell, sizeof(cell))) > 0) - default_freq = (int)fdt32_to_cpu(cell)/1000000; + err = bcm2835_mbox_set_power_state(BCM2835_MBOX_POWER_ID_EMMC, + TRUE); + if (err != 0) { + if (bootverbose) + device_printf(dev, "Unable to enable the power\n"); + return (err); + } - dprintf("SDHCI frequency: %dMHz\n", default_freq); + default_freq = 0; + err = bcm2835_mbox_get_clock_rate(BCM2835_MBOX_CLOCK_ID_EMMC, + &default_freq); + if (err == 0) { + /* Convert to MHz */ + default_freq /= 1000000; + } + if (default_freq == 0) { + node = ofw_bus_get_node(sc->sc_dev); + if ((OF_getencprop(node, "clock-frequency", &cell, + sizeof(cell))) > 0) + default_freq = cell / 1000000; + } + if (default_freq == 0) + default_freq = BCM2835_DEFAULT_SDHCI_FREQ; - mtx_init(&sc->sc_mtx, "bcm sdhci", "sdhci", MTX_DEF); + if (bootverbose) + device_printf(dev, "SDHCI frequency: %dMHz\n", default_freq); rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); err = ENXIO; goto fail; } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->sc_irq_res) { device_printf(dev, "cannot allocate interrupt\n"); err = ENXIO; goto fail; } if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, bcm_sdhci_intr, sc, &sc->sc_intrhand)) { device_printf(dev, "cannot setup interrupt handler\n"); err = ENXIO; goto fail; } if (!bcm2835_sdhci_pio_mode) sc->sc_slot.opt = SDHCI_PLATFORM_TRANSFER; sc->sc_slot.caps = SDHCI_CAN_VDD_330 | SDHCI_CAN_VDD_180; if (bcm2835_sdhci_hs) sc->sc_slot.caps |= SDHCI_CAN_DO_HISPD; sc->sc_slot.caps |= (default_freq << SDHCI_CLOCK_BASE_SHIFT); sc->sc_slot.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_DONT_SET_HISPD_BIT | SDHCI_QUIRK_MISSING_CAPS; sdhci_init_slot(dev, &sc->sc_slot, 0); - sc->sc_dma_ch = bcm_dma_allocate(BCM_DMA_CH_FAST1); + sc->sc_dma_ch = bcm_dma_allocate(BCM_DMA_CH_ANY); if (sc->sc_dma_ch == BCM_DMA_CH_INVALID) - sc->sc_dma_ch = bcm_dma_allocate(BCM_DMA_CH_FAST2); - if (sc->sc_dma_ch == BCM_DMA_CH_INVALID) - sc->sc_dma_ch = bcm_dma_allocate(BCM_DMA_CH_ANY); - if (sc->sc_dma_ch == BCM_DMA_CH_INVALID) goto fail; bcm_dma_setup_intr(sc->sc_dma_ch, bcm_sdhci_dma_intr, sc); /* Allocate bus_dma resources. */ err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BCM_SDHCI_BUFFER_SIZE, NUM_DMA_SEGS, BCM_SDHCI_BUFFER_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_dma_tag); if (err) { device_printf(dev, "failed allocate DMA tag"); goto fail; } err = bus_dmamap_create(sc->sc_dma_tag, 0, &sc->sc_dma_map); if (err) { device_printf(dev, "bus_dmamap_create failed\n"); goto fail; } sc->sc_sdhci_buffer_phys = BUS_SPACE_PHYSADDR(sc->sc_mem_res, SDHCI_BUFFER); bus_generic_probe(dev); bus_generic_attach(dev); sdhci_start_slot(&sc->sc_slot); return (0); fail: if (sc->sc_intrhand) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); - mtx_destroy(&sc->sc_mtx); return (err); } static int bcm_sdhci_detach(device_t dev) { return (EBUSY); } static void bcm_sdhci_intr(void *arg) { struct bcm_sdhci_softc *sc = arg; sdhci_generic_intr(&sc->sc_slot); } static int bcm_sdhci_get_ro(device_t bus, device_t child) { return (0); } static inline uint32_t RD4(struct bcm_sdhci_softc *sc, bus_size_t off) { uint32_t val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off); return val; } static inline void WR4(struct bcm_sdhci_softc *sc, bus_size_t off, uint32_t val) { bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val); /* * The Arasan HC has a bug where it may lose the content of * consecutive writes to registers that are within two SD-card * clock cycles of each other (a clock domain crossing problem). */ if (sc->sc_slot.clock > 0) DELAY(((2 * 1000000) / sc->sc_slot.clock) + 1); } static uint8_t bcm_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct bcm_sdhci_softc *sc = device_get_softc(dev); uint32_t val = RD4(sc, off & ~3); return ((val >> (off & 3)*8) & 0xff); } static uint16_t bcm_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct bcm_sdhci_softc *sc = device_get_softc(dev); uint32_t val = RD4(sc, off & ~3); /* * Standard 32-bit handling of command and transfer mode. */ if (off == SDHCI_TRANSFER_MODE) { return (sc->cmd_and_mode >> 16); } else if (off == SDHCI_COMMAND_FLAGS) { return (sc->cmd_and_mode & 0x0000ffff); } return ((val >> (off & 3)*8) & 0xffff); } static uint32_t bcm_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct bcm_sdhci_softc *sc = device_get_softc(dev); return RD4(sc, off); } static void bcm_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t *data, bus_size_t count) { struct bcm_sdhci_softc *sc = device_get_softc(dev); bus_space_read_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count); } static void bcm_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val) { struct bcm_sdhci_softc *sc = device_get_softc(dev); uint32_t val32 = RD4(sc, off & ~3); val32 &= ~(0xff << (off & 3)*8); val32 |= (val << (off & 3)*8); WR4(sc, off & ~3, val32); } static void bcm_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val) { struct bcm_sdhci_softc *sc = device_get_softc(dev); uint32_t val32; if (off == SDHCI_COMMAND_FLAGS) val32 = sc->cmd_and_mode; else val32 = RD4(sc, off & ~3); val32 &= ~(0xffff << (off & 3)*8); val32 |= (val << (off & 3)*8); if (off == SDHCI_TRANSFER_MODE) sc->cmd_and_mode = val32; else { WR4(sc, off & ~3, val32); if (off == SDHCI_COMMAND_FLAGS) sc->cmd_and_mode = val32; } } static void bcm_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val) { struct bcm_sdhci_softc *sc = device_get_softc(dev); WR4(sc, off, val); } static void bcm_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t *data, bus_size_t count) { struct bcm_sdhci_softc *sc = device_get_softc(dev); bus_space_write_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count); } static void bcm_sdhci_start_dma_seg(struct bcm_sdhci_softc *sc) { struct sdhci_slot *slot; vm_paddr_t pdst, psrc; int err, idx, len, sync_op; slot = &sc->sc_slot; idx = sc->dmamap_seg_index++; len = sc->dmamap_seg_sizes[idx]; slot->offset += len; if (slot->curcmd->data->flags & MMC_DATA_READ) { bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC, BCM_DMA_SAME_ADDR, BCM_DMA_32BIT); bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_NONE, BCM_DMA_INC_ADDR, (len & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT); psrc = sc->sc_sdhci_buffer_phys; pdst = sc->dmamap_seg_addrs[idx]; sync_op = BUS_DMASYNC_PREREAD; } else { bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_NONE, BCM_DMA_INC_ADDR, (len & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT); bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC, BCM_DMA_SAME_ADDR, BCM_DMA_32BIT); psrc = sc->dmamap_seg_addrs[idx]; pdst = sc->sc_sdhci_buffer_phys; sync_op = BUS_DMASYNC_PREWRITE; } /* * When starting a new DMA operation do the busdma sync operation, and * disable SDCHI data interrrupts because we'll be driven by DMA * interrupts (or SDHCI error interrupts) until the IO is done. */ if (idx == 0) { bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op); slot->intmask &= ~(SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END); bcm_sdhci_write_4(sc->sc_dev, &sc->sc_slot, SDHCI_SIGNAL_ENABLE, slot->intmask); } /* * Start the DMA transfer. Only programming errors (like failing to * allocate a channel) cause a non-zero return from bcm_dma_start(). */ err = bcm_dma_start(sc->sc_dma_ch, psrc, pdst, len); KASSERT((err == 0), ("bcm2835_sdhci: failed DMA start")); } static void bcm_sdhci_dma_intr(int ch, void *arg) { struct bcm_sdhci_softc *sc = (struct bcm_sdhci_softc *)arg; struct sdhci_slot *slot = &sc->sc_slot; uint32_t reg, mask; int left, sync_op; mtx_lock(&slot->mtx); /* * If there are more segments for the current dma, start the next one. * Otherwise unload the dma map and decide what to do next based on the * status of the sdhci controller and whether there's more data left. */ if (sc->dmamap_seg_index < sc->dmamap_seg_count) { bcm_sdhci_start_dma_seg(sc); mtx_unlock(&slot->mtx); return; } if (slot->curcmd->data->flags & MMC_DATA_READ) { sync_op = BUS_DMASYNC_POSTREAD; mask = SDHCI_INT_DATA_AVAIL; } else { sync_op = BUS_DMASYNC_POSTWRITE; mask = SDHCI_INT_SPACE_AVAIL; } bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op); bus_dmamap_unload(sc->sc_dma_tag, sc->sc_dma_map); sc->dmamap_seg_count = 0; sc->dmamap_seg_index = 0; left = min(BCM_SDHCI_BUFFER_SIZE, slot->curcmd->data->len - slot->offset); /* DATA END? */ reg = bcm_sdhci_read_4(slot->bus, slot, SDHCI_INT_STATUS); if (reg & SDHCI_INT_DATA_END) { /* ACK for all outstanding interrupts */ bcm_sdhci_write_4(slot->bus, slot, SDHCI_INT_STATUS, reg); /* enable INT */ slot->intmask |= SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END; bcm_sdhci_write_4(slot->bus, slot, SDHCI_SIGNAL_ENABLE, slot->intmask); /* finish this data */ sdhci_finish_data(slot); } else { /* already available? */ if (reg & mask) { /* ACK for DATA_AVAIL or SPACE_AVAIL */ bcm_sdhci_write_4(slot->bus, slot, SDHCI_INT_STATUS, mask); /* continue next DMA transfer */ if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, (uint8_t *)slot->curcmd->data->data + slot->offset, left, bcm_sdhci_dmacb, sc, BUS_DMA_NOWAIT) != 0 || sc->dmamap_status != 0) { slot->curcmd->error = MMC_ERR_NO_MEMORY; sdhci_finish_data(slot); } else { bcm_sdhci_start_dma_seg(sc); } } else { /* wait for next data by INT */ /* enable INT */ slot->intmask |= SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END; bcm_sdhci_write_4(slot->bus, slot, SDHCI_SIGNAL_ENABLE, slot->intmask); } } mtx_unlock(&slot->mtx); } static void bcm_sdhci_read_dma(device_t dev, struct sdhci_slot *slot) { struct bcm_sdhci_softc *sc = device_get_softc(slot->bus); size_t left; if (sc->dmamap_seg_count != 0) { device_printf(sc->sc_dev, "DMA in use\n"); return; } left = min(BCM_SDHCI_BUFFER_SIZE, slot->curcmd->data->len - slot->offset); KASSERT((left & 3) == 0, ("%s: len = %d, not word-aligned", __func__, left)); if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, (uint8_t *)slot->curcmd->data->data + slot->offset, left, bcm_sdhci_dmacb, sc, BUS_DMA_NOWAIT) != 0 || sc->dmamap_status != 0) { slot->curcmd->error = MMC_ERR_NO_MEMORY; return; } /* DMA start */ bcm_sdhci_start_dma_seg(sc); } static void bcm_sdhci_write_dma(device_t dev, struct sdhci_slot *slot) { struct bcm_sdhci_softc *sc = device_get_softc(slot->bus); size_t left; if (sc->dmamap_seg_count != 0) { device_printf(sc->sc_dev, "DMA in use\n"); return; } left = min(BCM_SDHCI_BUFFER_SIZE, slot->curcmd->data->len - slot->offset); KASSERT((left & 3) == 0, ("%s: len = %d, not word-aligned", __func__, left)); if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, (uint8_t *)slot->curcmd->data->data + slot->offset, left, bcm_sdhci_dmacb, sc, BUS_DMA_NOWAIT) != 0 || sc->dmamap_status != 0) { slot->curcmd->error = MMC_ERR_NO_MEMORY; return; } /* DMA start */ bcm_sdhci_start_dma_seg(sc); } static int bcm_sdhci_will_handle_transfer(device_t dev, struct sdhci_slot *slot) { size_t left; /* * Do not use DMA for transfers less than block size or with a length * that is not a multiple of four. */ left = min(BCM_DMA_BLOCK_SIZE, slot->curcmd->data->len - slot->offset); if (left < BCM_DMA_BLOCK_SIZE) return (0); if (left & 0x03) return (0); return (1); } static void bcm_sdhci_start_transfer(device_t dev, struct sdhci_slot *slot, uint32_t *intmask) { /* DMA transfer FIFO 1KB */ if (slot->curcmd->data->flags & MMC_DATA_READ) bcm_sdhci_read_dma(dev, slot); else bcm_sdhci_write_dma(dev, slot); } static void bcm_sdhci_finish_transfer(device_t dev, struct sdhci_slot *slot) { sdhci_finish_data(slot); } static device_method_t bcm_sdhci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_sdhci_probe), DEVMETHOD(device_attach, bcm_sdhci_attach), DEVMETHOD(device_detach, bcm_sdhci_detach), /* Bus interface */ DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar), DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar), /* MMC bridge interface */ DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios), DEVMETHOD(mmcbr_request, sdhci_generic_request), DEVMETHOD(mmcbr_get_ro, bcm_sdhci_get_ro), DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host), DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host), /* Platform transfer methods */ DEVMETHOD(sdhci_platform_will_handle, bcm_sdhci_will_handle_transfer), DEVMETHOD(sdhci_platform_start_transfer, bcm_sdhci_start_transfer), DEVMETHOD(sdhci_platform_finish_transfer, bcm_sdhci_finish_transfer), /* SDHCI registers accessors */ DEVMETHOD(sdhci_read_1, bcm_sdhci_read_1), DEVMETHOD(sdhci_read_2, bcm_sdhci_read_2), DEVMETHOD(sdhci_read_4, bcm_sdhci_read_4), DEVMETHOD(sdhci_read_multi_4, bcm_sdhci_read_multi_4), DEVMETHOD(sdhci_write_1, bcm_sdhci_write_1), DEVMETHOD(sdhci_write_2, bcm_sdhci_write_2), DEVMETHOD(sdhci_write_4, bcm_sdhci_write_4), DEVMETHOD(sdhci_write_multi_4, bcm_sdhci_write_multi_4), DEVMETHOD_END }; static devclass_t bcm_sdhci_devclass; static driver_t bcm_sdhci_driver = { "sdhci_bcm", bcm_sdhci_methods, sizeof(struct bcm_sdhci_softc), }; DRIVER_MODULE(sdhci_bcm, simplebus, bcm_sdhci_driver, bcm_sdhci_devclass, NULL, NULL); MODULE_DEPEND(sdhci_bcm, sdhci, 1, 1, 1); MMC_DECLARE_BRIDGE(sdhci_bcm); Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_spi.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_spi.c (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_spi.c (revision 322724) @@ -1,523 +1,528 @@ /*- * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2013 Luiz Otavio O Souza * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "spibus_if.h" +static struct ofw_compat_data compat_data[] = { + {"broadcom,bcm2835-spi", 1}, + {"brcm,bcm2835-spi", 1}, + {NULL, 0} +}; + static void bcm_spi_intr(void *); #ifdef BCM_SPI_DEBUG static void bcm_spi_printr(device_t dev) { struct bcm_spi_softc *sc; uint32_t reg; sc = device_get_softc(dev); reg = BCM_SPI_READ(sc, SPI_CS); device_printf(dev, "CS=%b\n", reg, "\20\1CS0\2CS1\3CPHA\4CPOL\7CSPOL" "\10TA\11DMAEN\12INTD\13INTR\14ADCS\15REN\16LEN" "\21DONE\22RXD\23TXD\24RXR\25RXF\26CSPOL0\27CSPOL1" "\30CSPOL2\31DMA_LEN\32LEN_LONG"); reg = BCM_SPI_READ(sc, SPI_CLK) & SPI_CLK_MASK; if (reg % 2) reg--; if (reg == 0) reg = 65536; device_printf(dev, "CLK=%uMhz/%d=%luhz\n", SPI_CORE_CLK / 1000000, reg, SPI_CORE_CLK / reg); reg = BCM_SPI_READ(sc, SPI_DLEN) & SPI_DLEN_MASK; device_printf(dev, "DLEN=%d\n", reg); reg = BCM_SPI_READ(sc, SPI_LTOH) & SPI_LTOH_MASK; device_printf(dev, "LTOH=%d\n", reg); reg = BCM_SPI_READ(sc, SPI_DC); device_printf(dev, "DC=RPANIC=%#x RDREQ=%#x TPANIC=%#x TDREQ=%#x\n", (reg & SPI_DC_RPANIC_MASK) >> SPI_DC_RPANIC_SHIFT, (reg & SPI_DC_RDREQ_MASK) >> SPI_DC_RDREQ_SHIFT, (reg & SPI_DC_TPANIC_MASK) >> SPI_DC_TPANIC_SHIFT, (reg & SPI_DC_TDREQ_MASK) >> SPI_DC_TDREQ_SHIFT); } #endif static void bcm_spi_modifyreg(struct bcm_spi_softc *sc, uint32_t off, uint32_t mask, uint32_t value) { uint32_t reg; mtx_assert(&sc->sc_mtx, MA_OWNED); reg = BCM_SPI_READ(sc, off); reg &= ~mask; reg |= value; BCM_SPI_WRITE(sc, off, reg); } static int bcm_spi_clock_proc(SYSCTL_HANDLER_ARGS) { struct bcm_spi_softc *sc; uint32_t clk; int error; sc = (struct bcm_spi_softc *)arg1; BCM_SPI_LOCK(sc); clk = BCM_SPI_READ(sc, SPI_CLK); BCM_SPI_UNLOCK(sc); clk &= 0xffff; if (clk == 0) clk = 65536; clk = SPI_CORE_CLK / clk; error = sysctl_handle_int(oidp, &clk, sizeof(clk), req); if (error != 0 || req->newptr == NULL) return (error); clk = SPI_CORE_CLK / clk; if (clk <= 1) clk = 2; else if (clk % 2) clk--; if (clk > 0xffff) clk = 0; BCM_SPI_LOCK(sc); BCM_SPI_WRITE(sc, SPI_CLK, clk); BCM_SPI_UNLOCK(sc); return (0); } static int bcm_spi_cs_bit_proc(SYSCTL_HANDLER_ARGS, uint32_t bit) { struct bcm_spi_softc *sc; uint32_t reg; int error; sc = (struct bcm_spi_softc *)arg1; BCM_SPI_LOCK(sc); reg = BCM_SPI_READ(sc, SPI_CS); BCM_SPI_UNLOCK(sc); reg = (reg & bit) ? 1 : 0; error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); if (reg) reg = bit; BCM_SPI_LOCK(sc); bcm_spi_modifyreg(sc, SPI_CS, bit, reg); BCM_SPI_UNLOCK(sc); return (0); } static int bcm_spi_cpol_proc(SYSCTL_HANDLER_ARGS) { return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CPOL)); } static int bcm_spi_cpha_proc(SYSCTL_HANDLER_ARGS) { return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CPHA)); } static int bcm_spi_cspol0_proc(SYSCTL_HANDLER_ARGS) { return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL0)); } static int bcm_spi_cspol1_proc(SYSCTL_HANDLER_ARGS) { return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL1)); } static void bcm_spi_sysctl_init(struct bcm_spi_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid *tree_node; struct sysctl_oid_list *tree; /* * Add system sysctl tree/handlers. */ ctx = device_get_sysctl_ctx(sc->sc_dev); tree_node = device_get_sysctl_tree(sc->sc_dev); tree = SYSCTL_CHILDREN(tree_node); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clock", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_clock_proc, "IU", "SPI BUS clock frequency"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cpol", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cpol_proc, "IU", "SPI BUS clock polarity"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cpha", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cpha_proc, "IU", "SPI BUS clock phase"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol0", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cspol0_proc, "IU", "SPI BUS chip select 0 polarity"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol1", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cspol1_proc, "IU", "SPI BUS chip select 1 polarity"); } static int bcm_spi_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-spi")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2708/2835 SPI controller"); return (BUS_PROBE_DEFAULT); } static int bcm_spi_attach(device_t dev) { struct bcm_spi_softc *sc; device_t gpio; int i, rid; if (device_get_unit(dev) != 0) { device_printf(dev, "only one SPI controller supported\n"); return (ENXIO); } sc = device_get_softc(dev); sc->sc_dev = dev; /* Configure the GPIO pins to ALT0 function to enable SPI the pins. */ gpio = devclass_get_device(devclass_find("gpio"), 0); if (!gpio) { device_printf(dev, "cannot find gpio0\n"); return (ENXIO); } for (i = 0; i < nitems(bcm_spi_pins); i++) bcm_gpio_set_alternate(gpio, bcm_spi_pins[i], BCM_GPIO_ALT0); rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate interrupt\n"); return (ENXIO); } /* Hook up our interrupt handler. */ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, bcm_spi_intr, sc, &sc->sc_intrhand)) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot setup the interrupt handler\n"); return (ENXIO); } mtx_init(&sc->sc_mtx, "bcm_spi", NULL, MTX_DEF); /* Add sysctl nodes. */ bcm_spi_sysctl_init(sc); #ifdef BCM_SPI_DEBUG bcm_spi_printr(dev); #endif /* * Enable the SPI controller. Clear the rx and tx FIFO. * Defaults to SPI mode 0. */ BCM_SPI_WRITE(sc, SPI_CS, SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO); /* Set the SPI clock to 500Khz. */ BCM_SPI_WRITE(sc, SPI_CLK, SPI_CORE_CLK / 500000); #ifdef BCM_SPI_DEBUG bcm_spi_printr(dev); #endif device_add_child(dev, "spibus", -1); return (bus_generic_attach(dev)); } static int bcm_spi_detach(device_t dev) { struct bcm_spi_softc *sc; bus_generic_detach(dev); sc = device_get_softc(dev); mtx_destroy(&sc->sc_mtx); if (sc->sc_intrhand) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (0); } static void bcm_spi_fill_fifo(struct bcm_spi_softc *sc) { struct spi_command *cmd; uint32_t cs, written; uint8_t *data; cmd = sc->sc_cmd; cs = BCM_SPI_READ(sc, SPI_CS) & (SPI_CS_TA | SPI_CS_TXD); while (sc->sc_written < sc->sc_len && cs == (SPI_CS_TA | SPI_CS_TXD)) { data = (uint8_t *)cmd->tx_cmd; written = sc->sc_written++; if (written >= cmd->tx_cmd_sz) { data = (uint8_t *)cmd->tx_data; written -= cmd->tx_cmd_sz; } BCM_SPI_WRITE(sc, SPI_FIFO, data[written]); cs = BCM_SPI_READ(sc, SPI_CS) & (SPI_CS_TA | SPI_CS_TXD); } } static void bcm_spi_drain_fifo(struct bcm_spi_softc *sc) { struct spi_command *cmd; uint32_t cs, read; uint8_t *data; cmd = sc->sc_cmd; cs = BCM_SPI_READ(sc, SPI_CS) & SPI_CS_RXD; while (sc->sc_read < sc->sc_len && cs == SPI_CS_RXD) { data = (uint8_t *)cmd->rx_cmd; read = sc->sc_read++; if (read >= cmd->rx_cmd_sz) { data = (uint8_t *)cmd->rx_data; read -= cmd->rx_cmd_sz; } data[read] = BCM_SPI_READ(sc, SPI_FIFO) & 0xff; cs = BCM_SPI_READ(sc, SPI_CS) & SPI_CS_RXD; } } static void bcm_spi_intr(void *arg) { struct bcm_spi_softc *sc; sc = (struct bcm_spi_softc *)arg; BCM_SPI_LOCK(sc); /* Filter stray interrupts. */ if ((sc->sc_flags & BCM_SPI_BUSY) == 0) { BCM_SPI_UNLOCK(sc); return; } /* TX - Fill up the FIFO. */ bcm_spi_fill_fifo(sc); /* RX - Drain the FIFO. */ bcm_spi_drain_fifo(sc); /* Check for end of transfer. */ if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len) { /* Disable interrupts and the SPI engine. */ bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0); wakeup(sc->sc_dev); } BCM_SPI_UNLOCK(sc); } static int bcm_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) { struct bcm_spi_softc *sc; int cs, err; sc = device_get_softc(dev); KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, ("TX/RX command sizes should be equal")); KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, ("TX/RX data sizes should be equal")); + /* Get the proper chip select for this child. */ + spibus_get_cs(child, &cs); + if (cs < 0 || cs > 2) { + device_printf(dev, + "Invalid chip select %d requested by %s\n", cs, + device_get_nameunit(child)); + return (EINVAL); + } + BCM_SPI_LOCK(sc); /* If the controller is in use wait until it is available. */ while (sc->sc_flags & BCM_SPI_BUSY) mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", 0); /* Now we have control over SPI controller. */ sc->sc_flags = BCM_SPI_BUSY; /* Clear the FIFO. */ bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO, SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO); - /* Get the proper chip select for this child. */ - spibus_get_cs(child, &cs); - if (cs < 0 || cs > 2) { - device_printf(dev, - "Invalid chip select %d requested by %s\n", cs, - device_get_nameunit(child)); - BCM_SPI_UNLOCK(sc); - return (EINVAL); - } - /* Save a pointer to the SPI command. */ sc->sc_cmd = cmd; sc->sc_read = 0; sc->sc_written = 0; sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz; /* * Set the CS for this transaction, enable interrupts and announce * we're ready to tx. This will kick off the first interrupt. */ bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_MASK | SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, cs | SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD); /* Wait for the transaction to complete. */ err = mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", hz * 2); /* Make sure the SPI engine and interrupts are disabled. */ bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0); - /* Clear the controller flags. */ + /* Release the controller and wakeup the next thread waiting for it. */ sc->sc_flags = 0; + wakeup_one(dev); + BCM_SPI_UNLOCK(sc); /* * Check for transfer timeout. The SPI controller doesn't * return errors. */ if (err == EWOULDBLOCK) { device_printf(sc->sc_dev, "SPI error\n"); err = EIO; } - - BCM_SPI_UNLOCK(sc); return (err); } static phandle_t bcm_spi_get_node(device_t bus, device_t dev) { /* We only have one child, the SPI bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t bcm_spi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_spi_probe), DEVMETHOD(device_attach, bcm_spi_attach), DEVMETHOD(device_detach, bcm_spi_detach), /* SPI interface */ DEVMETHOD(spibus_transfer, bcm_spi_transfer), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, bcm_spi_get_node), DEVMETHOD_END }; static devclass_t bcm_spi_devclass; static driver_t bcm_spi_driver = { "spi", bcm_spi_methods, sizeof(struct bcm_spi_softc), }; DRIVER_MODULE(bcm2835_spi, simplebus, bcm_spi_driver, bcm_spi_devclass, 0, 0); Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_vcbus.h =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_vcbus.h (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_vcbus.h (revision 322724) @@ -1,66 +1,72 @@ /*- * Copyright (c) 2012 Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Defines for converting physical address to VideoCore bus address and back */ #ifndef _BCM2835_VCBUS_H_ #define _BCM2835_VCBUS_H_ #define BCM2835_VCBUS_SDRAM_CACHED 0x40000000 #define BCM2835_VCBUS_IO_BASE 0x7E000000 #define BCM2835_VCBUS_SDRAM_UNCACHED 0xC0000000 +#ifdef SOC_BCM2836 +#define BCM2835_ARM_IO_BASE 0x3f000000 +#define BCM2835_VCBUS_SDRAM_BASE BCM2835_VCBUS_SDRAM_UNCACHED +#else #define BCM2835_ARM_IO_BASE 0x20000000 -#define BCM2835_ARM_IO_SIZE 0x02000000 +#define BCM2835_VCBUS_SDRAM_BASE BCM2835_VCBUS_SDRAM_CACHED +#endif +#define BCM2835_ARM_IO_SIZE 0x01000000 /* * Convert physical address to VC bus address. Should be used * when submitting address over mailbox interface */ -#define PHYS_TO_VCBUS(pa) ((pa) + BCM2835_VCBUS_SDRAM_CACHED) +#define PHYS_TO_VCBUS(pa) ((pa) + BCM2835_VCBUS_SDRAM_BASE) /* Check whether pa bellong top IO window */ #define BCM2835_ARM_IS_IO(pa) (((pa) >= BCM2835_ARM_IO_BASE) && \ ((pa) < BCM2835_ARM_IO_BASE + BCM2835_ARM_IO_SIZE)) /* * Convert physical address in IO space to VC bus address. */ #define IO_TO_VCBUS(pa) ((pa - BCM2835_ARM_IO_BASE) + \ BCM2835_VCBUS_IO_BASE) /* * Convert address from VC bus space to physical. Should be used * when address is returned by VC over mailbox interface. e.g. * framebuffer base */ -#define VCBUS_TO_PHYS(vca) ((vca) - BCM2835_VCBUS_SDRAM_CACHED) +#define VCBUS_TO_PHYS(vca) ((vca) & ~(BCM2835_VCBUS_SDRAM_BASE)) #endif /* _BCM2835_VCBUS_H_ */ Index: stable/10/sys/arm/broadcom/bcm2835/bcm2835_wdog.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2835_wdog.c (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2835_wdog.c (revision 322724) @@ -1,209 +1,225 @@ /*- * Copyright (c) 2012 Alexander Rybalko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#define BCM2835_PASWORD 0x5a +#define BCM2835_PASSWORD 0x5a #define BCM2835_WDOG_RESET 0 #define BCM2835_PASSWORD_MASK 0xff000000 #define BCM2835_PASSWORD_SHIFT 24 #define BCM2835_WDOG_TIME_MASK 0x000fffff #define BCM2835_WDOG_TIME_SHIFT 0 -#define READ(_sc, _r) bus_space_read_4((_sc)->bst, (_sc)->bsh, (_r)) -#define WRITE(_sc, _r, _v) bus_space_write_4((_sc)->bst, (_sc)->bsh, (_r), (_v)) +#define READ(_sc, _r) bus_space_read_4((_sc)->bst, (_sc)->bsh, (_r) + (_sc)->regs_offset) +#define WRITE(_sc, _r, _v) bus_space_write_4((_sc)->bst, (_sc)->bsh, (_r) + (_sc)->regs_offset, (_v)) #define BCM2835_RSTC_WRCFG_CLR 0xffffffcf #define BCM2835_RSTC_WRCFG_SET 0x00000030 #define BCM2835_RSTC_WRCFG_FULL_RESET 0x00000020 #define BCM2835_RSTC_RESET 0x00000102 #define BCM2835_RSTC_REG 0x00 #define BCM2835_RSTS_REG 0x04 #define BCM2835_WDOG_REG 0x08 static struct bcmwd_softc *bcmwd_lsc = NULL; struct bcmwd_softc { device_t dev; struct resource * res; bus_space_tag_t bst; bus_space_handle_t bsh; int wdog_armed; int wdog_period; char wdog_passwd; struct mtx mtx; + int regs_offset; }; +#define BSD_DTB 1 +#define UPSTREAM_DTB 2 +#define UPSTREAM_DTB_REGS_OFFSET 0x1c + +static struct ofw_compat_data compat_data[] = { + {"broadcom,bcm2835-wdt", BSD_DTB}, + {"brcm,bcm2835-pm-wdt", UPSTREAM_DTB}, + {NULL, 0} +}; + static void bcmwd_watchdog_fn(void *private, u_int cmd, int *error); static int bcmwd_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (ofw_bus_is_compatible(dev, "broadcom,bcm2835-wdt")) { - device_set_desc(dev, "BCM2708/2835 Watchdog"); - return (BUS_PROBE_DEFAULT); - } + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); - return (ENXIO); + device_set_desc(dev, "BCM2708/2835 Watchdog"); + + return (BUS_PROBE_DEFAULT); } static int bcmwd_attach(device_t dev) { struct bcmwd_softc *sc; int rid; if (bcmwd_lsc != NULL) return (ENXIO); sc = device_get_softc(dev); sc->wdog_period = 7; - sc->wdog_passwd = BCM2835_PASWORD; + sc->wdog_passwd = BCM2835_PASSWORD; sc->wdog_armed = 0; sc->dev = dev; rid = 0; sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->res); sc->bsh = rman_get_bushandle(sc->res); + /* compensate base address difference */ + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data + == UPSTREAM_DTB) + sc->regs_offset = UPSTREAM_DTB_REGS_OFFSET; + bcmwd_lsc = sc; mtx_init(&sc->mtx, "BCM2835 Watchdog", "bcmwd", MTX_DEF); EVENTHANDLER_REGISTER(watchdog_list, bcmwd_watchdog_fn, sc, 0); return (0); } static void bcmwd_watchdog_fn(void *private, u_int cmd, int *error) { struct bcmwd_softc *sc; uint64_t sec; uint32_t ticks, reg; sc = private; mtx_lock(&sc->mtx); cmd &= WD_INTERVAL; if (cmd > 0) { sec = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000000; if (sec == 0 || sec > 15) { /* * Can't arm * disable watchdog as watchdog(9) requires */ device_printf(sc->dev, "Can't arm, timeout must be between 1-15 seconds\n"); WRITE(sc, BCM2835_RSTC_REG, - (BCM2835_PASWORD << BCM2835_PASSWORD_SHIFT) | + (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) | BCM2835_RSTC_RESET); mtx_unlock(&sc->mtx); return; } ticks = (sec << 16) & BCM2835_WDOG_TIME_MASK; - reg = (BCM2835_PASWORD << BCM2835_PASSWORD_SHIFT) | ticks; + reg = (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) | ticks; WRITE(sc, BCM2835_WDOG_REG, reg); reg = READ(sc, BCM2835_RSTC_REG); reg &= BCM2835_RSTC_WRCFG_CLR; reg |= BCM2835_RSTC_WRCFG_FULL_RESET; - reg |= (BCM2835_PASWORD << BCM2835_PASSWORD_SHIFT); + reg |= (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT); WRITE(sc, BCM2835_RSTC_REG, reg); *error = 0; } else WRITE(sc, BCM2835_RSTC_REG, - (BCM2835_PASWORD << BCM2835_PASSWORD_SHIFT) | + (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) | BCM2835_RSTC_RESET); mtx_unlock(&sc->mtx); } void bcmwd_watchdog_reset() { if (bcmwd_lsc == NULL) return; WRITE(bcmwd_lsc, BCM2835_WDOG_REG, - (BCM2835_PASWORD << BCM2835_PASSWORD_SHIFT) | 10); + (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) | 10); WRITE(bcmwd_lsc, BCM2835_RSTC_REG, (READ(bcmwd_lsc, BCM2835_RSTC_REG) & BCM2835_RSTC_WRCFG_CLR) | - (BCM2835_PASWORD << BCM2835_PASSWORD_SHIFT) | + (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) | BCM2835_RSTC_WRCFG_FULL_RESET); } static device_method_t bcmwd_methods[] = { DEVMETHOD(device_probe, bcmwd_probe), DEVMETHOD(device_attach, bcmwd_attach), DEVMETHOD_END }; static driver_t bcmwd_driver = { "bcmwd", bcmwd_methods, sizeof(struct bcmwd_softc), }; static devclass_t bcmwd_devclass; DRIVER_MODULE(bcmwd, simplebus, bcmwd_driver, bcmwd_devclass, 0, 0); Index: stable/10/sys/arm/broadcom/bcm2835/bcm2836.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2836.c (nonexistent) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2836.c (revision 322724) @@ -0,0 +1,184 @@ +/* + * Copyright 2015 Andrew Turner. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#define ARM_LOCAL_BASE 0x40000000 +#define ARM_LOCAL_SIZE 0x00001000 + +#define ARM_LOCAL_CONTROL 0x00 +#define ARM_LOCAL_PRESCALER 0x08 +#define PRESCALER_19_2 0x80000000 /* 19.2 MHz */ +#define ARM_LOCAL_INT_TIMER(n) (0x40 + (n) * 4) +#define ARM_LOCAL_INT_MAILBOX(n) (0x50 + (n) * 4) +#define ARM_LOCAL_INT_PENDING(n) (0x60 + (n) * 4) +#define INT_PENDING_MASK 0x0f + +/* + * A driver for features of the bcm2836. + */ + +struct bcm2836_softc { + device_t sc_dev; + struct resource *sc_mem; +}; + +static device_identify_t bcm2836_identify; +static device_probe_t bcm2836_probe; +static device_attach_t bcm2836_attach; + +struct bcm2836_softc *softc; + +static void +bcm2836_identify(driver_t *driver, device_t parent) +{ + + if (BUS_ADD_CHILD(parent, 0, "bcm2836", -1) == NULL) + device_printf(parent, "add child failed\n"); +} + +static int +bcm2836_probe(device_t dev) +{ + + if (softc != NULL) + return (ENXIO); + + device_set_desc(dev, "Broadcom bcm2836"); + + return (BUS_PROBE_DEFAULT); +} + +static int +bcm2836_attach(device_t dev) +{ + int i, rid; + + softc = device_get_softc(dev); + softc->sc_dev = dev; + + rid = 0; + softc->sc_mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + ARM_LOCAL_BASE, ARM_LOCAL_BASE + ARM_LOCAL_SIZE, ARM_LOCAL_SIZE, + RF_ACTIVE); + if (softc->sc_mem == NULL) { + device_printf(dev, "could not allocate memory resource\n"); + return (ENXIO); + } + + bus_write_4(softc->sc_mem, ARM_LOCAL_CONTROL, 0); + bus_write_4(softc->sc_mem, ARM_LOCAL_PRESCALER, PRESCALER_19_2); + + for (i = 0; i < 4; i++) + bus_write_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i), 0); + + for (i = 0; i < 4; i++) + bus_write_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(i), 1); + + return (0); +} + +int +bcm2836_get_next_irq(int last_irq) +{ + uint32_t reg; + int cpu; + int irq; + + cpu = PCPU_GET(cpuid); + + reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_PENDING(cpu)); + reg &= INT_PENDING_MASK; + if (reg == 0) + return (-1); + + irq = ffs(reg) - 1; + + return (irq); +} + +void +bcm2836_mask_irq(uintptr_t irq) +{ + uint32_t reg; + int i; + + for (i = 0; i < 4; i++) { + reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i)); + reg &= ~(1 << irq); + bus_write_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i), reg); + } +} + +void +bcm2836_unmask_irq(uintptr_t irq) +{ + uint32_t reg; + int i; + + for (i = 0; i < 4; i++) { + reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i)); + reg |= (1 << irq); + bus_write_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i), reg); + } +} + +static device_method_t bcm2836_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, bcm2836_identify), + DEVMETHOD(device_probe, bcm2836_probe), + DEVMETHOD(device_attach, bcm2836_attach), + + DEVMETHOD_END +}; + +static devclass_t bcm2836_devclass; + +static driver_t bcm2836_driver = { + "bcm2836", + bcm2836_methods, + sizeof(struct bcm2836_softc), +}; + +EARLY_DRIVER_MODULE(bcm2836, nexus, bcm2836_driver, bcm2836_devclass, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); Property changes on: stable/10/sys/arm/broadcom/bcm2835/bcm2836.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/10/sys/arm/broadcom/bcm2835/bcm2836.h =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm2836.h (nonexistent) +++ stable/10/sys/arm/broadcom/bcm2835/bcm2836.h (revision 322724) @@ -0,0 +1,37 @@ +/* + * Copyright 2015 Andrew Turner. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _BCM2815_BCM2836_H +#define _BCM2815_BCM2836_H + +int bcm2836_get_next_irq(int); +void bcm2836_mask_irq(uintptr_t); +void bcm2836_unmask_irq(uintptr_t); + +#endif Property changes on: stable/10/sys/arm/broadcom/bcm2835/bcm2836.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/10/sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c (nonexistent) +++ stable/10/sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c (revision 322724) @@ -0,0 +1,104 @@ +/* + * Copyright 2015 Andrew Turner. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +static struct ofw_compat_data compat_data[] = { + {"broadcom,bcm2835-usb", 1}, + {"brcm,bcm2708-usb", 1}, + {NULL, 0} +}; + +static device_probe_t bcm283x_dwc_otg_probe; +static device_attach_t bcm283x_dwc_otg_attach; + +static int +bcm283x_dwc_otg_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "DWC OTG 2.0 integrated USB controller (bcm283x)"); + + return (BUS_PROBE_VENDOR); +} + +static int +bcm283x_dwc_otg_attach(device_t dev) +{ + int err; + + err = bcm2835_mbox_set_power_state(BCM2835_MBOX_POWER_ID_USB_HCD, TRUE); + if (err) + device_printf(dev, "failed to set power state, err=%d\n", err); + + return (dwc_otg_attach(dev)); +} + +static device_method_t bcm283x_dwc_otg_methods[] = { + /* bus interface */ + DEVMETHOD(device_probe, bcm283x_dwc_otg_probe), + DEVMETHOD(device_attach, bcm283x_dwc_otg_attach), + + DEVMETHOD_END +}; + +static devclass_t bcm283x_dwc_otg_devclass; + +DEFINE_CLASS_1(bcm283x_dwcotg, bcm283x_dwc_otg_driver, bcm283x_dwc_otg_methods, + sizeof(struct dwc_otg_fdt_softc), dwc_otg_driver); +DRIVER_MODULE(bcm283x_dwcotg, simplebus, bcm283x_dwc_otg_driver, + bcm283x_dwc_otg_devclass, 0, 0); +MODULE_DEPEND(bcm283x_dwcotg, usb, 1, 1, 1); Property changes on: stable/10/sys/arm/broadcom/bcm2835/bcm283x_dwc_fdt.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/10/sys/arm/broadcom/bcm2835/files.bcm2835 =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/files.bcm2835 (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/files.bcm2835 (revision 322724) @@ -1,30 +1,32 @@ # $FreeBSD$ arm/broadcom/bcm2835/bcm2835_bsc.c optional bcm2835_bsc arm/broadcom/bcm2835/bcm2835_common.c optional fdt arm/broadcom/bcm2835/bcm2835_cpufreq.c standard arm/broadcom/bcm2835/bcm2835_dma.c standard arm/broadcom/bcm2835/bcm2835_fb.c optional sc arm/broadcom/bcm2835/bcm2835_fbd.c optional vt arm/broadcom/bcm2835/bcm2835_gpio.c optional gpio arm/broadcom/bcm2835/bcm2835_intr.c standard arm/broadcom/bcm2835/bcm2835_machdep.c standard arm/broadcom/bcm2835/bcm2835_mbox.c standard arm/broadcom/bcm2835/bcm2835_sdhci.c optional sdhci arm/broadcom/bcm2835/bcm2835_spi.c optional bcm2835_spi arm/broadcom/bcm2835/bcm2835_systimer.c standard arm/broadcom/bcm2835/bcm2835_wdog.c standard dev/usb/controller/dwc_otg_fdt.c optional dwcotg +arm/broadcom/bcm2835/bcm283x_dwc_fdt.c optional dwcotg fdt + arm/arm/bus_space_base.c standard arm/arm/bus_space_generic.c standard arm/arm/bus_space_asm_generic.S standard arm/arm/cpufunc_asm_arm11.S standard arm/arm/cpufunc_asm_arm11x6.S standard arm/arm/cpufunc_asm_armv5.S standard arm/arm/cpufunc_asm_armv6.S standard kern/kern_clocksource.c standard dev/mbox/mbox_if.m standard dev/ofw/ofw_cpu.c standard Index: stable/10/sys/arm/broadcom/bcm2835/files.bcm2836 =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/files.bcm2836 (nonexistent) +++ stable/10/sys/arm/broadcom/bcm2835/files.bcm2836 (revision 322724) @@ -0,0 +1,6 @@ +# $FreeBSD$ + +arm/arm/cpufunc_asm_armv7.S standard +arm/arm/generic_timer.c standard + +arm/broadcom/bcm2835/bcm2836.c standard Property changes on: stable/10/sys/arm/broadcom/bcm2835/files.bcm2836 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/10/sys/arm/broadcom/bcm2835/std.bcm2835 =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/std.bcm2835 (revision 322723) +++ stable/10/sys/arm/broadcom/bcm2835/std.bcm2835 (revision 322724) @@ -1,8 +1,9 @@ # $FreeBSD$ machine arm armv6 cpu CPU_ARM1176 makeoptions CONF_CFLAGS="-mcpu=arm1176jzf-s -Wa,-mcpu=arm1176jzf-s" +options SOC_BCM2835 files "../broadcom/bcm2835/files.bcm2835" Index: stable/10/sys/arm/broadcom/bcm2835/std.bcm2836 =================================================================== --- stable/10/sys/arm/broadcom/bcm2835/std.bcm2836 (nonexistent) +++ stable/10/sys/arm/broadcom/bcm2835/std.bcm2836 (revision 322724) @@ -0,0 +1,10 @@ +# $FreeBSD$ + +machine arm armv6 +cpu CPU_CORTEXA +makeoptions CONF_CFLAGS="-march=armv7a" +options SOC_BCM2836 + +files "../broadcom/bcm2835/files.bcm2836" +files "../broadcom/bcm2835/files.bcm283x" + Property changes on: stable/10/sys/arm/broadcom/bcm2835/std.bcm2836 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/10/sys/conf/options.arm =================================================================== --- stable/10/sys/conf/options.arm (revision 322723) +++ stable/10/sys/conf/options.arm (revision 322724) @@ -1,64 +1,66 @@ #$FreeBSD$ ARM9_CACHE_WRITE_THROUGH opt_global.h ARMV6 opt_global.h ARM_CACHE_LOCK_ENABLE opt_global.h ARM_KERN_DIRECTMAP opt_vm.h ARM_L2_PIPT opt_global.h ARM_MANY_BOARD opt_global.h ARM_VFP_SUPPORT opt_global.h ARM_WANT_TP_ADDRESS opt_global.h COUNTS_PER_SEC opt_timer.h CPU_ARM9 opt_global.h CPU_ARM9E opt_global.h CPU_ARM1136 opt_global.h CPU_ARM1176 opt_global.h CPU_CORTEXA opt_global.h CPU_KRAIT opt_global.h CPU_FA526 opt_global.h CPU_FA626TE opt_global.h CPU_MV_PJ4B opt_global.h CPU_XSCALE_80219 opt_global.h CPU_XSCALE_80321 opt_global.h CPU_XSCALE_81342 opt_global.h CPU_XSCALE_IXP425 opt_global.h CPU_XSCALE_IXP435 opt_global.h CPU_XSCALE_PXA2X0 opt_global.h FLASHADDR opt_global.h IPI_IRQ_START opt_smp.h IPI_IRQ_END opt_smp.h FREEBSD_BOOT_LOADER opt_global.h IXP4XX_FLASH_SIZE opt_global.h KERNBASE opt_global.h KERNPHYSADDR opt_global.h KERNVIRTADDR opt_global.h LINUX_BOOT_ABI opt_global.h LOADERRAMADDR opt_global.h PHYSADDR opt_global.h SOCDEV_PA opt_global.h SOCDEV_VA opt_global.h PV_STATS opt_pmap.h QEMU_WORKAROUNDS opt_global.h +SOC_BCM2835 opt_global.h +SOC_BCM2836 opt_global.h SOC_MV_ARMADAXP opt_global.h SOC_MV_DISCOVERY opt_global.h SOC_MV_DOVE opt_global.h SOC_MV_FREY opt_global.h SOC_MV_KIRKWOOD opt_global.h SOC_MV_LOKIPLUS opt_global.h SOC_MV_ORION opt_global.h SOC_OMAP3 opt_global.h SOC_OMAP4 opt_global.h SOC_TI_AM335X opt_global.h SOC_TEGRA2 opt_global.h XSCALE_CACHE_READ_WRITE_ALLOCATE opt_global.h XSACLE_DISABLE_CCNT opt_timer.h VERBOSE_INIT_ARM opt_global.h VM_MAXUSER_ADDRESS opt_global.h AT91_ATE_USE_RMII opt_at91.h AT91_MCI_ALLOW_OVERCLOCK opt_at91.h AT91_MCI_HAS_4WIRE opt_at91.h AT91_MCI_SLOT_B opt_at91.h GFB_DEBUG opt_gfb.h GFB_NO_FONT_LOADING opt_gfb.h GFB_NO_MODE_CHANGE opt_gfb.h AT91C_MAIN_CLOCK opt_at91.h VFP opt_global.h Index: stable/10/sys/dev/usb/controller/dwc_otg_fdt.c =================================================================== --- stable/10/sys/dev/usb/controller/dwc_otg_fdt.c (revision 322723) +++ stable/10/sys/dev/usb/controller/dwc_otg_fdt.c (revision 322724) @@ -1,210 +1,211 @@ /*- * Copyright (c) 2012 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include static device_probe_t dwc_otg_probe; -static device_attach_t dwc_otg_attach; static device_detach_t dwc_otg_detach; -struct dwc_otg_super_softc { - struct dwc_otg_softc sc_otg; /* must be first */ -}; - static int dwc_otg_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "synopsys,designware-hs-otg2")) return (ENXIO); device_set_desc(dev, "DWC OTG 2.0 integrated USB controller"); - return (0); + return (BUS_PROBE_DEFAULT); } -static int +int dwc_otg_attach(device_t dev) { - struct dwc_otg_super_softc *sc = device_get_softc(dev); + struct dwc_otg_fdt_softc *sc = device_get_softc(dev); int err; int rid; /* initialise some bus fields */ sc->sc_otg.sc_bus.parent = dev; sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices; sc->sc_otg.sc_bus.devices_max = DWC_OTG_MAX_DEVICES; sc->sc_otg.sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_otg.sc_bus, USB_GET_DMA_TAG(dev), NULL)) { return (ENOMEM); } rid = 0; sc->sc_otg.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!(sc->sc_otg.sc_io_res)) { err = ENOMEM; goto error; } sc->sc_otg.sc_io_tag = rman_get_bustag(sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_hdl = rman_get_bushandle(sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_size = rman_get_size(sc->sc_otg.sc_io_res); - rid = 0; + + /* + * brcm,bcm2708-usb FDT provides two interrupts, + * we need only second one (VC_USB) + */ + rid = ofw_bus_is_compatible(dev, "brcm,bcm2708-usb") ? 1 : 0; sc->sc_otg.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_otg.sc_irq_res == NULL) goto error; sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (sc->sc_otg.sc_bus.bdev == NULL) goto error; device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus); err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_TTY | INTR_MPSAFE, &dwc_otg_filter_interrupt, &dwc_otg_interrupt, sc, &sc->sc_otg.sc_intr_hdl); if (err) { sc->sc_otg.sc_intr_hdl = NULL; goto error; } err = dwc_otg_init(&sc->sc_otg); if (err == 0) { err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev); } if (err) goto error; return (0); error: dwc_otg_detach(dev); return (ENXIO); } static int dwc_otg_detach(device_t dev) { - struct dwc_otg_super_softc *sc = device_get_softc(dev); + struct dwc_otg_fdt_softc *sc = device_get_softc(dev); int err; /* during module unload there are lots of children leftover */ device_delete_children(dev); if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) { /* * only call dwc_otg_uninit() after dwc_otg_init() */ dwc_otg_uninit(&sc->sc_otg); err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res, sc->sc_otg.sc_intr_hdl); sc->sc_otg.sc_intr_hdl = NULL; } /* free IRQ channel, if any */ if (sc->sc_otg.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_otg.sc_irq_res); sc->sc_otg.sc_irq_res = NULL; } /* free memory resource, if any */ if (sc->sc_otg.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_otg.sc_bus, NULL); return (0); } static device_method_t dwc_otg_methods[] = { /* Device interface */ DEVMETHOD(device_probe, dwc_otg_probe), DEVMETHOD(device_attach, dwc_otg_attach), DEVMETHOD(device_detach, dwc_otg_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; -static driver_t dwc_otg_driver = { +driver_t dwc_otg_driver = { .name = "dwcotg", .methods = dwc_otg_methods, - .size = sizeof(struct dwc_otg_super_softc), + .size = sizeof(struct dwc_otg_fdt_softc), }; static devclass_t dwc_otg_devclass; DRIVER_MODULE(dwcotg, simplebus, dwc_otg_driver, dwc_otg_devclass, 0, 0); MODULE_DEPEND(dwcotg, usb, 1, 1, 1); Index: stable/10/sys/dev/usb/controller/dwc_otg_fdt.h =================================================================== --- stable/10/sys/dev/usb/controller/dwc_otg_fdt.h (nonexistent) +++ stable/10/sys/dev/usb/controller/dwc_otg_fdt.h (revision 322724) @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2012 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DWC_OTG_FDT_H_ +#define _DWC_OTG_FDT_H_ + +struct dwc_otg_fdt_softc { + struct dwc_otg_softc sc_otg; /* must be first */ +}; + +extern driver_t dwc_otg_driver; + +device_attach_t dwc_otg_attach; + +#endif Property changes on: stable/10/sys/dev/usb/controller/dwc_otg_fdt.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/10/sys/dev/usb/net/if_smsc.c =================================================================== --- stable/10/sys/dev/usb/net/if_smsc.c (revision 322723) +++ stable/10/sys/dev/usb/net/if_smsc.c (revision 322724) @@ -1,1841 +1,1907 @@ /*- * Copyright (c) 2012 * Ben Gray . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * SMSC LAN9xxx devices (http://www.smsc.com/) * * The LAN9500 & LAN9500A devices are stand-alone USB to Ethernet chips that * support USB 2.0 and 10/100 Mbps Ethernet. * * The LAN951x devices are an integrated USB hub and USB to Ethernet adapter. * The driver only covers the Ethernet part, the standard USB hub driver * supports the hub part. * * This driver is closely modelled on the Linux driver written and copyrighted * by SMSC. * * * * * H/W TCP & UDP Checksum Offloading * --------------------------------- * The chip supports both tx and rx offloading of UDP & TCP checksums, this * feature can be dynamically enabled/disabled. * * RX checksuming is performed across bytes after the IPv4 header to the end of * the Ethernet frame, this means if the frame is padded with non-zero values * the H/W checksum will be incorrect, however the rx code compensates for this. * * TX checksuming is more complicated, the device requires a special header to * be prefixed onto the start of the frame which indicates the start and end * positions of the UDP or TCP frame. This requires the driver to manually * go through the packet data and decode the headers prior to sending. * On Linux they generally provide cues to the location of the csum and the * area to calculate it over, on FreeBSD we seem to have to do it all ourselves, * hence this is not as optimal and therefore h/w tX checksum is currently not * implemented. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opt_platform.h" #ifdef FDT #include #include #include #endif #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR smsc_debug #include #include #include #include #ifdef USB_DEBUG static int smsc_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, smsc, CTLFLAG_RW, 0, "USB smsc"); SYSCTL_INT(_hw_usb_smsc, OID_AUTO, debug, CTLFLAG_RW, &smsc_debug, 0, "Debug level"); #endif /* * Various supported device vendors/products. */ static const struct usb_device_id smsc_devs[] = { #define SMSC_DEV(p,i) { USB_VPI(USB_VENDOR_SMC2, USB_PRODUCT_SMC2_##p, i) } SMSC_DEV(LAN9514_ETH, 0), #undef SMSC_DEV }; #ifdef USB_DEBUG #define smsc_dbg_printf(sc, fmt, args...) \ do { \ if (smsc_debug > 0) \ device_printf((sc)->sc_ue.ue_dev, "debug: " fmt, ##args); \ } while(0) #else #define smsc_dbg_printf(sc, fmt, args...) do { } while (0) #endif #define smsc_warn_printf(sc, fmt, args...) \ device_printf((sc)->sc_ue.ue_dev, "warning: " fmt, ##args) #define smsc_err_printf(sc, fmt, args...) \ device_printf((sc)->sc_ue.ue_dev, "error: " fmt, ##args) #define ETHER_IS_ZERO(addr) \ (!(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5])) #define ETHER_IS_VALID(addr) \ (!ETHER_IS_MULTICAST(addr) && !ETHER_IS_ZERO(addr)) static device_probe_t smsc_probe; static device_attach_t smsc_attach; static device_detach_t smsc_detach; static usb_callback_t smsc_bulk_read_callback; static usb_callback_t smsc_bulk_write_callback; static miibus_readreg_t smsc_miibus_readreg; static miibus_writereg_t smsc_miibus_writereg; static miibus_statchg_t smsc_miibus_statchg; #if __FreeBSD_version > 1000000 static int smsc_attach_post_sub(struct usb_ether *ue); #endif static uether_fn_t smsc_attach_post; static uether_fn_t smsc_init; static uether_fn_t smsc_stop; static uether_fn_t smsc_start; static uether_fn_t smsc_tick; static uether_fn_t smsc_setmulti; static uether_fn_t smsc_setpromisc; static int smsc_ifmedia_upd(struct ifnet *); static void smsc_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int smsc_chip_init(struct smsc_softc *sc); static int smsc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); static const struct usb_config smsc_config[SMSC_N_TRANSFER] = { [SMSC_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .frames = 16, .bufsize = 16 * (MCLBYTES + 16), .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = smsc_bulk_write_callback, .timeout = 10000, /* 10 seconds */ }, [SMSC_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = 20480, /* bytes */ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = smsc_bulk_read_callback, .timeout = 0, /* no timeout */ }, /* The SMSC chip supports an interrupt endpoints, however they aren't * needed as we poll on the MII status. */ }; static const struct usb_ether_methods smsc_ue_methods = { .ue_attach_post = smsc_attach_post, #if __FreeBSD_version > 1000000 .ue_attach_post_sub = smsc_attach_post_sub, #endif .ue_start = smsc_start, .ue_ioctl = smsc_ioctl, .ue_init = smsc_init, .ue_stop = smsc_stop, .ue_tick = smsc_tick, .ue_setmulti = smsc_setmulti, .ue_setpromisc = smsc_setpromisc, .ue_mii_upd = smsc_ifmedia_upd, .ue_mii_sts = smsc_ifmedia_sts, }; /** * smsc_read_reg - Reads a 32-bit register on the device * @sc: driver soft context * @off: offset of the register * @data: pointer a value that will be populated with the register value * * LOCKING: * The device lock must be held before calling this function. * * RETURNS: * 0 on success, a USB_ERR_?? error code on failure. */ static int smsc_read_reg(struct smsc_softc *sc, uint32_t off, uint32_t *data) { struct usb_device_request req; uint32_t buf; usb_error_t err; SMSC_LOCK_ASSERT(sc, MA_OWNED); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = SMSC_UR_READ_REG; USETW(req.wValue, 0); USETW(req.wIndex, off); USETW(req.wLength, 4); err = uether_do_request(&sc->sc_ue, &req, &buf, 1000); if (err != 0) smsc_warn_printf(sc, "Failed to read register 0x%0x\n", off); *data = le32toh(buf); return (err); } /** * smsc_write_reg - Writes a 32-bit register on the device * @sc: driver soft context * @off: offset of the register * @data: the 32-bit value to write into the register * * LOCKING: * The device lock must be held before calling this function. * * RETURNS: * 0 on success, a USB_ERR_?? error code on failure. */ static int smsc_write_reg(struct smsc_softc *sc, uint32_t off, uint32_t data) { struct usb_device_request req; uint32_t buf; usb_error_t err; SMSC_LOCK_ASSERT(sc, MA_OWNED); buf = htole32(data); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = SMSC_UR_WRITE_REG; USETW(req.wValue, 0); USETW(req.wIndex, off); USETW(req.wLength, 4); err = uether_do_request(&sc->sc_ue, &req, &buf, 1000); if (err != 0) smsc_warn_printf(sc, "Failed to write register 0x%0x\n", off); return (err); } /** * smsc_wait_for_bits - Polls on a register value until bits are cleared * @sc: soft context * @reg: offset of the register * @bits: if the bits are clear the function returns * * LOCKING: * The device lock must be held before calling this function. * * RETURNS: * 0 on success, or a USB_ERR_?? error code on failure. */ static int smsc_wait_for_bits(struct smsc_softc *sc, uint32_t reg, uint32_t bits) { usb_ticks_t start_ticks; const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); uint32_t val; int err; SMSC_LOCK_ASSERT(sc, MA_OWNED); start_ticks = (usb_ticks_t)ticks; do { if ((err = smsc_read_reg(sc, reg, &val)) != 0) return (err); if (!(val & bits)) return (0); uether_pause(&sc->sc_ue, hz / 100); } while (((usb_ticks_t)(ticks - start_ticks)) < max_ticks); return (USB_ERR_TIMEOUT); } /** * smsc_eeprom_read - Reads the attached EEPROM * @sc: soft context * @off: the eeprom address offset * @buf: stores the bytes * @buflen: the number of bytes to read * * Simply reads bytes from an attached eeprom. * * LOCKING: * The function takes and releases the device lock if it is not already held. * * RETURNS: * 0 on success, or a USB_ERR_?? error code on failure. */ static int smsc_eeprom_read(struct smsc_softc *sc, uint16_t off, uint8_t *buf, uint16_t buflen) { usb_ticks_t start_ticks; const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); int err; int locked; uint32_t val; uint16_t i; locked = mtx_owned(&sc->sc_mtx); if (!locked) SMSC_LOCK(sc); err = smsc_wait_for_bits(sc, SMSC_EEPROM_CMD, SMSC_EEPROM_CMD_BUSY); if (err != 0) { smsc_warn_printf(sc, "eeprom busy, failed to read data\n"); goto done; } /* start reading the bytes, one at a time */ for (i = 0; i < buflen; i++) { val = SMSC_EEPROM_CMD_BUSY | (SMSC_EEPROM_CMD_ADDR_MASK & (off + i)); if ((err = smsc_write_reg(sc, SMSC_EEPROM_CMD, val)) != 0) goto done; start_ticks = (usb_ticks_t)ticks; do { if ((err = smsc_read_reg(sc, SMSC_EEPROM_CMD, &val)) != 0) goto done; if (!(val & SMSC_EEPROM_CMD_BUSY) || (val & SMSC_EEPROM_CMD_TIMEOUT)) break; uether_pause(&sc->sc_ue, hz / 100); } while (((usb_ticks_t)(ticks - start_ticks)) < max_ticks); if (val & (SMSC_EEPROM_CMD_BUSY | SMSC_EEPROM_CMD_TIMEOUT)) { smsc_warn_printf(sc, "eeprom command failed\n"); err = USB_ERR_IOERROR; break; } if ((err = smsc_read_reg(sc, SMSC_EEPROM_DATA, &val)) != 0) goto done; buf[i] = (val & 0xff); } done: if (!locked) SMSC_UNLOCK(sc); return (err); } /** * smsc_miibus_readreg - Reads a MII/MDIO register * @dev: usb ether device * @phy: the number of phy reading from * @reg: the register address * * Attempts to read a phy register over the MII bus. * * LOCKING: * Takes and releases the device mutex lock if not already held. * * RETURNS: * Returns the 16-bits read from the MII register, if this function fails 0 * is returned. */ static int smsc_miibus_readreg(device_t dev, int phy, int reg) { struct smsc_softc *sc = device_get_softc(dev); int locked; uint32_t addr; uint32_t val = 0; locked = mtx_owned(&sc->sc_mtx); if (!locked) SMSC_LOCK(sc); if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) { smsc_warn_printf(sc, "MII is busy\n"); goto done; } addr = (phy << 11) | (reg << 6) | SMSC_MII_READ; smsc_write_reg(sc, SMSC_MII_ADDR, addr); if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) smsc_warn_printf(sc, "MII read timeout\n"); smsc_read_reg(sc, SMSC_MII_DATA, &val); val = le32toh(val); done: if (!locked) SMSC_UNLOCK(sc); return (val & 0xFFFF); } /** * smsc_miibus_writereg - Writes a MII/MDIO register * @dev: usb ether device * @phy: the number of phy writing to * @reg: the register address * @val: the value to write * * Attempts to write a phy register over the MII bus. * * LOCKING: * Takes and releases the device mutex lock if not already held. * * RETURNS: * Always returns 0 regardless of success or failure. */ static int smsc_miibus_writereg(device_t dev, int phy, int reg, int val) { struct smsc_softc *sc = device_get_softc(dev); int locked; uint32_t addr; if (sc->sc_phyno != phy) return (0); locked = mtx_owned(&sc->sc_mtx); if (!locked) SMSC_LOCK(sc); if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) { smsc_warn_printf(sc, "MII is busy\n"); goto done; } val = htole32(val); smsc_write_reg(sc, SMSC_MII_DATA, val); addr = (phy << 11) | (reg << 6) | SMSC_MII_WRITE; smsc_write_reg(sc, SMSC_MII_ADDR, addr); if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) smsc_warn_printf(sc, "MII write timeout\n"); done: if (!locked) SMSC_UNLOCK(sc); return (0); } /** * smsc_miibus_statchg - Called to detect phy status change * @dev: usb ether device * * This function is called periodically by the system to poll for status * changes of the link. * * LOCKING: * Takes and releases the device mutex lock if not already held. */ static void smsc_miibus_statchg(device_t dev) { struct smsc_softc *sc = device_get_softc(dev); struct mii_data *mii = uether_getmii(&sc->sc_ue); struct ifnet *ifp; int locked; int err; uint32_t flow; uint32_t afc_cfg; locked = mtx_owned(&sc->sc_mtx); if (!locked) SMSC_LOCK(sc); ifp = uether_getifp(&sc->sc_ue); if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) goto done; /* Use the MII status to determine link status */ sc->sc_flags &= ~SMSC_FLAG_LINK; if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID)) { switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: sc->sc_flags |= SMSC_FLAG_LINK; break; case IFM_1000_T: /* Gigabit ethernet not supported by chipset */ break; default: break; } } /* Lost link, do nothing. */ if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) { smsc_dbg_printf(sc, "link flag not set\n"); goto done; } err = smsc_read_reg(sc, SMSC_AFC_CFG, &afc_cfg); if (err) { smsc_warn_printf(sc, "failed to read initial AFC_CFG, error %d\n", err); goto done; } /* Enable/disable full duplex operation and TX/RX pause */ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { smsc_dbg_printf(sc, "full duplex operation\n"); sc->sc_mac_csr &= ~SMSC_MAC_CSR_RCVOWN; sc->sc_mac_csr |= SMSC_MAC_CSR_FDPX; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) flow = 0xffff0002; else flow = 0; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) afc_cfg |= 0xf; else afc_cfg &= ~0xf; } else { smsc_dbg_printf(sc, "half duplex operation\n"); sc->sc_mac_csr &= ~SMSC_MAC_CSR_FDPX; sc->sc_mac_csr |= SMSC_MAC_CSR_RCVOWN; flow = 0; afc_cfg |= 0xf; } err = smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); err += smsc_write_reg(sc, SMSC_FLOW, flow); err += smsc_write_reg(sc, SMSC_AFC_CFG, afc_cfg); if (err) smsc_warn_printf(sc, "media change failed, error %d\n", err); done: if (!locked) SMSC_UNLOCK(sc); } /** * smsc_ifmedia_upd - Set media options * @ifp: interface pointer * * Basically boilerplate code that simply calls the mii functions to set the * media options. * * LOCKING: * The device lock must be held before this function is called. * * RETURNS: * Returns 0 on success or a negative error code. */ static int smsc_ifmedia_upd(struct ifnet *ifp) { struct smsc_softc *sc = ifp->if_softc; struct mii_data *mii = uether_getmii(&sc->sc_ue); struct mii_softc *miisc; int err; SMSC_LOCK_ASSERT(sc, MA_OWNED); LIST_FOREACH(miisc, &mii->mii_phys, mii_list) PHY_RESET(miisc); err = mii_mediachg(mii); return (err); } /** * smsc_ifmedia_sts - Report current media status * @ifp: inet interface pointer * @ifmr: interface media request * * Basically boilerplate code that simply calls the mii functions to get the * media status. * * LOCKING: * Internally takes and releases the device lock. */ static void smsc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct smsc_softc *sc = ifp->if_softc; struct mii_data *mii = uether_getmii(&sc->sc_ue); SMSC_LOCK(sc); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; SMSC_UNLOCK(sc); } /** * smsc_hash - Calculate the hash of a mac address * @addr: The mac address to calculate the hash on * * This function is used when configuring a range of m'cast mac addresses to * filter on. The hash of the mac address is put in the device's mac hash * table. * * RETURNS: * Returns a value from 0-63 value which is the hash of the mac address. */ static inline uint32_t smsc_hash(uint8_t addr[ETHER_ADDR_LEN]) { return (ether_crc32_be(addr, ETHER_ADDR_LEN) >> 26) & 0x3f; } /** * smsc_setmulti - Setup multicast * @ue: usb ethernet device context * * Tells the device to either accept frames with a multicast mac address, a * select group of m'cast mac addresses or just the devices mac address. * * LOCKING: * Should be called with the SMSC lock held. */ static void smsc_setmulti(struct usb_ether *ue) { struct smsc_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); struct ifmultiaddr *ifma; uint32_t hashtbl[2] = { 0, 0 }; uint32_t hash; SMSC_LOCK_ASSERT(sc, MA_OWNED); if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { smsc_dbg_printf(sc, "receive all multicast enabled\n"); sc->sc_mac_csr |= SMSC_MAC_CSR_MCPAS; sc->sc_mac_csr &= ~SMSC_MAC_CSR_HPFILT; } else { /* Take the lock of the mac address list before hashing each of them */ if_maddr_rlock(ifp); if (!TAILQ_EMPTY(&ifp->if_multiaddrs)) { /* We are filtering on a set of address so calculate hashes of each * of the address and set the corresponding bits in the register. */ sc->sc_mac_csr |= SMSC_MAC_CSR_HPFILT; sc->sc_mac_csr &= ~(SMSC_MAC_CSR_PRMS | SMSC_MAC_CSR_MCPAS); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; hash = smsc_hash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); hashtbl[hash >> 5] |= 1 << (hash & 0x1F); } } else { /* Only receive packets with destination set to our mac address */ sc->sc_mac_csr &= ~(SMSC_MAC_CSR_MCPAS | SMSC_MAC_CSR_HPFILT); } if_maddr_runlock(ifp); /* Debug */ if (sc->sc_mac_csr & SMSC_MAC_CSR_HPFILT) smsc_dbg_printf(sc, "receive select group of macs\n"); else smsc_dbg_printf(sc, "receive own packets only\n"); } /* Write the hash table and mac control registers */ smsc_write_reg(sc, SMSC_HASHH, hashtbl[1]); smsc_write_reg(sc, SMSC_HASHL, hashtbl[0]); smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); } /** * smsc_setpromisc - Enables/disables promiscuous mode * @ue: usb ethernet device context * * LOCKING: * Should be called with the SMSC lock held. */ static void smsc_setpromisc(struct usb_ether *ue) { struct smsc_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); smsc_dbg_printf(sc, "promiscuous mode %sabled\n", (ifp->if_flags & IFF_PROMISC) ? "en" : "dis"); SMSC_LOCK_ASSERT(sc, MA_OWNED); if (ifp->if_flags & IFF_PROMISC) sc->sc_mac_csr |= SMSC_MAC_CSR_PRMS; else sc->sc_mac_csr &= ~SMSC_MAC_CSR_PRMS; smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); } /** * smsc_sethwcsum - Enable or disable H/W UDP and TCP checksumming * @sc: driver soft context * * LOCKING: * Should be called with the SMSC lock held. * * RETURNS: * Returns 0 on success or a negative error code. */ static int smsc_sethwcsum(struct smsc_softc *sc) { struct ifnet *ifp = uether_getifp(&sc->sc_ue); uint32_t val; int err; if (!ifp) return (-EIO); SMSC_LOCK_ASSERT(sc, MA_OWNED); err = smsc_read_reg(sc, SMSC_COE_CTRL, &val); if (err != 0) { smsc_warn_printf(sc, "failed to read SMSC_COE_CTRL (err=%d)\n", err); return (err); } /* Enable/disable the Rx checksum */ if ((ifp->if_capabilities & ifp->if_capenable) & IFCAP_RXCSUM) val |= SMSC_COE_CTRL_RX_EN; else val &= ~SMSC_COE_CTRL_RX_EN; /* Enable/disable the Tx checksum (currently not supported) */ if ((ifp->if_capabilities & ifp->if_capenable) & IFCAP_TXCSUM) val |= SMSC_COE_CTRL_TX_EN; else val &= ~SMSC_COE_CTRL_TX_EN; err = smsc_write_reg(sc, SMSC_COE_CTRL, val); if (err != 0) { smsc_warn_printf(sc, "failed to write SMSC_COE_CTRL (err=%d)\n", err); return (err); } return (0); } /** * smsc_setmacaddress - Sets the mac address in the device * @sc: driver soft context * @addr: pointer to array contain at least 6 bytes of the mac * * Writes the MAC address into the device, usually the MAC is programmed with * values from the EEPROM. * * LOCKING: * Should be called with the SMSC lock held. * * RETURNS: * Returns 0 on success or a negative error code. */ static int smsc_setmacaddress(struct smsc_softc *sc, const uint8_t *addr) { int err; uint32_t val; smsc_dbg_printf(sc, "setting mac address to %02x:%02x:%02x:%02x:%02x:%02x\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); SMSC_LOCK_ASSERT(sc, MA_OWNED); val = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; if ((err = smsc_write_reg(sc, SMSC_MAC_ADDRL, val)) != 0) goto done; val = (addr[5] << 8) | addr[4]; err = smsc_write_reg(sc, SMSC_MAC_ADDRH, val); done: return (err); } /** * smsc_reset - Reset the SMSC chip * @sc: device soft context * * LOCKING: * Should be called with the SMSC lock held. */ static void smsc_reset(struct smsc_softc *sc) { struct usb_config_descriptor *cd; usb_error_t err; cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev); err = usbd_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx, cd->bConfigurationValue); if (err) smsc_warn_printf(sc, "reset failed (ignored)\n"); /* Wait a little while for the chip to get its brains in order. */ uether_pause(&sc->sc_ue, hz / 100); /* Reinitialize controller to achieve full reset. */ smsc_chip_init(sc); } /** * smsc_init - Initialises the LAN95xx chip * @ue: USB ether interface * * Called when the interface is brought up (i.e. ifconfig ue0 up), this * initialise the interface and the rx/tx pipes. * * LOCKING: * Should be called with the SMSC lock held. */ static void smsc_init(struct usb_ether *ue) { struct smsc_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); SMSC_LOCK_ASSERT(sc, MA_OWNED); if (smsc_setmacaddress(sc, IF_LLADDR(ifp))) smsc_dbg_printf(sc, "setting MAC address failed\n"); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) return; /* Cancel pending I/O */ smsc_stop(ue); #if __FreeBSD_version <= 1000000 /* On earlier versions this was the first place we could tell the system * that we supported h/w csuming, however this is only called after the * the interface has been brought up - not ideal. */ if (!(ifp->if_capabilities & IFCAP_RXCSUM)) { ifp->if_capabilities |= IFCAP_RXCSUM; ifp->if_capenable |= IFCAP_RXCSUM; ifp->if_hwassist = 0; } /* TX checksuming is disabled for now ifp->if_capabilities |= IFCAP_TXCSUM; ifp->if_capenable |= IFCAP_TXCSUM; ifp->if_hwassist = CSUM_TCP | CSUM_UDP; */ #endif /* Reset the ethernet interface. */ smsc_reset(sc); /* Load the multicast filter. */ smsc_setmulti(ue); /* TCP/UDP checksum offload engines. */ smsc_sethwcsum(sc); usbd_xfer_set_stall(sc->sc_xfer[SMSC_BULK_DT_WR]); /* Indicate we are up and running. */ ifp->if_drv_flags |= IFF_DRV_RUNNING; /* Switch to selected media. */ smsc_ifmedia_upd(ifp); smsc_start(ue); } /** * smsc_bulk_read_callback - Read callback used to process the USB URB * @xfer: the USB transfer * @error: * * Reads the URB data which can contain one or more ethernet frames, the * frames are copyed into a mbuf and given to the system. * * LOCKING: * No locking required, doesn't access internal driver settings. */ static void smsc_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct smsc_softc *sc = usbd_xfer_softc(xfer); struct usb_ether *ue = &sc->sc_ue; struct ifnet *ifp = uether_getifp(ue); struct mbuf *m; struct usb_page_cache *pc; uint32_t rxhdr; uint16_t pktlen; int off; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); smsc_dbg_printf(sc, "rx : actlen %d\n", actlen); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: /* There is always a zero length frame after bringing the IF up */ if (actlen < (sizeof(rxhdr) + ETHER_CRC_LEN)) goto tr_setup; /* There maybe multiple packets in the USB frame, each will have a * header and each needs to have it's own mbuf allocated and populated * for it. */ pc = usbd_xfer_get_frame(xfer, 0); off = 0; while (off < actlen) { /* The frame header is always aligned on a 4 byte boundary */ off = ((off + 0x3) & ~0x3); usbd_copy_out(pc, off, &rxhdr, sizeof(rxhdr)); off += (sizeof(rxhdr) + ETHER_ALIGN); rxhdr = le32toh(rxhdr); pktlen = (uint16_t)SMSC_RX_STAT_FRM_LENGTH(rxhdr); smsc_dbg_printf(sc, "rx : rxhdr 0x%08x : pktlen %d : actlen %d : " "off %d\n", rxhdr, pktlen, actlen, off); if (rxhdr & SMSC_RX_STAT_ERROR) { smsc_dbg_printf(sc, "rx error (hdr 0x%08x)\n", rxhdr); ifp->if_ierrors++; if (rxhdr & SMSC_RX_STAT_COLLISION) ifp->if_collisions++; } else { /* Check if the ethernet frame is too big or too small */ if ((pktlen < ETHER_HDR_LEN) || (pktlen > (actlen - off))) goto tr_setup; /* Create a new mbuf to store the packet in */ m = uether_newbuf(); if (m == NULL) { smsc_warn_printf(sc, "failed to create new mbuf\n"); ifp->if_iqdrops++; goto tr_setup; } usbd_copy_out(pc, off, mtod(m, uint8_t *), pktlen); /* Check if RX TCP/UDP checksumming is being offloaded */ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) { struct ether_header *eh; eh = mtod(m, struct ether_header *); /* Remove the extra 2 bytes of the csum */ pktlen -= 2; /* The checksum appears to be simplistically calculated * over the udp/tcp header and data up to the end of the * eth frame. Which means if the eth frame is padded * the csum calculation is incorrectly performed over * the padding bytes as well. Therefore to be safe we * ignore the H/W csum on frames less than or equal to * 64 bytes. * * Ignore H/W csum for non-IPv4 packets. */ if ((be16toh(eh->ether_type) == ETHERTYPE_IP) && (pktlen > ETHER_MIN_LEN)) { struct ip *ip; ip = (struct ip *)(eh + 1); if ((ip->ip_v == IPVERSION) && ((ip->ip_p == IPPROTO_TCP) || (ip->ip_p == IPPROTO_UDP))) { /* Indicate the UDP/TCP csum has been calculated */ m->m_pkthdr.csum_flags |= CSUM_DATA_VALID; /* Copy the TCP/UDP checksum from the last 2 bytes * of the transfer and put in the csum_data field. */ usbd_copy_out(pc, (off + pktlen), &m->m_pkthdr.csum_data, 2); /* The data is copied in network order, but the * csum algorithm in the kernel expects it to be * in host network order. */ m->m_pkthdr.csum_data = ntohs(m->m_pkthdr.csum_data); smsc_dbg_printf(sc, "RX checksum offloaded (0x%04x)\n", m->m_pkthdr.csum_data); } } /* Need to adjust the offset as well or we'll be off * by 2 because the csum is removed from the packet * length. */ off += 2; } /* Finally enqueue the mbuf on the receive queue */ /* Remove 4 trailing bytes */ if (pktlen < (4 + ETHER_HDR_LEN)) { m_freem(m); goto tr_setup; } uether_rxmbuf(ue, m, pktlen - 4); } /* Update the offset to move to the next potential packet */ off += pktlen; } /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); uether_rxflush(ue); return; default: if (error != USB_ERR_CANCELLED) { smsc_warn_printf(sc, "bulk read error, %s\n", usbd_errstr(error)); usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } /** * smsc_bulk_write_callback - Write callback used to send ethernet frame(s) * @xfer: the USB transfer * @error: error code if the transfers is in an errored state * * The main write function that pulls ethernet frames off the queue and sends * them out. * * LOCKING: * */ static void smsc_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct smsc_softc *sc = usbd_xfer_softc(xfer); struct ifnet *ifp = uether_getifp(&sc->sc_ue); struct usb_page_cache *pc; struct mbuf *m; uint32_t txhdr; uint32_t frm_len = 0; int nframes; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: if ((sc->sc_flags & SMSC_FLAG_LINK) == 0 || (ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) { /* Don't send anything if there is no link or controller is busy. */ return; } for (nframes = 0; nframes < 16 && !IFQ_DRV_IS_EMPTY(&ifp->if_snd); nframes++) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; usbd_xfer_set_frame_offset(xfer, nframes * MCLBYTES, nframes); frm_len = 0; pc = usbd_xfer_get_frame(xfer, nframes); /* Each frame is prefixed with two 32-bit values describing the * length of the packet and buffer. */ txhdr = SMSC_TX_CTRL_0_BUF_SIZE(m->m_pkthdr.len) | SMSC_TX_CTRL_0_FIRST_SEG | SMSC_TX_CTRL_0_LAST_SEG; txhdr = htole32(txhdr); usbd_copy_in(pc, 0, &txhdr, sizeof(txhdr)); txhdr = SMSC_TX_CTRL_1_PKT_LENGTH(m->m_pkthdr.len); txhdr = htole32(txhdr); usbd_copy_in(pc, 4, &txhdr, sizeof(txhdr)); frm_len += 8; /* Next copy in the actual packet */ usbd_m_copy_in(pc, frm_len, m, 0, m->m_pkthdr.len); frm_len += m->m_pkthdr.len; ifp->if_opackets++; /* If there's a BPF listener, bounce a copy of this frame to him */ BPF_MTAP(ifp, m); m_freem(m); /* Set frame length. */ usbd_xfer_set_frame_len(xfer, nframes, frm_len); } if (nframes != 0) { usbd_xfer_set_frames(xfer, nframes); usbd_transfer_submit(xfer); ifp->if_drv_flags |= IFF_DRV_OACTIVE; } return; default: ifp->if_oerrors++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if (error != USB_ERR_CANCELLED) { smsc_err_printf(sc, "usb error on tx: %s\n", usbd_errstr(error)); usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } /** * smsc_tick - Called periodically to monitor the state of the LAN95xx chip * @ue: USB ether interface * * Simply calls the mii status functions to check the state of the link. * * LOCKING: * Should be called with the SMSC lock held. */ static void smsc_tick(struct usb_ether *ue) { struct smsc_softc *sc = uether_getsc(ue); struct mii_data *mii = uether_getmii(&sc->sc_ue); SMSC_LOCK_ASSERT(sc, MA_OWNED); mii_tick(mii); if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) { smsc_miibus_statchg(ue->ue_dev); if ((sc->sc_flags & SMSC_FLAG_LINK) != 0) smsc_start(ue); } } /** * smsc_start - Starts communication with the LAN95xx chip * @ue: USB ether interface * * * */ static void smsc_start(struct usb_ether *ue) { struct smsc_softc *sc = uether_getsc(ue); /* * start the USB transfers, if not already started: */ usbd_transfer_start(sc->sc_xfer[SMSC_BULK_DT_RD]); usbd_transfer_start(sc->sc_xfer[SMSC_BULK_DT_WR]); } /** * smsc_stop - Stops communication with the LAN95xx chip * @ue: USB ether interface * * * */ static void smsc_stop(struct usb_ether *ue) { struct smsc_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); SMSC_LOCK_ASSERT(sc, MA_OWNED); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->sc_flags &= ~SMSC_FLAG_LINK; /* * stop all the transfers, if not already stopped: */ usbd_transfer_stop(sc->sc_xfer[SMSC_BULK_DT_WR]); usbd_transfer_stop(sc->sc_xfer[SMSC_BULK_DT_RD]); } /** * smsc_phy_init - Initialises the in-built SMSC phy * @sc: driver soft context * * Resets the PHY part of the chip and then initialises it to default * values. The 'link down' and 'auto-negotiation complete' interrupts * from the PHY are also enabled, however we don't monitor the interrupt * endpoints for the moment. * * RETURNS: * Returns 0 on success or EIO if failed to reset the PHY. */ static int smsc_phy_init(struct smsc_softc *sc) { int bmcr; usb_ticks_t start_ticks; const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); SMSC_LOCK_ASSERT(sc, MA_OWNED); /* Reset phy and wait for reset to complete */ smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, BMCR_RESET); start_ticks = ticks; do { uether_pause(&sc->sc_ue, hz / 100); bmcr = smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR); } while ((bmcr & MII_BMCR) && ((ticks - start_ticks) < max_ticks)); if (((usb_ticks_t)(ticks - start_ticks)) >= max_ticks) { smsc_err_printf(sc, "PHY reset timed-out"); return (EIO); } smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_ANAR, ANAR_10 | ANAR_10_FD | ANAR_TX | ANAR_TX_FD | /* all modes */ ANAR_CSMA | ANAR_FC | ANAR_PAUSE_ASYM); /* Setup the phy to interrupt when the link goes down or autoneg completes */ smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, SMSC_PHY_INTR_STAT); smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, SMSC_PHY_INTR_MASK, (SMSC_PHY_INTR_ANEG_COMP | SMSC_PHY_INTR_LINK_DOWN)); /* Restart auto-negotation */ bmcr = smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR); bmcr |= BMCR_STARTNEG; smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, bmcr); return (0); } /** * smsc_chip_init - Initialises the chip after power on * @sc: driver soft context * * This initialisation sequence is modelled on the procedure in the Linux * driver. * * RETURNS: * Returns 0 on success or an error code on failure. */ static int smsc_chip_init(struct smsc_softc *sc) { int err; int locked; uint32_t reg_val; int burst_cap; locked = mtx_owned(&sc->sc_mtx); if (!locked) SMSC_LOCK(sc); /* Enter H/W config mode */ smsc_write_reg(sc, SMSC_HW_CFG, SMSC_HW_CFG_LRST); if ((err = smsc_wait_for_bits(sc, SMSC_HW_CFG, SMSC_HW_CFG_LRST)) != 0) { smsc_warn_printf(sc, "timed-out waiting for reset to complete\n"); goto init_failed; } /* Reset the PHY */ smsc_write_reg(sc, SMSC_PM_CTRL, SMSC_PM_CTRL_PHY_RST); if ((err = smsc_wait_for_bits(sc, SMSC_PM_CTRL, SMSC_PM_CTRL_PHY_RST)) != 0) { smsc_warn_printf(sc, "timed-out waiting for phy reset to complete\n"); goto init_failed; } /* Set the mac address */ if ((err = smsc_setmacaddress(sc, sc->sc_ue.ue_eaddr)) != 0) { smsc_warn_printf(sc, "failed to set the MAC address\n"); goto init_failed; } /* Don't know what the HW_CFG_BIR bit is, but following the reset sequence * as used in the Linux driver. */ if ((err = smsc_read_reg(sc, SMSC_HW_CFG, ®_val)) != 0) { smsc_warn_printf(sc, "failed to read HW_CFG: %d\n", err); goto init_failed; } reg_val |= SMSC_HW_CFG_BIR; smsc_write_reg(sc, SMSC_HW_CFG, reg_val); /* There is a so called 'turbo mode' that the linux driver supports, it * seems to allow you to jam multiple frames per Rx transaction. By default * this driver supports that and therefore allows multiple frames per URB. * * The xfer buffer size needs to reflect this as well, therefore based on * the calculations in the Linux driver the RX bufsize is set to 18944, * bufsz = (16 * 1024 + 5 * 512) * * Burst capability is the number of URBs that can be in a burst of data/ * ethernet frames. */ if (usbd_get_speed(sc->sc_ue.ue_udev) == USB_SPEED_HIGH) burst_cap = 37; else burst_cap = 128; smsc_write_reg(sc, SMSC_BURST_CAP, burst_cap); /* Set the default bulk in delay (magic value from Linux driver) */ smsc_write_reg(sc, SMSC_BULK_IN_DLY, 0x00002000); /* * Initialise the RX interface */ if ((err = smsc_read_reg(sc, SMSC_HW_CFG, ®_val)) < 0) { smsc_warn_printf(sc, "failed to read HW_CFG: (err = %d)\n", err); goto init_failed; } /* Adjust the packet offset in the buffer (designed to try and align IP * header on 4 byte boundary) */ reg_val &= ~SMSC_HW_CFG_RXDOFF; reg_val |= (ETHER_ALIGN << 9) & SMSC_HW_CFG_RXDOFF; /* The following setings are used for 'turbo mode', a.k.a multiple frames * per Rx transaction (again info taken form Linux driver). */ reg_val |= (SMSC_HW_CFG_MEF | SMSC_HW_CFG_BCE); smsc_write_reg(sc, SMSC_HW_CFG, reg_val); /* Clear the status register ? */ smsc_write_reg(sc, SMSC_INTR_STATUS, 0xffffffff); /* Read and display the revision register */ if ((err = smsc_read_reg(sc, SMSC_ID_REV, &sc->sc_rev_id)) < 0) { smsc_warn_printf(sc, "failed to read ID_REV (err = %d)\n", err); goto init_failed; } device_printf(sc->sc_ue.ue_dev, "chip 0x%04lx, rev. %04lx\n", (sc->sc_rev_id & SMSC_ID_REV_CHIP_ID_MASK) >> 16, (sc->sc_rev_id & SMSC_ID_REV_CHIP_REV_MASK)); /* GPIO/LED setup */ reg_val = SMSC_LED_GPIO_CFG_SPD_LED | SMSC_LED_GPIO_CFG_LNK_LED | SMSC_LED_GPIO_CFG_FDX_LED; smsc_write_reg(sc, SMSC_LED_GPIO_CFG, reg_val); /* * Initialise the TX interface */ smsc_write_reg(sc, SMSC_FLOW, 0); smsc_write_reg(sc, SMSC_AFC_CFG, AFC_CFG_DEFAULT); /* Read the current MAC configuration */ if ((err = smsc_read_reg(sc, SMSC_MAC_CSR, &sc->sc_mac_csr)) < 0) { smsc_warn_printf(sc, "failed to read MAC_CSR (err=%d)\n", err); goto init_failed; } /* Vlan */ smsc_write_reg(sc, SMSC_VLAN1, (uint32_t)ETHERTYPE_VLAN); /* * Initialise the PHY */ if ((err = smsc_phy_init(sc)) != 0) goto init_failed; /* * Start TX */ sc->sc_mac_csr |= SMSC_MAC_CSR_TXEN; smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); smsc_write_reg(sc, SMSC_TX_CFG, SMSC_TX_CFG_ON); /* * Start RX */ sc->sc_mac_csr |= SMSC_MAC_CSR_RXEN; smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); if (!locked) SMSC_UNLOCK(sc); return (0); init_failed: if (!locked) SMSC_UNLOCK(sc); smsc_err_printf(sc, "smsc_chip_init failed (err=%d)\n", err); return (err); } /** * smsc_ioctl - ioctl function for the device * @ifp: interface pointer * @cmd: the ioctl command * @data: data passed in the ioctl call, typically a pointer to struct ifreq. * * The ioctl routine is overridden to detect change requests for the H/W * checksum capabilities. * * RETURNS: * 0 on success and an error code on failure. */ static int smsc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct usb_ether *ue = ifp->if_softc; struct smsc_softc *sc; struct ifreq *ifr; int rc; int mask; int reinit; if (cmd == SIOCSIFCAP) { sc = uether_getsc(ue); ifr = (struct ifreq *)data; SMSC_LOCK(sc); rc = 0; reinit = 0; mask = ifr->ifr_reqcap ^ ifp->if_capenable; /* Modify the RX CSUM enable bits */ if ((mask & IFCAP_RXCSUM) != 0 && (ifp->if_capabilities & IFCAP_RXCSUM) != 0) { ifp->if_capenable ^= IFCAP_RXCSUM; if (ifp->if_drv_flags & IFF_DRV_RUNNING) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; reinit = 1; } } SMSC_UNLOCK(sc); if (reinit) #if __FreeBSD_version > 1000000 uether_init(ue); #else ifp->if_init(ue); #endif } else { rc = uether_ioctl(ifp, cmd, data); } return (rc); } #ifdef FDT +/* + * This is FreeBSD-specific compatibility strings for RPi/RPi2 + */ static phandle_t smsc_fdt_find_eth_node(phandle_t start) { phandle_t child, node; /* Traverse through entire tree to find usb ethernet nodes. */ for (node = OF_child(start); node != 0; node = OF_peer(node)) { if (fdt_is_compatible(node, "net,ethernet") && fdt_is_compatible(node, "usb,device")) return (node); child = smsc_fdt_find_eth_node(node); - if (child != 0) + if (child != -1) return (child); } - return (0); + return (-1); } +/* + * Check if node's path is <*>/usb/hub/ethernet + */ +static int +smsc_fdt_is_usb_eth(phandle_t node) +{ + char name[16]; + int len; + + memset(name, 0, sizeof(name)); + len = OF_getprop(node, "name", name, sizeof(name)); + if (len <= 0) + return (0); + + if (strcmp(name, "ethernet")) + return (0); + + node = OF_parent(node); + if (node == -1) + return (0); + len = OF_getprop(node, "name", name, sizeof(name)); + if (len <= 0) + return (0); + + if (strcmp(name, "hub")) + return (0); + + node = OF_parent(node); + if (node == -1) + return (0); + len = OF_getprop(node, "name", name, sizeof(name)); + if (len <= 0) + return (0); + + if (strcmp(name, "usb")) + return (0); + + return (1); +} + +static phandle_t +smsc_fdt_find_eth_node_by_path(phandle_t start) +{ + phandle_t child, node; + + /* Traverse through entire tree to find usb ethernet nodes. */ + for (node = OF_child(start); node != 0; node = OF_peer(node)) { + if (smsc_fdt_is_usb_eth(node)) + return (node); + child = smsc_fdt_find_eth_node_by_path(node); + if (child != -1) + return (child); + } + + return (-1); +} + /** * Get MAC address from FDT blob. Firmware or loader should fill * mac-address or local-mac-address property. Returns 0 if MAC address * obtained, error code otherwise. */ static int smsc_fdt_find_mac(unsigned char *mac) { phandle_t node, root; int len; root = OF_finddevice("/"); node = smsc_fdt_find_eth_node(root); - if (node != 0) { + /* + * If it's not FreeBSD FDT blob for RPi, try more + * generic .../usb/hub/ethernet + */ + if (node == -1) + node = smsc_fdt_find_eth_node_by_path(root); + if (node != -1) { /* Check if there is property */ if ((len = OF_getproplen(node, "local-mac-address")) > 0) { if (len != ETHER_ADDR_LEN) return (EINVAL); OF_getprop(node, "local-mac-address", mac, ETHER_ADDR_LEN); return (0); } if ((len = OF_getproplen(node, "mac-address")) > 0) { if (len != ETHER_ADDR_LEN) return (EINVAL); OF_getprop(node, "mac-address", mac, ETHER_ADDR_LEN); return (0); } } return (ENXIO); } #endif /** * smsc_attach_post - Called after the driver attached to the USB interface * @ue: the USB ethernet device * * This is where the chip is intialised for the first time. This is different * from the smsc_init() function in that that one is designed to setup the * H/W to match the UE settings and can be called after a reset. * * */ static void smsc_attach_post(struct usb_ether *ue) { struct smsc_softc *sc = uether_getsc(ue); uint32_t mac_h, mac_l; int err; smsc_dbg_printf(sc, "smsc_attach_post\n"); /* Setup some of the basics */ sc->sc_phyno = 1; /* Attempt to get the mac address, if an EEPROM is not attached this * will just return FF:FF:FF:FF:FF:FF, so in such cases we invent a MAC * address based on urandom. */ memset(sc->sc_ue.ue_eaddr, 0xff, ETHER_ADDR_LEN); /* Check if there is already a MAC address in the register */ if ((smsc_read_reg(sc, SMSC_MAC_ADDRL, &mac_l) == 0) && (smsc_read_reg(sc, SMSC_MAC_ADDRH, &mac_h) == 0)) { sc->sc_ue.ue_eaddr[5] = (uint8_t)((mac_h >> 8) & 0xff); sc->sc_ue.ue_eaddr[4] = (uint8_t)((mac_h) & 0xff); sc->sc_ue.ue_eaddr[3] = (uint8_t)((mac_l >> 24) & 0xff); sc->sc_ue.ue_eaddr[2] = (uint8_t)((mac_l >> 16) & 0xff); sc->sc_ue.ue_eaddr[1] = (uint8_t)((mac_l >> 8) & 0xff); sc->sc_ue.ue_eaddr[0] = (uint8_t)((mac_l) & 0xff); } /* MAC address is not set so try to read from EEPROM, if that fails generate * a random MAC address. */ if (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr)) { err = smsc_eeprom_read(sc, 0x01, sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN); #ifdef FDT if ((err != 0) || (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr))) err = smsc_fdt_find_mac(sc->sc_ue.ue_eaddr); #endif if ((err != 0) || (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr))) { read_random(sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN); sc->sc_ue.ue_eaddr[0] &= ~0x01; /* unicast */ sc->sc_ue.ue_eaddr[0] |= 0x02; /* locally administered */ } } /* Initialise the chip for the first time */ smsc_chip_init(sc); } /** * smsc_attach_post_sub - Called after the driver attached to the USB interface * @ue: the USB ethernet device * * Most of this is boilerplate code and copied from the base USB ethernet * driver. It has been overriden so that we can indicate to the system that * the chip supports H/W checksumming. * * RETURNS: * Returns 0 on success or a negative error code. */ #if __FreeBSD_version > 1000000 static int smsc_attach_post_sub(struct usb_ether *ue) { struct smsc_softc *sc; struct ifnet *ifp; int error; sc = uether_getsc(ue); ifp = ue->ue_ifp; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_start = uether_start; ifp->if_ioctl = smsc_ioctl; ifp->if_init = uether_init; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; IFQ_SET_READY(&ifp->if_snd); /* The chip supports TCP/UDP checksum offloading on TX and RX paths, however * currently only RX checksum is supported in the driver (see top of file). */ ifp->if_capabilities |= IFCAP_RXCSUM | IFCAP_VLAN_MTU; ifp->if_hwassist = 0; /* TX checksuming is disabled (for now?) ifp->if_capabilities |= IFCAP_TXCSUM; ifp->if_capenable |= IFCAP_TXCSUM; ifp->if_hwassist = CSUM_TCP | CSUM_UDP; */ ifp->if_capenable = ifp->if_capabilities; mtx_lock(&Giant); error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp, uether_ifmedia_upd, ue->ue_methods->ue_mii_sts, BMSR_DEFCAPMASK, sc->sc_phyno, MII_OFFSET_ANY, 0); mtx_unlock(&Giant); return (error); } #endif /* __FreeBSD_version > 1000000 */ /** * smsc_probe - Probe the interface. * @dev: smsc device handle * * Checks if the device is a match for this driver. * * RETURNS: * Returns 0 on success or an error code on failure. */ static int smsc_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != SMSC_CONFIG_INDEX) return (ENXIO); if (uaa->info.bIfaceIndex != SMSC_IFACE_IDX) return (ENXIO); return (usbd_lookup_id_by_uaa(smsc_devs, sizeof(smsc_devs), uaa)); } /** * smsc_attach - Attach the interface. * @dev: smsc device handle * * Allocate softc structures, do ifmedia setup and ethernet/BPF attach. * * RETURNS: * Returns 0 on success or a negative error code. */ static int smsc_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct smsc_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; uint8_t iface_index; int err; sc->sc_flags = USB_GET_DRIVER_INFO(uaa); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); /* Setup the endpoints for the SMSC LAN95xx device(s) */ iface_index = SMSC_IFACE_IDX; err = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, smsc_config, SMSC_N_TRANSFER, sc, &sc->sc_mtx); if (err) { device_printf(dev, "error: allocating USB transfers failed\n"); goto detach; } ue->ue_sc = sc; ue->ue_dev = dev; ue->ue_udev = uaa->device; ue->ue_mtx = &sc->sc_mtx; ue->ue_methods = &smsc_ue_methods; err = uether_ifattach(ue); if (err) { device_printf(dev, "error: could not attach interface\n"); goto detach; } return (0); /* success */ detach: smsc_detach(dev); return (ENXIO); /* failure */ } /** * smsc_detach - Detach the interface. * @dev: smsc device handle * * RETURNS: * Returns 0. */ static int smsc_detach(device_t dev) { struct smsc_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; usbd_transfer_unsetup(sc->sc_xfer, SMSC_N_TRANSFER); uether_ifdetach(ue); mtx_destroy(&sc->sc_mtx); return (0); } static device_method_t smsc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, smsc_probe), DEVMETHOD(device_attach, smsc_attach), DEVMETHOD(device_detach, smsc_detach), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, smsc_miibus_readreg), DEVMETHOD(miibus_writereg, smsc_miibus_writereg), DEVMETHOD(miibus_statchg, smsc_miibus_statchg), DEVMETHOD_END }; static driver_t smsc_driver = { .name = "smsc", .methods = smsc_methods, .size = sizeof(struct smsc_softc), }; static devclass_t smsc_devclass; DRIVER_MODULE(smsc, uhub, smsc_driver, smsc_devclass, NULL, 0); DRIVER_MODULE(miibus, smsc, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(smsc, uether, 1, 1, 1); MODULE_DEPEND(smsc, usb, 1, 1, 1); MODULE_DEPEND(smsc, ether, 1, 1, 1); MODULE_DEPEND(smsc, miibus, 1, 1, 1); MODULE_VERSION(smsc, 1); Index: stable/10 =================================================================== --- stable/10 (revision 322723) +++ stable/10 (revision 322724) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r266470,276017,277932,278797,278861,279153,279778,279780,280283-280284,280294,280452,280558,280571,281863,282049,282357-282359,282440-282441,283550,283918,290171,290381,290533,290666-290667,292483,295659,297545,298305,298383,298428,306489,306557,307067-307068,307087-307089,307091-307093,307098,307115,307154,307240-307241,315967,316476