Index: head/sys/arm/allwinner/a10_clk.c =================================================================== --- head/sys/arm/allwinner/a10_clk.c (revision 281084) +++ head/sys/arm/allwinner/a10_clk.c (revision 281085) @@ -1,192 +1,191 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * 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. */ /* Simple clock driver for Allwinner A10 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include "a10_clk.h" struct a10_ccm_softc { struct resource *res; bus_space_tag_t bst; bus_space_handle_t bsh; }; static struct a10_ccm_softc *a10_ccm_sc = NULL; #define ccm_read_4(sc, reg) \ bus_space_read_4((sc)->bst, (sc)->bsh, (reg)) #define ccm_write_4(sc, reg, val) \ bus_space_write_4((sc)->bst, (sc)->bsh, (reg), (val)) static int a10_ccm_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "allwinner,sun4i-ccm")) { device_set_desc(dev, "Allwinner Clock Control Module"); return(BUS_PROBE_DEFAULT); } return (ENXIO); } static int a10_ccm_attach(device_t dev) { struct a10_ccm_softc *sc = device_get_softc(dev); int rid = 0; if (a10_ccm_sc) return (ENXIO); sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->res) { device_printf(dev, "could not allocate resource\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->res); sc->bsh = rman_get_bushandle(sc->res); a10_ccm_sc = sc; return (0); } static device_method_t a10_ccm_methods[] = { DEVMETHOD(device_probe, a10_ccm_probe), DEVMETHOD(device_attach, a10_ccm_attach), { 0, 0 } }; static driver_t a10_ccm_driver = { "a10_ccm", a10_ccm_methods, sizeof(struct a10_ccm_softc), }; static devclass_t a10_ccm_devclass; DRIVER_MODULE(a10_ccm, simplebus, a10_ccm_driver, a10_ccm_devclass, 0, 0); int a10_clk_usb_activate(void) { struct a10_ccm_softc *sc = a10_ccm_sc; uint32_t reg_value; if (sc == NULL) return (ENXIO); /* Gating AHB clock for USB */ reg_value = ccm_read_4(sc, CCM_AHB_GATING0); reg_value |= CCM_AHB_GATING_USB0; /* AHB clock gate usb0 */ reg_value |= CCM_AHB_GATING_EHCI0; /* AHB clock gate ehci0 */ reg_value |= CCM_AHB_GATING_EHCI1; /* AHB clock gate ehci1 */ ccm_write_4(sc, CCM_AHB_GATING0, reg_value); /* Enable clock for USB */ reg_value = ccm_read_4(sc, CCM_USB_CLK); reg_value |= CCM_USB_PHY; /* USBPHY */ reg_value |= CCM_USB0_RESET; /* disable reset for USB0 */ reg_value |= CCM_USB1_RESET; /* disable reset for USB1 */ reg_value |= CCM_USB2_RESET; /* disable reset for USB2 */ ccm_write_4(sc, CCM_USB_CLK, reg_value); return (0); } int a10_clk_usb_deactivate(void) { struct a10_ccm_softc *sc = a10_ccm_sc; uint32_t reg_value; if (sc == NULL) return (ENXIO); /* Disable clock for USB */ reg_value = ccm_read_4(sc, CCM_USB_CLK); reg_value &= ~CCM_USB_PHY; /* USBPHY */ reg_value &= ~CCM_USB0_RESET; /* reset for USB0 */ reg_value &= ~CCM_USB1_RESET; /* reset for USB1 */ reg_value &= ~CCM_USB2_RESET; /* reset for USB2 */ ccm_write_4(sc, CCM_USB_CLK, reg_value); /* Disable gating AHB clock for USB */ reg_value = ccm_read_4(sc, CCM_AHB_GATING0); reg_value &= ~CCM_AHB_GATING_USB0; /* disable AHB clock gate usb0 */ reg_value &= ~CCM_AHB_GATING_EHCI0; /* disable AHB clock gate ehci0 */ reg_value &= ~CCM_AHB_GATING_EHCI1; /* disable AHB clock gate ehci1 */ ccm_write_4(sc, CCM_AHB_GATING0, reg_value); return (0); } int a10_clk_emac_activate(void) { struct a10_ccm_softc *sc = a10_ccm_sc; uint32_t reg_value; if (sc == NULL) return (ENXIO); /* Gating AHB clock for EMAC */ reg_value = ccm_read_4(sc, CCM_AHB_GATING0); reg_value |= CCM_AHB_GATING_EMAC; ccm_write_4(sc, CCM_AHB_GATING0, reg_value); return (0); } Index: head/sys/arm/allwinner/a10_common.c =================================================================== --- head/sys/arm/allwinner/a10_common.c (revision 281084) +++ head/sys/arm/allwinner/a10_common.c (revision 281085) @@ -1,69 +1,68 @@ /*- * Copyright (c) 2012 Ganbold Tsagaankhuu * 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 struct fdt_fixup_entry fdt_fixup_table[] = { { NULL, NULL } }; static int fdt_aintc_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, int *pol) { int offset; if (fdt_is_compatible(node, "allwinner,sun4i-ic")) offset = 0; else if (fdt_is_compatible(node, "arm,gic")) offset = 32; else return (ENXIO); *interrupt = fdt32_to_cpu(intr[0]) + offset; *trig = INTR_TRIGGER_CONFORM; *pol = INTR_POLARITY_CONFORM; return (0); } fdt_pic_decode_t fdt_pic_table[] = { &fdt_aintc_decode_ic, NULL }; Index: head/sys/arm/allwinner/a10_gpio.c =================================================================== --- head/sys/arm/allwinner/a10_gpio.c (revision 281084) +++ head/sys/arm/allwinner/a10_gpio.c (revision 281085) @@ -1,541 +1,540 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * 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 "gpio_if.h" #include "a10_gpio.h" /* * A10 have 9 banks of gpio. * 32 pins per bank: * PA0 - PA17 | PB0 - PB23 | PC0 - PC24 * PD0 - PD27 | PE0 - PE31 | PF0 - PF5 * PG0 - PG9 | PH0 - PH27 | PI0 - PI12 */ #define A10_GPIO_PINS 288 #define A10_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) #define A10_GPIO_NONE 0 #define A10_GPIO_PULLUP 1 #define A10_GPIO_PULLDOWN 2 #define A10_GPIO_INPUT 0 #define A10_GPIO_OUTPUT 1 struct a10_gpio_softc { device_t sc_dev; device_t sc_busdev; 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; struct gpio_pin sc_gpio_pins[A10_GPIO_PINS]; }; #define A10_GPIO_LOCK(_sc) mtx_lock(&_sc->sc_mtx) #define A10_GPIO_UNLOCK(_sc) mtx_unlock(&_sc->sc_mtx) #define A10_GPIO_LOCK_ASSERT(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED) #define A10_GPIO_GP_CFG(_bank, _pin) 0x00 + ((_bank) * 0x24) + ((_pin)<<2) #define A10_GPIO_GP_DAT(_bank) 0x10 + ((_bank) * 0x24) #define A10_GPIO_GP_DRV(_bank, _pin) 0x14 + ((_bank) * 0x24) + ((_pin)<<2) #define A10_GPIO_GP_PUL(_bank, _pin) 0x1c + ((_bank) * 0x24) + ((_pin)<<2) #define A10_GPIO_GP_INT_CFG0 0x200 #define A10_GPIO_GP_INT_CFG1 0x204 #define A10_GPIO_GP_INT_CFG2 0x208 #define A10_GPIO_GP_INT_CFG3 0x20c #define A10_GPIO_GP_INT_CTL 0x210 #define A10_GPIO_GP_INT_STA 0x214 #define A10_GPIO_GP_INT_DEB 0x218 static struct a10_gpio_softc *a10_gpio_sc; #define A10_GPIO_WRITE(_sc, _off, _val) \ bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) #define A10_GPIO_READ(_sc, _off) \ bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off) static uint32_t a10_gpio_get_function(struct a10_gpio_softc *sc, uint32_t pin) { uint32_t bank, func, offset; bank = pin / 32; pin = pin - 32 * bank; func = pin >> 3; offset = ((pin & 0x07) << 2); A10_GPIO_LOCK(sc); func = (A10_GPIO_READ(sc, A10_GPIO_GP_CFG(bank, func)) >> offset) & 7; A10_GPIO_UNLOCK(sc); return (func); } static uint32_t a10_gpio_func_flag(uint32_t nfunc) { switch (nfunc) { case A10_GPIO_INPUT: return (GPIO_PIN_INPUT); case A10_GPIO_OUTPUT: return (GPIO_PIN_OUTPUT); } return (0); } static void a10_gpio_set_function(struct a10_gpio_softc *sc, uint32_t pin, uint32_t f) { uint32_t bank, func, data, offset; /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); bank = pin / 32; pin = pin - 32 * bank; func = pin >> 3; offset = ((pin & 0x07) << 2); data = A10_GPIO_READ(sc, A10_GPIO_GP_CFG(bank, func)); data &= ~(7 << offset); data |= (f << offset); A10_GPIO_WRITE(sc, A10_GPIO_GP_CFG(bank, func), data); } static void a10_gpio_set_pud(struct a10_gpio_softc *sc, uint32_t pin, uint32_t state) { uint32_t bank, offset, pull, val; /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); bank = pin / 32; pin = pin - 32 * bank; pull = pin >> 4; offset = ((pin & 0x0f) << 1); val = A10_GPIO_READ(sc, A10_GPIO_GP_PUL(bank, pull)); val &= ~(0x03 << offset); val |= (state << offset); A10_GPIO_WRITE(sc, A10_GPIO_GP_PUL(bank, pull), val); } static void a10_gpio_pin_configure(struct a10_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { A10_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; a10_gpio_set_function(sc, pin->gp_pin, A10_GPIO_OUTPUT); } else { pin->gp_flags |= GPIO_PIN_INPUT; a10_gpio_set_function(sc, pin->gp_pin, A10_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; a10_gpio_set_pud(sc, pin->gp_pin, A10_GPIO_PULLUP); } else { pin->gp_flags |= GPIO_PIN_PULLDOWN; a10_gpio_set_pud(sc, pin->gp_pin, A10_GPIO_PULLDOWN); } } else a10_gpio_set_pud(sc, pin->gp_pin, A10_GPIO_NONE); A10_GPIO_UNLOCK(sc); } static device_t a10_gpio_get_bus(device_t dev) { struct a10_gpio_softc *sc; sc = device_get_softc(dev); return (sc->sc_busdev); } static int a10_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = A10_GPIO_PINS - 1; return (0); } static int a10_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct a10_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); A10_GPIO_LOCK(sc); *caps = sc->sc_gpio_pins[i].gp_caps; A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct a10_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); A10_GPIO_LOCK(sc); *flags = sc->sc_gpio_pins[i].gp_flags; A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct a10_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); A10_GPIO_LOCK(sc); memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct a10_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); a10_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); return (0); } static int a10_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct a10_gpio_softc *sc = device_get_softc(dev); uint32_t bank, offset, 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; pin = pin - 32 * bank; offset = pin & 0x1f; A10_GPIO_LOCK(sc); data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); if (value) data |= (1 << offset); else data &= ~(1 << offset); A10_GPIO_WRITE(sc, A10_GPIO_GP_DAT(bank), data); A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct a10_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; pin = pin - 32 * bank; offset = pin & 0x1f; A10_GPIO_LOCK(sc); reg_data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); A10_GPIO_UNLOCK(sc); *val = (reg_data & (1 << offset)) ? 1 : 0; return (0); } static int a10_gpio_pin_toggle(device_t dev, uint32_t pin) { struct a10_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); bank = pin / 32; pin = pin - 32 * bank; offset = pin & 0x1f; A10_GPIO_LOCK(sc); data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); if (data & (1 << offset)) data &= ~(1 << offset); else data |= (1 << offset); A10_GPIO_WRITE(sc, A10_GPIO_GP_DAT(bank), data); A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-gpio")) return (ENXIO); device_set_desc(dev, "Allwinner GPIO controller"); return (BUS_PROBE_DEFAULT); } static int a10_gpio_attach(device_t dev) { struct a10_gpio_softc *sc = device_get_softc(dev); uint32_t func; int i, rid; phandle_t gpio; sc->sc_dev = dev; mtx_init(&sc->sc_mtx, "a10 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"); 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"); goto fail; } /* 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; /* Initialize the software controlled pins. */ for (i = 0; i < A10_GPIO_PINS; i++) { snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, "pin %d", i); func = a10_gpio_get_function(sc, i); sc->sc_gpio_pins[i].gp_pin = i; sc->sc_gpio_pins[i].gp_caps = A10_GPIO_DEFAULT_CAPS; sc->sc_gpio_pins[i].gp_flags = a10_gpio_func_flag(func); } sc->sc_gpio_npins = i; a10_gpio_sc = sc; sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) goto fail; return (0); 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); mtx_destroy(&sc->sc_mtx); return (ENXIO); } static int a10_gpio_detach(device_t dev) { return (EBUSY); } static device_method_t a10_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, a10_gpio_probe), DEVMETHOD(device_attach, a10_gpio_attach), DEVMETHOD(device_detach, a10_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, a10_gpio_get_bus), DEVMETHOD(gpio_pin_max, a10_gpio_pin_max), DEVMETHOD(gpio_pin_getname, a10_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, a10_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, a10_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, a10_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, a10_gpio_pin_get), DEVMETHOD(gpio_pin_set, a10_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, a10_gpio_pin_toggle), DEVMETHOD_END }; static devclass_t a10_gpio_devclass; static driver_t a10_gpio_driver = { "gpio", a10_gpio_methods, sizeof(struct a10_gpio_softc), }; DRIVER_MODULE(a10_gpio, simplebus, a10_gpio_driver, a10_gpio_devclass, 0, 0); int a10_emac_gpio_config(uint32_t pin) { struct a10_gpio_softc *sc = a10_gpio_sc; if (sc == NULL) return (ENXIO); /* Configure pin mux settings for MII. */ A10_GPIO_LOCK(sc); a10_gpio_set_function(sc, pin, A10_GPIO_PULLDOWN); A10_GPIO_UNLOCK(sc); return (0); } Index: head/sys/arm/allwinner/a10_sramc.c =================================================================== --- head/sys/arm/allwinner/a10_sramc.c (revision 281084) +++ head/sys/arm/allwinner/a10_sramc.c (revision 281085) @@ -1,134 +1,133 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * 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$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include "a10_sramc.h" #define SRAM_CTL1_CFG 0x04 struct a10_sramc_softc { struct resource *res; bus_space_tag_t bst; bus_space_handle_t bsh; }; static struct a10_sramc_softc *a10_sramc_sc; #define sramc_read_4(sc, reg) \ bus_space_read_4((sc)->bst, (sc)->bsh, (reg)) #define sramc_write_4(sc, reg, val) \ bus_space_write_4((sc)->bst, (sc)->bsh, (reg), (val)) static int a10_sramc_probe(device_t dev) { if (ofw_bus_is_compatible(dev, "allwinner,sun4i-sramc")) { device_set_desc(dev, "Allwinner sramc module"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int a10_sramc_attach(device_t dev) { struct a10_sramc_softc *sc = device_get_softc(dev); int rid = 0; sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->res) { device_printf(dev, "could not allocate resource\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->res); sc->bsh = rman_get_bushandle(sc->res); a10_sramc_sc = sc; return (0); } static device_method_t a10_sramc_methods[] = { DEVMETHOD(device_probe, a10_sramc_probe), DEVMETHOD(device_attach, a10_sramc_attach), { 0, 0 } }; static driver_t a10_sramc_driver = { "a10_sramc", a10_sramc_methods, sizeof(struct a10_sramc_softc), }; static devclass_t a10_sramc_devclass; DRIVER_MODULE(a10_sramc, simplebus, a10_sramc_driver, a10_sramc_devclass, 0, 0); int a10_map_to_emac(void) { struct a10_sramc_softc *sc = a10_sramc_sc; uint32_t reg_value; if (sc == NULL) return (ENXIO); /* Map SRAM to EMAC, set bit 2 and 4. */ reg_value = sramc_read_4(sc, SRAM_CTL1_CFG); reg_value |= 0x5 << 2; sramc_write_4(sc, SRAM_CTL1_CFG, reg_value); return (0); } Index: head/sys/arm/allwinner/a10_wdog.c =================================================================== --- head/sys/arm/allwinner/a10_wdog.c (revision 281084) +++ head/sys/arm/allwinner/a10_wdog.c (revision 281085) @@ -1,208 +1,207 @@ /*- * Copyright (c) 2013 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 #define READ(_sc, _r) bus_read_4((_sc)->res, (_r)) #define WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v)) #define WDOG_CTRL 0x00 #define WDOG_CTRL_RESTART (1 << 0) #define WDOG_MODE 0x04 #define WDOG_MODE_INTVL_SHIFT 3 #define WDOG_MODE_RST_EN (1 << 1) #define WDOG_MODE_EN (1 << 0) struct a10wd_interval { uint64_t milliseconds; unsigned int value; }; struct a10wd_interval wd_intervals[] = { { 500, 0 }, { 1000, 1 }, { 2000, 2 }, { 3000, 3 }, { 4000, 4 }, { 5000, 5 }, { 6000, 6 }, { 8000, 7 }, { 10000, 8 }, { 12000, 9 }, { 14000, 10 }, { 16000, 11 }, { 0, 0 } /* sentinel */ }; static struct a10wd_softc *a10wd_sc = NULL; struct a10wd_softc { device_t dev; struct resource * res; struct mtx mtx; }; static void a10wd_watchdog_fn(void *private, u_int cmd, int *error); static int a10wd_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "allwinner,sun4i-wdt")) { device_set_desc(dev, "Allwinner A10 Watchdog"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int a10wd_attach(device_t dev) { struct a10wd_softc *sc; int rid; if (a10wd_sc != NULL) return (ENXIO); sc = device_get_softc(dev); 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); } a10wd_sc = sc; mtx_init(&sc->mtx, "A10 Watchdog", "a10wd", MTX_DEF); EVENTHANDLER_REGISTER(watchdog_list, a10wd_watchdog_fn, sc, 0); return (0); } static void a10wd_watchdog_fn(void *private, u_int cmd, int *error) { struct a10wd_softc *sc; uint64_t ms; int i; sc = private; mtx_lock(&sc->mtx); cmd &= WD_INTERVAL; if (cmd > 0) { ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000; i = 0; while (wd_intervals[i].milliseconds && (ms > wd_intervals[i].milliseconds)) i++; if (wd_intervals[i].milliseconds) { WRITE(sc, WDOG_MODE, (wd_intervals[i].value << WDOG_MODE_INTVL_SHIFT) | WDOG_MODE_EN | WDOG_MODE_RST_EN); WRITE(sc, WDOG_CTRL, WDOG_CTRL_RESTART); *error = 0; } else { /* * Can't arm * disable watchdog as watchdog(9) requires */ device_printf(sc->dev, "Can't arm, timeout is more than 16 sec\n"); mtx_unlock(&sc->mtx); WRITE(sc, WDOG_MODE, 0); return; } } else WRITE(sc, WDOG_MODE, 0); mtx_unlock(&sc->mtx); } void a10wd_watchdog_reset() { if (a10wd_sc == NULL) { printf("Reset: watchdog device has not been initialized\n"); return; } WRITE(a10wd_sc, WDOG_MODE, (wd_intervals[0].value << WDOG_MODE_INTVL_SHIFT) | WDOG_MODE_EN | WDOG_MODE_RST_EN); while(1) ; } static device_method_t a10wd_methods[] = { DEVMETHOD(device_probe, a10wd_probe), DEVMETHOD(device_attach, a10wd_attach), DEVMETHOD_END }; static driver_t a10wd_driver = { "a10wd", a10wd_methods, sizeof(struct a10wd_softc), }; static devclass_t a10wd_devclass; DRIVER_MODULE(a10wd, simplebus, a10wd_driver, a10wd_devclass, 0, 0); Index: head/sys/arm/allwinner/a20/a20_cpu_cfg.c =================================================================== --- head/sys/arm/allwinner/a20/a20_cpu_cfg.c (revision 281084) +++ head/sys/arm/allwinner/a20/a20_cpu_cfg.c (revision 281085) @@ -1,138 +1,137 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * 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. */ /* CPU configuration module for Allwinner A20 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include "a20_cpu_cfg.h" struct a20_cpu_cfg_softc { struct resource *res; bus_space_tag_t bst; bus_space_handle_t bsh; }; static struct a20_cpu_cfg_softc *a20_cpu_cfg_sc = NULL; #define cpu_cfg_read_4(sc, reg) \ bus_space_read_4((sc)->bst, (sc)->bsh, (reg)) #define cpu_cfg_write_4(sc, reg, val) \ bus_space_write_4((sc)->bst, (sc)->bsh, (reg), (val)) static int a20_cpu_cfg_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "allwinner,sun7i-cpu-cfg")) { device_set_desc(dev, "A20 CPU Configuration Module"); return(BUS_PROBE_DEFAULT); } return (ENXIO); } static int a20_cpu_cfg_attach(device_t dev) { struct a20_cpu_cfg_softc *sc = device_get_softc(dev); int rid = 0; if (a20_cpu_cfg_sc) return (ENXIO); sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->res) { device_printf(dev, "could not allocate resource\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->res); sc->bsh = rman_get_bushandle(sc->res); a20_cpu_cfg_sc = sc; return (0); } static device_method_t a20_cpu_cfg_methods[] = { DEVMETHOD(device_probe, a20_cpu_cfg_probe), DEVMETHOD(device_attach, a20_cpu_cfg_attach), { 0, 0 } }; static driver_t a20_cpu_cfg_driver = { "a20_cpu_cfg", a20_cpu_cfg_methods, sizeof(struct a20_cpu_cfg_softc), }; static devclass_t a20_cpu_cfg_devclass; DRIVER_MODULE(a20_cpu_cfg, simplebus, a20_cpu_cfg_driver, a20_cpu_cfg_devclass, 0, 0); uint64_t a20_read_counter64(void) { uint32_t lo, hi; /* Latch counter, wait for it to be ready to read. */ cpu_cfg_write_4(a20_cpu_cfg_sc, OSC24M_CNT64_CTRL_REG, CNT64_RL_EN); while (cpu_cfg_read_4(a20_cpu_cfg_sc, OSC24M_CNT64_CTRL_REG) & CNT64_RL_EN) continue; hi = cpu_cfg_read_4(a20_cpu_cfg_sc, OSC24M_CNT64_HIGH_REG); lo = cpu_cfg_read_4(a20_cpu_cfg_sc, OSC24M_CNT64_LOW_REG); return (((uint64_t)hi << 32) | lo); } Index: head/sys/arm/allwinner/timer.c =================================================================== --- head/sys/arm/allwinner/timer.c (revision 281084) +++ head/sys/arm/allwinner/timer.c (revision 281085) @@ -1,377 +1,376 @@ /*- * Copyright (c) 2012 Ganbold Tsagaankhuu * 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 "a20/a20_cpu_cfg.h" /** * Timer registers addr * */ #define SW_TIMER_IRQ_EN_REG 0x00 #define SW_TIMER_IRQ_STA_REG 0x04 #define SW_TIMER0_CTRL_REG 0x10 #define SW_TIMER0_INT_VALUE_REG 0x14 #define SW_TIMER0_CUR_VALUE_REG 0x18 #define SW_COUNTER64LO_REG 0xa4 #define SW_COUNTER64HI_REG 0xa8 #define CNT64_CTRL_REG 0xa0 #define CNT64_RL_EN 0x02 /* read latch enable */ #define TIMER_ENABLE (1<<0) #define TIMER_AUTORELOAD (1<<1) #define TIMER_OSC24M (1<<2) /* oscillator = 24mhz */ #define TIMER_PRESCALAR (0<<4) /* prescalar = 1 */ #define SYS_TIMER_CLKSRC 24000000 /* clock source */ struct a10_timer_softc { device_t sc_dev; struct resource *res[2]; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; void *sc_ih; /* interrupt handler */ uint32_t sc_period; uint32_t timer0_freq; struct eventtimer et; uint8_t sc_timer_type; /* 0 for A10, 1 for A20 */ }; int a10_timer_get_timerfreq(struct a10_timer_softc *); #define timer_read_4(sc, reg) \ bus_space_read_4(sc->sc_bst, sc->sc_bsh, reg) #define timer_write_4(sc, reg, val) \ bus_space_write_4(sc->sc_bst, sc->sc_bsh, reg, val) static u_int a10_timer_get_timecount(struct timecounter *); static int a10_timer_timer_start(struct eventtimer *, sbintime_t first, sbintime_t period); static int a10_timer_timer_stop(struct eventtimer *); static uint64_t timer_read_counter64(void); static int a10_timer_initialized = 0; static int a10_timer_hardclock(void *); static int a10_timer_probe(device_t); static int a10_timer_attach(device_t); static struct timecounter a10_timer_timecounter = { .tc_name = "a10_timer timer0", .tc_get_timecount = a10_timer_get_timecount, .tc_counter_mask = ~0u, .tc_frequency = 0, .tc_quality = 1000, }; struct a10_timer_softc *a10_timer_sc = NULL; static struct resource_spec a10_timer_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static uint64_t timer_read_counter64(void) { uint32_t lo, hi; /* In case of A20 get appropriate counter info */ if (a10_timer_sc->sc_timer_type) return (a20_read_counter64()); /* Latch counter, wait for it to be ready to read. */ timer_write_4(a10_timer_sc, CNT64_CTRL_REG, CNT64_RL_EN); while (timer_read_4(a10_timer_sc, CNT64_CTRL_REG) & CNT64_RL_EN) continue; hi = timer_read_4(a10_timer_sc, SW_COUNTER64HI_REG); lo = timer_read_4(a10_timer_sc, SW_COUNTER64LO_REG); return (((uint64_t)hi << 32) | lo); } static int a10_timer_probe(device_t dev) { struct a10_timer_softc *sc; sc = device_get_softc(dev); if (ofw_bus_is_compatible(dev, "allwinner,sun4i-timer")) sc->sc_timer_type = 0; else if (ofw_bus_is_compatible(dev, "allwinner,sun7i-timer")) sc->sc_timer_type = 1; else return (ENXIO); device_set_desc(dev, "Allwinner A10/A20 timer"); return (BUS_PROBE_DEFAULT); } static int a10_timer_attach(device_t dev) { struct a10_timer_softc *sc; int err; uint32_t val; sc = device_get_softc(dev); if (bus_alloc_resources(dev, a10_timer_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->sc_dev = dev; sc->sc_bst = rman_get_bustag(sc->res[0]); sc->sc_bsh = rman_get_bushandle(sc->res[0]); /* Setup and enable the timer interrupt */ err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_CLK, a10_timer_hardclock, NULL, sc, &sc->sc_ih); if (err != 0) { bus_release_resources(dev, a10_timer_spec, sc->res); device_printf(dev, "Unable to setup the clock irq handler, " "err = %d\n", err); return (ENXIO); } /* Set clock source to OSC24M, 16 pre-division */ val = timer_read_4(sc, SW_TIMER0_CTRL_REG); val |= TIMER_PRESCALAR | TIMER_OSC24M; timer_write_4(sc, SW_TIMER0_CTRL_REG, val); /* Enable timer0 */ val = timer_read_4(sc, SW_TIMER_IRQ_EN_REG); val |= TIMER_ENABLE; timer_write_4(sc, SW_TIMER_IRQ_EN_REG, val); sc->timer0_freq = SYS_TIMER_CLKSRC; /* Set desired frequency in event timer and timecounter */ sc->et.et_frequency = sc->timer0_freq; sc->et.et_name = "a10_timer Eventtimer"; sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERIODIC; sc->et.et_quality = 1000; sc->et.et_min_period = (0x00000005LLU << 32) / sc->et.et_frequency; sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; sc->et.et_start = a10_timer_timer_start; sc->et.et_stop = a10_timer_timer_stop; sc->et.et_priv = sc; et_register(&sc->et); if (device_get_unit(dev) == 0) a10_timer_sc = sc; a10_timer_timecounter.tc_frequency = sc->timer0_freq; tc_init(&a10_timer_timecounter); if (bootverbose) { device_printf(sc->sc_dev, "clock: hz=%d stathz = %d\n", hz, stathz); device_printf(sc->sc_dev, "event timer clock frequency %u\n", sc->timer0_freq); device_printf(sc->sc_dev, "timecounter clock frequency %lld\n", a10_timer_timecounter.tc_frequency); } a10_timer_initialized = 1; return (0); } static int a10_timer_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { struct a10_timer_softc *sc; uint32_t count; uint32_t val; sc = (struct a10_timer_softc *)et->et_priv; if (period != 0) sc->sc_period = ((uint32_t)et->et_frequency * period) >> 32; else sc->sc_period = 0; if (first != 0) count = ((uint32_t)et->et_frequency * first) >> 32; else count = sc->sc_period; /* Update timer values */ timer_write_4(sc, SW_TIMER0_INT_VALUE_REG, sc->sc_period); timer_write_4(sc, SW_TIMER0_CUR_VALUE_REG, count); val = timer_read_4(sc, SW_TIMER0_CTRL_REG); if (period != 0) { /* periodic */ val |= TIMER_AUTORELOAD; } else { /* oneshot */ val &= ~TIMER_AUTORELOAD; } /* Enable timer0 */ val |= TIMER_ENABLE; timer_write_4(sc, SW_TIMER0_CTRL_REG, val); return (0); } static int a10_timer_timer_stop(struct eventtimer *et) { struct a10_timer_softc *sc; uint32_t val; sc = (struct a10_timer_softc *)et->et_priv; /* Disable timer0 */ val = timer_read_4(sc, SW_TIMER0_CTRL_REG); val &= ~TIMER_ENABLE; timer_write_4(sc, SW_TIMER0_CTRL_REG, val); sc->sc_period = 0; return (0); } int a10_timer_get_timerfreq(struct a10_timer_softc *sc) { return (sc->timer0_freq); } static int a10_timer_hardclock(void *arg) { struct a10_timer_softc *sc; uint32_t val; sc = (struct a10_timer_softc *)arg; /* Clear interrupt pending bit. */ timer_write_4(sc, SW_TIMER_IRQ_STA_REG, 0x1); val = timer_read_4(sc, SW_TIMER0_CTRL_REG); /* * Disabled autoreload and sc_period > 0 means * timer_start was called with non NULL first value. * Now we will set periodic timer with the given period * value. */ if ((val & (1<<1)) == 0 && sc->sc_period > 0) { /* Update timer */ timer_write_4(sc, SW_TIMER0_CUR_VALUE_REG, sc->sc_period); /* Make periodic and enable */ val |= TIMER_AUTORELOAD | TIMER_ENABLE; timer_write_4(sc, SW_TIMER0_CTRL_REG, val); } if (sc->et.et_active) sc->et.et_event_cb(&sc->et, sc->et.et_arg); return (FILTER_HANDLED); } u_int a10_timer_get_timecount(struct timecounter *tc) { if (a10_timer_sc == NULL) return (0); return ((u_int)timer_read_counter64()); } static device_method_t a10_timer_methods[] = { DEVMETHOD(device_probe, a10_timer_probe), DEVMETHOD(device_attach, a10_timer_attach), DEVMETHOD_END }; static driver_t a10_timer_driver = { "a10_timer", a10_timer_methods, sizeof(struct a10_timer_softc), }; static devclass_t a10_timer_devclass; DRIVER_MODULE(a10_timer, simplebus, a10_timer_driver, a10_timer_devclass, 0, 0); void DELAY(int usec) { uint32_t counter; uint64_t end, now; if (!a10_timer_initialized) { for (; usec > 0; usec--) for (counter = 50; counter > 0; counter--) cpufunc_nullop(); return; } now = timer_read_counter64(); end = now + (a10_timer_sc->timer0_freq / 1000000) * (usec + 1); while (now < end) now = timer_read_counter64(); } Index: head/sys/arm/altera/socfpga/socfpga_gpio.c =================================================================== --- head/sys/arm/altera/socfpga/socfpga_gpio.c (revision 281084) +++ head/sys/arm/altera/socfpga/socfpga_gpio.c (revision 281085) @@ -1,455 +1,454 @@ /*- * Copyright (c) 2015 Ruslan Bukin * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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. */ /* * SOCFPGA General-Purpose I/O Interface. * Chapter 22, Cyclone V Device Handbook (CV-5V2 2014.07.22) */ /* * The GPIO modules are instances of the Synopsys® DesignWare® APB General * Purpose Programming I/O (DW_apb_gpio) peripheral. */ #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 "gpio_if.h" #define READ4(_sc, _reg) \ bus_read_4((_sc)->res[0], _reg) #define WRITE4(_sc, _reg, _val) \ bus_write_4((_sc)->res[0], _reg, _val) #define GPIO_SWPORTA_DR 0x00 /* Port A Data Register */ #define GPIO_SWPORTA_DDR 0x04 /* Port A Data Direction Register */ #define GPIO_INTEN 0x30 /* Interrupt Enable Register */ #define GPIO_INTMASK 0x34 /* Interrupt Mask Register */ #define GPIO_INTTYPE_LEVEL 0x38 /* Interrupt Level Register */ #define GPIO_INT_POLARITY 0x3C /* Interrupt Polarity Register */ #define GPIO_INTSTATUS 0x40 /* Interrupt Status Register */ #define GPIO_RAW_INTSTATUS 0x44 /* Raw Interrupt Status Register */ #define GPIO_DEBOUNCE 0x48 /* Debounce Enable Register */ #define GPIO_PORTA_EOI 0x4C /* Clear Interrupt Register */ #define GPIO_EXT_PORTA 0x50 /* External Port A Register */ #define GPIO_LS_SYNC 0x60 /* Synchronization Level Register */ #define GPIO_ID_CODE 0x64 /* ID Code Register */ #define GPIO_VER_ID_CODE 0x6C /* GPIO Version Register */ #define GPIO_CONFIG_REG2 0x70 /* Configuration Register 2 */ #define ENCODED_ID_PWIDTH_M 0x1f /* Width of GPIO Port N Mask */ #define ENCODED_ID_PWIDTH_S(n) (5 * n) /* Width of GPIO Port N Shift */ #define GPIO_CONFIG_REG1 0x74 /* Configuration Register 1 */ enum port_no { PORTA, PORTB, PORTC, PORTD, }; #define NR_GPIO_MAX 32 /* Maximum pins per port */ #define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) /* * GPIO interface */ static device_t socfpga_gpio_get_bus(device_t); static int socfpga_gpio_pin_max(device_t, int *); static int socfpga_gpio_pin_getcaps(device_t, uint32_t, uint32_t *); static int socfpga_gpio_pin_getname(device_t, uint32_t, char *); static int socfpga_gpio_pin_getflags(device_t, uint32_t, uint32_t *); static int socfpga_gpio_pin_setflags(device_t, uint32_t, uint32_t); static int socfpga_gpio_pin_set(device_t, uint32_t, unsigned int); static int socfpga_gpio_pin_get(device_t, uint32_t, unsigned int *); static int socfpga_gpio_pin_toggle(device_t, uint32_t pin); struct socfpga_gpio_softc { struct resource *res[1]; bus_space_tag_t bst; bus_space_handle_t bsh; device_t dev; device_t busdev; struct mtx sc_mtx; int gpio_npins; struct gpio_pin gpio_pins[NR_GPIO_MAX]; }; struct socfpga_gpio_softc *gpio_sc; static struct resource_spec socfpga_gpio_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static int socfpga_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "snps,dw-apb-gpio")) return (ENXIO); device_set_desc(dev, "DesignWare General-Purpose I/O Interface"); return (BUS_PROBE_DEFAULT); } static int socfpga_gpio_attach(device_t dev) { struct socfpga_gpio_softc *sc; int version; int nr_pins; int cfg2; int i; sc = device_get_softc(dev); sc->dev = dev; mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); if (bus_alloc_resources(dev, socfpga_gpio_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); mtx_destroy(&sc->sc_mtx); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); gpio_sc = sc; version = READ4(sc, GPIO_VER_ID_CODE); #if 0 device_printf(sc->dev, "Version = 0x%08x\n", version); #endif /* * Take number of pins from hardware. * XXX: Assume we have GPIO port A only. */ cfg2 = READ4(sc, GPIO_CONFIG_REG2); nr_pins = (cfg2 >> ENCODED_ID_PWIDTH_S(PORTA)) & \ ENCODED_ID_PWIDTH_M; sc->gpio_npins = nr_pins + 1; for (i = 0; i < sc->gpio_npins; i++) { sc->gpio_pins[i].gp_pin = i; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; sc->gpio_pins[i].gp_flags = (READ4(sc, GPIO_SWPORTA_DDR) & (1 << i)) ? GPIO_PIN_OUTPUT: GPIO_PIN_INPUT; snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "socfpga_gpio%d.%d", device_get_unit(dev), i); } sc->busdev = gpiobus_attach_bus(dev); if (sc->busdev == NULL) { bus_release_resources(dev, socfpga_gpio_spec, sc->res); mtx_destroy(&sc->sc_mtx); return (ENXIO); } return (0); } static device_t socfpga_gpio_get_bus(device_t dev) { struct socfpga_gpio_softc *sc; sc = device_get_softc(dev); return (sc->busdev); } static int socfpga_gpio_pin_max(device_t dev, int *maxpin) { struct socfpga_gpio_softc *sc; sc = device_get_softc(dev); *maxpin = sc->gpio_npins - 1; return (0); } static int socfpga_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct socfpga_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); GPIO_UNLOCK(sc); return (0); } static int socfpga_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct socfpga_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *caps = sc->gpio_pins[i].gp_caps; GPIO_UNLOCK(sc); return (0); } static int socfpga_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct socfpga_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *flags = sc->gpio_pins[i].gp_flags; GPIO_UNLOCK(sc); return (0); } static int socfpga_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct socfpga_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *val = (READ4(sc, GPIO_EXT_PORTA) & (1 << i)) ? 1 : 0; GPIO_UNLOCK(sc); return (0); } static int socfpga_gpio_pin_toggle(device_t dev, uint32_t pin) { struct socfpga_gpio_softc *sc; int reg; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); reg = READ4(sc, GPIO_SWPORTA_DR); if (reg & (1 << i)) reg &= ~(1 << i); else reg |= (1 << i); WRITE4(sc, GPIO_SWPORTA_DR, reg); GPIO_UNLOCK(sc); return (0); } static void socfpga_gpio_pin_configure(struct socfpga_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { int reg; GPIO_LOCK(sc); /* * Manage input/output */ reg = READ4(sc, GPIO_SWPORTA_DDR); 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; reg |= (1 << pin->gp_pin); } else { pin->gp_flags |= GPIO_PIN_INPUT; reg &= ~(1 << pin->gp_pin); } } WRITE4(sc, GPIO_SWPORTA_DDR, reg); GPIO_UNLOCK(sc); } static int socfpga_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct socfpga_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); socfpga_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); return (0); } static int socfpga_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct socfpga_gpio_softc *sc; int reg; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); reg = READ4(sc, GPIO_SWPORTA_DR); if (value) reg |= (1 << i); else reg &= ~(1 << i); WRITE4(sc, GPIO_SWPORTA_DR, reg); GPIO_UNLOCK(sc); return (0); } static device_method_t socfpga_gpio_methods[] = { DEVMETHOD(device_probe, socfpga_gpio_probe), DEVMETHOD(device_attach, socfpga_gpio_attach), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, socfpga_gpio_get_bus), DEVMETHOD(gpio_pin_max, socfpga_gpio_pin_max), DEVMETHOD(gpio_pin_getname, socfpga_gpio_pin_getname), DEVMETHOD(gpio_pin_getcaps, socfpga_gpio_pin_getcaps), DEVMETHOD(gpio_pin_getflags, socfpga_gpio_pin_getflags), DEVMETHOD(gpio_pin_get, socfpga_gpio_pin_get), DEVMETHOD(gpio_pin_toggle, socfpga_gpio_pin_toggle), DEVMETHOD(gpio_pin_setflags, socfpga_gpio_pin_setflags), DEVMETHOD(gpio_pin_set, socfpga_gpio_pin_set), { 0, 0 } }; static driver_t socfpga_gpio_driver = { "gpio", socfpga_gpio_methods, sizeof(struct socfpga_gpio_softc), }; static devclass_t socfpga_gpio_devclass; DRIVER_MODULE(socfpga_gpio, simplebus, socfpga_gpio_driver, socfpga_gpio_devclass, 0, 0); Index: head/sys/arm/altera/socfpga/socfpga_manager.c =================================================================== --- head/sys/arm/altera/socfpga/socfpga_manager.c (revision 281084) +++ head/sys/arm/altera/socfpga/socfpga_manager.c (revision 281085) @@ -1,433 +1,432 @@ /*- * Copyright (c) 2014 Ruslan Bukin * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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. */ /* * Altera FPGA Manager. * Chapter 4, Cyclone V Device Handbook (CV-5V2 2014.07.22) */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include /* FPGA Manager Module Registers */ #define FPGAMGR_STAT 0x0 /* Status Register */ #define STAT_MSEL_MASK 0x1f #define STAT_MSEL_SHIFT 3 #define STAT_MODE_SHIFT 0 #define STAT_MODE_MASK 0x7 #define FPGAMGR_CTRL 0x4 /* Control Register */ #define CTRL_AXICFGEN (1 << 8) #define CTRL_CDRATIO_MASK 0x3 #define CTRL_CDRATIO_SHIFT 6 #define CTRL_CFGWDTH_MASK 1 #define CTRL_CFGWDTH_SHIFT 9 #define CTRL_NCONFIGPULL (1 << 2) #define CTRL_NCE (1 << 1) #define CTRL_EN (1 << 0) #define FPGAMGR_DCLKCNT 0x8 /* DCLK Count Register */ #define FPGAMGR_DCLKSTAT 0xC /* DCLK Status Register */ #define FPGAMGR_GPO 0x10 /* General-Purpose Output Register */ #define FPGAMGR_GPI 0x14 /* General-Purpose Input Register */ #define FPGAMGR_MISCI 0x18 /* Miscellaneous Input Register */ /* Configuration Monitor (MON) Registers */ #define GPIO_INTEN 0x830 /* Interrupt Enable Register */ #define GPIO_INTMASK 0x834 /* Interrupt Mask Register */ #define GPIO_INTTYPE_LEVEL 0x838 /* Interrupt Level Register */ #define GPIO_INT_POLARITY 0x83C /* Interrupt Polarity Register */ #define GPIO_INTSTATUS 0x840 /* Interrupt Status Register */ #define GPIO_RAW_INTSTATUS 0x844 /* Raw Interrupt Status Register */ #define GPIO_PORTA_EOI 0x84C /* Clear Interrupt Register */ #define PORTA_EOI_NS (1 << 0) #define GPIO_EXT_PORTA 0x850 /* External Port A Register */ #define EXT_PORTA_CDP (1 << 10) /* Configuration done */ #define GPIO_LS_SYNC 0x860 /* Synchronization Level Register */ #define GPIO_VER_ID_CODE 0x86C /* GPIO Version Register */ #define GPIO_CONFIG_REG2 0x870 /* Configuration Register 2 */ #define GPIO_CONFIG_REG1 0x874 /* Configuration Register 1 */ #define MSEL_PP16_FAST_NOAES_NODC 0x0 #define MSEL_PP16_FAST_AES_NODC 0x1 #define MSEL_PP16_FAST_AESOPT_DC 0x2 #define MSEL_PP16_SLOW_NOAES_NODC 0x4 #define MSEL_PP16_SLOW_AES_NODC 0x5 #define MSEL_PP16_SLOW_AESOPT_DC 0x6 #define MSEL_PP32_FAST_NOAES_NODC 0x8 #define MSEL_PP32_FAST_AES_NODC 0x9 #define MSEL_PP32_FAST_AESOPT_DC 0xa #define MSEL_PP32_SLOW_NOAES_NODC 0xc #define MSEL_PP32_SLOW_AES_NODC 0xd #define MSEL_PP32_SLOW_AESOPT_DC 0xe #define CFGWDTH_16 0 #define CFGWDTH_32 1 #define CDRATIO_1 0 #define CDRATIO_2 1 #define CDRATIO_4 2 #define CDRATIO_8 3 #define FPGAMGR_MODE_POWEROFF 0x0 #define FPGAMGR_MODE_RESET 0x1 #define FPGAMGR_MODE_CONFIG 0x2 #define FPGAMGR_MODE_INIT 0x3 #define FPGAMGR_MODE_USER 0x4 struct cfgmgr_mode { int msel; int cfgwdth; int cdratio; }; static struct cfgmgr_mode cfgmgr_modes[] = { { MSEL_PP16_FAST_NOAES_NODC, CFGWDTH_16, CDRATIO_1 }, { MSEL_PP16_FAST_AES_NODC, CFGWDTH_16, CDRATIO_2 }, { MSEL_PP16_FAST_AESOPT_DC, CFGWDTH_16, CDRATIO_4 }, { MSEL_PP16_SLOW_NOAES_NODC, CFGWDTH_16, CDRATIO_1 }, { MSEL_PP16_SLOW_AES_NODC, CFGWDTH_16, CDRATIO_2 }, { MSEL_PP16_SLOW_AESOPT_DC, CFGWDTH_16, CDRATIO_4 }, { MSEL_PP32_FAST_NOAES_NODC, CFGWDTH_32, CDRATIO_1 }, { MSEL_PP32_FAST_AES_NODC, CFGWDTH_32, CDRATIO_4 }, { MSEL_PP32_FAST_AESOPT_DC, CFGWDTH_32, CDRATIO_8 }, { MSEL_PP32_SLOW_NOAES_NODC, CFGWDTH_32, CDRATIO_1 }, { MSEL_PP32_SLOW_AES_NODC, CFGWDTH_32, CDRATIO_4 }, { MSEL_PP32_SLOW_AESOPT_DC, CFGWDTH_32, CDRATIO_8 }, { -1, -1, -1 }, }; struct fpgamgr_softc { struct resource *res[3]; bus_space_tag_t bst_data; bus_space_handle_t bsh_data; struct cdev *mgr_cdev; device_t dev; }; static struct resource_spec fpgamgr_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static int fpgamgr_state_get(struct fpgamgr_softc *sc) { int reg; reg = READ4(sc, FPGAMGR_STAT); reg >>= STAT_MODE_SHIFT; reg &= STAT_MODE_MASK; return reg; } static int fpgamgr_state_wait(struct fpgamgr_softc *sc, int state) { int tout; tout = 1000; while (tout > 0) { if (fpgamgr_state_get(sc) == state) break; tout--; DELAY(10); } if (tout == 0) { return (1); } return (0); } static int fpga_open(struct cdev *dev, int flags __unused, int fmt __unused, struct thread *td __unused) { struct fpgamgr_softc *sc; struct cfgmgr_mode *mode; int msel; int reg; int i; sc = dev->si_drv1; msel = READ4(sc, FPGAMGR_STAT); msel >>= STAT_MSEL_SHIFT; msel &= STAT_MSEL_MASK; mode = NULL; for (i = 0; cfgmgr_modes[i].msel != -1; i++) { if (msel == cfgmgr_modes[i].msel) { mode = &cfgmgr_modes[i]; break; } } if (mode == NULL) { device_printf(sc->dev, "Can't configure: unknown mode\n"); return (ENXIO); } reg = READ4(sc, FPGAMGR_CTRL); reg &= ~(CTRL_CDRATIO_MASK << CTRL_CDRATIO_SHIFT); reg |= (mode->cdratio << CTRL_CDRATIO_SHIFT); reg &= ~(CTRL_CFGWDTH_MASK << CTRL_CFGWDTH_SHIFT); reg |= (mode->cfgwdth << CTRL_CFGWDTH_SHIFT); reg &= ~(CTRL_NCE); WRITE4(sc, FPGAMGR_CTRL, reg); /* Enable configuration */ reg = READ4(sc, FPGAMGR_CTRL); reg |= (CTRL_EN); WRITE4(sc, FPGAMGR_CTRL, reg); /* Reset FPGA */ reg = READ4(sc, FPGAMGR_CTRL); reg |= (CTRL_NCONFIGPULL); WRITE4(sc, FPGAMGR_CTRL, reg); /* Wait reset state */ if (fpgamgr_state_wait(sc, FPGAMGR_MODE_RESET)) { device_printf(sc->dev, "Can't get RESET state\n"); return (ENXIO); } /* Release from reset */ reg = READ4(sc, FPGAMGR_CTRL); reg &= ~(CTRL_NCONFIGPULL); WRITE4(sc, FPGAMGR_CTRL, reg); if (fpgamgr_state_wait(sc, FPGAMGR_MODE_CONFIG)) { device_printf(sc->dev, "Can't get CONFIG state\n"); return (ENXIO); } /* Clear nSTATUS edge interrupt */ WRITE4(sc, GPIO_PORTA_EOI, PORTA_EOI_NS); /* Enter configuration state */ reg = READ4(sc, FPGAMGR_CTRL); reg |= (CTRL_AXICFGEN); WRITE4(sc, FPGAMGR_CTRL, reg); return (0); } static int fpga_wait_dclk_pulses(struct fpgamgr_softc *sc, int npulses) { int tout; /* Clear done bit, if any */ if (READ4(sc, FPGAMGR_DCLKSTAT) != 0) WRITE4(sc, FPGAMGR_DCLKSTAT, 0x1); /* Request DCLK pulses */ WRITE4(sc, FPGAMGR_DCLKCNT, npulses); /* Wait finish */ tout = 1000; while (tout > 0) { if (READ4(sc, FPGAMGR_DCLKSTAT) == 1) { WRITE4(sc, FPGAMGR_DCLKSTAT, 0x1); break; } tout--; DELAY(10); } if (tout == 0) { return (1); } return (0); } static int fpga_close(struct cdev *dev, int flags __unused, int fmt __unused, struct thread *td __unused) { struct fpgamgr_softc *sc; int reg; sc = dev->si_drv1; reg = READ4(sc, GPIO_EXT_PORTA); if ((reg & EXT_PORTA_CDP) == 0) { device_printf(sc->dev, "Err: configuration failed\n"); return (ENXIO); } /* Exit configuration state */ reg = READ4(sc, FPGAMGR_CTRL); reg &= ~(CTRL_AXICFGEN); WRITE4(sc, FPGAMGR_CTRL, reg); /* Wait dclk pulses */ if (fpga_wait_dclk_pulses(sc, 4)) { device_printf(sc->dev, "Can't proceed 4 dclk pulses\n"); return (ENXIO); } if (fpgamgr_state_wait(sc, FPGAMGR_MODE_USER)) { device_printf(sc->dev, "Can't get USER mode\n"); return (ENXIO); } /* Disable configuration */ reg = READ4(sc, FPGAMGR_CTRL); reg &= ~(CTRL_EN); WRITE4(sc, FPGAMGR_CTRL, reg); return (0); } static int fpga_write(struct cdev *dev, struct uio *uio, int ioflag) { struct fpgamgr_softc *sc; int buffer; sc = dev->si_drv1; /* * Device supports 4-byte copy only. * TODO: add padding for <4 bytes. */ while (uio->uio_resid > 0) { uiomove(&buffer, 4, uio); bus_space_write_4(sc->bst_data, sc->bsh_data, 0x0, buffer); } return (0); } static int fpga_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { return (0); } static struct cdevsw fpga_cdevsw = { .d_version = D_VERSION, .d_open = fpga_open, .d_close = fpga_close, .d_write = fpga_write, .d_ioctl = fpga_ioctl, .d_name = "FPGA Manager", }; static int fpgamgr_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "altr,fpga-mgr")) return (ENXIO); device_set_desc(dev, "FPGA Manager"); return (BUS_PROBE_DEFAULT); } static int fpgamgr_attach(device_t dev) { struct fpgamgr_softc *sc; sc = device_get_softc(dev); sc->dev = dev; if (bus_alloc_resources(dev, fpgamgr_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst_data = rman_get_bustag(sc->res[1]); sc->bsh_data = rman_get_bushandle(sc->res[1]); sc->mgr_cdev = make_dev(&fpga_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "fpga%d", device_get_unit(sc->dev)); if (sc->mgr_cdev == NULL) { device_printf(dev, "Failed to create character device.\n"); return (ENXIO); } sc->mgr_cdev->si_drv1 = sc; return (0); } static device_method_t fpgamgr_methods[] = { DEVMETHOD(device_probe, fpgamgr_probe), DEVMETHOD(device_attach, fpgamgr_attach), { 0, 0 } }; static driver_t fpgamgr_driver = { "fpgamgr", fpgamgr_methods, sizeof(struct fpgamgr_softc), }; static devclass_t fpgamgr_devclass; DRIVER_MODULE(fpgamgr, simplebus, fpgamgr_driver, fpgamgr_devclass, 0, 0); Index: head/sys/arm/broadcom/bcm2835/bcm2835_common.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_common.c (revision 281084) +++ head/sys/arm/broadcom/bcm2835/bcm2835_common.c (revision 281085) @@ -1,73 +1,72 @@ /*- * 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 __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); } fdt_pic_decode_t fdt_pic_table[] = { &fdt_intc_decode_ic, NULL }; Index: head/sys/arm/broadcom/bcm2835/bcm2835_spi.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_spi.c (revision 281084) +++ head/sys/arm/broadcom/bcm2835/bcm2835_spi.c (revision 281085) @@ -1,523 +1,522 @@ /*- * 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 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")) 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")); 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. */ sc->sc_flags = 0; /* * 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: head/sys/arm/broadcom/bcm2835/bcm2835_systimer.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_systimer.c (revision 281084) +++ head/sys/arm/broadcom/bcm2835/bcm2835_systimer.c (revision 281085) @@ -1,310 +1,309 @@ /* * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2012 Damjan Marion * 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 #define BCM2835_NUM_TIMERS 4 #define DEFAULT_TIMER 3 #define DEFAULT_FREQUENCY 1000000 #define MIN_PERIOD 5LLU #define SYSTIMER_CS 0x00 #define SYSTIMER_CLO 0x04 #define SYSTIMER_CHI 0x08 #define SYSTIMER_C0 0x0C #define SYSTIMER_C1 0x10 #define SYSTIMER_C2 0x14 #define SYSTIMER_C3 0x18 struct systimer { int index; bool enabled; struct eventtimer et; }; struct bcm_systimer_softc { struct resource* mem_res; struct resource* irq_res[BCM2835_NUM_TIMERS]; void* intr_hl[BCM2835_NUM_TIMERS]; uint32_t sysclk_freq; bus_space_tag_t bst; bus_space_handle_t bsh; struct systimer st[BCM2835_NUM_TIMERS]; }; static struct resource_spec bcm_systimer_irq_spec[] = { { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { SYS_RES_IRQ, 3, RF_ACTIVE }, { -1, 0, 0 } }; static struct bcm_systimer_softc *bcm_systimer_sc = NULL; /* Read/Write macros for Timer used as timecounter */ #define bcm_systimer_tc_read_4(reg) \ bus_space_read_4(bcm_systimer_sc->bst, \ bcm_systimer_sc->bsh, reg) #define bcm_systimer_tc_write_4(reg, val) \ bus_space_write_4(bcm_systimer_sc->bst, \ bcm_systimer_sc->bsh, reg, val) static unsigned bcm_systimer_tc_get_timecount(struct timecounter *); static struct timecounter bcm_systimer_tc = { .tc_name = "BCM2835 Timecounter", .tc_get_timecount = bcm_systimer_tc_get_timecount, .tc_poll_pps = NULL, .tc_counter_mask = ~0u, .tc_frequency = 0, .tc_quality = 1000, }; static unsigned bcm_systimer_tc_get_timecount(struct timecounter *tc) { return bcm_systimer_tc_read_4(SYSTIMER_CLO); } static int bcm_systimer_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { struct systimer *st = et->et_priv; uint32_t clo, clo1; uint32_t count; register_t s; if (first != 0) { count = ((uint32_t)et->et_frequency * first) >> 32; s = intr_disable(); clo = bcm_systimer_tc_read_4(SYSTIMER_CLO); restart: clo += count; /* * Clear pending interrupts */ bcm_systimer_tc_write_4(SYSTIMER_CS, (1 << st->index)); bcm_systimer_tc_write_4(SYSTIMER_C0 + st->index*4, clo); clo1 = bcm_systimer_tc_read_4(SYSTIMER_CLO); if ((int32_t)(clo1 - clo) >= 0) { count *= 2; clo = clo1; goto restart; } st->enabled = 1; intr_restore(s); return (0); } return (EINVAL); } static int bcm_systimer_stop(struct eventtimer *et) { struct systimer *st = et->et_priv; st->enabled = 0; return (0); } static int bcm_systimer_intr(void *arg) { struct systimer *st = (struct systimer *)arg; uint32_t cs; cs = bcm_systimer_tc_read_4(SYSTIMER_CS); if ((cs & (1 << st->index)) == 0) return (FILTER_STRAY); /* ACK interrupt */ bcm_systimer_tc_write_4(SYSTIMER_CS, (1 << st->index)); if (st->enabled) { if (st->et.et_active) { st->et.et_event_cb(&st->et, st->et.et_arg); } } return (FILTER_HANDLED); } static int bcm_systimer_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "broadcom,bcm2835-system-timer")) { device_set_desc(dev, "BCM2835 System Timer"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int bcm_systimer_attach(device_t dev) { struct bcm_systimer_softc *sc = device_get_softc(dev); int err; int rid = 0; if (bcm_systimer_sc != NULL) return (EINVAL); 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); /* Request the IRQ resources */ err = bus_alloc_resources(dev, bcm_systimer_irq_spec, sc->irq_res); if (err) { device_printf(dev, "Error: could not allocate irq resources\n"); return (ENXIO); } /* TODO: get frequency from FDT */ sc->sysclk_freq = DEFAULT_FREQUENCY; /* Setup and enable the timer */ if (bus_setup_intr(dev, sc->irq_res[DEFAULT_TIMER], INTR_TYPE_CLK, bcm_systimer_intr, NULL, &sc->st[DEFAULT_TIMER], &sc->intr_hl[DEFAULT_TIMER]) != 0) { bus_release_resources(dev, bcm_systimer_irq_spec, sc->irq_res); device_printf(dev, "Unable to setup the clock irq handler.\n"); return (ENXIO); } sc->st[DEFAULT_TIMER].index = DEFAULT_TIMER; sc->st[DEFAULT_TIMER].enabled = 0; sc->st[DEFAULT_TIMER].et.et_name = malloc(64, M_DEVBUF, M_NOWAIT | M_ZERO); sprintf(sc->st[DEFAULT_TIMER].et.et_name, "BCM2835 Event Timer %d", DEFAULT_TIMER); sc->st[DEFAULT_TIMER].et.et_flags = ET_FLAGS_ONESHOT; sc->st[DEFAULT_TIMER].et.et_quality = 1000; sc->st[DEFAULT_TIMER].et.et_frequency = sc->sysclk_freq; sc->st[DEFAULT_TIMER].et.et_min_period = (MIN_PERIOD << 32) / sc->st[DEFAULT_TIMER].et.et_frequency + 1; sc->st[DEFAULT_TIMER].et.et_max_period = (0x7ffffffeLLU << 32) / sc->st[DEFAULT_TIMER].et.et_frequency; sc->st[DEFAULT_TIMER].et.et_start = bcm_systimer_start; sc->st[DEFAULT_TIMER].et.et_stop = bcm_systimer_stop; sc->st[DEFAULT_TIMER].et.et_priv = &sc->st[DEFAULT_TIMER]; et_register(&sc->st[DEFAULT_TIMER].et); bcm_systimer_sc = sc; bcm_systimer_tc.tc_frequency = DEFAULT_FREQUENCY; tc_init(&bcm_systimer_tc); return (0); } static device_method_t bcm_systimer_methods[] = { DEVMETHOD(device_probe, bcm_systimer_probe), DEVMETHOD(device_attach, bcm_systimer_attach), { 0, 0 } }; static driver_t bcm_systimer_driver = { "systimer", bcm_systimer_methods, sizeof(struct bcm_systimer_softc), }; static devclass_t bcm_systimer_devclass; DRIVER_MODULE(bcm_systimer, simplebus, bcm_systimer_driver, bcm_systimer_devclass, 0, 0); void DELAY(int usec) { int32_t counts; uint32_t first, last; if (bcm_systimer_sc == NULL) { for (; usec > 0; usec--) for (counts = 200; counts > 0; counts--) /* Prevent gcc from optimizing out the loop */ cpufunc_nullop(); return; } /* Get the number of times to count */ counts = usec * (bcm_systimer_tc.tc_frequency / 1000000) + 1; first = bcm_systimer_tc_read_4(SYSTIMER_CLO); while (counts > 0) { last = bcm_systimer_tc_read_4(SYSTIMER_CLO); if (last == first) continue; if (last>first) { counts -= (int32_t)(last - first); } else { counts -= (int32_t)((0xFFFFFFFF - first) + last); } first = last; } } Index: head/sys/arm/broadcom/bcm2835/bcm2835_wdog.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_wdog.c (revision 281084) +++ head/sys/arm/broadcom/bcm2835/bcm2835_wdog.c (revision 281085) @@ -1,209 +1,208 @@ /*- * 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_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 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; }; 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); } return (ENXIO); } 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_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); 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_RSTC_RESET); mtx_unlock(&sc->mtx); return; } ticks = (sec << 16) & BCM2835_WDOG_TIME_MASK; reg = (BCM2835_PASWORD << 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); WRITE(sc, BCM2835_RSTC_REG, reg); *error = 0; } else WRITE(sc, BCM2835_RSTC_REG, (BCM2835_PASWORD << 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); WRITE(bcmwd_lsc, BCM2835_RSTC_REG, (READ(bcmwd_lsc, BCM2835_RSTC_REG) & BCM2835_RSTC_WRCFG_CLR) | (BCM2835_PASWORD << 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: head/sys/arm/freescale/imx/imx51_ccm.c =================================================================== --- head/sys/arm/freescale/imx/imx51_ccm.c (revision 281084) +++ head/sys/arm/freescale/imx/imx51_ccm.c (revision 281085) @@ -1,589 +1,588 @@ /* $NetBSD: imx51_ccm.c,v 1.1 2012/04/17 09:33:31 bsh Exp $ */ /* * Copyright (c) 2010, 2011, 2012 Genetec Corporation. All rights reserved. * Written by Hashimoto Kenichi for Genetec Corporation. * * 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 GENETEC CORPORATION ``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 GENETEC CORPORATION * 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. */ /*- * 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. */ /* * Clock Controller Module (CCM) */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #define IMXCCMDEBUG #undef IMXCCMDEBUG #ifndef IMX51_OSC_FREQ #define IMX51_OSC_FREQ (24 * 1000 * 1000) /* 24MHz */ #endif #ifndef IMX51_CKIL_FREQ #define IMX51_CKIL_FREQ 32768 #endif struct imxccm_softc { device_t sc_dev; struct resource *res[7]; u_int64_t pll_freq[IMX51_N_DPLLS]; }; struct imxccm_softc *ccm_softc = NULL; static uint64_t imx51_get_pll_freq(u_int); static int imxccm_match(device_t); static int imxccm_attach(device_t); static device_method_t imxccm_methods[] = { DEVMETHOD(device_probe, imxccm_match), DEVMETHOD(device_attach, imxccm_attach), DEVMETHOD_END }; static driver_t imxccm_driver = { "imxccm", imxccm_methods, sizeof(struct imxccm_softc), }; static devclass_t imxccm_devclass; EARLY_DRIVER_MODULE(imxccm, simplebus, imxccm_driver, imxccm_devclass, 0, 0, BUS_PASS_CPU); static struct resource_spec imxccm_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Global registers */ { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* DPLLIP1 */ { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* DPLLIP2 */ { SYS_RES_MEMORY, 3, RF_ACTIVE }, /* DPLLIP3 */ { SYS_RES_IRQ, 0, RF_ACTIVE }, /* 71 */ { SYS_RES_IRQ, 1, RF_ACTIVE }, /* 72 */ { -1, 0 } }; static int imxccm_match(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,imx51-ccm") && !ofw_bus_is_compatible(dev, "fsl,imx53-ccm")) return (ENXIO); device_set_desc(dev, "Freescale Clock Control Module"); return (BUS_PROBE_DEFAULT); } static int imxccm_attach(device_t dev) { struct imxccm_softc *sc; sc = device_get_softc(dev); sc->sc_dev = dev; if (bus_alloc_resources(dev, imxccm_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } ccm_softc = sc; imx51_get_pll_freq(1); imx51_get_pll_freq(2); imx51_get_pll_freq(3); device_printf(dev, "PLL1=%lluMHz, PLL2=%lluMHz, PLL3=%lluMHz\n", sc->pll_freq[0] / 1000000, sc->pll_freq[1] / 1000000, sc->pll_freq[2] / 1000000); device_printf(dev, "CPU clock=%d, UART clock=%d\n", imx51_get_clock(IMX51CLK_ARM_ROOT), imx51_get_clock(IMX51CLK_UART_CLK_ROOT)); device_printf(dev, "mainbus clock=%d, ahb clock=%d ipg clock=%d perclk=%d\n", imx51_get_clock(IMX51CLK_MAIN_BUS_CLK), imx51_get_clock(IMX51CLK_AHB_CLK_ROOT), imx51_get_clock(IMX51CLK_IPG_CLK_ROOT), imx51_get_clock(IMX51CLK_PERCLK_ROOT)); return (0); } u_int imx51_get_clock(enum imx51_clock clk) { u_int freq; u_int sel; uint32_t cacrr; /* ARM clock root register */ uint32_t ccsr; uint32_t cscdr1; uint32_t cscmr1; uint32_t cbcdr; uint32_t cbcmr; uint32_t cdcr; if (ccm_softc == NULL) return (0); switch (clk) { case IMX51CLK_PLL1: case IMX51CLK_PLL2: case IMX51CLK_PLL3: return ccm_softc->pll_freq[clk-IMX51CLK_PLL1]; case IMX51CLK_PLL1SW: ccsr = bus_read_4(ccm_softc->res[0], CCMC_CCSR); if ((ccsr & CCSR_PLL1_SW_CLK_SEL) == 0) return ccm_softc->pll_freq[1-1]; /* step clock */ /* FALLTHROUGH */ case IMX51CLK_PLL1STEP: ccsr = bus_read_4(ccm_softc->res[0], CCMC_CCSR); switch ((ccsr & CCSR_STEP_SEL_MASK) >> CCSR_STEP_SEL_SHIFT) { case 0: return imx51_get_clock(IMX51CLK_LP_APM); case 1: return 0; /* XXX PLL bypass clock */ case 2: return ccm_softc->pll_freq[2-1] / (1 + ((ccsr & CCSR_PLL2_DIV_PODF_MASK) >> CCSR_PLL2_DIV_PODF_SHIFT)); case 3: return ccm_softc->pll_freq[3-1] / (1 + ((ccsr & CCSR_PLL3_DIV_PODF_MASK) >> CCSR_PLL3_DIV_PODF_SHIFT)); } /*NOTREACHED*/ case IMX51CLK_PLL2SW: ccsr = bus_read_4(ccm_softc->res[0], CCMC_CCSR); if ((ccsr & CCSR_PLL2_SW_CLK_SEL) == 0) return imx51_get_clock(IMX51CLK_PLL2); return 0; /* XXX PLL2 bypass clk */ case IMX51CLK_PLL3SW: ccsr = bus_read_4(ccm_softc->res[0], CCMC_CCSR); if ((ccsr & CCSR_PLL3_SW_CLK_SEL) == 0) return imx51_get_clock(IMX51CLK_PLL3); return 0; /* XXX PLL3 bypass clk */ case IMX51CLK_LP_APM: ccsr = bus_read_4(ccm_softc->res[0], CCMC_CCSR); return (ccsr & CCSR_LP_APM) ? imx51_get_clock(IMX51CLK_FPM) : IMX51_OSC_FREQ; case IMX51CLK_ARM_ROOT: freq = imx51_get_clock(IMX51CLK_PLL1SW); cacrr = bus_read_4(ccm_softc->res[0], CCMC_CACRR); return freq / (cacrr + 1); /* ... */ case IMX51CLK_MAIN_BUS_CLK_SRC: cbcdr = bus_read_4(ccm_softc->res[0], CCMC_CBCDR); if ((cbcdr & CBCDR_PERIPH_CLK_SEL) == 0) freq = imx51_get_clock(IMX51CLK_PLL2SW); else { freq = 0; cbcmr = bus_read_4(ccm_softc->res[0], CCMC_CBCMR); switch ((cbcmr & CBCMR_PERIPH_APM_SEL_MASK) >> CBCMR_PERIPH_APM_SEL_SHIFT) { case 0: freq = imx51_get_clock(IMX51CLK_PLL1SW); break; case 1: freq = imx51_get_clock(IMX51CLK_PLL3SW); break; case 2: freq = imx51_get_clock(IMX51CLK_LP_APM); break; case 3: /* XXX: error */ break; } } return freq; case IMX51CLK_MAIN_BUS_CLK: freq = imx51_get_clock(IMX51CLK_MAIN_BUS_CLK_SRC); cdcr = bus_read_4(ccm_softc->res[0], CCMC_CDCR); return freq / (1 + ((cdcr & CDCR_PERIPH_CLK_DVFS_PODF_MASK) >> CDCR_PERIPH_CLK_DVFS_PODF_SHIFT)); case IMX51CLK_AHB_CLK_ROOT: freq = imx51_get_clock(IMX51CLK_MAIN_BUS_CLK); cbcdr = bus_read_4(ccm_softc->res[0], CCMC_CBCDR); return freq / (1 + ((cbcdr & CBCDR_AHB_PODF_MASK) >> CBCDR_AHB_PODF_SHIFT)); case IMX51CLK_IPG_CLK_ROOT: freq = imx51_get_clock(IMX51CLK_AHB_CLK_ROOT); cbcdr = bus_read_4(ccm_softc->res[0], CCMC_CBCDR); return freq / (1 + ((cbcdr & CBCDR_IPG_PODF_MASK) >> CBCDR_IPG_PODF_SHIFT)); case IMX51CLK_PERCLK_ROOT: cbcmr = bus_read_4(ccm_softc->res[0], CCMC_CBCMR); if (cbcmr & CBCMR_PERCLK_IPG_SEL) return imx51_get_clock(IMX51CLK_IPG_CLK_ROOT); if (cbcmr & CBCMR_PERCLK_LP_APM_SEL) freq = imx51_get_clock(IMX51CLK_LP_APM); else freq = imx51_get_clock(IMX51CLK_MAIN_BUS_CLK_SRC); cbcdr = bus_read_4(ccm_softc->res[0], CCMC_CBCDR); #ifdef IMXCCMDEBUG printf("cbcmr=%x cbcdr=%x\n", cbcmr, cbcdr); #endif freq /= 1 + ((cbcdr & CBCDR_PERCLK_PRED1_MASK) >> CBCDR_PERCLK_PRED1_SHIFT); freq /= 1 + ((cbcdr & CBCDR_PERCLK_PRED2_MASK) >> CBCDR_PERCLK_PRED2_SHIFT); freq /= 1 + ((cbcdr & CBCDR_PERCLK_PODF_MASK) >> CBCDR_PERCLK_PODF_SHIFT); return freq; case IMX51CLK_UART_CLK_ROOT: cscdr1 = bus_read_4(ccm_softc->res[0], CCMC_CSCDR1); cscmr1 = bus_read_4(ccm_softc->res[0], CCMC_CSCMR1); #ifdef IMXCCMDEBUG printf("cscdr1=%x cscmr1=%x\n", cscdr1, cscmr1); #endif sel = (cscmr1 & CSCMR1_UART_CLK_SEL_MASK) >> CSCMR1_UART_CLK_SEL_SHIFT; freq = 0; /* shut up GCC */ switch (sel) { case 0: case 1: case 2: freq = imx51_get_clock(IMX51CLK_PLL1SW + sel); break; case 3: freq = imx51_get_clock(IMX51CLK_LP_APM); break; } return freq / (1 + ((cscdr1 & CSCDR1_UART_CLK_PRED_MASK) >> CSCDR1_UART_CLK_PRED_SHIFT)) / (1 + ((cscdr1 & CSCDR1_UART_CLK_PODF_MASK) >> CSCDR1_UART_CLK_PODF_SHIFT)); case IMX51CLK_IPU_HSP_CLK_ROOT: freq = 0; cbcmr = bus_read_4(ccm_softc->res[0], CCMC_CBCMR); switch ((cbcmr & CBCMR_IPU_HSP_CLK_SEL_MASK) >> CBCMR_IPU_HSP_CLK_SEL_SHIFT) { case 0: freq = imx51_get_clock(IMX51CLK_ARM_AXI_A_CLK); break; case 1: freq = imx51_get_clock(IMX51CLK_ARM_AXI_B_CLK); break; case 2: freq = imx51_get_clock( IMX51CLK_EMI_SLOW_CLK_ROOT); break; case 3: freq = imx51_get_clock(IMX51CLK_AHB_CLK_ROOT); break; } return freq; default: device_printf(ccm_softc->sc_dev, "clock %d: not supported yet\n", clk); return 0; } } static uint64_t imx51_get_pll_freq(u_int pll_no) { uint32_t dp_ctrl; uint32_t dp_op; uint32_t dp_mfd; uint32_t dp_mfn; uint32_t mfi; int32_t mfn; uint32_t mfd; uint32_t pdf; uint32_t ccr; uint64_t freq = 0; u_int ref = 0; KASSERT(1 <= pll_no && pll_no <= IMX51_N_DPLLS, ("Wrong PLL id")); dp_ctrl = bus_read_4(ccm_softc->res[pll_no], DPLL_DP_CTL); if (dp_ctrl & DP_CTL_HFSM) { dp_op = bus_read_4(ccm_softc->res[pll_no], DPLL_DP_HFS_OP); dp_mfd = bus_read_4(ccm_softc->res[pll_no], DPLL_DP_HFS_MFD); dp_mfn = bus_read_4(ccm_softc->res[pll_no], DPLL_DP_HFS_MFN); } else { dp_op = bus_read_4(ccm_softc->res[pll_no], DPLL_DP_OP); dp_mfd = bus_read_4(ccm_softc->res[pll_no], DPLL_DP_MFD); dp_mfn = bus_read_4(ccm_softc->res[pll_no], DPLL_DP_MFN); } pdf = dp_op & DP_OP_PDF_MASK; mfi = max(5, (dp_op & DP_OP_MFI_MASK) >> DP_OP_MFI_SHIFT); mfd = dp_mfd; if (dp_mfn & 0x04000000) /* 27bit signed value */ mfn = (uint32_t)(0xf8000000 | dp_mfn); else mfn = dp_mfn; switch (dp_ctrl & DP_CTL_REF_CLK_SEL_MASK) { case DP_CTL_REF_CLK_SEL_COSC: /* Internal Oscillator */ /* TODO: get from FDT "fsl,imx-osc" */ ref = 24000000; /* IMX51_OSC_FREQ */ break; case DP_CTL_REF_CLK_SEL_FPM: ccr = bus_read_4(ccm_softc->res[0], CCMC_CCR); if (ccr & CCR_FPM_MULT) /* TODO: get from FDT "fsl,imx-ckil" */ ref = 32768 * 1024; else /* TODO: get from FDT "fsl,imx-ckil" */ ref = 32768 * 512; break; default: ref = 0; } if (dp_ctrl & DP_CTL_REF_CLK_DIV) ref /= 2; ref *= 4; freq = (int64_t)ref * mfi + (int64_t)ref * mfn / (mfd + 1); freq /= pdf + 1; if (!(dp_ctrl & DP_CTL_DPDCK0_2_EN)) freq /= 2; #ifdef IMXCCMDEBUG printf("ref: %dKHz ", ref); printf("dp_ctl: %08x ", dp_ctrl); printf("pdf: %3d ", pdf); printf("mfi: %3d ", mfi); printf("mfd: %3d ", mfd); printf("mfn: %3d ", mfn); printf("pll: %d\n", (uint32_t)freq); #endif ccm_softc->pll_freq[pll_no-1] = freq; return (freq); } void imx51_clk_gating(int clk_src, int mode) { int field, group; uint32_t reg; group = CCMR_CCGR_MODULE(clk_src); field = clk_src % CCMR_CCGR_NSOURCE; reg = bus_read_4(ccm_softc->res[0], CCMC_CCGR(group)); reg &= ~(0x03 << field * 2); reg |= (mode << field * 2); bus_write_4(ccm_softc->res[0], CCMC_CCGR(group), reg); } int imx51_get_clk_gating(int clk_src) { uint32_t reg; reg = bus_read_4(ccm_softc->res[0], CCMC_CCGR(CCMR_CCGR_MODULE(clk_src))); return ((reg >> (clk_src % CCMR_CCGR_NSOURCE) * 2) & 0x03); } /* * Code from here down is temporary, in lieu of a SoC-independent clock API. */ void imx_ccm_usb_enable(device_t dev) { uint32_t regval; /* * Select PLL2 as the source for the USB clock. * The default is PLL3, but U-boot changes it to PLL2. */ regval = bus_read_4(ccm_softc->res[0], CCMC_CSCMR1); regval &= ~CSCMR1_USBOH3_CLK_SEL_MASK; regval |= 1 << CSCMR1_USBOH3_CLK_SEL_SHIFT; bus_write_4(ccm_softc->res[0], CCMC_CSCMR1, regval); /* * Set the USB clock pre-divider to div-by-5, post-divider to div-by-2. */ regval = bus_read_4(ccm_softc->res[0], CCMC_CSCDR1); regval &= ~CSCDR1_USBOH3_CLK_PODF_MASK; regval &= ~CSCDR1_USBOH3_CLK_PRED_MASK; regval |= 4 << CSCDR1_USBOH3_CLK_PRED_SHIFT; regval |= 1 << CSCDR1_USBOH3_CLK_PODF_SHIFT; bus_write_4(ccm_softc->res[0], CCMC_CSCDR1, regval); /* * The same two clocks gates are used on imx51 and imx53. */ imx51_clk_gating(CCGR_USBOH3_IPG_AHB_CLK, CCGR_CLK_MODE_ALWAYS); imx51_clk_gating(CCGR_USBOH3_60M_CLK, CCGR_CLK_MODE_ALWAYS); } void imx_ccm_usbphy_enable(device_t dev) { uint32_t regval; /* * Select PLL3 as the source for the USBPHY clock. U-boot does this * only for imx53, but the bit exists on imx51. That seems a bit * strange, but we'll go with it until more is known. */ if (imx_soc_type() == IMXSOC_53) { regval = bus_read_4(ccm_softc->res[0], CCMC_CSCMR1); regval |= 1 << CSCMR1_USBPHY_CLK_SEL_SHIFT; bus_write_4(ccm_softc->res[0], CCMC_CSCMR1, regval); } /* * For the imx51 there's just one phy gate control, enable it. */ if (imx_soc_type() == IMXSOC_51) { imx51_clk_gating(CCGR_USB_PHY_CLK, CCGR_CLK_MODE_ALWAYS); return; } /* * For imx53 we don't have a full set of clock defines yet, but the * datasheet says: * gate reg 4, bits 13-12 usb ph2 clock (usb_phy2_clk_enable) * gate reg 4, bits 11-10 usb ph1 clock (usb_phy1_clk_enable) * * We should use the fdt data for the device to figure out which of * the two we're working on, but for now just turn them both on. */ if (imx_soc_type() == IMXSOC_53) { imx51_clk_gating(__CCGR_NUM(4, 5), CCGR_CLK_MODE_ALWAYS); imx51_clk_gating(__CCGR_NUM(4, 6), CCGR_CLK_MODE_ALWAYS); return; } } uint32_t imx_ccm_ipg_hz(void) { return (imx51_get_clock(IMX51CLK_IPG_CLK_ROOT)); } uint32_t imx_ccm_sdhci_hz(void) { return (imx51_get_clock(IMX51CLK_ESDHC1_CLK_ROOT)); } uint32_t imx_ccm_perclk_hz(void) { return (imx51_get_clock(IMX51CLK_PERCLK_ROOT)); } uint32_t imx_ccm_uart_hz(void) { return (imx51_get_clock(IMX51CLK_UART_CLK_ROOT)); } uint32_t imx_ccm_ahb_hz(void) { return (imx51_get_clock(IMX51CLK_AHB_CLK_ROOT)); } Index: head/sys/arm/freescale/imx/imx51_ipuv3_fbd.c =================================================================== --- head/sys/arm/freescale/imx/imx51_ipuv3_fbd.c (revision 281084) +++ head/sys/arm/freescale/imx/imx51_ipuv3_fbd.c (revision 281085) @@ -1,366 +1,365 @@ /*- * 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 "fb_if.h" #define IMX51_IPU_HSP_CLOCK 665000000 struct ipu3sc_softc { device_t dev; device_t sc_fbd; /* fbd child */ struct fb_info sc_info; bus_space_tag_t iot; bus_space_handle_t ioh; bus_space_handle_t cm_ioh; bus_space_handle_t dp_ioh; bus_space_handle_t di0_ioh; bus_space_handle_t di1_ioh; bus_space_handle_t dctmpl_ioh; bus_space_handle_t dc_ioh; bus_space_handle_t dmfc_ioh; bus_space_handle_t idmac_ioh; bus_space_handle_t cpmem_ioh; }; static struct ipu3sc_softc *ipu3sc_softc; #define IPUV3_READ(ipuv3, module, reg) \ bus_space_read_4((ipuv3)->iot, (ipuv3)->module##_ioh, (reg)) #define IPUV3_WRITE(ipuv3, module, reg, val) \ bus_space_write_4((ipuv3)->iot, (ipuv3)->module##_ioh, (reg), (val)) #define CPMEM_CHANNEL_OFFSET(_c) ((_c) * 0x40) #define CPMEM_WORD_OFFSET(_w) ((_w) * 0x20) #define CPMEM_DP_OFFSET(_d) ((_d) * 0x10000) #define IMX_IPU_DP0 0 #define IMX_IPU_DP1 1 #define CPMEM_CHANNEL(_dp, _ch, _w) \ (CPMEM_DP_OFFSET(_dp) + CPMEM_CHANNEL_OFFSET(_ch) + \ CPMEM_WORD_OFFSET(_w)) #define CPMEM_OFFSET(_dp, _ch, _w, _o) \ (CPMEM_CHANNEL((_dp), (_ch), (_w)) + (_o)) static int ipu3_fb_probe(device_t); static int ipu3_fb_attach(device_t); static void ipu3_fb_init(struct ipu3sc_softc *sc) { uint64_t w0sh96; uint32_t w1sh96; /* FW W0[137:125] - 96 = [41:29] */ /* FH W0[149:138] - 96 = [53:42] */ w0sh96 = IPUV3_READ(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 0, 16)); w0sh96 <<= 32; w0sh96 |= IPUV3_READ(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 0, 12)); sc->sc_info.fb_width = ((w0sh96 >> 29) & 0x1fff) + 1; sc->sc_info.fb_height = ((w0sh96 >> 42) & 0x0fff) + 1; /* SLY W1[115:102] - 96 = [19:6] */ w1sh96 = IPUV3_READ(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 1, 12)); sc->sc_info.fb_stride = ((w1sh96 >> 6) & 0x3fff) + 1; printf("%dx%d [%d]\n", sc->sc_info.fb_width, sc->sc_info.fb_height, sc->sc_info.fb_stride); sc->sc_info.fb_size = sc->sc_info.fb_height * sc->sc_info.fb_stride; sc->sc_info.fb_vbase = (intptr_t)contigmalloc(sc->sc_info.fb_size, M_DEVBUF, M_ZERO, 0, ~0, PAGE_SIZE, 0); sc->sc_info.fb_pbase = (intptr_t)vtophys(sc->sc_info.fb_vbase); /* DP1 + config_ch_23 + word_2 */ IPUV3_WRITE(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 1, 0), (((uint32_t)sc->sc_info.fb_pbase >> 3) | (((uint32_t)sc->sc_info.fb_pbase >> 3) << 29)) & 0xffffffff); IPUV3_WRITE(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 1, 4), (((uint32_t)sc->sc_info.fb_pbase >> 3) >> 3) & 0xffffffff); /* XXX: fetch or set it from/to IPU. */ sc->sc_info.fb_bpp = sc->sc_info.fb_depth = sc->sc_info.fb_stride / sc->sc_info.fb_width * 8; } /* Use own color map, because of different RGB offset. */ static int ipu3_fb_init_cmap(uint32_t *cmap, int bytespp) { switch (bytespp) { case 8: return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 0x7, 5, 0x7, 2, 0x3, 0)); case 15: return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 0x1f, 10, 0x1f, 5, 0x1f, 0)); case 16: return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 0x1f, 11, 0x3f, 5, 0x1f, 0)); case 24: case 32: /* Ignore alpha. */ return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 0xff, 0, 0xff, 8, 0xff, 16)); default: return (1); } } static int ipu3_fb_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,ipu3")) return (ENXIO); device_set_desc(dev, "i.MX5x Image Processing Unit v3 (FB)"); return (BUS_PROBE_DEFAULT); } static int ipu3_fb_attach(device_t dev) { struct ipu3sc_softc *sc = device_get_softc(dev); bus_space_tag_t iot; bus_space_handle_t ioh; phandle_t node; pcell_t reg; int err; uintptr_t base; ipu3sc_softc = sc; if (bootverbose) device_printf(dev, "clock gate status is %d\n", imx51_get_clk_gating(IMX51CLK_IPU_HSP_CLK_ROOT)); sc->dev = dev; sc = device_get_softc(dev); sc->iot = iot = fdtbus_bs_tag; /* * Retrieve the device address based on the start address in the * DTS. The DTS for i.MX51 specifies 0x5e000000 as the first register * address, so we just subtract IPU_CM_BASE to get the offset at which * the IPU device was memory mapped. * On i.MX53, the offset is 0. */ node = ofw_bus_get_node(dev); if ((OF_getprop(node, "reg", ®, sizeof(reg))) <= 0) base = 0; else base = fdt32_to_cpu(reg) - IPU_CM_BASE(0); /* map controller registers */ err = bus_space_map(iot, IPU_CM_BASE(base), IPU_CM_SIZE, 0, &ioh); if (err) goto fail_retarn_cm; sc->cm_ioh = ioh; /* map Display Multi FIFO Controller registers */ err = bus_space_map(iot, IPU_DMFC_BASE(base), IPU_DMFC_SIZE, 0, &ioh); if (err) goto fail_retarn_dmfc; sc->dmfc_ioh = ioh; /* map Display Interface 0 registers */ err = bus_space_map(iot, IPU_DI0_BASE(base), IPU_DI0_SIZE, 0, &ioh); if (err) goto fail_retarn_di0; sc->di0_ioh = ioh; /* map Display Interface 1 registers */ err = bus_space_map(iot, IPU_DI1_BASE(base), IPU_DI0_SIZE, 0, &ioh); if (err) goto fail_retarn_di1; sc->di1_ioh = ioh; /* map Display Processor registers */ err = bus_space_map(iot, IPU_DP_BASE(base), IPU_DP_SIZE, 0, &ioh); if (err) goto fail_retarn_dp; sc->dp_ioh = ioh; /* map Display Controller registers */ err = bus_space_map(iot, IPU_DC_BASE(base), IPU_DC_SIZE, 0, &ioh); if (err) goto fail_retarn_dc; sc->dc_ioh = ioh; /* map Image DMA Controller registers */ err = bus_space_map(iot, IPU_IDMAC_BASE(base), IPU_IDMAC_SIZE, 0, &ioh); if (err) goto fail_retarn_idmac; sc->idmac_ioh = ioh; /* map CPMEM registers */ err = bus_space_map(iot, IPU_CPMEM_BASE(base), IPU_CPMEM_SIZE, 0, &ioh); if (err) goto fail_retarn_cpmem; sc->cpmem_ioh = ioh; /* map DCTEMPL registers */ err = bus_space_map(iot, IPU_DCTMPL_BASE(base), IPU_DCTMPL_SIZE, 0, &ioh); if (err) goto fail_retarn_dctmpl; sc->dctmpl_ioh = ioh; #ifdef notyet sc->ih = imx51_ipuv3_intr_establish(IMX51_INT_IPUV3, IPL_BIO, ipuv3intr, sc); if (sc->ih == NULL) { device_printf(sc->dev, "unable to establish interrupt at irq %d\n", IMX51_INT_IPUV3); return (ENXIO); } #endif /* * We have to wait until interrupts are enabled. * Mailbox relies on it to get data from VideoCore */ ipu3_fb_init(sc); sc->sc_info.fb_name = device_get_nameunit(dev); ipu3_fb_init_cmap(sc->sc_info.fb_cmap, sc->sc_info.fb_depth); sc->sc_info.fb_cmsize = 16; /* Ask newbus to attach framebuffer device to me. */ sc->sc_fbd = device_add_child(dev, "fbd", device_get_unit(dev)); if (sc->sc_fbd == NULL) device_printf(dev, "Can't attach fbd device\n"); return (bus_generic_attach(dev)); fail_retarn_dctmpl: bus_space_unmap(sc->iot, sc->cpmem_ioh, IPU_CPMEM_SIZE); fail_retarn_cpmem: bus_space_unmap(sc->iot, sc->idmac_ioh, IPU_IDMAC_SIZE); fail_retarn_idmac: bus_space_unmap(sc->iot, sc->dc_ioh, IPU_DC_SIZE); fail_retarn_dp: bus_space_unmap(sc->iot, sc->dp_ioh, IPU_DP_SIZE); fail_retarn_dc: bus_space_unmap(sc->iot, sc->di1_ioh, IPU_DI1_SIZE); fail_retarn_di1: bus_space_unmap(sc->iot, sc->di0_ioh, IPU_DI0_SIZE); fail_retarn_di0: bus_space_unmap(sc->iot, sc->dmfc_ioh, IPU_DMFC_SIZE); fail_retarn_dmfc: bus_space_unmap(sc->iot, sc->dc_ioh, IPU_CM_SIZE); fail_retarn_cm: device_printf(sc->dev, "failed to map registers (errno=%d)\n", err); return (err); } static struct fb_info * ipu3_fb_getinfo(device_t dev) { struct ipu3sc_softc *sc = device_get_softc(dev); return (&sc->sc_info); } static device_method_t ipu3_fb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ipu3_fb_probe), DEVMETHOD(device_attach, ipu3_fb_attach), /* Framebuffer service methods */ DEVMETHOD(fb_getinfo, ipu3_fb_getinfo), { 0, 0 } }; static devclass_t ipu3_fb_devclass; static driver_t ipu3_fb_driver = { "fb", ipu3_fb_methods, sizeof(struct ipu3sc_softc), }; DRIVER_MODULE(fb, simplebus, ipu3_fb_driver, ipu3_fb_devclass, 0, 0); Index: head/sys/arm/freescale/imx/imx6_anatop.c =================================================================== --- head/sys/arm/freescale/imx/imx6_anatop.c (revision 281084) +++ head/sys/arm/freescale/imx/imx6_anatop.c (revision 281085) @@ -1,783 +1,782 @@ /*- * Copyright (c) 2013 Ian Lepore * Copyright (c) 2014 Steven Lawrance * 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$"); /* * Analog PLL and power regulator driver for Freescale i.MX6 family of SoCs. * Also, temperature montoring and cpu frequency control. It was Freescale who * kitchen-sinked this device, not us. :) * * We don't really do anything with analog PLLs, but the registers for * controlling them belong to the same block as the power regulator registers. * Since the newbus hierarchy makes it hard for anyone other than us to get at * them, we just export a couple public functions to allow the imx6 CCM clock * driver to read and write those registers. * * We also don't do anything about power regulation yet, but when the need * arises, this would be the place for that code to live. * * I have no idea where the "anatop" name comes from. It's in the standard DTS * source describing i.MX6 SoCs, and in the linux and u-boot code which comes * from Freescale, but it's not in the SoC manual. * * Note that temperature values throughout this code are handled in two types of * units. Items with '_cnt' in the name use the hardware temperature count * units (higher counts are lower temperatures). Items with '_val' in the name * are deci-Celcius, which are converted to/from deci-Kelvins in the sysctl * handlers (dK is the standard unit for temperature in sysctl). */ #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include static SYSCTL_NODE(_hw, OID_AUTO, imx6, CTLFLAG_RW, NULL, "i.MX6 container"); static struct resource_spec imx6_anatop_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; #define MEMRES 0 #define IRQRES 1 struct imx6_anatop_softc { device_t dev; struct resource *res[2]; struct intr_config_hook intr_setup_hook; uint32_t cpu_curmhz; uint32_t cpu_curmv; uint32_t cpu_minmhz; uint32_t cpu_minmv; uint32_t cpu_maxmhz; uint32_t cpu_maxmv; uint32_t cpu_maxmhz_hw; boolean_t cpu_overclock_enable; boolean_t cpu_init_done; uint32_t refosc_mhz; void *temp_intrhand; uint32_t temp_high_val; uint32_t temp_high_cnt; uint32_t temp_last_cnt; uint32_t temp_room_cnt; struct callout temp_throttle_callout; sbintime_t temp_throttle_delay; uint32_t temp_throttle_reset_cnt; uint32_t temp_throttle_trigger_cnt; uint32_t temp_throttle_val; }; static struct imx6_anatop_softc *imx6_anatop_sc; /* * Table of "operating points". * These are combinations of frequency and voltage blessed by Freescale. */ static struct oppt { uint32_t mhz; uint32_t mv; } imx6_oppt_table[] = { /* { 396, 925}, XXX: need functional ccm code for this speed */ { 792, 1150}, { 852, 1225}, { 996, 1225}, {1200, 1275}, }; /* * Table of CPU max frequencies. This is used to translate the max frequency * value (0-3) from the ocotp CFG3 register into a mhz value that can be looked * up in the operating points table. */ static uint32_t imx6_ocotp_mhz_tab[] = {792, 852, 996, 1200}; #define TZ_ZEROC 2732 /* deci-Kelvin <-> deci-Celcius offset. */ uint32_t imx6_anatop_read_4(bus_size_t offset) { KASSERT(imx6_anatop_sc != NULL, ("imx6_anatop_read_4 sc NULL")); return (bus_read_4(imx6_anatop_sc->res[MEMRES], offset)); } void imx6_anatop_write_4(bus_size_t offset, uint32_t value) { KASSERT(imx6_anatop_sc != NULL, ("imx6_anatop_write_4 sc NULL")); bus_write_4(imx6_anatop_sc->res[MEMRES], offset, value); } static void vdd_set(struct imx6_anatop_softc *sc, int mv) { int newtarg, oldtarg; uint32_t delay, pmureg; static boolean_t init_done = false; /* * The datasheet says VDD_PU and VDD_SOC must be equal, and VDD_ARM * can't be more than 50mV above or 200mV below them. For now to keep * things simple we set all three to the same value. */ pmureg = imx6_anatop_read_4(IMX6_ANALOG_PMU_REG_CORE); oldtarg = pmureg & IMX6_ANALOG_PMU_REG0_TARG_MASK; /* Convert mV to target value. Clamp target to valid range. */ if (mv < 725) newtarg = 0x00; else if (mv > 1450) newtarg = 0x1F; else newtarg = (mv - 700) / 25; /* * The first time through the 3 voltages might not be equal so use a * long conservative delay. After that we need to delay 3uS for every * 25mV step upward. No need to delay at all when lowering. */ if (init_done) { if (newtarg == oldtarg) return; else if (newtarg > oldtarg) delay = (newtarg - oldtarg) * 3; else delay = 0; } else { delay = 700 / 25 * 3; init_done = true; } /* * Make the change and wait for it to take effect. */ pmureg &= ~(IMX6_ANALOG_PMU_REG0_TARG_MASK | IMX6_ANALOG_PMU_REG1_TARG_MASK | IMX6_ANALOG_PMU_REG2_TARG_MASK); pmureg |= newtarg << IMX6_ANALOG_PMU_REG0_TARG_SHIFT; pmureg |= newtarg << IMX6_ANALOG_PMU_REG1_TARG_SHIFT; pmureg |= newtarg << IMX6_ANALOG_PMU_REG2_TARG_SHIFT; imx6_anatop_write_4(IMX6_ANALOG_PMU_REG_CORE, pmureg); DELAY(delay); sc->cpu_curmv = newtarg * 25 + 700; } static inline uint32_t cpufreq_mhz_from_div(struct imx6_anatop_softc *sc, uint32_t div) { return (sc->refosc_mhz * (div / 2)); } static inline uint32_t cpufreq_mhz_to_div(struct imx6_anatop_softc *sc, uint32_t cpu_mhz) { return (cpu_mhz / (sc->refosc_mhz / 2)); } static inline uint32_t cpufreq_actual_mhz(struct imx6_anatop_softc *sc, uint32_t cpu_mhz) { return (cpufreq_mhz_from_div(sc, cpufreq_mhz_to_div(sc, cpu_mhz))); } static struct oppt * cpufreq_nearest_oppt(struct imx6_anatop_softc *sc, uint32_t cpu_newmhz) { int d, diff, i, nearest; if (cpu_newmhz > sc->cpu_maxmhz_hw && !sc->cpu_overclock_enable) cpu_newmhz = sc->cpu_maxmhz_hw; diff = INT_MAX; nearest = 0; for (i = 0; i < nitems(imx6_oppt_table); ++i) { d = abs((int)cpu_newmhz - (int)imx6_oppt_table[i].mhz); if (diff > d) { diff = d; nearest = i; } } return (&imx6_oppt_table[nearest]); } static void cpufreq_set_clock(struct imx6_anatop_softc * sc, struct oppt *op) { uint32_t timeout, wrk32; /* If increasing the frequency, we must first increase the voltage. */ if (op->mhz > sc->cpu_curmhz) { vdd_set(sc, op->mv); } /* * I can't find a documented procedure for changing the ARM PLL divisor, * but some trial and error came up with this: * - Set the bypass clock source to REF_CLK_24M (source #0). * - Set the PLL into bypass mode; cpu should now be running at 24mhz. * - Change the divisor. * - Wait for the LOCK bit to come on; it takes ~50 loop iterations. * - Turn off bypass mode; cpu should now be running at the new speed. */ imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_CLR, IMX6_ANALOG_CCM_PLL_ARM_CLK_SRC_MASK); imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_SET, IMX6_ANALOG_CCM_PLL_ARM_BYPASS); wrk32 = imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM); wrk32 &= ~IMX6_ANALOG_CCM_PLL_ARM_DIV_MASK; wrk32 |= cpufreq_mhz_to_div(sc, op->mhz); imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM, wrk32); timeout = 10000; while ((imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM) & IMX6_ANALOG_CCM_PLL_ARM_LOCK) == 0) if (--timeout == 0) panic("imx6_set_cpu_clock(): PLL never locked"); imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_CLR, IMX6_ANALOG_CCM_PLL_ARM_BYPASS); /* If lowering the frequency, it is now safe to lower the voltage. */ if (op->mhz < sc->cpu_curmhz) vdd_set(sc, op->mv); sc->cpu_curmhz = op->mhz; /* Tell the mpcore timer that its frequency has changed. */ arm_tmr_change_frequency( cpufreq_actual_mhz(sc, sc->cpu_curmhz) * 1000000 / 2); } static int cpufreq_sysctl_minmhz(SYSCTL_HANDLER_ARGS) { struct imx6_anatop_softc *sc; struct oppt * op; uint32_t temp; int err; sc = arg1; temp = sc->cpu_minmhz; err = sysctl_handle_int(oidp, &temp, 0, req); if (err != 0 || req->newptr == NULL) return (err); op = cpufreq_nearest_oppt(sc, temp); if (op->mhz > sc->cpu_maxmhz) return (ERANGE); else if (op->mhz == sc->cpu_minmhz) return (0); /* * Value changed, update softc. If the new min is higher than the * current speed, raise the current speed to match. */ sc->cpu_minmhz = op->mhz; if (sc->cpu_minmhz > sc->cpu_curmhz) { cpufreq_set_clock(sc, op); } return (err); } static int cpufreq_sysctl_maxmhz(SYSCTL_HANDLER_ARGS) { struct imx6_anatop_softc *sc; struct oppt * op; uint32_t temp; int err; sc = arg1; temp = sc->cpu_maxmhz; err = sysctl_handle_int(oidp, &temp, 0, req); if (err != 0 || req->newptr == NULL) return (err); op = cpufreq_nearest_oppt(sc, temp); if (op->mhz < sc->cpu_minmhz) return (ERANGE); else if (op->mhz == sc->cpu_maxmhz) return (0); /* * Value changed, update softc and hardware. The hardware update is * unconditional. We always try to run at max speed, so any change of * the max means we need to change the current speed too, regardless of * whether it is higher or lower than the old max. */ sc->cpu_maxmhz = op->mhz; cpufreq_set_clock(sc, op); return (err); } static void cpufreq_initialize(struct imx6_anatop_softc *sc) { uint32_t cfg3speed; struct oppt * op; SYSCTL_ADD_INT(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx6), OID_AUTO, "cpu_mhz", CTLFLAG_RD, &sc->cpu_curmhz, 0, "CPU frequency"); SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx6), OID_AUTO, "cpu_minmhz", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH, sc, 0, cpufreq_sysctl_minmhz, "IU", "Minimum CPU frequency"); SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx6), OID_AUTO, "cpu_maxmhz", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH, sc, 0, cpufreq_sysctl_maxmhz, "IU", "Maximum CPU frequency"); SYSCTL_ADD_INT(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx6), OID_AUTO, "cpu_maxmhz_hw", CTLFLAG_RD, &sc->cpu_maxmhz_hw, 0, "Maximum CPU frequency allowed by hardware"); SYSCTL_ADD_INT(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx6), OID_AUTO, "cpu_overclock_enable", CTLFLAG_RWTUN, &sc->cpu_overclock_enable, 0, "Allow setting CPU frequency higher than cpu_maxmhz_hw"); /* * XXX 24mhz shouldn't be hard-coded, should get this from imx6_ccm * (even though in the real world it will always be 24mhz). Oh wait a * sec, I never wrote imx6_ccm. */ sc->refosc_mhz = 24; /* * Get the maximum speed this cpu can be set to. The values in the * OCOTP CFG3 register are not documented in the reference manual. * The following info was in an archived email found via web search: * - 2b'11: 1200000000Hz; * - 2b'10: 996000000Hz; * - 2b'01: 852000000Hz; -- i.MX6Q Only, exclusive with 996MHz. * - 2b'00: 792000000Hz; * The default hardware max speed can be overridden by a tunable. */ cfg3speed = (fsl_ocotp_read_4(FSL_OCOTP_CFG3) & FSL_OCOTP_CFG3_SPEED_MASK) >> FSL_OCOTP_CFG3_SPEED_SHIFT; sc->cpu_maxmhz_hw = imx6_ocotp_mhz_tab[cfg3speed]; sc->cpu_maxmhz = sc->cpu_maxmhz_hw; TUNABLE_INT_FETCH("hw.imx6.cpu_minmhz", &sc->cpu_minmhz); op = cpufreq_nearest_oppt(sc, sc->cpu_minmhz); sc->cpu_minmhz = op->mhz; sc->cpu_minmv = op->mv; TUNABLE_INT_FETCH("hw.imx6.cpu_maxmhz", &sc->cpu_maxmhz); op = cpufreq_nearest_oppt(sc, sc->cpu_maxmhz); sc->cpu_maxmhz = op->mhz; sc->cpu_maxmv = op->mv; /* * Set the CPU to maximum speed. * * We won't have thermal throttling until interrupts are enabled, but we * want to run at full speed through all the device init stuff. This * basically assumes that a single core can't overheat before interrupts * are enabled; empirical testing shows that to be a safe assumption. */ cpufreq_set_clock(sc, op); } static inline uint32_t temp_from_count(struct imx6_anatop_softc *sc, uint32_t count) { return (((sc->temp_high_val - (count - sc->temp_high_cnt) * (sc->temp_high_val - 250) / (sc->temp_room_cnt - sc->temp_high_cnt)))); } static inline uint32_t temp_to_count(struct imx6_anatop_softc *sc, uint32_t temp) { return ((sc->temp_room_cnt - sc->temp_high_cnt) * (sc->temp_high_val - temp) / (sc->temp_high_val - 250) + sc->temp_high_cnt); } static void temp_update_count(struct imx6_anatop_softc *sc) { uint32_t val; val = imx6_anatop_read_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0); if (!(val & IMX6_ANALOG_TEMPMON_TEMPSENSE0_VALID)) return; sc->temp_last_cnt = (val & IMX6_ANALOG_TEMPMON_TEMPSENSE0_TEMP_CNT_MASK) >> IMX6_ANALOG_TEMPMON_TEMPSENSE0_TEMP_CNT_SHIFT; } static int temp_sysctl_handler(SYSCTL_HANDLER_ARGS) { struct imx6_anatop_softc *sc = arg1; uint32_t t; temp_update_count(sc); t = temp_from_count(sc, sc->temp_last_cnt) + TZ_ZEROC; return (sysctl_handle_int(oidp, &t, 0, req)); } static int temp_throttle_sysctl_handler(SYSCTL_HANDLER_ARGS) { struct imx6_anatop_softc *sc = arg1; int err; uint32_t temp; temp = sc->temp_throttle_val + TZ_ZEROC; err = sysctl_handle_int(oidp, &temp, 0, req); if (temp < TZ_ZEROC) return (ERANGE); temp -= TZ_ZEROC; if (err != 0 || req->newptr == NULL || temp == sc->temp_throttle_val) return (err); /* Value changed, update counts in softc and hardware. */ sc->temp_throttle_val = temp; sc->temp_throttle_trigger_cnt = temp_to_count(sc, sc->temp_throttle_val); sc->temp_throttle_reset_cnt = temp_to_count(sc, sc->temp_throttle_val - 100); imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0_CLR, IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_MASK); imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0_SET, (sc->temp_throttle_trigger_cnt << IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_SHIFT)); return (err); } static void tempmon_gofast(struct imx6_anatop_softc *sc) { if (sc->cpu_curmhz < sc->cpu_maxmhz) { cpufreq_set_clock(sc, cpufreq_nearest_oppt(sc, sc->cpu_maxmhz)); } } static void tempmon_goslow(struct imx6_anatop_softc *sc) { if (sc->cpu_curmhz > sc->cpu_minmhz) { cpufreq_set_clock(sc, cpufreq_nearest_oppt(sc, sc->cpu_minmhz)); } } static int tempmon_intr(void *arg) { struct imx6_anatop_softc *sc = arg; /* * XXX Note that this code doesn't currently run (for some mysterious * reason we just never get an interrupt), so the real monitoring is * done by tempmon_throttle_check(). */ tempmon_goslow(sc); /* XXX Schedule callout to speed back up eventually. */ return (FILTER_HANDLED); } static void tempmon_throttle_check(void *arg) { struct imx6_anatop_softc *sc = arg; /* Lower counts are higher temperatures. */ if (sc->temp_last_cnt < sc->temp_throttle_trigger_cnt) tempmon_goslow(sc); else if (sc->temp_last_cnt > (sc->temp_throttle_reset_cnt)) tempmon_gofast(sc); callout_reset_sbt(&sc->temp_throttle_callout, sc->temp_throttle_delay, 0, tempmon_throttle_check, sc, 0); } static void initialize_tempmon(struct imx6_anatop_softc *sc) { uint32_t cal; /* * Fetch calibration data: a sensor count at room temperature (25C), * a sensor count at a high temperature, and that temperature */ cal = fsl_ocotp_read_4(FSL_OCOTP_ANA1); sc->temp_room_cnt = (cal & 0xFFF00000) >> 20; sc->temp_high_cnt = (cal & 0x000FFF00) >> 8; sc->temp_high_val = (cal & 0x000000FF) * 10; /* * Throttle to a lower cpu freq at 10C below the "hot" temperature, and * reset back to max cpu freq at 5C below the trigger. */ sc->temp_throttle_val = sc->temp_high_val - 100; sc->temp_throttle_trigger_cnt = temp_to_count(sc, sc->temp_throttle_val); sc->temp_throttle_reset_cnt = temp_to_count(sc, sc->temp_throttle_val - 50); /* * Set the sensor to sample automatically at 16Hz (32.768KHz/0x800), set * the throttle count, and begin making measurements. */ imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE1, 0x0800); imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0, (sc->temp_throttle_trigger_cnt << IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_SHIFT) | IMX6_ANALOG_TEMPMON_TEMPSENSE0_MEASURE); /* * XXX Note that the alarm-interrupt feature isn't working yet, so * we'll use a callout handler to check at 10Hz. Make sure we have an * initial temperature reading before starting up the callouts so we * don't get a bogus reading of zero. */ while (sc->temp_last_cnt == 0) temp_update_count(sc); sc->temp_throttle_delay = 100 * SBT_1MS; callout_init(&sc->temp_throttle_callout, 0); callout_reset_sbt(&sc->temp_throttle_callout, sc->temp_throttle_delay, 0, tempmon_throttle_check, sc, 0); SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx6), OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0, temp_sysctl_handler, "IK", "Current die temperature"); SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx6), OID_AUTO, "throttle_temperature", CTLTYPE_INT | CTLFLAG_RW, sc, 0, temp_throttle_sysctl_handler, "IK", "Throttle CPU when exceeding this temperature"); } static void intr_setup(void *arg) { struct imx6_anatop_softc *sc; sc = arg; bus_setup_intr(sc->dev, sc->res[IRQRES], INTR_TYPE_MISC | INTR_MPSAFE, tempmon_intr, NULL, sc, &sc->temp_intrhand); config_intrhook_disestablish(&sc->intr_setup_hook); } static void imx6_anatop_new_pass(device_t dev) { struct imx6_anatop_softc *sc; const int cpu_init_pass = BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE; /* * We attach during BUS_PASS_BUS (because some day we will be a * simplebus that has regulator devices as children), but some of our * init work cannot be done until BUS_PASS_CPU (we rely on other devices * that attach on the CPU pass). */ sc = device_get_softc(dev); if (!sc->cpu_init_done && bus_current_pass >= cpu_init_pass) { sc->cpu_init_done = true; cpufreq_initialize(sc); initialize_tempmon(sc); if (bootverbose) { device_printf(sc->dev, "CPU %uMHz @ %umV\n", sc->cpu_curmhz, sc->cpu_curmv); } } bus_generic_new_pass(dev); } static int imx6_anatop_detach(device_t dev) { /* This device can never detach. */ return (EBUSY); } static int imx6_anatop_attach(device_t dev) { struct imx6_anatop_softc *sc; int err; sc = device_get_softc(dev); sc->dev = dev; /* Allocate bus_space resources. */ if (bus_alloc_resources(dev, imx6_anatop_spec, sc->res)) { device_printf(dev, "Cannot allocate resources\n"); err = ENXIO; goto out; } sc->intr_setup_hook.ich_func = intr_setup; sc->intr_setup_hook.ich_arg = sc; config_intrhook_establish(&sc->intr_setup_hook); SYSCTL_ADD_UINT(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "cpu_voltage", CTLFLAG_RD, &sc->cpu_curmv, 0, "Current CPU voltage in millivolts"); imx6_anatop_sc = sc; /* * Other code seen on the net sets this SELFBIASOFF flag around the same * time the temperature sensor is set up, although it's unclear how the * two are related (if at all). */ imx6_anatop_write_4(IMX6_ANALOG_PMU_MISC0_SET, IMX6_ANALOG_PMU_MISC0_SELFBIASOFF); /* * Some day, when we're ready to deal with the actual anatop regulators * that are described in fdt data as children of this "bus", this would * be the place to invoke a simplebus helper routine to instantiate the * children from the fdt data. */ err = 0; out: if (err != 0) { bus_release_resources(dev, imx6_anatop_spec, sc->res); } return (err); } uint32_t pll4_configure_output(uint32_t mfi, uint32_t mfn, uint32_t mfd) { int reg; /* * Audio PLL (PLL4). * PLL output frequency = Fref * (DIV_SELECT + NUM/DENOM) */ reg = (IMX6_ANALOG_CCM_PLL_AUDIO_ENABLE); reg &= ~(IMX6_ANALOG_CCM_PLL_AUDIO_DIV_SELECT_MASK << \ IMX6_ANALOG_CCM_PLL_AUDIO_DIV_SELECT_SHIFT); reg |= (mfi << IMX6_ANALOG_CCM_PLL_AUDIO_DIV_SELECT_SHIFT); imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_AUDIO, reg); imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_AUDIO_NUM, mfn); imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_AUDIO_DENOM, mfd); return (0); } static int imx6_anatop_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "fsl,imx6q-anatop") == 0) return (ENXIO); device_set_desc(dev, "Freescale i.MX6 Analog PLLs and Power"); return (BUS_PROBE_DEFAULT); } uint32_t imx6_get_cpu_clock() { uint32_t div; div = imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM) & IMX6_ANALOG_CCM_PLL_ARM_DIV_MASK; return (cpufreq_mhz_from_div(imx6_anatop_sc, div)); } static device_method_t imx6_anatop_methods[] = { /* Device interface */ DEVMETHOD(device_probe, imx6_anatop_probe), DEVMETHOD(device_attach, imx6_anatop_attach), DEVMETHOD(device_detach, imx6_anatop_detach), /* Bus interface */ DEVMETHOD(bus_new_pass, imx6_anatop_new_pass), DEVMETHOD_END }; static driver_t imx6_anatop_driver = { "imx6_anatop", imx6_anatop_methods, sizeof(struct imx6_anatop_softc) }; static devclass_t imx6_anatop_devclass; EARLY_DRIVER_MODULE(imx6_anatop, simplebus, imx6_anatop_driver, imx6_anatop_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); EARLY_DRIVER_MODULE(imx6_anatop, ofwbus, imx6_anatop_driver, imx6_anatop_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Index: head/sys/arm/freescale/imx/imx6_audmux.c =================================================================== --- head/sys/arm/freescale/imx/imx6_audmux.c (revision 281084) +++ head/sys/arm/freescale/imx/imx6_audmux.c (revision 281085) @@ -1,159 +1,158 @@ /*- * Copyright (c) 2015 Ruslan Bukin * 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. */ /* * i.MX6 Digital Audio Multiplexer (AUDMUX) * Chapter 16, i.MX 6Dual/6Quad Applications Processor Reference Manual, * Rev. 1, 04/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #define READ4(_sc, _reg) \ bus_space_read_4(_sc->bst, _sc->bsh, _reg) #define WRITE4(_sc, _reg, _val) \ bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val) #define AUDMUX_PTCR(n) (0x8 * (n - 1)) /* Port Timing Control Register */ #define PTCR_TFS_DIR (1 << 31) /* Transmit Frame Sync Direction Control */ #define PTCR_TFSEL_S 27 /* Transmit Frame Sync Select */ #define PTCR_TFSEL_M 0xf #define PTCR_TCLKDIR (1 << 26) /* Transmit Clock Direction Control */ #define PTCR_TCSEL_S 22 /* Transmit Clock Select. */ #define PTCR_TCSEL_M 0xf #define PTCR_RFS_DIR (1 << 21) /* Receive Frame Sync Direction Control */ #define PTCR_SYN (1 << 11) #define AUDMUX_PDCR(n) (0x8 * (n - 1) + 0x4) /* Port Data Control Reg */ #define PDCR_RXDSEL_S 13 /* Receive Data Select */ #define PDCR_RXDSEL_M 0x3 #define PDCR_RXDSEL_PORT(n) (n - 1) struct audmux_softc { struct resource *res[1]; bus_space_tag_t bst; bus_space_handle_t bsh; void *ih; }; static struct resource_spec audmux_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static int audmux_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,imx6q-audmux")) return (ENXIO); device_set_desc(dev, "i.MX6 Digital Audio Multiplexer"); return (BUS_PROBE_DEFAULT); } static int audmux_configure(struct audmux_softc *sc, int ssi_port, int audmux_port) { uint32_t reg; /* Direction: output */ reg = (PTCR_TFS_DIR | PTCR_TCLKDIR | PTCR_SYN); WRITE4(sc, AUDMUX_PTCR(audmux_port), reg); /* Select source */ reg = (PDCR_RXDSEL_PORT(ssi_port) << PDCR_RXDSEL_S); WRITE4(sc, AUDMUX_PDCR(audmux_port), reg); return (0); } static int audmux_attach(device_t dev) { struct audmux_softc *sc; sc = device_get_softc(dev); if (bus_alloc_resources(dev, audmux_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); /* * Direct SSI1 output to AUDMUX5 pins. * TODO: dehardcore this. */ audmux_configure(sc, 1, 5); return (0); }; static device_method_t audmux_methods[] = { /* Device interface */ DEVMETHOD(device_probe, audmux_probe), DEVMETHOD(device_attach, audmux_attach), { 0, 0 } }; static driver_t audmux_driver = { "audmux", audmux_methods, sizeof(struct audmux_softc), }; static devclass_t audmux_devclass; DRIVER_MODULE(audmux, simplebus, audmux_driver, audmux_devclass, 0, 0); Index: head/sys/arm/freescale/imx/imx6_sdma.c =================================================================== --- head/sys/arm/freescale/imx/imx6_sdma.c (revision 281084) +++ head/sys/arm/freescale/imx/imx6_sdma.c (revision 281085) @@ -1,518 +1,517 @@ /*- * Copyright (c) 2015 Ruslan Bukin * 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. */ /* * i.MX6 Smart Direct Memory Access Controller (sDMA) * Chapter 41, i.MX 6Dual/6Quad Applications Processor Reference Manual, * Rev. 1, 04/2013 */ #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 #define MAX_BD (PAGE_SIZE / sizeof(struct sdma_buffer_descriptor)) #define READ4(_sc, _reg) \ bus_space_read_4(_sc->bst, _sc->bsh, _reg) #define WRITE4(_sc, _reg, _val) \ bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val) struct sdma_softc *sdma_sc; static struct resource_spec sdma_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static void sdma_intr(void *arg) { struct sdma_buffer_descriptor *bd; struct sdma_channel *channel; struct sdma_conf *conf; struct sdma_softc *sc; int pending; int i; int j; sc = arg; pending = READ4(sc, SDMAARM_INTR); /* Ack intr */ WRITE4(sc, SDMAARM_INTR, pending); for (i = 0; i < SDMA_N_CHANNELS; i++) { if ((pending & (1 << i)) == 0) continue; channel = &sc->channel[i]; conf = channel->conf; if (!conf) continue; for (j = 0; j < conf->num_bd; j++) { bd = &channel->bd[j]; bd->mode.status |= BD_DONE; if (bd->mode.status & BD_RROR) printf("sDMA error\n"); } conf->ih(conf->ih_user, 1); WRITE4(sc, SDMAARM_HSTART, (1 << i)); } } static int sdma_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,imx6q-sdma")) return (ENXIO); device_set_desc(dev, "i.MX6 Smart Direct Memory Access Controller"); return (BUS_PROBE_DEFAULT); } int sdma_start(int chn) { struct sdma_softc *sc; sc = sdma_sc; WRITE4(sc, SDMAARM_HSTART, (1 << chn)); return (0); } int sdma_stop(int chn) { struct sdma_softc *sc; sc = sdma_sc; WRITE4(sc, SDMAARM_STOP_STAT, (1 << chn)); return (0); } int sdma_alloc(void) { struct sdma_channel *channel; struct sdma_softc *sc; int found; int chn; int i; sc = sdma_sc; found = 0; /* Channel 0 can't be used */ for (i = 1; i < SDMA_N_CHANNELS; i++) { channel = &sc->channel[i]; if (channel->in_use == 0) { channel->in_use = 1; found = 1; break; } } if (!found) return (-1); chn = i; /* Allocate area for buffer descriptors */ channel->bd = (void *)kmem_alloc_contig(kernel_arena, PAGE_SIZE, M_ZERO, 0, ~0, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE); return (chn); } int sdma_free(int chn) { struct sdma_channel *channel; struct sdma_softc *sc; sc = sdma_sc; channel = &sc->channel[chn]; channel->in_use = 0; kmem_free(kernel_arena, (vm_offset_t)channel->bd, PAGE_SIZE); return (0); } static int sdma_overrides(struct sdma_softc *sc, int chn, int evt, int host, int dsp) { int reg; /* Ignore sDMA requests */ reg = READ4(sc, SDMAARM_EVTOVR); if (evt) reg |= (1 << chn); else reg &= ~(1 << chn); WRITE4(sc, SDMAARM_EVTOVR, reg); /* Ignore enable bit (HE) */ reg = READ4(sc, SDMAARM_HOSTOVR); if (host) reg |= (1 << chn); else reg &= ~(1 << chn); WRITE4(sc, SDMAARM_HOSTOVR, reg); /* Prevent sDMA channel from starting */ reg = READ4(sc, SDMAARM_DSPOVR); if (!dsp) reg |= (1 << chn); else reg &= ~(1 << chn); WRITE4(sc, SDMAARM_DSPOVR, reg); return (0); } int sdma_configure(int chn, struct sdma_conf *conf) { struct sdma_buffer_descriptor *bd0; struct sdma_buffer_descriptor *bd; struct sdma_context_data *context; struct sdma_channel *channel; struct sdma_softc *sc; #if 0 int timeout; int ret; #endif int i; sc = sdma_sc; channel = &sc->channel[chn]; channel->conf = conf; /* Ensure operation has stopped */ sdma_stop(chn); /* Set priority and enable the channel */ WRITE4(sc, SDMAARM_SDMA_CHNPRI(chn), 1); WRITE4(sc, SDMAARM_CHNENBL(conf->event), (1 << chn)); sdma_overrides(sc, chn, 0, 0, 0); if (conf->num_bd > MAX_BD) { device_printf(sc->dev, "Error: too much buffer" " descriptors requested\n"); return (-1); } for (i = 0; i < conf->num_bd; i++) { bd = &channel->bd[i]; bd->mode.command = conf->command; bd->mode.status = BD_DONE | BD_EXTD | BD_CONT | BD_INTR; if (i == (conf->num_bd - 1)) bd->mode.status |= BD_WRAP; bd->mode.count = conf->period; bd->buffer_addr = conf->saddr + (conf->period * i); bd->ext_buffer_addr = 0; } sc->ccb[chn].base_bd_ptr = vtophys(channel->bd); sc->ccb[chn].current_bd_ptr = vtophys(channel->bd); /* * Load context. * * i.MX6 Reference Manual: Appendix A SDMA Scripts * A.3.1.7.1 (mcu_2_app) */ /* * TODO: allow using other scripts */ context = sc->context; memset(context, 0, sizeof(*context)); context->channel_state.pc = sc->fw_scripts->mcu_2_app_addr; /* * Tx FIFO 0 address (r6) * Event_mask (r1) * Event2_mask (r0) * Watermark level (r7) */ if (conf->event > 32) { context->gReg[0] = (1 << (conf->event % 32)); context->gReg[1] = 0; } else { context->gReg[0] = 0; context->gReg[1] = (1 << conf->event); } context->gReg[6] = conf->daddr; context->gReg[7] = conf->word_length; bd0 = sc->bd0; bd0->mode.command = C0_SETDM; bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD; bd0->mode.count = sizeof(*context) / 4; bd0->buffer_addr = sc->context_phys; bd0->ext_buffer_addr = 2048 + (sizeof(*context) / 4) * chn; WRITE4(sc, SDMAARM_HSTART, 1); #if 0 /* Debug purposes */ timeout = 1000; while (!(ret = READ4(sc, SDMAARM_INTR) & 1)) { if (timeout-- <= 0) break; DELAY(10); }; if (!ret) { device_printf(sc->dev, "Failed to load context.\n"); return (-1); } WRITE4(sc, SDMAARM_INTR, ret); device_printf(sc->dev, "Context loaded successfully.\n"); #endif return (0); } static int load_firmware(struct sdma_softc *sc) { struct sdma_firmware_header *header; const struct firmware *fp; fp = firmware_get("sdma_fw"); if (fp == NULL) { device_printf(sc->dev, "Can't get firmware.\n"); return (-1); } header = (struct sdma_firmware_header *)fp->data; if (header->magic != FW_HEADER_MAGIC) { device_printf(sc->dev, "Can't use firmware.\n"); return (-1); } sc->fw_header = header; sc->fw_scripts = (void *)((char *)header + header->script_addrs_start); return (0); } static int boot_firmware(struct sdma_softc *sc) { struct sdma_buffer_descriptor *bd0; uint32_t *ram_code; int timeout; int ret; int chn; int sz; int i; ram_code = (void *)((char *)sc->fw_header + sc->fw_header->ram_code_start); /* Make sure SDMA has not started yet */ WRITE4(sc, SDMAARM_MC0PTR, 0); sz = SDMA_N_CHANNELS * sizeof(struct sdma_channel_control) + \ sizeof(struct sdma_context_data); sc->ccb = (void *)kmem_alloc_contig(kernel_arena, sz, M_ZERO, 0, ~0, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE); sc->ccb_phys = vtophys(sc->ccb); sc->context = (void *)((char *)sc->ccb + \ SDMA_N_CHANNELS * sizeof(struct sdma_channel_control)); sc->context_phys = vtophys(sc->context); /* Disable all the channels */ for (i = 0; i < SDMA_N_EVENTS; i++) WRITE4(sc, SDMAARM_CHNENBL(i), 0); /* All channels have priority 0 */ for (i = 0; i < SDMA_N_CHANNELS; i++) WRITE4(sc, SDMAARM_SDMA_CHNPRI(i), 0); /* Channel 0 is used for booting firmware */ chn = 0; sc->bd0 = (void *)kmem_alloc_contig(kernel_arena, PAGE_SIZE, M_ZERO, 0, ~0, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE); bd0 = sc->bd0; sc->ccb[chn].base_bd_ptr = vtophys(bd0); sc->ccb[chn].current_bd_ptr = vtophys(bd0); WRITE4(sc, SDMAARM_SDMA_CHNPRI(chn), 1); sdma_overrides(sc, chn, 1, 0, 0); /* XXX: not sure what is that */ WRITE4(sc, SDMAARM_CHN0ADDR, 0x4050); WRITE4(sc, SDMAARM_CONFIG, 0); WRITE4(sc, SDMAARM_MC0PTR, sc->ccb_phys); WRITE4(sc, SDMAARM_CONFIG, CONFIG_CSM); WRITE4(sc, SDMAARM_SDMA_CHNPRI(chn), 1); bd0->mode.command = C0_SETPM; bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD; bd0->mode.count = sc->fw_header->ram_code_size / 2; bd0->buffer_addr = vtophys(ram_code); bd0->ext_buffer_addr = sc->fw_scripts->ram_code_start_addr; WRITE4(sc, SDMAARM_HSTART, 1); timeout = 100; while (!(ret = READ4(sc, SDMAARM_INTR) & 1)) { if (timeout-- <= 0) break; DELAY(10); }; if (ret == 0) { device_printf(sc->dev, "SDMA failed to boot\n"); return (-1); } WRITE4(sc, SDMAARM_INTR, ret); #if 0 device_printf(sc->dev, "SDMA booted successfully.\n"); #endif /* Debug is disabled */ WRITE4(sc, SDMAARM_ONCE_ENB, 0); return (0); } static int sdma_attach(device_t dev) { struct sdma_softc *sc; int err; sc = device_get_softc(dev); sc->dev = dev; if (bus_alloc_resources(dev, sdma_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); sdma_sc = sc; /* Setup interrupt handler */ err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, NULL, sdma_intr, sc, &sc->ih); if (err) { device_printf(dev, "Unable to alloc interrupt resource.\n"); return (ENXIO); } if (load_firmware(sc) == -1) return (ENXIO); if (boot_firmware(sc) == -1) return (ENXIO); return (0); }; static device_method_t sdma_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sdma_probe), DEVMETHOD(device_attach, sdma_attach), { 0, 0 } }; static driver_t sdma_driver = { "sdma", sdma_methods, sizeof(struct sdma_softc), }; static devclass_t sdma_devclass; DRIVER_MODULE(sdma, simplebus, sdma_driver, sdma_devclass, 0, 0); Index: head/sys/arm/freescale/imx/imx6_ssi.c =================================================================== --- head/sys/arm/freescale/imx/imx6_ssi.c (revision 281084) +++ head/sys/arm/freescale/imx/imx6_ssi.c (revision 281085) @@ -1,855 +1,854 @@ /*- * Copyright (c) 2015 Ruslan Bukin * 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. */ /* * i.MX6 Synchronous Serial Interface (SSI) * * Chapter 61, i.MX 6Dual/6Quad Applications Processor Reference Manual, * Rev. 1, 04/2013 */ #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 #define READ4(_sc, _reg) \ bus_space_read_4(_sc->bst, _sc->bsh, _reg) #define WRITE4(_sc, _reg, _val) \ bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val) #define SSI_NCHANNELS 1 /* i.MX6 SSI registers */ #define SSI_STX0 0x00 /* Transmit Data Register n */ #define SSI_STX1 0x04 /* Transmit Data Register n */ #define SSI_SRX0 0x08 /* Receive Data Register n */ #define SSI_SRX1 0x0C /* Receive Data Register n */ #define SSI_SCR 0x10 /* Control Register */ #define SCR_I2S_MODE_S 5 /* I2S Mode Select. */ #define SCR_I2S_MODE_M 0x3 #define SCR_SYN (1 << 4) #define SCR_NET (1 << 3) /* Network mode */ #define SCR_RE (1 << 2) /* Receive Enable. */ #define SCR_TE (1 << 1) /* Transmit Enable. */ #define SCR_SSIEN (1 << 0) /* SSI Enable */ #define SSI_SISR 0x14 /* Interrupt Status Register */ #define SSI_SIER 0x18 /* Interrupt Enable Register */ #define SIER_RDMAE (1 << 22) /* Receive DMA Enable. */ #define SIER_RIE (1 << 21) /* Receive Interrupt Enable. */ #define SIER_TDMAE (1 << 20) /* Transmit DMA Enable. */ #define SIER_TIE (1 << 19) /* Transmit Interrupt Enable. */ #define SIER_TDE0IE (1 << 12) /* Transmit Data Register Empty 0. */ #define SIER_TUE0IE (1 << 8) /* Transmitter Underrun Error 0. */ #define SIER_TFE0IE (1 << 0) /* Transmit FIFO Empty 0 IE. */ #define SSI_STCR 0x1C /* Transmit Configuration Register */ #define STCR_TXBIT0 (1 << 9) /* Transmit Bit 0 shift MSB/LSB */ #define STCR_TFEN1 (1 << 8) /* Transmit FIFO Enable 1. */ #define STCR_TFEN0 (1 << 7) /* Transmit FIFO Enable 0. */ #define STCR_TFDIR (1 << 6) /* Transmit Frame Direction. */ #define STCR_TXDIR (1 << 5) /* Transmit Clock Direction. */ #define STCR_TSHFD (1 << 4) /* Transmit Shift Direction. */ #define STCR_TSCKP (1 << 3) /* Transmit Clock Polarity. */ #define STCR_TFSI (1 << 2) /* Transmit Frame Sync Invert. */ #define STCR_TFSL (1 << 1) /* Transmit Frame Sync Length. */ #define STCR_TEFS (1 << 0) /* Transmit Early Frame Sync. */ #define SSI_SRCR 0x20 /* Receive Configuration Register */ #define SSI_STCCR 0x24 /* Transmit Clock Control Register */ #define STCCR_DIV2 (1 << 18) /* Divide By 2. */ #define STCCR_PSR (1 << 17) /* Divide clock by 8. */ #define WL3_WL0_S 13 #define WL3_WL0_M 0xf #define DC4_DC0_S 8 #define DC4_DC0_M 0x1f #define PM7_PM0_S 0 #define PM7_PM0_M 0xff #define SSI_SRCCR 0x28 /* Receive Clock Control Register */ #define SSI_SFCSR 0x2C /* FIFO Control/Status Register */ #define SFCSR_RFWM1_S 20 /* Receive FIFO Empty WaterMark 1 */ #define SFCSR_RFWM1_M 0xf #define SFCSR_TFWM1_S 16 /* Transmit FIFO Empty WaterMark 1 */ #define SFCSR_TFWM1_M 0xf #define SFCSR_RFWM0_S 4 /* Receive FIFO Empty WaterMark 0 */ #define SFCSR_RFWM0_M 0xf #define SFCSR_TFWM0_S 0 /* Transmit FIFO Empty WaterMark 0 */ #define SFCSR_TFWM0_M 0xf #define SSI_SACNT 0x38 /* AC97 Control Register */ #define SSI_SACADD 0x3C /* AC97 Command Address Register */ #define SSI_SACDAT 0x40 /* AC97 Command Data Register */ #define SSI_SATAG 0x44 /* AC97 Tag Register */ #define SSI_STMSK 0x48 /* Transmit Time Slot Mask Register */ #define SSI_SRMSK 0x4C /* Receive Time Slot Mask Register */ #define SSI_SACCST 0x50 /* AC97 Channel Status Register */ #define SSI_SACCEN 0x54 /* AC97 Channel Enable Register */ #define SSI_SACCDIS 0x58 /* AC97 Channel Disable Register */ static MALLOC_DEFINE(M_SSI, "ssi", "ssi audio"); uint32_t ssi_dma_intr(void *arg, int chn); struct ssi_rate { uint32_t speed; uint32_t mfi; /* PLL4 Multiplication Factor Integer */ uint32_t mfn; /* PLL4 Multiplication Factor Numerator */ uint32_t mfd; /* PLL4 Multiplication Factor Denominator */ /* More dividers to configure can be added here */ }; static struct ssi_rate rate_map[] = { { 192000, 49, 152, 1000 }, /* PLL4 49.152 Mhz */ /* TODO: add more frequences */ { 0, 0 }, }; /* * i.MX6 example bit clock formula * * BCLK = 2 channels * 192000 hz * 24 bit = 9216000 hz = * (24000000 * (49 + 152/1000.0) / 4 / 4 / 2 / 2 / 2 / 1 / 1) * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ * | | | | | | | | | | | * Fref ------/ | | | | | | | | | | * PLL4 div select -/ | | | | | | | | | * PLL4 num --------------/ | | | | | | | | * PLL4 denom -------------------/ | | | | | | | * PLL4 post div ---------------------/ | | | | | | * CCM ssi pre div (CCM_CS1CDR) ----------/ | | | | | * CCM ssi post div (CCM_CS1CDR) -------------/ | | | | * SSI PM7_PM0_S ---------------------------------/ | | | * SSI Fixed divider ---------------------------------/ | | * SSI DIV2 ----------------------------------------------/ | * SSI PSR (prescaler /1 or /8) ------------------------------/ * * MCLK (Master clock) depends on DAC, usually BCLK * 4 */ struct sc_info { struct resource *res[2]; bus_space_tag_t bst; bus_space_handle_t bsh; device_t dev; struct mtx *lock; void *ih; int pos; int dma_size; bus_dma_tag_t dma_tag; bus_dmamap_t dma_map; bus_addr_t buf_base_phys; uint32_t *buf_base; struct sdma_conf *conf; struct ssi_rate *sr; struct sdma_softc *sdma_sc; int sdma_ev_rx; int sdma_ev_tx; int sdma_channel; }; /* Channel registers */ struct sc_chinfo { struct snd_dbuf *buffer; struct pcm_channel *channel; struct sc_pcminfo *parent; /* Channel information */ uint32_t dir; uint32_t format; /* Flags */ uint32_t run; }; /* PCM device private data */ struct sc_pcminfo { device_t dev; uint32_t (*ih)(struct sc_pcminfo *scp); uint32_t chnum; struct sc_chinfo chan[SSI_NCHANNELS]; struct sc_info *sc; }; static struct resource_spec ssi_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static int setup_dma(struct sc_pcminfo *scp); static void setup_ssi(struct sc_info *); static void ssi_configure_clock(struct sc_info *); /* * Mixer interface. */ static int ssimixer_init(struct snd_mixer *m) { struct sc_pcminfo *scp; struct sc_info *sc; int mask; scp = mix_getdevinfo(m); sc = scp->sc; if (sc == NULL) return -1; mask = SOUND_MASK_PCM; mask |= SOUND_MASK_VOLUME; snd_mtxlock(sc->lock); pcm_setflags(scp->dev, pcm_getflags(scp->dev) | SD_F_SOFTPCMVOL); mix_setdevs(m, mask); snd_mtxunlock(sc->lock); return (0); } static int ssimixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct sc_pcminfo *scp; scp = mix_getdevinfo(m); /* Here we can configure hardware volume on our DAC */ #if 1 device_printf(scp->dev, "ssimixer_set() %d %d\n", left, right); #endif return (0); } static kobj_method_t ssimixer_methods[] = { KOBJMETHOD(mixer_init, ssimixer_init), KOBJMETHOD(mixer_set, ssimixer_set), KOBJMETHOD_END }; MIXER_DECLARE(ssimixer); /* * Channel interface. */ static void * ssichan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) { struct sc_pcminfo *scp; struct sc_chinfo *ch; struct sc_info *sc; scp = (struct sc_pcminfo *)devinfo; sc = scp->sc; snd_mtxlock(sc->lock); ch = &scp->chan[0]; ch->dir = dir; ch->run = 0; ch->buffer = b; ch->channel = c; ch->parent = scp; snd_mtxunlock(sc->lock); if (sndbuf_setup(ch->buffer, sc->buf_base, sc->dma_size) != 0) { device_printf(scp->dev, "Can't setup sndbuf.\n"); return NULL; } return ch; } static int ssichan_free(kobj_t obj, void *data) { struct sc_chinfo *ch = data; struct sc_pcminfo *scp = ch->parent; struct sc_info *sc = scp->sc; #if 0 device_printf(scp->dev, "ssichan_free()\n"); #endif snd_mtxlock(sc->lock); /* TODO: free channel buffer */ snd_mtxunlock(sc->lock); return (0); } static int ssichan_setformat(kobj_t obj, void *data, uint32_t format) { struct sc_chinfo *ch = data; ch->format = format; return (0); } static uint32_t ssichan_setspeed(kobj_t obj, void *data, uint32_t speed) { struct sc_pcminfo *scp; struct sc_chinfo *ch; struct ssi_rate *sr; struct sc_info *sc; int threshold; int i; ch = data; scp = ch->parent; sc = scp->sc; sr = NULL; /* First look for equal frequency. */ for (i = 0; rate_map[i].speed != 0; i++) { if (rate_map[i].speed == speed) sr = &rate_map[i]; } /* If no match, just find nearest. */ if (sr == NULL) { for (i = 0; rate_map[i].speed != 0; i++) { sr = &rate_map[i]; threshold = sr->speed + ((rate_map[i + 1].speed != 0) ? ((rate_map[i + 1].speed - sr->speed) >> 1) : 0); if (speed < threshold) break; } } sc->sr = sr; ssi_configure_clock(sc); return (sr->speed); } static void ssi_configure_clock(struct sc_info *sc) { struct ssi_rate *sr; sr = sc->sr; pll4_configure_output(sr->mfi, sr->mfn, sr->mfd); /* Configure other dividers here, if any */ } static uint32_t ssichan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) { struct sc_chinfo *ch = data; struct sc_pcminfo *scp = ch->parent; struct sc_info *sc = scp->sc; sndbuf_resize(ch->buffer, sc->dma_size / blocksize, blocksize); setup_dma(scp); return (sndbuf_getblksz(ch->buffer)); } uint32_t ssi_dma_intr(void *arg, int chn) { struct sc_pcminfo *scp; struct sdma_conf *conf; struct sc_chinfo *ch; struct sc_info *sc; int bufsize; scp = arg; ch = &scp->chan[0]; sc = scp->sc; conf = sc->conf; bufsize = sndbuf_getsize(ch->buffer); sc->pos += conf->period; if (sc->pos >= bufsize) sc->pos -= bufsize; if (ch->run) chn_intr(ch->channel); return (0); } static int find_sdma_controller(struct sc_info *sc) { struct sdma_softc *sdma_sc; phandle_t node, sdma_node; device_t sdma_dev; int dts_value[8]; int len; if ((node = ofw_bus_get_node(sc->dev)) == -1) return (ENXIO); if ((len = OF_getproplen(node, "dmas")) <= 0) return (ENXIO); OF_getprop(node, "dmas", &dts_value, len); sc->sdma_ev_rx = fdt32_to_cpu(dts_value[1]); sc->sdma_ev_tx = fdt32_to_cpu(dts_value[5]); sdma_node = OF_node_from_xref(fdt32_to_cpu(dts_value[0])); sdma_sc = NULL; sdma_dev = devclass_get_device(devclass_find("sdma"), 0); if (sdma_dev) sdma_sc = device_get_softc(sdma_dev); if (sdma_sc == NULL) { device_printf(sc->dev, "No sDMA found. Can't operate\n"); return (ENXIO); }; sc->sdma_sc = sdma_sc; return (0); }; static int setup_dma(struct sc_pcminfo *scp) { struct sdma_conf *conf; struct sc_chinfo *ch; struct sc_info *sc; int fmt; ch = &scp->chan[0]; sc = scp->sc; conf = sc->conf; conf->ih = ssi_dma_intr; conf->ih_user = scp; conf->saddr = sc->buf_base_phys; conf->daddr = rman_get_start(sc->res[0]) + SSI_STX0; conf->event = sc->sdma_ev_tx; /* SDMA TX event */ conf->period = sndbuf_getblksz(ch->buffer); conf->num_bd = sndbuf_getblkcnt(ch->buffer); /* * Word Length * Can be 32, 24, 16 or 8 for sDMA. * * SSI supports 24 at max. */ fmt = sndbuf_getfmt(ch->buffer); if (fmt & AFMT_16BIT) { conf->word_length = 16; conf->command = CMD_2BYTES; } else if (fmt & AFMT_24BIT) { conf->word_length = 24; conf->command = CMD_3BYTES; } else { device_printf(sc->dev, "Unknown format\n"); return (-1); } return (0); } static int ssi_start(struct sc_pcminfo *scp) { struct sc_info *sc; int reg; sc = scp->sc; if (sdma_configure(sc->sdma_channel, sc->conf) != 0) { device_printf(sc->dev, "Can't configure sDMA\n"); return (-1); } /* Enable DMA interrupt */ reg = (SIER_TDMAE); WRITE4(sc, SSI_SIER, reg); sdma_start(sc->sdma_channel); return (0); } static int ssi_stop(struct sc_pcminfo *scp) { struct sc_info *sc; int reg; sc = scp->sc; reg = READ4(sc, SSI_SIER); reg &= ~(SIER_TDMAE); WRITE4(sc, SSI_SIER, reg); sdma_stop(sc->sdma_channel); bzero(sc->buf_base, sc->dma_size); return (0); } static int ssichan_trigger(kobj_t obj, void *data, int go) { struct sc_pcminfo *scp; struct sc_chinfo *ch; struct sc_info *sc; ch = data; scp = ch->parent; sc = scp->sc; snd_mtxlock(sc->lock); switch (go) { case PCMTRIG_START: #if 0 device_printf(scp->dev, "trigger start\n"); #endif ch->run = 1; ssi_start(scp); break; case PCMTRIG_STOP: case PCMTRIG_ABORT: #if 0 device_printf(scp->dev, "trigger stop or abort\n"); #endif ch->run = 0; ssi_stop(scp); break; } snd_mtxunlock(sc->lock); return (0); } static uint32_t ssichan_getptr(kobj_t obj, void *data) { struct sc_pcminfo *scp; struct sc_chinfo *ch; struct sc_info *sc; ch = data; scp = ch->parent; sc = scp->sc; return (sc->pos); } static uint32_t ssi_pfmt[] = { SND_FORMAT(AFMT_S24_LE, 2, 0), 0 }; static struct pcmchan_caps ssi_pcaps = {44100, 192000, ssi_pfmt, 0}; static struct pcmchan_caps * ssichan_getcaps(kobj_t obj, void *data) { return (&ssi_pcaps); } static kobj_method_t ssichan_methods[] = { KOBJMETHOD(channel_init, ssichan_init), KOBJMETHOD(channel_free, ssichan_free), KOBJMETHOD(channel_setformat, ssichan_setformat), KOBJMETHOD(channel_setspeed, ssichan_setspeed), KOBJMETHOD(channel_setblocksize, ssichan_setblocksize), KOBJMETHOD(channel_trigger, ssichan_trigger), KOBJMETHOD(channel_getptr, ssichan_getptr), KOBJMETHOD(channel_getcaps, ssichan_getcaps), KOBJMETHOD_END }; CHANNEL_DECLARE(ssichan); static int ssi_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,imx6q-ssi")) return (ENXIO); device_set_desc(dev, "i.MX6 Synchronous Serial Interface (SSI)"); return (BUS_PROBE_DEFAULT); } static void ssi_intr(void *arg) { struct sc_pcminfo *scp; struct sc_chinfo *ch; struct sc_info *sc; scp = arg; sc = scp->sc; ch = &scp->chan[0]; /* We don't use SSI interrupt */ #if 0 device_printf(sc->dev, "SSI Intr 0x%08x\n", READ4(sc, SSI_SISR)); #endif } static void setup_ssi(struct sc_info *sc) { int reg; reg = READ4(sc, SSI_STCCR); reg &= ~(WL3_WL0_M << WL3_WL0_S); reg |= (0xb << WL3_WL0_S); /* 24 bit */ reg &= ~(DC4_DC0_M << DC4_DC0_S); reg |= (1 << DC4_DC0_S); /* 2 words per frame */ reg &= ~(STCCR_DIV2); /* Divide by 1 */ reg &= ~(STCCR_PSR); /* Divide by 1 */ reg &= ~(PM7_PM0_M << PM7_PM0_S); reg |= (1 << PM7_PM0_S); /* Divide by 2 */ WRITE4(sc, SSI_STCCR, reg); reg = READ4(sc, SSI_SFCSR); reg &= ~(SFCSR_TFWM0_M << SFCSR_TFWM0_S); reg |= (8 << SFCSR_TFWM0_S); /* empty slots */ WRITE4(sc, SSI_SFCSR, reg); reg = READ4(sc, SSI_STCR); reg |= (STCR_TFEN0); reg &= ~(STCR_TFEN1); reg &= ~(STCR_TSHFD); /* MSB */ reg |= (STCR_TXBIT0); reg |= (STCR_TXDIR | STCR_TFDIR); reg |= (STCR_TSCKP); /* falling edge */ reg |= (STCR_TFSI); reg &= ~(STCR_TFSI); /* active high frame sync */ reg &= ~(STCR_TFSL); reg |= STCR_TEFS; WRITE4(sc, SSI_STCR, reg); reg = READ4(sc, SSI_SCR); reg &= ~(SCR_I2S_MODE_M << SCR_I2S_MODE_S); /* Not master */ reg |= (SCR_SSIEN | SCR_TE); reg |= (SCR_NET); reg |= (SCR_SYN); WRITE4(sc, SSI_SCR, reg); } static void ssi_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 = segs[0].ds_addr; } static int ssi_attach(device_t dev) { char status[SND_STATUSLEN]; struct sc_pcminfo *scp; struct sc_info *sc; int err; sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); sc->dev = dev; sc->sr = &rate_map[0]; sc->pos = 0; sc->conf = malloc(sizeof(struct sdma_conf), M_DEVBUF, M_WAITOK | M_ZERO); sc->lock = snd_mtxcreate(device_get_nameunit(dev), "ssi softc"); if (sc->lock == NULL) { device_printf(dev, "Cant create mtx\n"); return (ENXIO); } if (bus_alloc_resources(dev, ssi_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); /* SDMA */ if (find_sdma_controller(sc)) { device_printf(dev, "could not find active SDMA\n"); return (ENXIO); } /* Setup PCM */ scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); scp->sc = sc; scp->dev = dev; /* * Maximum possible DMA buffer. * Will be used partialy to match 24 bit word. */ sc->dma_size = 131072; /* * Must use dma_size boundary as modulo feature required. * Modulo feature allows setup circular buffer. */ err = bus_dma_tag_create( bus_get_dma_tag(sc->dev), 4, sc->dma_size, /* 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); err = bus_dmamem_alloc(sc->dma_tag, (void **)&sc->buf_base, BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &sc->dma_map); if (err) { device_printf(dev, "cannot allocate framebuffer\n"); return (ENXIO); } err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->buf_base, sc->dma_size, ssi_dmamap_cb, &sc->buf_base_phys, BUS_DMA_NOWAIT); if (err) { device_printf(dev, "cannot load DMA map\n"); return (ENXIO); } bzero(sc->buf_base, sc->dma_size); /* Setup interrupt handler */ err = bus_setup_intr(dev, sc->res[1], INTR_MPSAFE | INTR_TYPE_AV, NULL, ssi_intr, scp, &sc->ih); if (err) { device_printf(dev, "Unable to alloc interrupt resource.\n"); return (ENXIO); } pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); err = pcm_register(dev, scp, 1, 0); if (err) { device_printf(dev, "Can't register pcm.\n"); return (ENXIO); } scp->chnum = 0; pcm_addchan(dev, PCMDIR_PLAY, &ssichan_class, scp); scp->chnum++; snprintf(status, SND_STATUSLEN, "at simplebus"); pcm_setstatus(dev, status); mixer_init(dev, &ssimixer_class, scp); setup_ssi(sc); imx_ccm_ssi_configure(dev); sc->sdma_channel = sdma_alloc(); if (sc->sdma_channel < 0) { device_printf(sc->dev, "Can't get sDMA channel\n"); return (1); } return (0); } static device_method_t ssi_pcm_methods[] = { DEVMETHOD(device_probe, ssi_probe), DEVMETHOD(device_attach, ssi_attach), { 0, 0 } }; static driver_t ssi_pcm_driver = { "pcm", ssi_pcm_methods, PCM_SOFTC_SIZE, }; DRIVER_MODULE(ssi, simplebus, ssi_pcm_driver, pcm_devclass, 0, 0); MODULE_DEPEND(ssi, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(ssi, 1); Index: head/sys/arm/freescale/imx/imx_common.c =================================================================== --- head/sys/arm/freescale/imx/imx_common.c (revision 281084) +++ head/sys/arm/freescale/imx/imx_common.c (revision 281085) @@ -1,73 +1,72 @@ /*- * Copyright (C) 2008-2011 MARVELL INTERNATIONAL LTD. * Copyright (c) 2012, 2013 The FreeBSD Foundation * All rights reserved. * * Developed by Semihalf. * * 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. * 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 __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) { *interrupt = fdt32_to_cpu(intr[0]); *trig = INTR_TRIGGER_CONFORM; *pol = INTR_POLARITY_CONFORM; return (0); } fdt_pic_decode_t fdt_pic_table[] = { &fdt_intc_decode_ic, NULL }; Index: head/sys/arm/freescale/imx/imx_gpt.c =================================================================== --- head/sys/arm/freescale/imx/imx_gpt.c (revision 281084) +++ head/sys/arm/freescale/imx/imx_gpt.c (revision 281085) @@ -1,416 +1,415 @@ /*- * Copyright (c) 2012, 2013 The FreeBSD Foundation * All rights reserved. * * This software was 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 #define WRITE4(_sc, _r, _v) \ bus_space_write_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r), (_v)) #define READ4(_sc, _r) \ bus_space_read_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r)) #define SET4(_sc, _r, _m) \ WRITE4((_sc), (_r), READ4((_sc), (_r)) | (_m)) #define CLEAR4(_sc, _r, _m) \ WRITE4((_sc), (_r), READ4((_sc), (_r)) & ~(_m)) static u_int imx_gpt_get_timecount(struct timecounter *); static int imx_gpt_timer_start(struct eventtimer *, sbintime_t, sbintime_t); static int imx_gpt_timer_stop(struct eventtimer *); static int imx_gpt_intr(void *); static int imx_gpt_probe(device_t); static int imx_gpt_attach(device_t); static struct timecounter imx_gpt_timecounter = { .tc_name = "iMXGPT", .tc_get_timecount = imx_gpt_get_timecount, .tc_counter_mask = ~0u, .tc_frequency = 0, .tc_quality = 1000, }; /* Global softc pointer for use in DELAY(). */ struct imx_gpt_softc *imx_gpt_sc = NULL; /* * Hand-calibrated delay-loop counter. This was calibrated on an i.MX6 running * at 792mhz. It will delay a bit too long on slower processors -- that's * better than not delaying long enough. In practice this is unlikely to get * used much since the clock driver is one of the first to start up, and once * we're attached the delay loop switches to using the timer hardware. */ static const int imx_gpt_delay_count = 78; /* Try to divide down an available fast clock to this frequency. */ #define TARGET_FREQUENCY 1000000000 /* Don't try to set an event timer period smaller than this. */ #define MIN_ET_PERIOD 10LLU static struct resource_spec imx_gpt_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static struct ofw_compat_data compat_data[] = { {"fsl,imx6q-gpt", 1}, {"fsl,imx53-gpt", 1}, {"fsl,imx51-gpt", 1}, {"fsl,imx31-gpt", 1}, {"fsl,imx27-gpt", 1}, {"fsl,imx25-gpt", 1}, {NULL, 0} }; static int imx_gpt_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "Freescale i.MX GPT timer"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int imx_gpt_attach(device_t dev) { struct imx_gpt_softc *sc; int ctlreg, err; uint32_t basefreq, prescale; sc = device_get_softc(dev); if (bus_alloc_resources(dev, imx_gpt_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->sc_dev = dev; sc->sc_iot = rman_get_bustag(sc->res[0]); sc->sc_ioh = rman_get_bushandle(sc->res[0]); /* * For now, just automatically choose a good clock for the hardware * we're running on. Eventually we could allow selection from the fdt; * the code in this driver will cope with any clock frequency. */ sc->sc_clksrc = GPT_CR_CLKSRC_IPG; ctlreg = 0; switch (sc->sc_clksrc) { case GPT_CR_CLKSRC_32K: basefreq = 32768; break; case GPT_CR_CLKSRC_IPG: basefreq = imx_ccm_ipg_hz(); break; case GPT_CR_CLKSRC_IPG_HIGH: basefreq = imx_ccm_ipg_hz() * 2; break; case GPT_CR_CLKSRC_24M: ctlreg |= GPT_CR_24MEN; basefreq = 24000000; break; case GPT_CR_CLKSRC_NONE:/* Can't run without a clock. */ case GPT_CR_CLKSRC_EXT: /* No way to get the freq of an ext clock. */ default: device_printf(dev, "Unsupported clock source '%d'\n", sc->sc_clksrc); return (EINVAL); } /* * The following setup sequence is from the I.MX6 reference manual, * "Selecting the clock source". First, disable the clock and * interrupts. This also clears input and output mode bits and in * general completes several of the early steps in the procedure. */ WRITE4(sc, IMX_GPT_CR, 0); WRITE4(sc, IMX_GPT_IR, 0); /* Choose the clock and the power-saving behaviors. */ ctlreg |= sc->sc_clksrc | /* Use selected clock */ GPT_CR_FRR | /* Just count (FreeRunner mode) */ GPT_CR_STOPEN | /* Run in STOP mode */ GPT_CR_DOZEEN | /* Run in DOZE mode */ GPT_CR_WAITEN | /* Run in WAIT mode */ GPT_CR_DBGEN; /* Run in DEBUG mode */ WRITE4(sc, IMX_GPT_CR, ctlreg); /* * The datasheet says to do the software reset after choosing the clock * source. It says nothing about needing to wait for the reset to * complete, but the register description does document the fact that * the reset isn't complete until the SWR bit reads 0, so let's be safe. * The reset also clears all registers except for a few of the bits in * CR, but we'll rewrite all the CR bits when we start the counter. */ WRITE4(sc, IMX_GPT_CR, ctlreg | GPT_CR_SWR); while (READ4(sc, IMX_GPT_CR) & GPT_CR_SWR) continue; /* Set a prescaler value that gets us near the target frequency. */ if (basefreq < TARGET_FREQUENCY) { prescale = 0; sc->clkfreq = basefreq; } else { prescale = basefreq / TARGET_FREQUENCY; sc->clkfreq = basefreq / prescale; prescale -= 1; /* 1..n range is 0..n-1 in hardware. */ } WRITE4(sc, IMX_GPT_PR, prescale); /* Clear the status register. */ WRITE4(sc, IMX_GPT_SR, GPT_IR_ALL); /* Start the counter. */ WRITE4(sc, IMX_GPT_CR, ctlreg | GPT_CR_EN); if (bootverbose) device_printf(dev, "Running on %dKHz clock, base freq %uHz CR=0x%08x, PR=0x%08x\n", sc->clkfreq / 1000, basefreq, READ4(sc, IMX_GPT_CR), READ4(sc, IMX_GPT_PR)); /* Setup the timer interrupt. */ err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_CLK, imx_gpt_intr, NULL, sc, &sc->sc_ih); if (err != 0) { bus_release_resources(dev, imx_gpt_spec, sc->res); device_printf(dev, "Unable to setup the clock irq handler, " "err = %d\n", err); return (ENXIO); } /* Register as an eventtimer. */ sc->et.et_name = "iMXGPT"; sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERIODIC; sc->et.et_quality = 800; sc->et.et_frequency = sc->clkfreq; sc->et.et_min_period = (MIN_ET_PERIOD << 32) / sc->et.et_frequency; sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; sc->et.et_start = imx_gpt_timer_start; sc->et.et_stop = imx_gpt_timer_stop; sc->et.et_priv = sc; et_register(&sc->et); /* Register as a timecounter. */ imx_gpt_timecounter.tc_frequency = sc->clkfreq; tc_init(&imx_gpt_timecounter); /* If this is the first unit, store the softc for use in DELAY. */ if (device_get_unit(dev) == 0) imx_gpt_sc = sc; return (0); } static int imx_gpt_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { struct imx_gpt_softc *sc; uint32_t ticks; sc = (struct imx_gpt_softc *)et->et_priv; if (period != 0) { sc->sc_period = ((uint32_t)et->et_frequency * period) >> 32; /* Set expected value */ WRITE4(sc, IMX_GPT_OCR2, READ4(sc, IMX_GPT_CNT) + sc->sc_period); /* Enable compare register 2 Interrupt */ SET4(sc, IMX_GPT_IR, GPT_IR_OF2); return (0); } else if (first != 0) { ticks = ((uint32_t)et->et_frequency * first) >> 32; /* Do not disturb, otherwise event will be lost */ spinlock_enter(); /* Set expected value */ WRITE4(sc, IMX_GPT_OCR3, READ4(sc, IMX_GPT_CNT) + ticks); /* Enable compare register 1 Interrupt */ SET4(sc, IMX_GPT_IR, GPT_IR_OF3); /* Now everybody can relax */ spinlock_exit(); return (0); } return (EINVAL); } static int imx_gpt_timer_stop(struct eventtimer *et) { struct imx_gpt_softc *sc; sc = (struct imx_gpt_softc *)et->et_priv; /* Disable OF2 Interrupt */ CLEAR4(sc, IMX_GPT_IR, GPT_IR_OF2); WRITE4(sc, IMX_GPT_SR, GPT_IR_OF2); sc->sc_period = 0; return (0); } int imx_gpt_get_timerfreq(struct imx_gpt_softc *sc) { return (sc->clkfreq); } static int imx_gpt_intr(void *arg) { struct imx_gpt_softc *sc; uint32_t status; sc = (struct imx_gpt_softc *)arg; status = READ4(sc, IMX_GPT_SR); /* * Clear interrupt status before invoking event callbacks. The callback * often sets up a new one-shot timer event and if the interval is short * enough it can fire before we get out of this function. If we cleared * at the bottom we'd miss the interrupt and hang until the clock wraps. */ WRITE4(sc, IMX_GPT_SR, status); /* Handle one-shot timer events. */ if (status & GPT_IR_OF3) { if (sc->et.et_active) { sc->et.et_event_cb(&sc->et, sc->et.et_arg); } } /* Handle periodic timer events. */ if (status & GPT_IR_OF2) { if (sc->et.et_active) sc->et.et_event_cb(&sc->et, sc->et.et_arg); if (sc->sc_period != 0) WRITE4(sc, IMX_GPT_OCR2, READ4(sc, IMX_GPT_CNT) + sc->sc_period); } return (FILTER_HANDLED); } u_int imx_gpt_get_timecount(struct timecounter *tc) { if (imx_gpt_sc == NULL) return (0); return (READ4(imx_gpt_sc, IMX_GPT_CNT)); } static device_method_t imx_gpt_methods[] = { DEVMETHOD(device_probe, imx_gpt_probe), DEVMETHOD(device_attach, imx_gpt_attach), DEVMETHOD_END }; static driver_t imx_gpt_driver = { "imx_gpt", imx_gpt_methods, sizeof(struct imx_gpt_softc), }; static devclass_t imx_gpt_devclass; EARLY_DRIVER_MODULE(imx_gpt, simplebus, imx_gpt_driver, imx_gpt_devclass, 0, 0, BUS_PASS_TIMER); void DELAY(int usec) { uint64_t curcnt, endcnt, startcnt, ticks; /* If the timer hardware is not accessible, just use a loop. */ if (imx_gpt_sc == NULL) { while (usec-- > 0) for (ticks = 0; ticks < imx_gpt_delay_count; ++ticks) cpufunc_nullop(); return; } /* * Calculate the tick count with 64-bit values so that it works for any * clock frequency. Loop until the hardware count reaches start+ticks. * If the 32-bit hardware count rolls over while we're looping, just * manually do a carry into the high bits after each read; don't worry * that doing this on each loop iteration is inefficient -- we're trying * to waste time here. */ ticks = 1 + ((uint64_t)usec * imx_gpt_sc->clkfreq) / 1000000; curcnt = startcnt = READ4(imx_gpt_sc, IMX_GPT_CNT); endcnt = startcnt + ticks; while (curcnt < endcnt) { curcnt = READ4(imx_gpt_sc, IMX_GPT_CNT); if (curcnt < startcnt) curcnt += 1ULL << 32; } } Index: head/sys/arm/freescale/imx/imx_iomux.c =================================================================== --- head/sys/arm/freescale/imx/imx_iomux.c (revision 281084) +++ head/sys/arm/freescale/imx/imx_iomux.c (revision 281085) @@ -1,325 +1,324 @@ /*- * Copyright (c) 2014 Ian Lepore * 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$ */ /* * Pin mux and pad control driver for imx5 and imx6. * * This driver implements the fdt_pinctrl interface for configuring the gpio and * peripheral pins based on fdt configuration data. * * When the driver attaches, it walks the entire fdt tree and automatically * configures the pins for each device which has a pinctrl-0 property and whose * status is "okay". In addition it implements the fdt_pinctrl_configure() * method which any other driver can call at any time to reconfigure its pins. * * The nature of the fsl,pins property in fdt data makes this driver's job very * easy. Instead of representing each pin and pad configuration using symbolic * properties such as pullup-enable="true" and so on, the data simply contains * the addresses of the registers that control the pins, and the raw values to * store in those registers. * * The imx5 and imx6 SoCs also have a small number of "general purpose * registers" in the iomuxc device which are used to control an assortment * of completely unrelated aspects of SoC behavior. This driver provides other * drivers with direct access to those registers via simple accessor functions. */ #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include struct iomux_softc { device_t dev; struct resource *mem_res; u_int last_gpreg; }; static struct iomux_softc *iomux_sc; static struct ofw_compat_data compat_data[] = { {"fsl,imx6dl-iomuxc", true}, {"fsl,imx6q-iomuxc", true}, {"fsl,imx6sl-iomuxc", true}, {"fsl,imx6sx-iomuxc", true}, {"fsl,imx53-iomuxc", true}, {"fsl,imx51-iomuxc", true}, {NULL, false}, }; /* * Each tuple in an fsl,pins property contains these fields. */ struct pincfg { uint32_t mux_reg; uint32_t padconf_reg; uint32_t input_reg; uint32_t mux_val; uint32_t input_val; uint32_t padconf_val; }; #define PADCONF_NONE (1U << 31) /* Do not configure pad. */ #define PADCONF_SION (1U << 30) /* Force SION bit in mux register. */ #define PADMUX_SION (1U << 4) /* The SION bit in the mux register. */ static inline uint32_t RD4(struct iomux_softc *sc, bus_size_t off) { return (bus_read_4(sc->mem_res, off)); } static inline void WR4(struct iomux_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->mem_res, off, val); } static void iomux_configure_input(struct iomux_softc *sc, uint32_t reg, uint32_t val) { u_int select, mask, shift, width; /* If register and value are zero, there is nothing to configure. */ if (reg == 0 && val == 0) return; /* * If the config value has 0xff in the high byte it is encoded: * 31 23 15 7 0 * | 0xff | shift | width | select | * We need to mask out the old select value and OR in the new, using a * mask of the given width and shifting the values up by shift. */ if ((val & 0xff000000) == 0xff000000) { select = val & 0x000000ff; width = (val & 0x0000ff00) >> 8; shift = (val & 0x00ff0000) >> 16; mask = ((1u << width) - 1) << shift; val = (RD4(sc, reg) & ~mask) | (select << shift); } WR4(sc, reg, val); } static int iomux_configure_pins(device_t dev, phandle_t cfgxref) { struct iomux_softc *sc; struct pincfg *cfgtuples, *cfg; phandle_t cfgnode; int i, ntuples; uint32_t sion; sc = device_get_softc(dev); cfgnode = OF_node_from_xref(cfgxref); ntuples = OF_getencprop_alloc(cfgnode, "fsl,pins", sizeof(*cfgtuples), (void **)&cfgtuples); if (ntuples < 0) return (ENOENT); if (ntuples == 0) return (0); /* Empty property is not an error. */ for (i = 0, cfg = cfgtuples; i < ntuples; i++, cfg++) { sion = (cfg->padconf_val & PADCONF_SION) ? PADMUX_SION : 0; WR4(sc, cfg->mux_reg, cfg->mux_val | sion); iomux_configure_input(sc, cfg->input_reg, cfg->input_val); if ((cfg->padconf_val & PADCONF_NONE) == 0) WR4(sc, cfg->padconf_reg, cfg->padconf_val); if (bootverbose) { char name[32]; OF_getprop(cfgnode, "name", &name, sizeof(name)); printf("%16s: muxreg 0x%04x muxval 0x%02x " "inpreg 0x%04x inpval 0x%02x " "padreg 0x%04x padval 0x%08x\n", name, cfg->mux_reg, cfg->mux_val | sion, cfg->input_reg, cfg->input_val, cfg->padconf_reg, cfg->padconf_val); } } free(cfgtuples, M_OFWPROP); return (0); } static int iomux_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) return (ENXIO); device_set_desc(dev, "Freescale i.MX pin configuration"); return (BUS_PROBE_DEFAULT); } static int iomux_detach(device_t dev) { /* This device is always present. */ return (EBUSY); } static int iomux_attach(device_t dev) { struct iomux_softc * sc; int rid; sc = device_get_softc(dev); sc->dev = dev; switch (imx_soc_type()) { case IMXSOC_51: sc->last_gpreg = 1; break; case IMXSOC_53: sc->last_gpreg = 2; break; case IMXSOC_6DL: case IMXSOC_6S: case IMXSOC_6SL: case IMXSOC_6Q: sc->last_gpreg = 13; break; default: device_printf(dev, "Unknown SoC type\n"); return (ENXIO); } rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); return (ENXIO); } iomux_sc = sc; /* * Register as a pinctrl device, and call the convenience function that * walks the entire device tree invoking FDT_PINCTRL_CONFIGURE() on any * pinctrl-0 property cells whose xref phandle refers to a configuration * that is a child node of our node in the tree. * * The pinctrl bindings documentation specifically mentions that the * pinctrl device itself may have a pinctrl-0 property which contains * static configuration to be applied at device init time. The tree * walk will automatically handle this for us when it passes through our * node in the tree. */ fdt_pinctrl_register(dev, "fsl,pins"); fdt_pinctrl_configure_tree(dev); return (0); } uint32_t imx_iomux_gpr_get(u_int regnum) { struct iomux_softc * sc; sc = iomux_sc; KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__)); KASSERT(regnum >= 0 && regnum <= sc->last_gpreg, ("%s bad regnum %u, max %u", __FUNCTION__, regnum, sc->last_gpreg)); return (RD4(iomux_sc, regnum * 4)); } void imx_iomux_gpr_set(u_int regnum, uint32_t val) { struct iomux_softc * sc; sc = iomux_sc; KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__)); KASSERT(regnum >= 0 && regnum <= sc->last_gpreg, ("%s bad regnum %u, max %u", __FUNCTION__, regnum, sc->last_gpreg)); WR4(iomux_sc, regnum * 4, val); } void imx_iomux_gpr_set_masked(u_int regnum, uint32_t clrbits, uint32_t setbits) { struct iomux_softc * sc; uint32_t val; sc = iomux_sc; KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__)); KASSERT(regnum >= 0 && regnum <= sc->last_gpreg, ("%s bad regnum %u, max %u", __FUNCTION__, regnum, sc->last_gpreg)); val = RD4(iomux_sc, regnum * 4); val = (val & ~clrbits) | setbits; WR4(iomux_sc, regnum * 4, val); } static device_method_t imx_iomux_methods[] = { /* Device interface */ DEVMETHOD(device_probe, iomux_probe), DEVMETHOD(device_attach, iomux_attach), DEVMETHOD(device_detach, iomux_detach), /* fdt_pinctrl interface */ DEVMETHOD(fdt_pinctrl_configure,iomux_configure_pins), DEVMETHOD_END }; static driver_t imx_iomux_driver = { "imx_iomux", imx_iomux_methods, sizeof(struct iomux_softc), }; static devclass_t imx_iomux_devclass; EARLY_DRIVER_MODULE(imx_iomux, simplebus, imx_iomux_driver, imx_iomux_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE); Index: head/sys/arm/freescale/imx/imx_wdog.c =================================================================== --- head/sys/arm/freescale/imx/imx_wdog.c (revision 281084) +++ head/sys/arm/freescale/imx/imx_wdog.c (revision 281085) @@ -1,167 +1,166 @@ /*- * Copyright (c) 2012, 2013 The FreeBSD Foundation * All rights reserved. * * This software was 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 struct imx_wdog_softc { struct mtx sc_mtx; device_t sc_dev; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; struct resource *sc_res[2]; uint32_t sc_timeout; }; static struct resource_spec imx_wdog_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static void imx_watchdog(void *, u_int, int *); static int imx_wdog_probe(device_t); static int imx_wdog_attach(device_t); static device_method_t imx_wdog_methods[] = { DEVMETHOD(device_probe, imx_wdog_probe), DEVMETHOD(device_attach, imx_wdog_attach), DEVMETHOD_END }; static driver_t imx_wdog_driver = { "imx_wdog", imx_wdog_methods, sizeof(struct imx_wdog_softc), }; static devclass_t imx_wdog_devclass; DRIVER_MODULE(imx_wdog, simplebus, imx_wdog_driver, imx_wdog_devclass, 0, 0); static void imx_watchdog(void *arg, u_int cmd, int *error) { struct imx_wdog_softc *sc; uint16_t reg; int timeout; sc = arg; mtx_lock(&sc->sc_mtx); /* Refresh counter, since we feels good */ WRITE(sc, WDOG_SR_REG, WDOG_SR_STEP1); WRITE(sc, WDOG_SR_REG, WDOG_SR_STEP2); /* We don't require precession, so "-10" (/1024) is ok */ timeout = (1 << ((cmd & WD_INTERVAL) - 10)) / 1000000; if (timeout > 1 && timeout < 128) { if (timeout != sc->sc_timeout) { device_printf(sc->sc_dev, "WARNING: watchdog can't be disabled!!!"); sc->sc_timeout = timeout; reg = READ(sc, WDOG_CR_REG); reg &= ~WDOG_CR_WT_MASK; reg |= (timeout << (WDOG_CR_WT_SHIFT + 1)) & WDOG_CR_WT_MASK; WRITE(sc, WDOG_CR_REG, reg); /* Refresh counter */ WRITE(sc, WDOG_SR_REG, WDOG_SR_STEP1); WRITE(sc, WDOG_SR_REG, WDOG_SR_STEP2); *error = 0; } else { *error = EOPNOTSUPP; } } else { device_printf(sc->sc_dev, "Can not be disabled.\n"); *error = EOPNOTSUPP; } mtx_unlock(&sc->sc_mtx); } static int imx_wdog_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,imx51-wdt") && !ofw_bus_is_compatible(dev, "fsl,imx53-wdt")) return (ENXIO); device_set_desc(dev, "Freescale i.MX5xx Watchdog Timer"); return (0); } static int imx_wdog_attach(device_t dev) { struct imx_wdog_softc *sc; sc = device_get_softc(dev); sc->sc_dev = dev; if (bus_alloc_resources(dev, imx_wdog_spec, sc->sc_res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "imx_wdt", MTX_DEF); sc->sc_dev = dev; sc->sc_bst = rman_get_bustag(sc->sc_res[0]); sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); /* TODO: handle interrupt */ EVENTHANDLER_REGISTER(watchdog_list, imx_watchdog, sc, 0); return (0); } Index: head/sys/arm/freescale/vybrid/vf_adc.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_adc.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_adc.c (revision 281085) @@ -1,245 +1,244 @@ /*- * Copyright (c) 2014 Ruslan Bukin * 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. */ /* * Vybrid Family 12-bit Analog to Digital Converter (ADC) * Chapter 37, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #define ADC_HC0 0x00 /* Ctrl reg for hardware triggers */ #define ADC_HC1 0x04 /* Ctrl reg for hardware triggers */ #define HC_AIEN (1 << 7) /* Conversion Complete Int Control */ #define HC_ADCH_M 0x1f /* Input Channel Select Mask */ #define HC_ADCH_S 0 /* Input Channel Select Shift */ #define ADC_HS 0x08 /* Status register for HW triggers */ #define HS_COCO0 (1 << 0) /* Conversion Complete Flag */ #define HS_COCO1 (1 << 1) /* Conversion Complete Flag */ #define ADC_R0 0x0C /* Data result reg for HW triggers */ #define ADC_R1 0x10 /* Data result reg for HW triggers */ #define ADC_CFG 0x14 /* Configuration register */ #define CFG_OVWREN (1 << 16) /* Data Overwrite Enable */ #define CFG_AVGS_M 0x3 /* Hardware Average select Mask */ #define CFG_AVGS_S 14 /* Hardware Average select Shift */ #define CFG_ADTRG (1 << 13) /* Conversion Trigger Select */ #define CFG_REFSEL_M 0x3 /* Voltage Reference Select Mask */ #define CFG_REFSEL_S 11 /* Voltage Reference Select Shift */ #define CFG_ADHSC (1 << 10) /* High Speed Configuration */ #define CFG_ADSTS_M 0x3 /* Defines the sample time duration */ #define CFG_ADSTS_S 8 /* Defines the sample time duration */ #define CFG_ADLPC (1 << 7) /* Low-Power Configuration */ #define CFG_ADIV_M 0x3 /* Clock Divide Select */ #define CFG_ADIV_S 5 /* Clock Divide Select */ #define CFG_ADLSMP (1 << 4) /* Long Sample Time Configuration */ #define CFG_MODE_M 0x3 /* Conversion Mode Selection Mask */ #define CFG_MODE_S 2 /* Conversion Mode Selection Shift */ #define CFG_MODE_12 0x2 /* 12-bit mode */ #define CFG_ADICLK_M 0x3 /* Input Clock Select Mask */ #define CFG_ADICLK_S 0 /* Input Clock Select Shift */ #define ADC_GC 0x18 /* General control register */ #define GC_CAL (1 << 7) /* Calibration */ #define GC_ADCO (1 << 6) /* Continuous Conversion Enable */ #define GC_AVGE (1 << 5) /* Hardware average enable */ #define GC_ACFE (1 << 4) /* Compare Function Enable */ #define GC_ACFGT (1 << 3) /* Compare Function Greater Than En */ #define GC_ACREN (1 << 2) /* Compare Function Range En */ #define GC_DMAEN (1 << 1) /* DMA Enable */ #define GC_ADACKEN (1 << 0) /* Asynchronous clock output enable */ #define ADC_GS 0x1C /* General status register */ #define GS_AWKST (1 << 2) /* Asynchronous wakeup int status */ #define GS_CALF (1 << 1) /* Calibration Failed Flag */ #define GS_ADACT (1 << 0) /* Conversion Active */ #define ADC_CV 0x20 /* Compare value register */ #define CV_CV2_M 0xfff /* Compare Value 2 Mask */ #define CV_CV2_S 16 /* Compare Value 2 Shift */ #define CV_CV1_M 0xfff /* Compare Value 1 Mask */ #define CV_CV1_S 0 /* Compare Value 1 Shift */ #define ADC_OFS 0x24 /* Offset correction value register */ #define OFS_SIGN 12 /* Sign bit */ #define OFS_M 0xfff /* Offset value Mask */ #define OFS_S 0 /* Offset value Shift */ #define ADC_CAL 0x28 /* Calibration value register */ #define CAL_CODE_M 0xf /* Calibration Result Value Mask */ #define CAL_CODE_S 0 /* Calibration Result Value Shift */ #define ADC_PCTL 0x30 /* Pin control register */ struct adc_softc { struct resource *res[2]; bus_space_tag_t bst; bus_space_handle_t bsh; void *ih; }; struct adc_softc *adc_sc; static struct resource_spec adc_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static int adc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-adc")) return (ENXIO); device_set_desc(dev, "Vybrid Family " "12-bit Analog to Digital Converter"); return (BUS_PROBE_DEFAULT); } static void adc_intr(void *arg) { struct adc_softc *sc; sc = arg; /* Conversation complete */ } uint32_t adc_read(void) { struct adc_softc *sc; sc = adc_sc; if (sc == NULL) return (0); return (READ4(sc, ADC_R0)); } uint32_t adc_enable(int channel) { struct adc_softc *sc; int reg; sc = adc_sc; if (sc == NULL) return (1); reg = READ4(sc, ADC_HC0); reg &= ~(HC_ADCH_M << HC_ADCH_S); reg |= (channel << HC_ADCH_S); WRITE4(sc, ADC_HC0, reg); return (0); } static int adc_attach(device_t dev) { struct adc_softc *sc; int err; int reg; sc = device_get_softc(dev); if (bus_alloc_resources(dev, adc_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); adc_sc = sc; /* Setup interrupt handler */ err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_BIO | INTR_MPSAFE, NULL, adc_intr, sc, &sc->ih); if (err) { device_printf(dev, "Unable to alloc interrupt resource.\n"); return (ENXIO); } /* Configure 12-bit mode */ reg = READ4(sc, ADC_CFG); reg &= ~(CFG_MODE_M << CFG_MODE_S); reg |= (CFG_MODE_12 << CFG_MODE_S); /* 12bit */ WRITE4(sc, ADC_CFG, reg); /* Configure for continuous conversion */ reg = READ4(sc, ADC_GC); reg |= (GC_ADCO | GC_AVGE); WRITE4(sc, ADC_GC, reg); /* Disable interrupts */ reg = READ4(sc, ADC_HC0); reg &= HC_AIEN; WRITE4(sc, ADC_HC0, reg); return (0); } static device_method_t adc_methods[] = { DEVMETHOD(device_probe, adc_probe), DEVMETHOD(device_attach, adc_attach), { 0, 0 } }; static driver_t adc_driver = { "adc", adc_methods, sizeof(struct adc_softc), }; static devclass_t adc_devclass; DRIVER_MODULE(adc, simplebus, adc_driver, adc_devclass, 0, 0); Index: head/sys/arm/freescale/vybrid/vf_anadig.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_anadig.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_anadig.c (revision 281085) @@ -1,246 +1,245 @@ /*- * Copyright (c) 2013-2014 Ruslan Bukin * 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. */ /* * Vybrid Family Analog components control digital interface (ANADIG) * Chapter 11, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #define ANADIG_PLL3_CTRL 0x010 /* PLL3 Control */ #define ANADIG_PLL7_CTRL 0x020 /* PLL7 Control */ #define ANADIG_PLL2_CTRL 0x030 /* PLL2 Control */ #define ANADIG_PLL2_SS 0x040 /* PLL2 Spread Spectrum */ #define ANADIG_PLL2_NUM 0x050 /* PLL2 Numerator */ #define ANADIG_PLL2_DENOM 0x060 /* PLL2 Denominator */ #define ANADIG_PLL4_CTRL 0x070 /* PLL4 Control */ #define ANADIG_PLL4_NUM 0x080 /* PLL4 Numerator */ #define ANADIG_PLL4_DENOM 0x090 /* PLL4 Denominator */ #define ANADIG_PLL6_CTRL 0x0A0 /* PLL6 Control */ #define ANADIG_PLL6_NUM 0x0B0 /* PLL6 Numerator */ #define ANADIG_PLL6_DENOM 0x0C0 /* PLL6 Denominator */ #define ANADIG_PLL5_CTRL 0x0E0 /* PLL5 Control */ #define ANADIG_PLL3_PFD 0x0F0 /* PLL3 PFD */ #define ANADIG_PLL2_PFD 0x100 /* PLL2 PFD */ #define ANADIG_REG_1P1 0x110 /* Regulator 1P1 */ #define ANADIG_REG_3P0 0x120 /* Regulator 3P0 */ #define ANADIG_REG_2P5 0x130 /* Regulator 2P5 */ #define ANADIG_ANA_MISC0 0x150 /* Analog Miscellaneous */ #define ANADIG_ANA_MISC1 0x160 /* Analog Miscellaneous */ #define ANADIG_ANADIG_DIGPROG 0x260 /* Digital Program */ #define ANADIG_PLL1_CTRL 0x270 /* PLL1 Control */ #define ANADIG_PLL1_SS 0x280 /* PLL1 Spread Spectrum */ #define ANADIG_PLL1_NUM 0x290 /* PLL1 Numerator */ #define ANADIG_PLL1_DENOM 0x2A0 /* PLL1 Denominator */ #define ANADIG_PLL1_PFD 0x2B0 /* PLL1_PFD */ #define ANADIG_PLL_LOCK 0x2C0 /* PLL Lock */ #define USB_VBUS_DETECT(n) (0x1A0 + 0x60 * n) #define USB_CHRG_DETECT(n) (0x1B0 + 0x60 * n) #define USB_VBUS_DETECT_STATUS(n) (0x1C0 + 0x60 * n) #define USB_CHRG_DETECT_STATUS(n) (0x1D0 + 0x60 * n) #define USB_LOOPBACK(n) (0x1E0 + 0x60 * n) #define USB_MISC(n) (0x1F0 + 0x60 * n) #define ANADIG_PLL_LOCKED (1U << 31) #define ENABLE_LINREG (1 << 0) #define EN_CLK_TO_UTMI (1 << 30) #define CTRL_BYPASS (1 << 16) #define CTRL_PWR (1 << 12) #define CTRL_PLL_EN (1 << 13) #define EN_USB_CLKS (1 << 6) #define PLL4_CTRL_DIV_SEL_S 0 #define PLL4_CTRL_DIV_SEL_M 0x7f struct anadig_softc { struct resource *res[1]; bus_space_tag_t bst; bus_space_handle_t bsh; }; struct anadig_softc *anadig_sc; static struct resource_spec anadig_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static int anadig_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-anadig")) return (ENXIO); device_set_desc(dev, "Vybrid Family ANADIG Unit"); return (BUS_PROBE_DEFAULT); } static int enable_pll(struct anadig_softc *sc, int pll_ctrl) { int reg; reg = READ4(sc, pll_ctrl); reg &= ~(CTRL_BYPASS | CTRL_PWR); if (pll_ctrl == ANADIG_PLL3_CTRL || pll_ctrl == ANADIG_PLL7_CTRL) { /* It is USB PLL. Power bit logic is reversed */ reg |= (CTRL_PWR | EN_USB_CLKS); } WRITE4(sc, pll_ctrl, reg); /* Wait for PLL lock */ while (!(READ4(sc, pll_ctrl) & ANADIG_PLL_LOCKED)) ; reg = READ4(sc, pll_ctrl); reg |= (CTRL_PLL_EN); WRITE4(sc, pll_ctrl, reg); return (0); } uint32_t pll4_configure_output(uint32_t mfi, uint32_t mfn, uint32_t mfd) { struct anadig_softc *sc; int reg; sc = anadig_sc; /* * PLLout = Fsys * (MFI+(MFN/MFD)) */ reg = READ4(sc, ANADIG_PLL4_CTRL); reg &= ~(PLL4_CTRL_DIV_SEL_M << PLL4_CTRL_DIV_SEL_S); reg |= (mfi << PLL4_CTRL_DIV_SEL_S); WRITE4(sc, ANADIG_PLL4_CTRL, reg); WRITE4(sc, ANADIG_PLL4_NUM, mfn); WRITE4(sc, ANADIG_PLL4_DENOM, mfd); return (0); } static int anadig_attach(device_t dev) { struct anadig_softc *sc; int reg; sc = device_get_softc(dev); if (bus_alloc_resources(dev, anadig_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); anadig_sc = sc; /* Enable USB PLLs */ enable_pll(sc, ANADIG_PLL3_CTRL); enable_pll(sc, ANADIG_PLL7_CTRL); /* Enable other PLLs */ enable_pll(sc, ANADIG_PLL1_CTRL); enable_pll(sc, ANADIG_PLL2_CTRL); enable_pll(sc, ANADIG_PLL4_CTRL); enable_pll(sc, ANADIG_PLL5_CTRL); enable_pll(sc, ANADIG_PLL6_CTRL); /* Enable USB voltage regulator */ reg = READ4(sc, ANADIG_REG_3P0); reg |= (ENABLE_LINREG); WRITE4(sc, ANADIG_REG_3P0, reg); /* Give clocks to USB */ reg = READ4(sc, USB_MISC(0)); reg |= (EN_CLK_TO_UTMI); WRITE4(sc, USB_MISC(0), reg); reg = READ4(sc, USB_MISC(1)); reg |= (EN_CLK_TO_UTMI); WRITE4(sc, USB_MISC(1), reg); #if 0 printf("USB_ANALOG_USB_MISC(0) == 0x%08x\n", READ4(sc, USB_ANALOG_USB_MISC(0))); printf("USB_ANALOG_USB_MISC(1) == 0x%08x\n", READ4(sc, USB_ANALOG_USB_MISC(1))); #endif return (0); } static device_method_t anadig_methods[] = { DEVMETHOD(device_probe, anadig_probe), DEVMETHOD(device_attach, anadig_attach), { 0, 0 } }; static driver_t anadig_driver = { "anadig", anadig_methods, sizeof(struct anadig_softc), }; static devclass_t anadig_devclass; DRIVER_MODULE(anadig, simplebus, anadig_driver, anadig_devclass, 0, 0); Index: head/sys/arm/freescale/vybrid/vf_ccm.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_ccm.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_ccm.c (revision 281085) @@ -1,501 +1,500 @@ /*- * Copyright (c) 2013-2014 Ruslan Bukin * 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. */ /* * Vybrid Family Clock Controller Module (CCM) * Chapter 10, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #define CCM_CCR 0x00 /* Control Register */ #define CCM_CSR 0x04 /* Status Register */ #define CCM_CCSR 0x08 /* Clock Switcher Register */ #define CCM_CACRR 0x0C /* ARM Clock Root Register */ #define CCM_CSCMR1 0x10 /* Serial Clock Multiplexer Register 1 */ #define CCM_CSCDR1 0x14 /* Serial Clock Divider Register 1 */ #define CCM_CSCDR2 0x18 /* Serial Clock Divider Register 2 */ #define CCM_CSCDR3 0x1C /* Serial Clock Divider Register 3 */ #define CCM_CSCMR2 0x20 /* Serial Clock Multiplexer Register 2 */ #define CCM_CTOR 0x28 /* Testing Observability Register */ #define CCM_CLPCR 0x2C /* Low Power Control Register */ #define CCM_CISR 0x30 /* Interrupt Status Register */ #define CCM_CIMR 0x34 /* Interrupt Mask Register */ #define CCM_CCOSR 0x38 /* Clock Output Source Register */ #define CCM_CGPR 0x3C /* General Purpose Register */ #define CCM_CCGRN 12 #define CCM_CCGR(n) (0x40 + (n * 0x04)) /* Clock Gating Register */ #define CCM_CMEOR(n) (0x70 + (n * 0x70)) /* Module Enable Override */ #define CCM_CCPGR(n) (0x90 + (n * 0x04)) /* Platform Clock Gating */ #define CCM_CPPDSR 0x88 /* PLL PFD Disable Status Register */ #define CCM_CCOWR 0x8C /* CORE Wakeup Register */ #define PLL3_PFD4_EN (1U << 31) #define PLL3_PFD3_EN (1 << 30) #define PLL3_PFD2_EN (1 << 29) #define PLL3_PFD1_EN (1 << 28) #define PLL2_PFD4_EN (1 << 15) #define PLL2_PFD3_EN (1 << 14) #define PLL2_PFD2_EN (1 << 13) #define PLL2_PFD1_EN (1 << 12) #define PLL1_PFD4_EN (1 << 11) #define PLL1_PFD3_EN (1 << 10) #define PLL1_PFD2_EN (1 << 9) #define PLL1_PFD1_EN (1 << 8) /* CCM_CCR */ #define FIRC_EN (1 << 16) #define FXOSC_EN (1 << 12) #define FXOSC_RDY (1 << 5) /* CCM_CSCDR1 */ #define ENET_TS_EN (1 << 23) #define RMII_CLK_EN (1 << 24) #define SAI3_EN (1 << 19) /* CCM_CSCDR2 */ #define ESAI_EN (1 << 30) #define ESDHC1_EN (1 << 29) #define ESDHC0_EN (1 << 28) #define NFC_EN (1 << 9) #define ESDHC1_DIV_S 20 #define ESDHC1_DIV_M 0xf #define ESDHC0_DIV_S 16 #define ESDHC0_DIV_M 0xf /* CCM_CSCDR3 */ #define DCU0_EN (1 << 19) #define QSPI1_EN (1 << 12) #define QSPI1_DIV (1 << 11) #define QSPI1_X2_DIV (1 << 10) #define QSPI1_X4_DIV_M 0x3 #define QSPI1_X4_DIV_S 8 #define QSPI0_EN (1 << 4) #define QSPI0_DIV (1 << 3) #define QSPI0_X2_DIV (1 << 2) #define QSPI0_X4_DIV_M 0x3 #define QSPI0_X4_DIV_S 0 #define SAI3_DIV_SHIFT 12 #define SAI3_DIV_MASK 0xf #define ESAI_DIV_SHIFT 24 #define ESAI_DIV_MASK 0xf #define PLL4_CLK_DIV_SHIFT 6 #define PLL4_CLK_DIV_MASK 0x7 #define IPG_CLK_DIV_SHIFT 11 #define IPG_CLK_DIV_MASK 0x3 #define ESAI_CLK_SEL_SHIFT 20 #define ESAI_CLK_SEL_MASK 0x3 #define SAI3_CLK_SEL_SHIFT 6 #define SAI3_CLK_SEL_MASK 0x3 #define CKO1_EN (1 << 10) #define CKO1_DIV_MASK 0xf #define CKO1_DIV_SHIFT 6 #define CKO1_SEL_MASK 0x3f #define CKO1_SEL_SHIFT 0 #define CKO1_PLL4_MAIN 0x6 #define CKO1_PLL4_DIVD 0x7 struct clk { uint32_t reg; uint32_t enable_reg; uint32_t div_mask; uint32_t div_shift; uint32_t div_val; uint32_t sel_reg; uint32_t sel_mask; uint32_t sel_shift; uint32_t sel_val; }; static struct clk ipg_clk = { .reg = CCM_CACRR, .enable_reg = 0, .div_mask = IPG_CLK_DIV_MASK, .div_shift = IPG_CLK_DIV_SHIFT, .div_val = 1, /* Divide by 2 */ .sel_reg = 0, .sel_mask = 0, .sel_shift = 0, .sel_val = 0, }; /* PLL4 clock divider (before switching the clocks should be gated) 000 Divide by 1 (only if PLL frequency less than or equal to 650 MHz) 001 Divide by 4 010 Divide by 6 011 Divide by 8 100 Divide by 10 101 Divide by 12 110 Divide by 14 111 Divide by 16 */ static struct clk pll4_clk = { .reg = CCM_CACRR, .enable_reg = 0, .div_mask = PLL4_CLK_DIV_MASK, .div_shift = PLL4_CLK_DIV_SHIFT, .div_val = 5, /* Divide by 12 */ .sel_reg = 0, .sel_mask = 0, .sel_shift = 0, .sel_val = 0, }; static struct clk sai3_clk = { .reg = CCM_CSCDR1, .enable_reg = SAI3_EN, .div_mask = SAI3_DIV_MASK, .div_shift = SAI3_DIV_SHIFT, .div_val = 1, .sel_reg = CCM_CSCMR1, .sel_mask = SAI3_CLK_SEL_MASK, .sel_shift = SAI3_CLK_SEL_SHIFT, .sel_val = 0x3, /* Divided PLL4 main clock */ }; static struct clk cko1_clk = { .reg = CCM_CCOSR, .enable_reg = CKO1_EN, .div_mask = CKO1_DIV_MASK, .div_shift = CKO1_DIV_SHIFT, .div_val = 1, .sel_reg = CCM_CCOSR, .sel_mask = CKO1_SEL_MASK, .sel_shift = CKO1_SEL_SHIFT, .sel_val = CKO1_PLL4_DIVD, }; static struct clk esdhc0_clk = { .reg = CCM_CSCDR2, .enable_reg = ESDHC0_EN, .div_mask = ESDHC0_DIV_M, .div_shift = ESDHC0_DIV_S, .div_val = 0x9, .sel_reg = 0, .sel_mask = 0, .sel_shift = 0, .sel_val = 0, }; static struct clk esdhc1_clk = { .reg = CCM_CSCDR2, .enable_reg = ESDHC1_EN, .div_mask = ESDHC1_DIV_M, .div_shift = ESDHC1_DIV_S, .div_val = 0x9, .sel_reg = 0, .sel_mask = 0, .sel_shift = 0, .sel_val = 0, }; static struct clk qspi0_clk = { .reg = CCM_CSCDR3, .enable_reg = QSPI0_EN, .div_mask = 0, .div_shift = 0, .div_val = 0, .sel_reg = 0, .sel_mask = 0, .sel_shift = 0, .sel_val = 0, }; static struct clk dcu0_clk = { .reg = CCM_CSCDR3, .enable_reg = DCU0_EN, .div_mask = 0x7, .div_shift = 16, /* DCU0_DIV */ .div_val = 0, /* divide by 1 */ .sel_reg = 0, .sel_mask = 0, .sel_shift = 0, .sel_val = 0, }; static struct clk enet_clk = { .reg = CCM_CSCDR1, .enable_reg = (ENET_TS_EN | RMII_CLK_EN), .div_mask = 0, .div_shift = 0, .div_val = 0, .sel_reg = 0, .sel_mask = 0, .sel_shift = 0, .sel_val = 0, }; static struct clk nand_clk = { .reg = CCM_CSCDR2, .enable_reg = NFC_EN, .div_mask = 0, .div_shift = 0, .div_val = 0, .sel_reg = 0, .sel_mask = 0, .sel_shift = 0, .sel_val = 0, }; /* Divider to generate ESAI clock 0000 Divide by 1 0001 Divide by 2 ... ... 1111 Divide by 16 */ static struct clk esai_clk = { .reg = CCM_CSCDR2, .enable_reg = ESAI_EN, .div_mask = ESAI_DIV_MASK, .div_shift = ESAI_DIV_SHIFT, .div_val = 3, /* Divide by 4 */ .sel_reg = CCM_CSCMR1, .sel_mask = ESAI_CLK_SEL_MASK, .sel_shift = ESAI_CLK_SEL_SHIFT, .sel_val = 0x3, /* Divided PLL4 main clock */ }; struct clock_entry { char *name; struct clk *clk; }; static struct clock_entry clock_map[] = { {"ipg", &ipg_clk}, {"pll4", &pll4_clk}, {"sai3", &sai3_clk}, {"cko1", &cko1_clk}, {"esdhc0", &esdhc0_clk}, {"esdhc1", &esdhc1_clk}, {"qspi0", &qspi0_clk}, {"dcu0", &dcu0_clk}, {"enet", &enet_clk}, {"nand", &nand_clk}, {"esai", &esai_clk}, {NULL, NULL} }; struct ccm_softc { struct resource *res[1]; bus_space_tag_t bst; bus_space_handle_t bsh; device_t dev; }; static struct resource_spec ccm_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static int ccm_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-ccm")) return (ENXIO); device_set_desc(dev, "Vybrid Family CCM Unit"); return (BUS_PROBE_DEFAULT); } static int set_clock(struct ccm_softc *sc, char *name) { struct clk *clk; int reg; int i; for (i = 0; clock_map[i].name != NULL; i++) { if (strcmp(clock_map[i].name, name) == 0) { #if 0 device_printf(sc->dev, "Configuring %s clk\n", name); #endif clk = clock_map[i].clk; if (clk->sel_reg != 0) { reg = READ4(sc, clk->sel_reg); reg &= ~(clk->sel_mask << clk->sel_shift); reg |= (clk->sel_val << clk->sel_shift); WRITE4(sc, clk->sel_reg, reg); }; reg = READ4(sc, clk->reg); reg |= clk->enable_reg; reg &= ~(clk->div_mask << clk->div_shift); reg |= (clk->div_val << clk->div_shift); WRITE4(sc, clk->reg, reg); }; }; return (0); } static int ccm_fdt_set(struct ccm_softc *sc) { phandle_t child, parent, root; int len; char *fdt_config, *name; root = OF_finddevice("/"); len = 0; parent = root; /* Find 'clock_names' prop in the tree */ for (child = OF_child(parent); child != 0; child = OF_peer(child)) { /* Find a 'leaf'. Start the search from this node. */ while (OF_child(child)) { parent = child; child = OF_child(child); } if (!fdt_is_enabled(child)) continue; if ((len = OF_getproplen(child, "clock_names")) > 0) { len = OF_getproplen(child, "clock_names"); OF_getprop_alloc(child, "clock_names", 1, (void **)&fdt_config); while (len > 0) { name = fdt_config; fdt_config += strlen(name) + 1; len -= strlen(name) + 1; set_clock(sc, name); }; }; if (OF_peer(child) == 0) { /* No more siblings. */ child = parent; parent = OF_parent(child); } } return (0); } static int ccm_attach(device_t dev) { struct ccm_softc *sc; int reg; int i; sc = device_get_softc(dev); sc->dev = dev; if (bus_alloc_resources(dev, ccm_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); /* Enable oscillator */ reg = READ4(sc, CCM_CCR); reg |= (FIRC_EN | FXOSC_EN); WRITE4(sc, CCM_CCR, reg); /* Wait 10 times */ for (i = 0; i < 10; i++) { if (READ4(sc, CCM_CSR) & FXOSC_RDY) { device_printf(sc->dev, "On board oscillator is ready.\n"); break; } cpufunc_nullop(); } /* Clock is on during all modes, except stop mode. */ for (i = 0; i < CCM_CCGRN; i++) { WRITE4(sc, CCM_CCGR(i), 0xffffffff); } /* Take and apply FDT clocks */ ccm_fdt_set(sc); return (0); } static device_method_t ccm_methods[] = { DEVMETHOD(device_probe, ccm_probe), DEVMETHOD(device_attach, ccm_attach), { 0, 0 } }; static driver_t ccm_driver = { "ccm", ccm_methods, sizeof(struct ccm_softc), }; static devclass_t ccm_devclass; DRIVER_MODULE(ccm, simplebus, ccm_driver, ccm_devclass, 0, 0); Index: head/sys/arm/freescale/vybrid/vf_dmamux.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_dmamux.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_dmamux.c (revision 281085) @@ -1,155 +1,154 @@ /*- * Copyright (c) 2014 Ruslan Bukin * 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. */ /* * Vybrid Family Direct Memory Access Multiplexer (DMAMUX) * Chapter 22, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #define DMAMUX_CHCFG(n) (0x1 * n) /* Channels 0-15 Cfg Reg */ #define CHCFG_ENBL (1 << 7) /* Channel Enable */ #define CHCFG_TRIG (1 << 6) /* Channel Trigger Enable */ #define CHCFG_SOURCE_MASK 0x3f /* Channel Source (Slot) */ #define CHCFG_SOURCE_SHIFT 0 struct dmamux_softc { struct resource *res[4]; bus_space_tag_t bst[4]; bus_space_handle_t bsh[4]; }; struct dmamux_softc *dmamux_sc; static struct resource_spec dmamux_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* DMAMUX0 */ { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* DMAMUX1 */ { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* DMAMUX2 */ { SYS_RES_MEMORY, 3, RF_ACTIVE }, /* DMAMUX3 */ { -1, 0 } }; static int dmamux_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-dmamux")) return (ENXIO); device_set_desc(dev, "Vybrid Family Direct Memory Access Multiplexer"); return (BUS_PROBE_DEFAULT); } int dmamux_configure(int mux, int source, int channel, int enable) { struct dmamux_softc *sc; int reg; sc = dmamux_sc; MUX_WRITE1(sc, mux, DMAMUX_CHCFG(channel), 0x0); reg = 0; if (enable) reg |= (CHCFG_ENBL); reg &= ~(CHCFG_SOURCE_MASK << CHCFG_SOURCE_SHIFT); reg |= (source << CHCFG_SOURCE_SHIFT); MUX_WRITE1(sc, mux, DMAMUX_CHCFG(channel), reg); return (0); } static int dmamux_attach(device_t dev) { struct dmamux_softc *sc; int i; sc = device_get_softc(dev); if (bus_alloc_resources(dev, dmamux_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ for (i = 0; i < 4; i++) { sc->bst[i] = rman_get_bustag(sc->res[i]); sc->bsh[i] = rman_get_bushandle(sc->res[i]); } dmamux_sc = sc; return (0); } static device_method_t dmamux_methods[] = { DEVMETHOD(device_probe, dmamux_probe), DEVMETHOD(device_attach, dmamux_attach), { 0, 0 } }; static driver_t dmamux_driver = { "dmamux", dmamux_methods, sizeof(struct dmamux_softc), }; static devclass_t dmamux_devclass; DRIVER_MODULE(dmamux, simplebus, dmamux_driver, dmamux_devclass, 0, 0); Index: head/sys/arm/freescale/vybrid/vf_edma.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_edma.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_edma.c (revision 281085) @@ -1,338 +1,337 @@ /*- * Copyright (c) 2014 Ruslan Bukin * 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. */ /* * Vybrid Family Enhanced Direct Memory Access Controller (eDMA) * Chapter 21, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include struct edma_channel { uint32_t enabled; uint32_t mux_num; uint32_t mux_src; uint32_t mux_chn; uint32_t (*ih) (void *, int); void *ih_user; }; static struct edma_channel edma_map[EDMA_NUM_CHANNELS]; static struct resource_spec edma_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* TCD */ { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Transfer complete */ { SYS_RES_IRQ, 1, RF_ACTIVE }, /* Error Interrupt */ { -1, 0 } }; static int edma_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-edma")) return (ENXIO); device_set_desc(dev, "Vybrid Family eDMA Controller"); return (BUS_PROBE_DEFAULT); } static void edma_transfer_complete_intr(void *arg) { struct edma_channel *ch; struct edma_softc *sc; int interrupts; int i; sc = arg; interrupts = READ4(sc, DMA_INT); WRITE1(sc, DMA_CINT, CINT_CAIR); for (i = 0; i < EDMA_NUM_CHANNELS; i++) { if (interrupts & (0x1 << i)) { ch = &edma_map[i]; if (ch->enabled == 1) { if (ch->ih != NULL) { ch->ih(ch->ih_user, i); } } } } } static void edma_err_intr(void *arg) { struct edma_softc *sc; int reg; sc = arg; reg = READ4(sc, DMA_ERR); #if 0 device_printf(sc->dev, "DMA_ERR 0x%08x, ES 0x%08x\n", reg, READ4(sc, DMA_ES)); #endif WRITE1(sc, DMA_CERR, CERR_CAEI); } static int channel_free(struct edma_softc *sc, int chnum) { struct edma_channel *ch; ch = &edma_map[chnum]; ch->enabled = 0; dmamux_configure(ch->mux_num, ch->mux_src, ch->mux_chn, 0); return (0); } static int channel_configure(struct edma_softc *sc, int mux_grp, int mux_src) { struct edma_channel *ch; int channel_first; int mux_num; int chnum; int i; if ((sc->device_id == 0 && mux_grp == 1) || \ (sc->device_id == 1 && mux_grp == 0)) { channel_first = NCHAN_PER_MUX; mux_num = (sc->device_id * 2) + 1; } else { channel_first = 0; mux_num = sc->device_id * 2; }; /* Take first unused eDMA channel */ ch = NULL; for (i = channel_first; i < (channel_first + NCHAN_PER_MUX); i++) { ch = &edma_map[i]; if (ch->enabled == 0) { break; } ch = NULL; }; if (ch == NULL) { /* Can't find free channel */ return (-1); }; chnum = i; ch->enabled = 1; ch->mux_num = mux_num; ch->mux_src = mux_src; ch->mux_chn = (chnum - channel_first); /* 0 to 15 */ dmamux_configure(ch->mux_num, ch->mux_src, ch->mux_chn, 1); return (chnum); } static int dma_stop(struct edma_softc *sc, int chnum) { int reg; reg = READ4(sc, DMA_ERQ); reg &= ~(0x1 << chnum); WRITE4(sc, DMA_ERQ, reg); return (0); } static int dma_setup(struct edma_softc *sc, struct tcd_conf *tcd) { struct edma_channel *ch; int chnum; int reg; chnum = tcd->channel; ch = &edma_map[chnum]; ch->ih = tcd->ih; ch->ih_user = tcd->ih_user; TCD_WRITE4(sc, DMA_TCDn_SADDR(chnum), tcd->saddr); TCD_WRITE4(sc, DMA_TCDn_DADDR(chnum), tcd->daddr); reg = (tcd->smod << TCD_ATTR_SMOD_SHIFT); reg |= (tcd->dmod << TCD_ATTR_DMOD_SHIFT); reg |= (tcd->ssize << TCD_ATTR_SSIZE_SHIFT); reg |= (tcd->dsize << TCD_ATTR_DSIZE_SHIFT); TCD_WRITE2(sc, DMA_TCDn_ATTR(chnum), reg); TCD_WRITE2(sc, DMA_TCDn_SOFF(chnum), tcd->soff); TCD_WRITE2(sc, DMA_TCDn_DOFF(chnum), tcd->doff); TCD_WRITE4(sc, DMA_TCDn_SLAST(chnum), tcd->slast); TCD_WRITE4(sc, DMA_TCDn_DLASTSGA(chnum), tcd->dlast_sga); TCD_WRITE4(sc, DMA_TCDn_NBYTES_MLOFFYES(chnum), tcd->nbytes); reg = tcd->nmajor; /* Current Major Iteration Count */ TCD_WRITE2(sc, DMA_TCDn_CITER_ELINKNO(chnum), reg); TCD_WRITE2(sc, DMA_TCDn_BITER_ELINKNO(chnum), reg); reg = (TCD_CSR_INTMAJOR); if(tcd->majorelink == 1) { reg |= TCD_CSR_MAJORELINK; reg |= (tcd->majorelinkch << TCD_CSR_MAJORELINKCH_SHIFT); } TCD_WRITE2(sc, DMA_TCDn_CSR(chnum), reg); /* Enable requests */ reg = READ4(sc, DMA_ERQ); reg |= (0x1 << chnum); WRITE4(sc, DMA_ERQ, reg); /* Enable error interrupts */ reg = READ4(sc, DMA_EEI); reg |= (0x1 << chnum); WRITE4(sc, DMA_EEI, reg); return (0); } static int dma_request(struct edma_softc *sc, int chnum) { int reg; /* Start */ reg = TCD_READ2(sc, DMA_TCDn_CSR(chnum)); reg |= TCD_CSR_START; TCD_WRITE2(sc, DMA_TCDn_CSR(chnum), reg); return (0); } static int edma_attach(device_t dev) { struct edma_softc *sc; phandle_t node; int dts_value; int len; sc = device_get_softc(dev); sc->dev = dev; if ((node = ofw_bus_get_node(sc->dev)) == -1) return (ENXIO); if ((len = OF_getproplen(node, "device-id")) <= 0) return (ENXIO); OF_getprop(node, "device-id", &dts_value, len); sc->device_id = fdt32_to_cpu(dts_value); sc->dma_stop = dma_stop; sc->dma_setup = dma_setup; sc->dma_request = dma_request; sc->channel_configure = channel_configure; sc->channel_free = channel_free; if (bus_alloc_resources(dev, edma_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); sc->bst_tcd = rman_get_bustag(sc->res[1]); sc->bsh_tcd = rman_get_bushandle(sc->res[1]); /* Setup interrupt handlers */ if (bus_setup_intr(dev, sc->res[2], INTR_TYPE_BIO | INTR_MPSAFE, NULL, edma_transfer_complete_intr, sc, &sc->tc_ih)) { device_printf(dev, "Unable to alloc DMA intr resource.\n"); return (ENXIO); } if (bus_setup_intr(dev, sc->res[3], INTR_TYPE_BIO | INTR_MPSAFE, NULL, edma_err_intr, sc, &sc->err_ih)) { device_printf(dev, "Unable to alloc DMA Err intr resource.\n"); return (ENXIO); } return (0); } static device_method_t edma_methods[] = { DEVMETHOD(device_probe, edma_probe), DEVMETHOD(device_attach, edma_attach), { 0, 0 } }; static driver_t edma_driver = { "edma", edma_methods, sizeof(struct edma_softc), }; static devclass_t edma_devclass; DRIVER_MODULE(edma, simplebus, edma_driver, edma_devclass, 0, 0); Index: head/sys/arm/freescale/vybrid/vf_gpio.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_gpio.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_gpio.c (revision 281085) @@ -1,388 +1,387 @@ /*- * Copyright (c) 2013 Ruslan Bukin * 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. */ /* * Vybrid Family General-Purpose Input/Output (GPIO) * Chapter 7, Vybrid Reference Manual, Rev. 5, 07/2013 */ #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 "gpio_if.h" #include #include #define GPIO_PDOR(n) (0x00 + 0x40 * (n >> 5)) #define GPIO_PSOR(n) (0x04 + 0x40 * (n >> 5)) #define GPIO_PCOR(n) (0x08 + 0x40 * (n >> 5)) #define GPIO_PTOR(n) (0x0C + 0x40 * (n >> 5)) #define GPIO_PDIR(n) (0x10 + 0x40 * (n >> 5)) #define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) /* * GPIO interface */ static device_t vf_gpio_get_bus(device_t); static int vf_gpio_pin_max(device_t, int *); static int vf_gpio_pin_getcaps(device_t, uint32_t, uint32_t *); static int vf_gpio_pin_getname(device_t, uint32_t, char *); static int vf_gpio_pin_getflags(device_t, uint32_t, uint32_t *); static int vf_gpio_pin_setflags(device_t, uint32_t, uint32_t); static int vf_gpio_pin_set(device_t, uint32_t, unsigned int); static int vf_gpio_pin_get(device_t, uint32_t, unsigned int *); static int vf_gpio_pin_toggle(device_t, uint32_t pin); struct vf_gpio_softc { struct resource *res[1]; bus_space_tag_t bst; bus_space_handle_t bsh; device_t sc_busdev; struct mtx sc_mtx; int gpio_npins; struct gpio_pin gpio_pins[NGPIO]; }; struct vf_gpio_softc *gpio_sc; static struct resource_spec vf_gpio_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static int vf_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-gpio")) return (ENXIO); device_set_desc(dev, "Vybrid Family GPIO Unit"); return (BUS_PROBE_DEFAULT); } static int vf_gpio_attach(device_t dev) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); if (bus_alloc_resources(dev, vf_gpio_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); mtx_destroy(&sc->sc_mtx); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); gpio_sc = sc; sc->gpio_npins = NGPIO; for (i = 0; i < sc->gpio_npins; i++) { sc->gpio_pins[i].gp_pin = i; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; sc->gpio_pins[i].gp_flags = (READ4(sc, GPIO_PDOR(i)) & (1 << (i % 32))) ? GPIO_PIN_OUTPUT: GPIO_PIN_INPUT; snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "vf_gpio%d.%d", device_get_unit(dev), i); } sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) { bus_release_resources(dev, vf_gpio_spec, sc->res); mtx_destroy(&sc->sc_mtx); return (ENXIO); } return (0); } static device_t vf_gpio_get_bus(device_t dev) { struct vf_gpio_softc *sc; sc = device_get_softc(dev); return (sc->sc_busdev); } static int vf_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = NGPIO - 1; return (0); } static int vf_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); GPIO_UNLOCK(sc); return (0); } static int vf_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *caps = sc->gpio_pins[i].gp_caps; GPIO_UNLOCK(sc); return (0); } static int vf_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *flags = sc->gpio_pins[i].gp_flags; GPIO_UNLOCK(sc); return (0); } static int vf_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *val = (READ4(sc, GPIO_PDIR(i)) & (1 << (i % 32))) ? 1 : 0; GPIO_UNLOCK(sc); return (0); } static int vf_gpio_pin_toggle(device_t dev, uint32_t pin) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); WRITE4(sc, GPIO_PTOR(i), (1 << (i % 32))); GPIO_UNLOCK(sc); return (0); } static void vf_gpio_pin_configure(struct vf_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { 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; } else { pin->gp_flags |= GPIO_PIN_INPUT; WRITE4(sc, GPIO_PCOR(pin->gp_pin), (1 << (pin->gp_pin % 32))); } } GPIO_UNLOCK(sc); } static int vf_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); vf_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); return (0); } static int vf_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); if (value) WRITE4(sc, GPIO_PSOR(i), (1 << (i % 32))); else WRITE4(sc, GPIO_PCOR(i), (1 << (i % 32))); GPIO_UNLOCK(sc); return (0); } static device_method_t vf_gpio_methods[] = { DEVMETHOD(device_probe, vf_gpio_probe), DEVMETHOD(device_attach, vf_gpio_attach), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, vf_gpio_get_bus), DEVMETHOD(gpio_pin_max, vf_gpio_pin_max), DEVMETHOD(gpio_pin_getname, vf_gpio_pin_getname), DEVMETHOD(gpio_pin_getcaps, vf_gpio_pin_getcaps), DEVMETHOD(gpio_pin_getflags, vf_gpio_pin_getflags), DEVMETHOD(gpio_pin_get, vf_gpio_pin_get), DEVMETHOD(gpio_pin_toggle, vf_gpio_pin_toggle), DEVMETHOD(gpio_pin_setflags, vf_gpio_pin_setflags), DEVMETHOD(gpio_pin_set, vf_gpio_pin_set), { 0, 0 } }; static driver_t vf_gpio_driver = { "gpio", vf_gpio_methods, sizeof(struct vf_gpio_softc), }; static devclass_t vf_gpio_devclass; DRIVER_MODULE(vf_gpio, simplebus, vf_gpio_driver, vf_gpio_devclass, 0, 0); Index: head/sys/arm/freescale/vybrid/vf_i2c.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_i2c.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_i2c.c (revision 281085) @@ -1,471 +1,470 @@ /*- * Copyright (c) 2014 Ruslan Bukin * 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. */ /* * Vybrid Family Inter-Integrated Circuit (I2C) * Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013 */ /* * This driver is based on the I2C driver for i.MX */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" #include #include #include #include #include -#include #include #include #include #define I2C_IBAD 0x0 /* I2C Bus Address Register */ #define I2C_IBFD 0x1 /* I2C Bus Frequency Divider Register */ #define I2C_IBCR 0x2 /* I2C Bus Control Register */ #define IBCR_MDIS (1 << 7) /* Module disable. */ #define IBCR_IBIE (1 << 6) /* I-Bus Interrupt Enable. */ #define IBCR_MSSL (1 << 5) /* Master/Slave mode select. */ #define IBCR_TXRX (1 << 4) /* Transmit/Receive mode select. */ #define IBCR_NOACK (1 << 3) /* Data Acknowledge disable. */ #define IBCR_RSTA (1 << 2) /* Repeat Start. */ #define IBCR_DMAEN (1 << 1) /* DMA Enable. */ #define I2C_IBSR 0x3 /* I2C Bus Status Register */ #define IBSR_TCF (1 << 7) /* Transfer complete. */ #define IBSR_IAAS (1 << 6) /* Addressed as a slave. */ #define IBSR_IBB (1 << 5) /* Bus busy. */ #define IBSR_IBAL (1 << 4) /* Arbitration Lost. */ #define IBSR_SRW (1 << 2) /* Slave Read/Write. */ #define IBSR_IBIF (1 << 1) /* I-Bus Interrupt Flag. */ #define IBSR_RXAK (1 << 0) /* Received Acknowledge. */ #define I2C_IBDR 0x4 /* I2C Bus Data I/O Register */ #define I2C_IBIC 0x5 /* I2C Bus Interrupt Config Register */ #define IBIC_BIIE (1 << 7) /* Bus Idle Interrupt Enable bit. */ #define I2C_IBDBG 0x6 /* I2C Bus Debug Register */ #ifdef DEBUG #define vf_i2c_dbg(_sc, fmt, args...) \ device_printf((_sc)->dev, fmt, ##args) #else #define vf_i2c_dbg(_sc, fmt, args...) #endif static int i2c_repeated_start(device_t, u_char, int); static int i2c_start(device_t, u_char, int); static int i2c_stop(device_t); static int i2c_reset(device_t, u_char, u_char, u_char *); static int i2c_read(device_t, char *, int, int *, int, int); static int i2c_write(device_t, const char *, int, int *, int); struct i2c_softc { struct resource *res[2]; bus_space_tag_t bst; bus_space_handle_t bsh; device_t dev; device_t iicbus; struct mtx mutex; }; static struct resource_spec i2c_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static int i2c_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-i2c")) return (ENXIO); device_set_desc(dev, "Vybrid Family Inter-Integrated Circuit (I2C)"); return (BUS_PROBE_DEFAULT); } static int i2c_attach(device_t dev) { struct i2c_softc *sc; sc = device_get_softc(dev); sc->dev = dev; mtx_init(&sc->mutex, device_get_nameunit(dev), "I2C", MTX_DEF); if (bus_alloc_resources(dev, i2c_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); WRITE1(sc, I2C_IBIC, IBIC_BIIE); sc->iicbus = device_add_child(dev, "iicbus", -1); if (sc->iicbus == NULL) { device_printf(dev, "could not add iicbus child"); mtx_destroy(&sc->mutex); return (ENXIO); } bus_generic_attach(dev); return (0); } /* Wait for transfer interrupt flag */ static int wait_for_iif(struct i2c_softc *sc) { int retry; retry = 1000; while (retry --) { if (READ1(sc, I2C_IBSR) & IBSR_IBIF) { WRITE1(sc, I2C_IBSR, IBSR_IBIF); return (IIC_NOERR); } DELAY(10); } return (IIC_ETIMEOUT); } /* Wait for free bus */ static int wait_for_nibb(struct i2c_softc *sc) { int retry; retry = 1000; while (retry --) { if ((READ1(sc, I2C_IBSR) & IBSR_IBB) == 0) return (IIC_NOERR); DELAY(10); } return (IIC_ETIMEOUT); } /* Wait for transfer complete+interrupt flag */ static int wait_for_icf(struct i2c_softc *sc) { int retry; retry = 1000; while (retry --) { if (READ1(sc, I2C_IBSR) & IBSR_TCF) { if (READ1(sc, I2C_IBSR) & IBSR_IBIF) { WRITE1(sc, I2C_IBSR, IBSR_IBIF); return (IIC_NOERR); } } DELAY(10); } return (IIC_ETIMEOUT); } static int i2c_repeated_start(device_t dev, u_char slave, int timeout) { struct i2c_softc *sc; int error; int reg; sc = device_get_softc(dev); vf_i2c_dbg(sc, "i2c repeated start\n"); mtx_lock(&sc->mutex); WRITE1(sc, I2C_IBAD, slave); if ((READ1(sc, I2C_IBSR) & IBSR_IBB) == 0) { mtx_unlock(&sc->mutex); return (IIC_EBUSBSY); } /* Set repeated start condition */ DELAY(10); reg = READ1(sc, I2C_IBCR); reg |= (IBCR_RSTA | IBCR_IBIE); WRITE1(sc, I2C_IBCR, reg); DELAY(10); /* Write target address - LSB is R/W bit */ WRITE1(sc, I2C_IBDR, slave); error = wait_for_iif(sc); mtx_unlock(&sc->mutex); if (error) return (error); return (IIC_NOERR); } static int i2c_start(device_t dev, u_char slave, int timeout) { struct i2c_softc *sc; int error; int reg; sc = device_get_softc(dev); vf_i2c_dbg(sc, "i2c start\n"); mtx_lock(&sc->mutex); WRITE1(sc, I2C_IBAD, slave); if (READ1(sc, I2C_IBSR) & IBSR_IBB) { mtx_unlock(&sc->mutex); vf_i2c_dbg(sc, "cant i2c start: IIC_EBUSBSY\n"); return (IIC_EBUSBSY); } /* Set start condition */ reg = (IBCR_MSSL | IBCR_NOACK | IBCR_IBIE); WRITE1(sc, I2C_IBCR, reg); DELAY(100); reg |= (IBCR_TXRX); WRITE1(sc, I2C_IBCR, reg); /* Write target address - LSB is R/W bit */ WRITE1(sc, I2C_IBDR, slave); error = wait_for_iif(sc); mtx_unlock(&sc->mutex); if (error) { vf_i2c_dbg(sc, "cant i2c start: iif error\n"); return (error); } return (IIC_NOERR); } static int i2c_stop(device_t dev) { struct i2c_softc *sc; sc = device_get_softc(dev); vf_i2c_dbg(sc, "i2c stop\n"); mtx_lock(&sc->mutex); WRITE1(sc, I2C_IBCR, IBCR_NOACK | IBCR_IBIE); DELAY(100); /* Reset controller if bus still busy after STOP */ if (wait_for_nibb(sc) == IIC_ETIMEOUT) { WRITE1(sc, I2C_IBCR, IBCR_MDIS); DELAY(1000); WRITE1(sc, I2C_IBCR, IBCR_NOACK); } mtx_unlock(&sc->mutex); return (IIC_NOERR); } static int i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldadr) { struct i2c_softc *sc; sc = device_get_softc(dev); vf_i2c_dbg(sc, "i2c reset\n"); switch (speed) { case IIC_FAST: case IIC_SLOW: case IIC_UNKNOWN: case IIC_FASTEST: default: break; } mtx_lock(&sc->mutex); WRITE1(sc, I2C_IBCR, IBCR_MDIS); DELAY(1000); WRITE1(sc, I2C_IBFD, 20); WRITE1(sc, I2C_IBCR, 0x0); /* Enable i2c */ DELAY(1000); mtx_unlock(&sc->mutex); return (IIC_NOERR); } static int i2c_read(device_t dev, char *buf, int len, int *read, int last, int delay) { struct i2c_softc *sc; int error; sc = device_get_softc(dev); vf_i2c_dbg(sc, "i2c read\n"); *read = 0; mtx_lock(&sc->mutex); if (len) { if (len == 1) WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL | \ IBCR_NOACK); else WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL); /* dummy read */ READ1(sc, I2C_IBDR); DELAY(1000); } while (*read < len) { error = wait_for_icf(sc); if (error) { mtx_unlock(&sc->mutex); return (error); } if ((*read == len - 2) && last) { /* NO ACK on last byte */ WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL | \ IBCR_NOACK); } if ((*read == len - 1) && last) { /* Transfer done, remove master bit */ WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_NOACK); } *buf++ = READ1(sc, I2C_IBDR); (*read)++; } mtx_unlock(&sc->mutex); return (IIC_NOERR); } static int i2c_write(device_t dev, const char *buf, int len, int *sent, int timeout) { struct i2c_softc *sc; int error; sc = device_get_softc(dev); vf_i2c_dbg(sc, "i2c write\n"); *sent = 0; mtx_lock(&sc->mutex); while (*sent < len) { WRITE1(sc, I2C_IBDR, *buf++); error = wait_for_iif(sc); if (error) { mtx_unlock(&sc->mutex); return (error); } (*sent)++; } mtx_unlock(&sc->mutex); return (IIC_NOERR); } static device_method_t i2c_methods[] = { DEVMETHOD(device_probe, i2c_probe), DEVMETHOD(device_attach, i2c_attach), DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_repeated_start, i2c_repeated_start), DEVMETHOD(iicbus_start, i2c_start), DEVMETHOD(iicbus_stop, i2c_stop), DEVMETHOD(iicbus_reset, i2c_reset), DEVMETHOD(iicbus_read, i2c_read), DEVMETHOD(iicbus_write, i2c_write), DEVMETHOD(iicbus_transfer, iicbus_transfer_gen), { 0, 0 } }; static driver_t i2c_driver = { "i2c", i2c_methods, sizeof(struct i2c_softc), }; static devclass_t i2c_devclass; DRIVER_MODULE(i2c, simplebus, i2c_driver, i2c_devclass, 0, 0); DRIVER_MODULE(iicbus, i2c, iicbus_driver, iicbus_devclass, 0, 0); Index: head/sys/arm/freescale/vybrid/vf_iomuxc.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_iomuxc.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_iomuxc.c (revision 281085) @@ -1,212 +1,211 @@ /*- * Copyright (c) 2013-2014 Ruslan Bukin * 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. */ /* * Vybrid Family Input/Output Multiplexer Controller (IOMUXC) * Chapter 5, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #define MUX_MODE_MASK 7 #define MUX_MODE_SHIFT 20 #define MUX_MODE_GPIO 0 #define MUX_MODE_VBUS_EN_OTG 2 #define IBE (1 << 0) /* Input Buffer Enable Field */ #define OBE (1 << 1) /* Output Buffer Enable Field. */ #define PUE (1 << 2) /* Pull / Keep Select Field. */ #define PKE (1 << 3) /* Pull / Keep Enable Field. */ #define HYS (1 << 9) /* Hysteresis Enable Field */ #define ODE (1 << 10) /* Open Drain Enable Field. */ #define SRE (1 << 11) /* Slew Rate Field. */ #define SPEED_SHIFT 12 #define SPEED_MASK 0x3 #define SPEED_LOW 0 /* 50 MHz */ #define SPEED_MEDIUM 0x1 /* 100 MHz */ #define SPEED_HIGH 0x3 /* 200 MHz */ #define PUS_SHIFT 4 /* Pull Up / Down Config Field Shift */ #define PUS_MASK 0x3 #define PUS_100_KOHM_PULL_DOWN 0 #define PUS_47_KOHM_PULL_UP 0x1 #define PUS_100_KOHM_PULL_UP 0x2 #define PUS_22_KOHM_PULL_UP 0x3 #define DSE_SHIFT 6 /* Drive Strength Field Shift */ #define DSE_MASK 0x7 #define DSE_DISABLED 0 /* Output driver disabled */ #define DSE_150_OHM 0x1 #define DSE_75_OHM 0x2 #define DSE_50_OHM 0x3 #define DSE_37_OHM 0x4 #define DSE_30_OHM 0x5 #define DSE_25_OHM 0x6 #define DSE_20_OHM 0x7 #define MAX_MUX_LEN 1024 struct iomuxc_softc { struct resource *tmr_res[1]; bus_space_tag_t bst; bus_space_handle_t bsh; device_t dev; }; static struct resource_spec iomuxc_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static int iomuxc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-iomuxc")) return (ENXIO); device_set_desc(dev, "Vybrid Family IOMUXC Unit"); return (BUS_PROBE_DEFAULT); } static int pinmux_set(struct iomuxc_softc *sc) { phandle_t child, parent, root; pcell_t iomux_config[MAX_MUX_LEN]; int len; int values; int pin; int pin_cfg; int i; root = OF_finddevice("/"); len = 0; parent = root; /* Find 'iomux_config' prop in the nodes */ for (child = OF_child(parent); child != 0; child = OF_peer(child)) { /* Find a 'leaf'. Start the search from this node. */ while (OF_child(child)) { parent = child; child = OF_child(child); } if (!fdt_is_enabled(child)) continue; if ((len = OF_getproplen(child, "iomux_config")) > 0) { OF_getprop(child, "iomux_config", &iomux_config, len); values = len / (sizeof(uint32_t)); for (i = 0; i < values; i += 2) { pin = fdt32_to_cpu(iomux_config[i]); pin_cfg = fdt32_to_cpu(iomux_config[i+1]); #if 0 device_printf(sc->dev, "Set pin %d to 0x%08x\n", pin, pin_cfg); #endif WRITE4(sc, IOMUXC(pin), pin_cfg); } } if (OF_peer(child) == 0) { /* No more siblings. */ child = parent; parent = OF_parent(child); } } return (0); } static int iomuxc_attach(device_t dev) { struct iomuxc_softc *sc; sc = device_get_softc(dev); sc->dev = dev; if (bus_alloc_resources(dev, iomuxc_spec, sc->tmr_res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->tmr_res[0]); sc->bsh = rman_get_bushandle(sc->tmr_res[0]); pinmux_set(sc); return (0); } static device_method_t iomuxc_methods[] = { DEVMETHOD(device_probe, iomuxc_probe), DEVMETHOD(device_attach, iomuxc_attach), { 0, 0 } }; static driver_t iomuxc_driver = { "iomuxc", iomuxc_methods, sizeof(struct iomuxc_softc), }; static devclass_t iomuxc_devclass; DRIVER_MODULE(iomuxc, simplebus, iomuxc_driver, iomuxc_devclass, 0, 0); Index: head/sys/arm/freescale/vybrid/vf_mscm.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_mscm.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_mscm.c (revision 281085) @@ -1,126 +1,125 @@ /*- * Copyright (c) 2013 Ruslan Bukin * 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. */ /* * Vybrid Family Miscellaneous System Control Module (MSCM) * Chapter 66, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #define VF_NINT 112 /* Total number of interrupts */ /* Int Router Shared Peripheral Routing Control */ #define MSCM_IRSPRC(n) (0x880 + 2 * n) struct mscm_softc { struct resource *res[1]; bus_space_tag_t bst; bus_space_handle_t bsh; }; static struct resource_spec mscm_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static int mscm_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-mscm")) return (ENXIO); device_set_desc(dev, "Vybrid Family Miscellaneous System Control Module"); return (BUS_PROBE_DEFAULT); } static int mscm_attach(device_t dev) { struct mscm_softc *sc; int i; sc = device_get_softc(dev); if (bus_alloc_resources(dev, mscm_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); /* Route all the interrupts to CP0 */ for (i = 0; i < VF_NINT; i++) WRITE2(sc, MSCM_IRSPRC(i), 1); return (0); } static device_method_t mscm_methods[] = { DEVMETHOD(device_probe, mscm_probe), DEVMETHOD(device_attach, mscm_attach), { 0, 0 } }; static driver_t mscm_driver = { "mscm", mscm_methods, sizeof(struct mscm_softc), }; static devclass_t mscm_devclass; DRIVER_MODULE(mscm, simplebus, mscm_driver, mscm_devclass, 0, 0); Index: head/sys/arm/freescale/vybrid/vf_nfc.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_nfc.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_nfc.c (revision 281085) @@ -1,527 +1,526 @@ /*- * Copyright (c) 2013 Ruslan Bukin * 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. */ /* * Vybrid Family NAND Flash Controller (NFC) * Chapter 31, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include "nfc_if.h" #include enum addr_type { ADDR_NONE, ADDR_ID, ADDR_ROW, ADDR_ROWCOL }; struct fsl_nfc_fcm { uint32_t addr_bits; enum addr_type addr_type; uint32_t col_addr_bits; uint32_t row_addr_bits; u_int read_ptr; u_int addr_ptr; u_int command; u_int code; }; struct vf_nand_softc { struct nand_softc nand_dev; bus_space_handle_t bsh; bus_space_tag_t bst; struct resource *res[2]; struct fsl_nfc_fcm fcm; }; static struct resource_spec nfc_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static int vf_nand_attach(device_t); static int vf_nand_probe(device_t); static int vf_nand_send_command(device_t, uint8_t); static int vf_nand_send_address(device_t, uint8_t); static int vf_nand_start_command(device_t); static uint8_t vf_nand_read_byte(device_t); static void vf_nand_read_buf(device_t, void *, uint32_t); static void vf_nand_write_buf(device_t, void *, uint32_t); static int vf_nand_select_cs(device_t, uint8_t); static int vf_nand_read_rnb(device_t); #define CMD_READ_PAGE 0x7EE0 #define CMD_PROG_PAGE 0x7FC0 #define CMD_PROG_PAGE_DMA 0xFFC8 #define CMD_ERASE 0x4EC0 #define CMD_READ_ID 0x4804 #define CMD_READ_STATUS 0x4068 #define CMD_RESET 0x4040 #define CMD_RANDOM_IN 0x7140 #define CMD_RANDOM_OUT 0x70E0 #define CMD_BYTE2_PROG_PAGE 0x10 #define CMD_BYTE2_PAGE_READ 0x30 #define CMD_BYTE2_ERASE 0xD0 #define NFC_CMD1 0x3F00 /* Flash command 1 */ #define NFC_CMD2 0x3F04 /* Flash command 2 */ #define NFC_CAR 0x3F08 /* Column address */ #define NFC_RAR 0x3F0C /* Row address */ #define NFC_RPT 0x3F10 /* Flash command repeat */ #define NFC_RAI 0x3F14 /* Row address increment */ #define NFC_SR1 0x3F18 /* Flash status 1 */ #define NFC_SR2 0x3F1C /* Flash status 2 */ #define NFC_DMA_CH1 0x3F20 /* DMA channel 1 address */ #define NFC_DMACFG 0x3F24 /* DMA configuration */ #define NFC_SWAP 0x3F28 /* Cach swap */ #define NFC_SECSZ 0x3F2C /* Sector size */ #define NFC_CFG 0x3F30 /* Flash configuration */ #define NFC_DMA_CH2 0x3F34 /* DMA channel 2 address */ #define NFC_ISR 0x3F38 /* Interrupt status */ #define ECCMODE_SHIFT 17 #define AIAD_SHIFT 5 #define AIBN_SHIFT 4 #define PAGECOUNT_SHIFT 0 #define BITWIDTH_SHIFT 7 #define BITWIDTH8 0 #define BITWIDTH16 1 #define PAGECOUNT_MASK 0xf #define CMD2_BYTE1_SHIFT 24 #define CMD2_CODE_SHIFT 8 #define CMD2_BUFNO_SHIFT 1 #define CMD2_START_SHIFT 0 static device_method_t vf_nand_methods[] = { DEVMETHOD(device_probe, vf_nand_probe), DEVMETHOD(device_attach, vf_nand_attach), DEVMETHOD(nfc_start_command, vf_nand_start_command), DEVMETHOD(nfc_send_command, vf_nand_send_command), DEVMETHOD(nfc_send_address, vf_nand_send_address), DEVMETHOD(nfc_read_byte, vf_nand_read_byte), DEVMETHOD(nfc_read_buf, vf_nand_read_buf), DEVMETHOD(nfc_write_buf, vf_nand_write_buf), DEVMETHOD(nfc_select_cs, vf_nand_select_cs), DEVMETHOD(nfc_read_rnb, vf_nand_read_rnb), { 0, 0 }, }; static driver_t vf_nand_driver = { "nand", vf_nand_methods, sizeof(struct vf_nand_softc), }; static devclass_t vf_nand_devclass; DRIVER_MODULE(vf_nand, simplebus, vf_nand_driver, vf_nand_devclass, 0, 0); static int vf_nand_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-nand")) return (ENXIO); device_set_desc(dev, "Vybrid Family NAND controller"); return (BUS_PROBE_DEFAULT); } static int vf_nand_attach(device_t dev) { struct vf_nand_softc *sc; int err; int reg; sc = device_get_softc(dev); if (bus_alloc_resources(dev, nfc_spec, sc->res)) { device_printf(dev, "could not allocate resources!\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); /* Size in bytes of one elementary transfer unit */ WRITE4(sc, NFC_SECSZ, 2048); /* Flash mode width */ reg = READ4(sc, NFC_CFG); reg |= (BITWIDTH16 << BITWIDTH_SHIFT); /* No correction, ECC bypass */ reg &= ~(0x7 << ECCMODE_SHIFT); /* Disable Auto-incrementing of flash row address */ reg &= ~(0x1 << AIAD_SHIFT); /* Disable Auto-incrementing of buffer numbers */ reg &= ~(0x1 << AIBN_SHIFT); /* * Number of virtual pages (in one physical flash page) * to be programmed or read, etc. */ reg &= ~(PAGECOUNT_MASK); reg |= (1 << PAGECOUNT_SHIFT); WRITE4(sc, NFC_CFG, reg); nand_init(&sc->nand_dev, dev, NAND_ECC_NONE, 0, 0, NULL, NULL); err = nandbus_create(dev); return (err); } static int vf_nand_start_command(device_t dev) { struct vf_nand_softc *sc; struct fsl_nfc_fcm *fcm; int reg; sc = device_get_softc(dev); fcm = &sc->fcm; nand_debug(NDBG_DRV,"vf_nand: start command %x", fcm->command); /* CMD2 */ reg = READ4(sc, NFC_CMD2); reg &= ~(0xff << CMD2_BYTE1_SHIFT); reg |= (fcm->command << CMD2_BYTE1_SHIFT); WRITE4(sc, NFC_CMD2, reg); /* CMD1 */ if ((fcm->command == NAND_CMD_READ) || (fcm->command == NAND_CMD_PROG) || (fcm->command == NAND_CMD_ERASE)) { reg = READ4(sc, NFC_CMD1); reg &= ~(0xff << 24); if (fcm->command == NAND_CMD_READ) reg |= (CMD_BYTE2_PAGE_READ << 24); else if (fcm->command == NAND_CMD_PROG) reg |= (CMD_BYTE2_PROG_PAGE << 24); else if (fcm->command == NAND_CMD_ERASE) reg |= (CMD_BYTE2_ERASE << 24); WRITE4(sc, NFC_CMD1, reg); } /* We work with 1st buffer */ reg = READ4(sc, NFC_CMD2); reg &= ~(0xf << CMD2_BUFNO_SHIFT); reg |= (0 << CMD2_BUFNO_SHIFT); WRITE4(sc, NFC_CMD2, reg); /* Cmd CODE */ reg = READ4(sc, NFC_CMD2); reg &= ~(0xffff << CMD2_CODE_SHIFT); reg |= (fcm->code << CMD2_CODE_SHIFT); WRITE4(sc, NFC_CMD2, reg); /* Col */ if (fcm->addr_type == ADDR_ROWCOL) { reg = READ4(sc, NFC_CAR); reg &= ~(0xffff); reg |= fcm->col_addr_bits; nand_debug(NDBG_DRV,"setting CAR to 0x%08x\n", reg); WRITE4(sc, NFC_CAR, reg); } /* Row */ reg = READ4(sc, NFC_RAR); reg &= ~(0xffffff); if (fcm->addr_type == ADDR_ID) reg |= fcm->addr_bits; else reg |= fcm->row_addr_bits; WRITE4(sc, NFC_RAR, reg); /* Start */ reg = READ4(sc, NFC_CMD2); reg |= (1 << CMD2_START_SHIFT); WRITE4(sc, NFC_CMD2, reg); /* Wait command completion */ while (READ4(sc, NFC_CMD2) & (1 << CMD2_START_SHIFT)) ; return (0); } static int vf_nand_send_command(device_t dev, uint8_t command) { struct vf_nand_softc *sc; struct fsl_nfc_fcm *fcm; nand_debug(NDBG_DRV,"vf_nand: send command %x", command); sc = device_get_softc(dev); fcm = &sc->fcm; if ((command == NAND_CMD_READ_END) || (command == NAND_CMD_PROG_END) || (command == NAND_CMD_ERASE_END)) { return (0); } fcm->command = command; fcm->code = 0; fcm->read_ptr = 0; fcm->addr_type = 0; fcm->addr_bits = 0; fcm->addr_ptr = 0; fcm->col_addr_bits = 0; fcm->row_addr_bits = 0; switch (command) { case NAND_CMD_READ: fcm->code = CMD_READ_PAGE; fcm->addr_type = ADDR_ROWCOL; break; case NAND_CMD_PROG: fcm->code = CMD_PROG_PAGE; fcm->addr_type = ADDR_ROWCOL; break; case NAND_CMD_PROG_END: break; case NAND_CMD_ERASE_END: break; case NAND_CMD_RESET: fcm->code = CMD_RESET; break; case NAND_CMD_READ_ID: fcm->code = CMD_READ_ID; fcm->addr_type = ADDR_ID; break; case NAND_CMD_READ_PARAMETER: fcm->code = CMD_READ_PAGE; fcm->addr_type = ADDR_ID; break; case NAND_CMD_STATUS: fcm->code = CMD_READ_STATUS; break; case NAND_CMD_ERASE: fcm->code = CMD_ERASE; fcm->addr_type = ADDR_ROW; break; default: nand_debug(NDBG_DRV, "unknown command %d\n", command); return (1); } return (0); } static int vf_nand_send_address(device_t dev, uint8_t addr) { struct vf_nand_softc *sc; struct fsl_nfc_fcm *fcm; nand_debug(NDBG_DRV,"vf_nand: send address %x", addr); sc = device_get_softc(dev); fcm = &sc->fcm; nand_debug(NDBG_DRV, "setting addr #%d to 0x%02x\n", fcm->addr_ptr, addr); if (fcm->addr_type == ADDR_ID) { fcm->addr_bits = addr; } else if (fcm->addr_type == ADDR_ROWCOL) { if (fcm->addr_ptr < 2) fcm->col_addr_bits |= (addr << (fcm->addr_ptr * 8)); else fcm->row_addr_bits |= (addr << ((fcm->addr_ptr - 2) * 8)); } else if (fcm->addr_type == ADDR_ROW) fcm->row_addr_bits |= (addr << (fcm->addr_ptr * 8)); fcm->addr_ptr += 1; return (0); } static uint8_t vf_nand_read_byte(device_t dev) { struct vf_nand_softc *sc; struct fsl_nfc_fcm *fcm; uint8_t data; int sr1, sr2; int b; sc = device_get_softc(dev); fcm = &sc->fcm; sr1 = READ4(sc, NFC_SR1); sr2 = READ4(sc, NFC_SR2); data = 0; if (fcm->addr_type == ADDR_ID) { b = 32 - ((fcm->read_ptr + 1) * 8); data = (sr1 >> b) & 0xff; fcm->read_ptr++; } else if (fcm->command == NAND_CMD_STATUS) { data = sr2 & 0xff; } nand_debug(NDBG_DRV,"vf_nand: read %x", data); return (data); } static void vf_nand_read_buf(device_t dev, void* buf, uint32_t len) { struct vf_nand_softc *sc; struct fsl_nfc_fcm *fcm; uint16_t *tmp; uint8_t *b; int i; b = (uint8_t*)buf; sc = device_get_softc(dev); fcm = &sc->fcm; nand_debug(NDBG_DRV, "vf_nand: read_buf len %d", len); if (fcm->command == NAND_CMD_READ_PARAMETER) { tmp = malloc(len, M_DEVBUF, M_NOWAIT); bus_read_region_2(sc->res[0], 0x0, tmp, len); for (i = 0; i < len; i += 2) { b[i] = tmp[i+1]; b[i+1] = tmp[i]; } free(tmp, M_DEVBUF); #ifdef NAND_DEBUG for (i = 0; i < len; i++) { if (!(i % 16)) printf("%s", i == 0 ? "vf_nand:\n" : "\n"); printf(" %x", b[i]); if (i == len - 1) printf("\n"); } #endif } else { for (i = 0; i < len; i++) { b[i] = READ1(sc, i); #ifdef NAND_DEBUG if (!(i % 16)) printf("%s", i == 0 ? "vf_nand:\n" : "\n"); printf(" %x", b[i]); if (i == len - 1) printf("\n"); #endif } } } static void vf_nand_write_buf(device_t dev, void* buf, uint32_t len) { struct vf_nand_softc *sc; struct fsl_nfc_fcm *fcm; uint8_t *b; int i; b = (uint8_t*)buf; sc = device_get_softc(dev); fcm = &sc->fcm; nand_debug(NDBG_DRV,"vf_nand: write_buf len %d", len); for (i = 0; i < len; i++) { WRITE1(sc, i, b[i]); #ifdef NAND_DEBUG if (!(i % 16)) printf("%s", i == 0 ? "vf_nand:\n" : "\n"); printf(" %x", b[i]); if (i == len - 1) printf("\n"); #endif } } static int vf_nand_select_cs(device_t dev, uint8_t cs) { if (cs > 0) return (ENODEV); return (0); } static int vf_nand_read_rnb(device_t dev) { /* no-op */ return (0); /* ready */ } Index: head/sys/arm/freescale/vybrid/vf_port.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_port.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_port.c (revision 281085) @@ -1,250 +1,249 @@ /*- * Copyright (c) 2014 Ruslan Bukin * 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. */ /* * Vybrid Family Port control and interrupts (PORT) * Chapter 6, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include /* Pin Control Register */ #define PORT_PCR(n) (0x1000 * (n >> 5) + 0x4 * (n % 32)) #define PCR_IRQC_S 16 #define PCR_IRQC_M 0xF #define PCR_DMA_RE 0x1 #define PCR_DMA_FE 0x2 #define PCR_DMA_EE 0x3 #define PCR_INT_LZ 0x8 #define PCR_INT_RE 0x9 #define PCR_INT_FE 0xA #define PCR_INT_EE 0xB #define PCR_INT_LO 0xC #define PCR_ISF (1 << 24) #define PORT0_ISFR 0xA0 /* Interrupt Status Flag Register */ #define PORT0_DFER 0xC0 /* Digital Filter Enable Register */ #define PORT0_DFCR 0xC4 /* Digital Filter Clock Register */ #define PORT0_DFWR 0xC8 /* Digital Filter Width Register */ struct port_event { uint32_t enabled; uint32_t mux_num; uint32_t mux_src; uint32_t mux_chn; void (*ih) (void *); void *ih_user; enum ev_type pevt; }; static struct port_event event_map[NGPIO]; struct port_softc { struct resource *res[6]; bus_space_tag_t bst; bus_space_handle_t bsh; void *gpio_ih[NGPIO]; }; struct port_softc *port_sc; static struct resource_spec port_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { SYS_RES_IRQ, 3, RF_ACTIVE }, { SYS_RES_IRQ, 4, RF_ACTIVE }, { -1, 0 } }; static int port_intr(void *arg) { struct port_event *pev; struct port_softc *sc; int reg; int i; sc = arg; for (i = 0; i < NGPIO; i++) { reg = READ4(sc, PORT_PCR(i)); if (reg & PCR_ISF) { /* Clear interrupt */ WRITE4(sc, PORT_PCR(i), reg); /* Handle event */ pev = &event_map[i]; if (pev->enabled == 1) { if (pev->ih != NULL) { pev->ih(pev->ih_user); } } } } return (FILTER_HANDLED); } int port_setup(int pnum, enum ev_type pevt, void (*ih)(void *), void *ih_user) { struct port_event *pev; struct port_softc *sc; int reg; int val; sc = port_sc; switch (pevt) { case DMA_RISING_EDGE: val = PCR_DMA_RE; break; case DMA_FALLING_EDGE: val = PCR_DMA_FE; break; case DMA_EITHER_EDGE: val = PCR_DMA_EE; break; case INT_LOGIC_ZERO: val = PCR_INT_LZ; break; case INT_RISING_EDGE: val = PCR_INT_RE; break; case INT_FALLING_EDGE: val = PCR_INT_FE; break; case INT_EITHER_EDGE: val = PCR_INT_EE; break; case INT_LOGIC_ONE: val = PCR_INT_LO; break; default: return (-1); }; reg = READ4(sc, PORT_PCR(pnum)); reg &= ~(PCR_IRQC_M << PCR_IRQC_S); reg |= (val << PCR_IRQC_S); WRITE4(sc, PORT_PCR(pnum), reg); pev = &event_map[pnum]; pev->ih = ih; pev->ih_user = ih_user; pev->pevt = pevt; pev->enabled = 1; return (0); } static int port_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-port")) return (ENXIO); device_set_desc(dev, "Vybrid Family Port control and interrupts"); return (BUS_PROBE_DEFAULT); } static int port_attach(device_t dev) { struct port_softc *sc; int irq; sc = device_get_softc(dev); if (bus_alloc_resources(dev, port_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); port_sc = sc; for (irq = 0; irq < NPORTS; irq ++) { if ((bus_setup_intr(dev, sc->res[1 + irq], INTR_TYPE_MISC, port_intr, NULL, sc, &sc->gpio_ih[irq]))) { device_printf(dev, "ERROR: Unable to register interrupt handler\n"); return (ENXIO); } } return (0); } static device_method_t port_methods[] = { DEVMETHOD(device_probe, port_probe), DEVMETHOD(device_attach, port_attach), { 0, 0 } }; static driver_t port_driver = { "port", port_methods, sizeof(struct port_softc), }; static devclass_t port_devclass; DRIVER_MODULE(port, simplebus, port_driver, port_devclass, 0, 0); Index: head/sys/arm/freescale/vybrid/vf_sai.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_sai.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_sai.c (revision 281085) @@ -1,805 +1,804 @@ /*- * Copyright (c) 2014 Ruslan Bukin * 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. */ /* * Vybrid Family Synchronous Audio Interface (SAI) * Chapter 51, Vybrid Reference Manual, Rev. 5, 07/2013 */ #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 #define I2S_TCSR 0x00 /* SAI Transmit Control */ #define I2S_TCR1 0x04 /* SAI Transmit Configuration 1 */ #define I2S_TCR2 0x08 /* SAI Transmit Configuration 2 */ #define I2S_TCR3 0x0C /* SAI Transmit Configuration 3 */ #define I2S_TCR4 0x10 /* SAI Transmit Configuration 4 */ #define I2S_TCR5 0x14 /* SAI Transmit Configuration 5 */ #define I2S_TDR0 0x20 /* SAI Transmit Data */ #define I2S_TFR0 0x40 /* SAI Transmit FIFO */ #define I2S_TMR 0x60 /* SAI Transmit Mask */ #define I2S_RCSR 0x80 /* SAI Receive Control */ #define I2S_RCR1 0x84 /* SAI Receive Configuration 1 */ #define I2S_RCR2 0x88 /* SAI Receive Configuration 2 */ #define I2S_RCR3 0x8C /* SAI Receive Configuration 3 */ #define I2S_RCR4 0x90 /* SAI Receive Configuration 4 */ #define I2S_RCR5 0x94 /* SAI Receive Configuration 5 */ #define I2S_RDR0 0xA0 /* SAI Receive Data */ #define I2S_RFR0 0xC0 /* SAI Receive FIFO */ #define I2S_RMR 0xE0 /* SAI Receive Mask */ #define TCR1_TFW_M 0x1f /* Transmit FIFO Watermark Mask */ #define TCR1_TFW_S 0 /* Transmit FIFO Watermark Shift */ #define TCR2_MSEL_M 0x3 /* MCLK Select Mask*/ #define TCR2_MSEL_S 26 /* MCLK Select Shift*/ #define TCR2_BCP (1 << 25) /* Bit Clock Polarity */ #define TCR2_BCD (1 << 24) /* Bit Clock Direction */ #define TCR3_TCE (1 << 16) /* Transmit Channel Enable */ #define TCR4_FRSZ_M 0x1f /* Frame size Mask */ #define TCR4_FRSZ_S 16 /* Frame size Shift */ #define TCR4_SYWD_M 0x1f /* Sync Width Mask */ #define TCR4_SYWD_S 8 /* Sync Width Shift */ #define TCR4_MF (1 << 4) /* MSB First */ #define TCR4_FSE (1 << 3) /* Frame Sync Early */ #define TCR4_FSP (1 << 1) /* Frame Sync Polarity Low */ #define TCR4_FSD (1 << 0) /* Frame Sync Direction Master */ #define TCR5_FBT_M 0x1f /* First Bit Shifted */ #define TCR5_FBT_S 8 /* First Bit Shifted */ #define TCR5_W0W_M 0x1f /* Word 0 Width */ #define TCR5_W0W_S 16 /* Word 0 Width */ #define TCR5_WNW_M 0x1f /* Word N Width */ #define TCR5_WNW_S 24 /* Word N Width */ #define TCSR_TE (1 << 31) /* Transmitter Enable */ #define TCSR_BCE (1 << 28) /* Bit Clock Enable */ #define TCSR_FRDE (1 << 0) /* FIFO Request DMA Enable */ #define SAI_NCHANNELS 1 static MALLOC_DEFINE(M_SAI, "sai", "sai audio"); struct sai_rate { uint32_t speed; uint32_t div; /* Bit Clock Divide. Division value is (div + 1) * 2. */ uint32_t mfi; /* PLL4 Multiplication Factor Integer */ uint32_t mfn; /* PLL4 Multiplication Factor Numerator */ uint32_t mfd; /* PLL4 Multiplication Factor Denominator */ }; /* * Bit clock divider formula * (div + 1) * 2 = MCLK/(nch * LRCLK * bits/1000000), * where: * MCLK - master clock * nch - number of channels * LRCLK - left right clock * e.g. (div + 1) * 2 = 16.9344/(2 * 44100 * 24/1000000) * * Example for 96khz, 24bit, 18.432 Mhz mclk (192fs) * { 96000, 1, 18, 40176000, 93000000 }, */ static struct sai_rate rate_map[] = { { 44100, 7, 33, 80798400, 93000000 }, /* 33.8688 Mhz */ { 96000, 3, 36, 80352000, 93000000 }, /* 36.864 Mhz */ { 192000, 1, 36, 80352000, 93000000 }, /* 36.864 Mhz */ { 0, 0 }, }; struct sc_info { struct resource *res[2]; bus_space_tag_t bst; bus_space_handle_t bsh; device_t dev; struct mtx *lock; uint32_t speed; uint32_t period; void *ih; int pos; int dma_size; bus_dma_tag_t dma_tag; bus_dmamap_t dma_map; bus_addr_t buf_base_phys; uint32_t *buf_base; struct tcd_conf *tcd; struct sai_rate *sr; struct edma_softc *edma_sc; int edma_chnum; }; /* Channel registers */ struct sc_chinfo { struct snd_dbuf *buffer; struct pcm_channel *channel; struct sc_pcminfo *parent; /* Channel information */ uint32_t dir; uint32_t format; /* Flags */ uint32_t run; }; /* PCM device private data */ struct sc_pcminfo { device_t dev; uint32_t (*ih) (struct sc_pcminfo *scp); uint32_t chnum; struct sc_chinfo chan[SAI_NCHANNELS]; struct sc_info *sc; }; static struct resource_spec sai_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static int setup_dma(struct sc_pcminfo *scp); static void setup_sai(struct sc_info *); static void sai_configure_clock(struct sc_info *); /* * Mixer interface. */ static int saimixer_init(struct snd_mixer *m) { struct sc_pcminfo *scp; struct sc_info *sc; int mask; scp = mix_getdevinfo(m); sc = scp->sc; if (sc == NULL) return -1; mask = SOUND_MASK_PCM; snd_mtxlock(sc->lock); pcm_setflags(scp->dev, pcm_getflags(scp->dev) | SD_F_SOFTPCMVOL); mix_setdevs(m, mask); snd_mtxunlock(sc->lock); return (0); } static int saimixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct sc_pcminfo *scp; scp = mix_getdevinfo(m); #if 0 device_printf(scp->dev, "saimixer_set() %d %d\n", left, right); #endif return (0); } static kobj_method_t saimixer_methods[] = { KOBJMETHOD(mixer_init, saimixer_init), KOBJMETHOD(mixer_set, saimixer_set), KOBJMETHOD_END }; MIXER_DECLARE(saimixer); /* * Channel interface. */ static void * saichan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) { struct sc_pcminfo *scp; struct sc_chinfo *ch; struct sc_info *sc; scp = (struct sc_pcminfo *)devinfo; sc = scp->sc; snd_mtxlock(sc->lock); ch = &scp->chan[0]; ch->dir = dir; ch->run = 0; ch->buffer = b; ch->channel = c; ch->parent = scp; snd_mtxunlock(sc->lock); if (sndbuf_setup(ch->buffer, sc->buf_base, sc->dma_size) != 0) { device_printf(scp->dev, "Can't setup sndbuf.\n"); return NULL; } return ch; } static int saichan_free(kobj_t obj, void *data) { struct sc_chinfo *ch = data; struct sc_pcminfo *scp = ch->parent; struct sc_info *sc = scp->sc; #if 0 device_printf(scp->dev, "saichan_free()\n"); #endif snd_mtxlock(sc->lock); /* TODO: free channel buffer */ snd_mtxunlock(sc->lock); return (0); } static int saichan_setformat(kobj_t obj, void *data, uint32_t format) { struct sc_chinfo *ch = data; ch->format = format; return (0); } static uint32_t saichan_setspeed(kobj_t obj, void *data, uint32_t speed) { struct sc_pcminfo *scp; struct sc_chinfo *ch; struct sai_rate *sr; struct sc_info *sc; int threshold; int i; ch = data; scp = ch->parent; sc = scp->sc; sr = NULL; /* First look for equal frequency. */ for (i = 0; rate_map[i].speed != 0; i++) { if (rate_map[i].speed == speed) sr = &rate_map[i]; } /* If no match, just find nearest. */ if (sr == NULL) { for (i = 0; rate_map[i].speed != 0; i++) { sr = &rate_map[i]; threshold = sr->speed + ((rate_map[i + 1].speed != 0) ? ((rate_map[i + 1].speed - sr->speed) >> 1) : 0); if (speed < threshold) break; } } sc->sr = sr; sai_configure_clock(sc); return (sr->speed); } static void sai_configure_clock(struct sc_info *sc) { struct sai_rate *sr; int reg; sr = sc->sr; /* * Manual says that TCR/RCR registers must not be * altered when TCSR[TE] is set. * We ignore it since we have problem sometimes * after re-enabling transmitter (DMA goes stall). */ reg = READ4(sc, I2S_TCR2); reg &= ~(0xff << 0); reg |= (sr->div << 0); WRITE4(sc, I2S_TCR2, reg); pll4_configure_output(sr->mfi, sr->mfn, sr->mfd); } static uint32_t saichan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) { struct sc_chinfo *ch = data; struct sc_pcminfo *scp = ch->parent; struct sc_info *sc = scp->sc; sndbuf_resize(ch->buffer, sc->dma_size / blocksize, blocksize); sc->period = sndbuf_getblksz(ch->buffer); return (sc->period); } uint32_t sai_dma_intr(void *arg, int chn); uint32_t sai_dma_intr(void *arg, int chn) { struct sc_pcminfo *scp; struct sc_chinfo *ch; struct sc_info *sc; struct tcd_conf *tcd; scp = arg; ch = &scp->chan[0]; sc = scp->sc; tcd = sc->tcd; sc->pos += (tcd->nbytes * tcd->nmajor); if (sc->pos >= sc->dma_size) sc->pos -= sc->dma_size; if (ch->run) chn_intr(ch->channel); return (0); } static int find_edma_controller(struct sc_info *sc) { struct edma_softc *edma_sc; phandle_t node, edma_node; int edma_src_transmit; int edma_mux_group; int edma_device_id; device_t edma_dev; int dts_value; int len; int i; if ((node = ofw_bus_get_node(sc->dev)) == -1) return (ENXIO); if ((len = OF_getproplen(node, "edma-controller")) <= 0) return (ENXIO); if ((len = OF_getproplen(node, "edma-src-transmit")) <= 0) return (ENXIO); if ((len = OF_getproplen(node, "edma-mux-group")) <= 0) return (ENXIO); OF_getprop(node, "edma-src-transmit", &dts_value, len); edma_src_transmit = fdt32_to_cpu(dts_value); OF_getprop(node, "edma-mux-group", &dts_value, len); edma_mux_group = fdt32_to_cpu(dts_value); OF_getprop(node, "edma-controller", &dts_value, len); edma_node = OF_node_from_xref(fdt32_to_cpu(dts_value)); if ((len = OF_getproplen(edma_node, "device-id")) <= 0) { return (ENXIO); }; OF_getprop(edma_node, "device-id", &dts_value, len); edma_device_id = fdt32_to_cpu(dts_value); edma_sc = NULL; for (i = 0; i < EDMA_NUM_DEVICES; i++) { edma_dev = devclass_get_device(devclass_find("edma"), i); if (edma_dev) { edma_sc = device_get_softc(edma_dev); if (edma_sc->device_id == edma_device_id) { /* found */ break; }; edma_sc = NULL; }; }; if (edma_sc == NULL) { device_printf(sc->dev, "no eDMA. can't operate\n"); return (ENXIO); }; sc->edma_sc = edma_sc; sc->edma_chnum = edma_sc->channel_configure(edma_sc, edma_mux_group, edma_src_transmit); if (sc->edma_chnum < 0) { /* cant setup eDMA */ return (ENXIO); }; return (0); }; static int setup_dma(struct sc_pcminfo *scp) { struct tcd_conf *tcd; struct sc_info *sc; sc = scp->sc; tcd = malloc(sizeof(struct tcd_conf), M_DEVBUF, M_WAITOK | M_ZERO); tcd->channel = sc->edma_chnum; tcd->ih = sai_dma_intr; tcd->ih_user = scp; tcd->saddr = sc->buf_base_phys; tcd->daddr = rman_get_start(sc->res[0]) + I2S_TDR0; /* * Bytes to transfer per each minor loop. * Hardware FIFO buffer size is 32x32bits. */ tcd->nbytes = 64; tcd->nmajor = 512; tcd->smod = 17; /* dma_size range */ tcd->dmod = 0; tcd->esg = 0; tcd->soff = 0x4; tcd->doff = 0; tcd->ssize = 0x2; tcd->dsize = 0x2; tcd->slast = 0; tcd->dlast_sga = 0; sc->tcd = tcd; sc->edma_sc->dma_setup(sc->edma_sc, sc->tcd); return (0); } static int saichan_trigger(kobj_t obj, void *data, int go) { struct sc_chinfo *ch = data; struct sc_pcminfo *scp = ch->parent; struct sc_info *sc = scp->sc; snd_mtxlock(sc->lock); switch (go) { case PCMTRIG_START: #if 0 device_printf(scp->dev, "trigger start\n"); #endif ch->run = 1; break; case PCMTRIG_STOP: case PCMTRIG_ABORT: #if 0 device_printf(scp->dev, "trigger stop or abort\n"); #endif ch->run = 0; break; } snd_mtxunlock(sc->lock); return (0); } static uint32_t saichan_getptr(kobj_t obj, void *data) { struct sc_pcminfo *scp; struct sc_chinfo *ch; struct sc_info *sc; ch = data; scp = ch->parent; sc = scp->sc; return (sc->pos); } static uint32_t sai_pfmt[] = { /* * eDMA doesn't allow 24-bit coping, * so we use 32. */ SND_FORMAT(AFMT_S32_LE, 2, 0), 0 }; static struct pcmchan_caps sai_pcaps = {44100, 192000, sai_pfmt, 0}; static struct pcmchan_caps * saichan_getcaps(kobj_t obj, void *data) { return (&sai_pcaps); } static kobj_method_t saichan_methods[] = { KOBJMETHOD(channel_init, saichan_init), KOBJMETHOD(channel_free, saichan_free), KOBJMETHOD(channel_setformat, saichan_setformat), KOBJMETHOD(channel_setspeed, saichan_setspeed), KOBJMETHOD(channel_setblocksize, saichan_setblocksize), KOBJMETHOD(channel_trigger, saichan_trigger), KOBJMETHOD(channel_getptr, saichan_getptr), KOBJMETHOD(channel_getcaps, saichan_getcaps), KOBJMETHOD_END }; CHANNEL_DECLARE(saichan); static int sai_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-sai")) return (ENXIO); device_set_desc(dev, "Vybrid Family Synchronous Audio Interface"); return (BUS_PROBE_DEFAULT); } static void sai_intr(void *arg) { struct sc_pcminfo *scp; struct sc_info *sc; scp = arg; sc = scp->sc; device_printf(sc->dev, "Error I2S_TCSR == 0x%08x\n", READ4(sc, I2S_TCSR)); } static void setup_sai(struct sc_info *sc) { int reg; /* * TCR/RCR registers must not be altered when TCSR[TE] is set. */ reg = READ4(sc, I2S_TCSR); reg &= ~(TCSR_BCE | TCSR_TE | TCSR_FRDE); WRITE4(sc, I2S_TCSR, reg); reg = READ4(sc, I2S_TCR3); reg &= ~(TCR3_TCE); WRITE4(sc, I2S_TCR3, reg); reg = (64 << TCR1_TFW_S); WRITE4(sc, I2S_TCR1, reg); reg = READ4(sc, I2S_TCR2); reg &= ~(TCR2_MSEL_M << TCR2_MSEL_S); reg |= (1 << TCR2_MSEL_S); reg |= (TCR2_BCP | TCR2_BCD); WRITE4(sc, I2S_TCR2, reg); sai_configure_clock(sc); reg = READ4(sc, I2S_TCR3); reg |= (TCR3_TCE); WRITE4(sc, I2S_TCR3, reg); /* Configure to 32-bit I2S mode */ reg = READ4(sc, I2S_TCR4); reg &= ~(TCR4_FRSZ_M << TCR4_FRSZ_S); reg |= (1 << TCR4_FRSZ_S); /* 2 words per frame */ reg &= ~(TCR4_SYWD_M << TCR4_SYWD_S); reg |= (23 << TCR4_SYWD_S); reg |= (TCR4_MF | TCR4_FSE | TCR4_FSP | TCR4_FSD); WRITE4(sc, I2S_TCR4, reg); reg = READ4(sc, I2S_TCR5); reg &= ~(TCR5_W0W_M << TCR5_W0W_S); reg |= (23 << TCR5_W0W_S); reg &= ~(TCR5_WNW_M << TCR5_WNW_S); reg |= (23 << TCR5_WNW_S); reg &= ~(TCR5_FBT_M << TCR5_FBT_S); reg |= (31 << TCR5_FBT_S); WRITE4(sc, I2S_TCR5, reg); /* Enable transmitter */ reg = READ4(sc, I2S_TCSR); reg |= (TCSR_BCE | TCSR_TE | TCSR_FRDE); reg |= (1 << 10); /* FEIE */ WRITE4(sc, I2S_TCSR, reg); } static void sai_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 = segs[0].ds_addr; } static int sai_attach(device_t dev) { char status[SND_STATUSLEN]; struct sc_pcminfo *scp; struct sc_info *sc; int err; sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); sc->dev = dev; sc->sr = &rate_map[0]; sc->pos = 0; sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sai softc"); if (sc->lock == NULL) { device_printf(dev, "Cant create mtx\n"); return (ENXIO); } if (bus_alloc_resources(dev, sai_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); /* eDMA */ if (find_edma_controller(sc)) { device_printf(dev, "could not find active eDMA\n"); return (ENXIO); } /* Setup PCM */ scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); scp->sc = sc; scp->dev = dev; /* DMA */ sc->dma_size = 131072; /* * Must use dma_size boundary as modulo feature required. * Modulo feature allows setup circular buffer. */ err = bus_dma_tag_create( bus_get_dma_tag(sc->dev), 4, sc->dma_size, /* 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); err = bus_dmamem_alloc(sc->dma_tag, (void **)&sc->buf_base, BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &sc->dma_map); if (err) { device_printf(dev, "cannot allocate framebuffer\n"); return (ENXIO); } err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->buf_base, sc->dma_size, sai_dmamap_cb, &sc->buf_base_phys, BUS_DMA_NOWAIT); if (err) { device_printf(dev, "cannot load DMA map\n"); return (ENXIO); } bzero(sc->buf_base, sc->dma_size); /* Setup interrupt handler */ err = bus_setup_intr(dev, sc->res[1], INTR_MPSAFE | INTR_TYPE_AV, NULL, sai_intr, scp, &sc->ih); if (err) { device_printf(dev, "Unable to alloc interrupt resource.\n"); return (ENXIO); } pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); err = pcm_register(dev, scp, 1, 0); if (err) { device_printf(dev, "Can't register pcm.\n"); return (ENXIO); } scp->chnum = 0; pcm_addchan(dev, PCMDIR_PLAY, &saichan_class, scp); scp->chnum++; snprintf(status, SND_STATUSLEN, "at simplebus"); pcm_setstatus(dev, status); mixer_init(dev, &saimixer_class, scp); setup_dma(scp); setup_sai(sc); return (0); } static device_method_t sai_pcm_methods[] = { DEVMETHOD(device_probe, sai_probe), DEVMETHOD(device_attach, sai_attach), { 0, 0 } }; static driver_t sai_pcm_driver = { "pcm", sai_pcm_methods, PCM_SOFTC_SIZE, }; DRIVER_MODULE(sai, simplebus, sai_pcm_driver, pcm_devclass, 0, 0); MODULE_DEPEND(sai, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(sai, 1); Index: head/sys/arm/freescale/vybrid/vf_spi.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_spi.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_spi.c (revision 281085) @@ -1,293 +1,292 @@ /*- * Copyright (c) 2014 Ruslan Bukin * 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. */ /* * Vybrid Family Serial Peripheral Interface (SPI) * Chapter 47, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "spibus_if.h" #include #include #include #include #include -#include #include #include #include #define SPI_FIFO_SIZE 4 #define SPI_MCR 0x00 /* Module Configuration */ #define MCR_MSTR (1 << 31) /* Master/Slave Mode Select */ #define MCR_CONT_SCKE (1 << 30) /* Continuous SCK Enable */ #define MCR_FRZ (1 << 27) /* Freeze */ #define MCR_PCSIS_S 16 /* Peripheral Chip Select */ #define MCR_PCSIS_M 0x3f #define MCR_MDIS (1 << 14) /* Module Disable */ #define MCR_CLR_TXF (1 << 11) /* Clear TX FIFO */ #define MCR_CLR_RXF (1 << 10) /* Clear RX FIFO */ #define MCR_HALT (1 << 0) /* Starts and stops SPI transfers */ #define SPI_TCR 0x08 /* Transfer Count */ #define SPI_CTAR0 0x0C /* Clock and Transfer Attributes */ #define SPI_CTAR0_SLAVE 0x0C /* Clock and Transfer Attributes */ #define SPI_CTAR1 0x10 /* Clock and Transfer Attributes */ #define SPI_CTAR2 0x14 /* Clock and Transfer Attributes */ #define SPI_CTAR3 0x18 /* Clock and Transfer Attributes */ #define CTAR_FMSZ_M 0xf #define CTAR_FMSZ_S 27 /* Frame Size */ #define CTAR_FMSZ_8 0x7 /* 8 bits */ #define CTAR_CPOL (1 << 26) /* Clock Polarity */ #define CTAR_CPHA (1 << 25) /* Clock Phase */ #define CTAR_LSBFE (1 << 24) /* Less significant bit first */ #define CTAR_PCSSCK_M 0x3 #define CTAR_PCSSCK_S 22 /* PCS to SCK Delay Prescaler */ #define CTAR_PBR_M 0x3 #define CTAR_PBR_S 16 /* Baud Rate Prescaler */ #define CTAR_PBR_7 0x3 /* Divide by 7 */ #define CTAR_CSSCK_M 0xf #define CTAR_CSSCK_S 12 /* PCS to SCK Delay Scaler */ #define CTAR_BR_M 0xf #define CTAR_BR_S 0 /* Baud Rate Scaler */ #define SPI_SR 0x2C /* Status Register */ #define SR_TCF (1 << 31) /* Transfer Complete Flag */ #define SR_EOQF (1 << 28) /* End of Queue Flag */ #define SR_TFFF (1 << 25) /* Transmit FIFO Fill Flag */ #define SR_RFDF (1 << 17) /* Receive FIFO Drain Flag */ #define SPI_RSER 0x30 /* DMA/Interrupt Select */ #define RSER_EOQF_RE (1 << 28) /* Finished Request Enable */ #define SPI_PUSHR 0x34 /* PUSH TX FIFO In Master Mode */ #define PUSHR_CONT (1 << 31) /* Continuous Peripheral CS */ #define PUSHR_EOQ (1 << 27) /* End Of Queue */ #define PUSHR_CTCNT (1 << 26) /* Clear Transfer Counter */ #define PUSHR_PCS_M 0x3f #define PUSHR_PCS_S 16 /* Select PCS signals */ #define SPI_PUSHR_SLAVE 0x34 /* PUSH TX FIFO Register In Slave Mode */ #define SPI_POPR 0x38 /* POP RX FIFO Register */ #define SPI_TXFR0 0x3C /* Transmit FIFO Registers */ #define SPI_TXFR1 0x40 #define SPI_TXFR2 0x44 #define SPI_TXFR3 0x48 #define SPI_RXFR0 0x7C /* Receive FIFO Registers */ #define SPI_RXFR1 0x80 #define SPI_RXFR2 0x84 #define SPI_RXFR3 0x88 struct spi_softc { struct resource *res[2]; bus_space_tag_t bst; bus_space_handle_t bsh; void *ih; }; static struct resource_spec spi_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static int spi_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-spi")) return (ENXIO); device_set_desc(dev, "Vybrid Family Serial Peripheral Interface"); return (BUS_PROBE_DEFAULT); } static int spi_attach(device_t dev) { struct spi_softc *sc; uint32_t reg; sc = device_get_softc(dev); if (bus_alloc_resources(dev, spi_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); reg = READ4(sc, SPI_MCR); reg |= MCR_MSTR; reg &= ~(MCR_CONT_SCKE | MCR_MDIS | MCR_FRZ); reg &= ~(MCR_PCSIS_M << MCR_PCSIS_S); reg |= (MCR_PCSIS_M << MCR_PCSIS_S); /* PCS Active low */ reg |= (MCR_CLR_TXF | MCR_CLR_RXF); WRITE4(sc, SPI_MCR, reg); reg = READ4(sc, SPI_RSER); reg |= RSER_EOQF_RE; WRITE4(sc, SPI_RSER, reg); reg = READ4(sc, SPI_MCR); reg &= ~MCR_HALT; WRITE4(sc, SPI_MCR, reg); reg = READ4(sc, SPI_CTAR0); reg &= ~(CTAR_FMSZ_M << CTAR_FMSZ_S); reg |= (CTAR_FMSZ_8 << CTAR_FMSZ_S); /* * TODO: calculate BR * SCK baud rate = ( fsys / PBR ) * (1 + DBR) / BR * * reg &= ~(CTAR_BR_M << CTAR_BR_S); */ reg &= ~CTAR_CPOL; /* Polarity */ reg |= CTAR_CPHA; /* * Set LSB (Less significant bit first) * must be used for some applications, e.g. some LCDs */ reg |= CTAR_LSBFE; WRITE4(sc, SPI_CTAR0, reg); reg = READ4(sc, SPI_CTAR0); reg &= ~(CTAR_PBR_M << CTAR_PBR_S); reg |= (CTAR_PBR_7 << CTAR_PBR_S); WRITE4(sc, SPI_CTAR0, reg); device_add_child(dev, "spibus", 0); return (bus_generic_attach(dev)); } static int spi_txrx(struct spi_softc *sc, uint8_t *out_buf, uint8_t *in_buf, int bufsz, int cs) { uint32_t reg, wreg; uint32_t txcnt; uint32_t i; txcnt = 0; for (i = 0; i < bufsz; i++) { txcnt++; wreg = out_buf[i]; wreg |= PUSHR_CONT; wreg |= (cs << PUSHR_PCS_S); if (i == 0) wreg |= PUSHR_CTCNT; if (i == (bufsz - 1) || txcnt == SPI_FIFO_SIZE) wreg |= PUSHR_EOQ; WRITE4(sc, SPI_PUSHR, wreg); if (i == (bufsz - 1) || txcnt == SPI_FIFO_SIZE) { txcnt = 0; /* Wait last entry in a queue to be transmitted */ while((READ4(sc, SPI_SR) & SR_EOQF) == 0) continue; reg = READ4(sc, SPI_SR); reg |= (SR_TCF | SR_EOQF); WRITE4(sc, SPI_SR, reg); } /* Wait until RX FIFO is empty */ while((READ4(sc, SPI_SR) & SR_RFDF) == 0) continue; in_buf[i] = READ1(sc, SPI_POPR); } return (0); } static int spi_transfer(device_t dev, device_t child, struct spi_command *cmd) { struct spi_softc *sc; uint32_t cs; sc = device_get_softc(dev); KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, ("%s: TX/RX command sizes should be equal", __func__)); KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, ("%s: TX/RX data sizes should be equal", __func__)); /* get the proper chip select */ spibus_get_cs(child, &cs); /* Command */ spi_txrx(sc, cmd->tx_cmd, cmd->rx_cmd, cmd->tx_cmd_sz, cs); /* Data */ spi_txrx(sc, cmd->tx_data, cmd->rx_data, cmd->tx_data_sz, cs); return (0); } static device_method_t spi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, spi_probe), DEVMETHOD(device_attach, spi_attach), /* SPI interface */ DEVMETHOD(spibus_transfer, spi_transfer), { 0, 0 } }; static driver_t spi_driver = { "spi", spi_methods, sizeof(struct spi_softc), }; static devclass_t spi_devclass; DRIVER_MODULE(spi, simplebus, spi_driver, spi_devclass, 0, 0); Index: head/sys/arm/freescale/vybrid/vf_src.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_src.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_src.c (revision 281085) @@ -1,150 +1,149 @@ /*- * Copyright (c) 2013 Ruslan Bukin * 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. */ /* * Vybrid Family System Reset Controller (SRC) * Chapter 18, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #define SRC_SCR 0x00 /* SRC Control Register */ #define SRC_SBMR1 0x04 /* SRC Boot Mode Register 1 */ #define SRC_SRSR 0x08 /* SRC Status Register */ #define SRC_SECR 0x0C /* SRC_SECR */ #define SRC_SICR 0x14 /* SRC Reset Interrupt Configuration Register */ #define SRC_SIMR 0x18 /* SRC Interrupt Masking Register */ #define SRC_SBMR2 0x1C /* SRC Boot Mode Register 2 */ #define SRC_GPR0 0x20 /* General Purpose Register */ #define SRC_GPR1 0x24 /* General Purpose Register */ #define SRC_GPR2 0x28 /* General Purpose Register */ #define SRC_GPR3 0x2C /* General Purpose Register */ #define SRC_GPR4 0x30 /* General Purpose Register */ #define SRC_MISC0 0x4C /* MISC0 */ #define SRC_MISC1 0x50 /* MISC1 */ #define SRC_MISC2 0x54 /* MISC2 */ #define SRC_MISC3 0x58 /* MISC3 */ struct src_softc { struct resource *res[1]; bus_space_tag_t bst; bus_space_handle_t bsh; }; struct src_softc *src_sc; static struct resource_spec src_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; int src_swreset(void) { if (src_sc == NULL) return (1); WRITE4(src_sc, SRC_SCR, SW_RST); return (0); } static int src_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-src")) return (ENXIO); device_set_desc(dev, "Vybrid Family System Reset Controller"); return (BUS_PROBE_DEFAULT); } static int src_attach(device_t dev) { struct src_softc *sc; sc = device_get_softc(dev); if (bus_alloc_resources(dev, src_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); src_sc = sc; return (0); } static device_method_t src_methods[] = { DEVMETHOD(device_probe, src_probe), DEVMETHOD(device_attach, src_attach), { 0, 0 } }; static driver_t src_driver = { "src", src_methods, sizeof(struct src_softc), }; static devclass_t src_devclass; DRIVER_MODULE(src, simplebus, src_driver, src_devclass, 0, 0); Index: head/sys/arm/freescale/vybrid/vf_tcon.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_tcon.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_tcon.c (revision 281085) @@ -1,138 +1,137 @@ /*- * Copyright (c) 2014 Ruslan Bukin * 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. */ /* * Vybrid Family Timing Controller (TCON) * Chapter 58, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #define TCON0_CTRL1 0x00 #define TCON_BYPASS (1 << 29) struct tcon_softc { struct resource *res[1]; bus_space_tag_t bst; bus_space_handle_t bsh; }; struct tcon_softc *tcon_sc; static struct resource_spec tcon_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; uint32_t tcon_bypass(void) { struct tcon_softc *sc; if (tcon_sc == NULL) return (1); sc = tcon_sc; WRITE4(tcon_sc, TCON0_CTRL1, TCON_BYPASS); return (0); } static int tcon_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-tcon")) return (ENXIO); device_set_desc(dev, "Vybrid Family Timing Controller (TCON)"); return (BUS_PROBE_DEFAULT); } static int tcon_attach(device_t dev) { struct tcon_softc *sc; sc = device_get_softc(dev); if (bus_alloc_resources(dev, tcon_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); tcon_sc = sc; return (0); } static device_method_t tcon_methods[] = { DEVMETHOD(device_probe, tcon_probe), DEVMETHOD(device_attach, tcon_attach), { 0, 0 } }; static driver_t tcon_driver = { "tcon", tcon_methods, sizeof(struct tcon_softc), }; static devclass_t tcon_devclass; DRIVER_MODULE(tcon, simplebus, tcon_driver, tcon_devclass, 0, 0); Index: head/sys/arm/freescale/vybrid/vf_uart.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_uart.c (revision 281084) +++ head/sys/arm/freescale/vybrid/vf_uart.c (revision 281085) @@ -1,516 +1,515 @@ /*- * Copyright (c) 2013 Ruslan Bukin * 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. */ /* * Vybrid Family Universal Asynchronous Receiver/Transmitter * Chapter 49, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include -#include #include #include #include #include #include "uart_if.h" #define UART_BDH 0x00 /* Baud Rate Registers: High */ #define UART_BDL 0x01 /* Baud Rate Registers: Low */ #define UART_C1 0x02 /* Control Register 1 */ #define UART_C2 0x03 /* Control Register 2 */ #define UART_S1 0x04 /* Status Register 1 */ #define UART_S2 0x05 /* Status Register 2 */ #define UART_C3 0x06 /* Control Register 3 */ #define UART_D 0x07 /* Data Register */ #define UART_MA1 0x08 /* Match Address Registers 1 */ #define UART_MA2 0x09 /* Match Address Registers 2 */ #define UART_C4 0x0A /* Control Register 4 */ #define UART_C5 0x0B /* Control Register 5 */ #define UART_ED 0x0C /* Extended Data Register */ #define UART_MODEM 0x0D /* Modem Register */ #define UART_IR 0x0E /* Infrared Register */ #define UART_PFIFO 0x10 /* FIFO Parameters */ #define UART_CFIFO 0x11 /* FIFO Control Register */ #define UART_SFIFO 0x12 /* FIFO Status Register */ #define UART_TWFIFO 0x13 /* FIFO Transmit Watermark */ #define UART_TCFIFO 0x14 /* FIFO Transmit Count */ #define UART_RWFIFO 0x15 /* FIFO Receive Watermark */ #define UART_RCFIFO 0x16 /* FIFO Receive Count */ #define UART_C7816 0x18 /* 7816 Control Register */ #define UART_IE7816 0x19 /* 7816 Interrupt Enable Register */ #define UART_IS7816 0x1A /* 7816 Interrupt Status Register */ #define UART_WP7816T0 0x1B /* 7816 Wait Parameter Register */ #define UART_WP7816T1 0x1B /* 7816 Wait Parameter Register */ #define UART_WN7816 0x1C /* 7816 Wait N Register */ #define UART_WF7816 0x1D /* 7816 Wait FD Register */ #define UART_ET7816 0x1E /* 7816 Error Threshold Register */ #define UART_TL7816 0x1F /* 7816 Transmit Length Register */ #define UART_C6 0x21 /* CEA709.1-B Control Register 6 */ #define UART_PCTH 0x22 /* CEA709.1-B Packet Cycle Time Counter High */ #define UART_PCTL 0x23 /* CEA709.1-B Packet Cycle Time Counter Low */ #define UART_B1T 0x24 /* CEA709.1-B Beta1 Timer */ #define UART_SDTH 0x25 /* CEA709.1-B Secondary Delay Timer High */ #define UART_SDTL 0x26 /* CEA709.1-B Secondary Delay Timer Low */ #define UART_PRE 0x27 /* CEA709.1-B Preamble */ #define UART_TPL 0x28 /* CEA709.1-B Transmit Packet Length */ #define UART_IE 0x29 /* CEA709.1-B Interrupt Enable Register */ #define UART_WB 0x2A /* CEA709.1-B WBASE */ #define UART_S3 0x2B /* CEA709.1-B Status Register */ #define UART_S4 0x2C /* CEA709.1-B Status Register */ #define UART_RPL 0x2D /* CEA709.1-B Received Packet Length */ #define UART_RPREL 0x2E /* CEA709.1-B Received Preamble Length */ #define UART_CPW 0x2F /* CEA709.1-B Collision Pulse Width */ #define UART_RIDT 0x30 /* CEA709.1-B Receive Indeterminate Time */ #define UART_TIDT 0x31 /* CEA709.1-B Transmit Indeterminate Time */ #define UART_C2_TE (1 << 3) /* Transmitter Enable */ #define UART_C2_TIE (1 << 7) /* Transmitter Interrupt Enable */ #define UART_C2_RE (1 << 2) /* Receiver Enable */ #define UART_C2_RIE (1 << 5) /* Receiver Interrupt Enable */ #define UART_S1_TDRE (1 << 7) /* Transmit Data Register Empty Flag */ #define UART_S1_RDRF (1 << 5) /* Receive Data Register Full Flag */ #define UART_S2_LBKDIF (1 << 7) /* LIN Break Detect Interrupt Flag */ #define UART_C4_BRFA 0x1f /* Baud Rate Fine Adjust */ #define UART_BDH_SBR 0x1f /* UART Baud Rate Bits */ /* * Low-level UART interface. */ static int vf_uart_probe(struct uart_bas *bas); static void vf_uart_init(struct uart_bas *bas, int, int, int, int); static void vf_uart_term(struct uart_bas *bas); static void vf_uart_putc(struct uart_bas *bas, int); static int vf_uart_rxready(struct uart_bas *bas); static int vf_uart_getc(struct uart_bas *bas, struct mtx *); void uart_reinit(struct uart_softc *,int,int); static struct uart_ops uart_vybrid_ops = { .probe = vf_uart_probe, .init = vf_uart_init, .term = vf_uart_term, .putc = vf_uart_putc, .rxready = vf_uart_rxready, .getc = vf_uart_getc, }; static int vf_uart_probe(struct uart_bas *bas) { return (0); } static void vf_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { } static void vf_uart_term(struct uart_bas *bas) { } static void vf_uart_putc(struct uart_bas *bas, int c) { while (!(uart_getreg(bas, UART_S1) & UART_S1_TDRE)) ; uart_setreg(bas, UART_D, c); } static int vf_uart_rxready(struct uart_bas *bas) { int usr1; usr1 = uart_getreg(bas, UART_S1); if (usr1 & UART_S1_RDRF) { return (1); } return (0); } static int vf_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c; uart_lock(hwmtx); while (!(uart_getreg(bas, UART_S1) & UART_S1_RDRF)) ; c = uart_getreg(bas, UART_D); uart_unlock(hwmtx); return (c & 0xff); } /* * High-level UART interface. */ struct vf_uart_softc { struct uart_softc base; }; void uart_reinit(struct uart_softc *sc, int clkspeed, int baud) { struct uart_bas *bas; int sbr; int brfa; int reg; bas = &sc->sc_bas; if (!bas) { printf("Error: cant reconfigure bas\n"); return; } uart_setreg(bas, UART_MODEM, 0x00); /* * Disable transmitter and receiver * for a while. */ reg = uart_getreg(bas, UART_C2); reg &= ~(UART_C2_RE | UART_C2_TE); uart_setreg(bas, UART_C2, 0x00); uart_setreg(bas, UART_C1, 0x00); sbr = (uint16_t) (clkspeed / (baud * 16)); brfa = (clkspeed / baud) - (sbr * 16); reg = uart_getreg(bas, UART_BDH); reg &= ~UART_BDH_SBR; reg |= ((sbr & 0x1f00) >> 8); uart_setreg(bas, UART_BDH, reg); reg = sbr & 0x00ff; uart_setreg(bas, UART_BDL, reg); reg = uart_getreg(bas, UART_C4); reg &= ~UART_C4_BRFA; reg |= (brfa & UART_C4_BRFA); uart_setreg(bas, UART_C4, reg); reg = uart_getreg(bas, UART_C2); reg |= (UART_C2_RE | UART_C2_TE); uart_setreg(bas, UART_C2, reg); } static int vf_uart_bus_attach(struct uart_softc *); static int vf_uart_bus_detach(struct uart_softc *); static int vf_uart_bus_flush(struct uart_softc *, int); static int vf_uart_bus_getsig(struct uart_softc *); static int vf_uart_bus_ioctl(struct uart_softc *, int, intptr_t); static int vf_uart_bus_ipend(struct uart_softc *); static int vf_uart_bus_param(struct uart_softc *, int, int, int, int); static int vf_uart_bus_probe(struct uart_softc *); static int vf_uart_bus_receive(struct uart_softc *); static int vf_uart_bus_setsig(struct uart_softc *, int); static int vf_uart_bus_transmit(struct uart_softc *); static kobj_method_t vf_uart_methods[] = { KOBJMETHOD(uart_attach, vf_uart_bus_attach), KOBJMETHOD(uart_detach, vf_uart_bus_detach), KOBJMETHOD(uart_flush, vf_uart_bus_flush), KOBJMETHOD(uart_getsig, vf_uart_bus_getsig), KOBJMETHOD(uart_ioctl, vf_uart_bus_ioctl), KOBJMETHOD(uart_ipend, vf_uart_bus_ipend), KOBJMETHOD(uart_param, vf_uart_bus_param), KOBJMETHOD(uart_probe, vf_uart_bus_probe), KOBJMETHOD(uart_receive, vf_uart_bus_receive), KOBJMETHOD(uart_setsig, vf_uart_bus_setsig), KOBJMETHOD(uart_transmit, vf_uart_bus_transmit), { 0, 0 } }; static struct uart_class uart_vybrid_class = { "vybrid", vf_uart_methods, sizeof(struct vf_uart_softc), .uc_ops = &uart_vybrid_ops, .uc_range = 0x100, .uc_rclk = 24000000 /* TODO: get value from CCM */ }; static struct ofw_compat_data compat_data[] = { {"fsl,mvf600-uart", (uintptr_t)&uart_vybrid_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); static int vf_uart_bus_attach(struct uart_softc *sc) { struct uart_bas *bas; int reg; bas = &sc->sc_bas; sc->sc_hwiflow = 0; sc->sc_hwoflow = 0; uart_reinit(sc, 66000000, 115200); reg = uart_getreg(bas, UART_C2); if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { reg &= ~UART_C2_RIE; } else { reg |= UART_C2_RIE; } uart_setreg(bas, UART_C2, reg); return (0); } static int vf_uart_bus_detach(struct uart_softc *sc) { /* TODO */ return (0); } static int vf_uart_bus_flush(struct uart_softc *sc, int what) { /* TODO */ return (0); } static int vf_uart_bus_getsig(struct uart_softc *sc) { /* TODO */ return (0); } static int vf_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas; int error; bas = &sc->sc_bas; error = 0; uart_lock(sc->sc_hwmtx); switch (request) { case UART_IOCTL_BREAK: /* TODO */ break; case UART_IOCTL_BAUD: /* TODO */ *(int*)data = 115200; break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int vf_uart_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; int ipend; uint32_t usr1, usr2; int reg; int sfifo; bas = &sc->sc_bas; ipend = 0; uart_lock(sc->sc_hwmtx); usr1 = uart_getreg(bas, UART_S1); usr2 = uart_getreg(bas, UART_S2); sfifo = uart_getreg(bas, UART_SFIFO); /* ack usr2 */ uart_setreg(bas, UART_S2, usr2); if (usr1 & UART_S1_TDRE) { reg = uart_getreg(bas, UART_C2); reg &= ~(UART_C2_TIE); uart_setreg(bas, UART_C2, reg); if (sc->sc_txbusy != 0) { ipend |= SER_INT_TXIDLE; } } if (usr1 & UART_S1_RDRF) { reg = uart_getreg(bas, UART_C2); reg &= ~(UART_C2_RIE); uart_setreg(bas, UART_C2, reg); ipend |= SER_INT_RXREADY; } if (usr2 & UART_S2_LBKDIF) { ipend |= SER_INT_BREAK; } uart_unlock(sc->sc_hwmtx); return (ipend); } static int vf_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { uart_lock(sc->sc_hwmtx); vf_uart_init(&sc->sc_bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (0); } static int vf_uart_bus_probe(struct uart_softc *sc) { int error; error = vf_uart_probe(&sc->sc_bas); if (error) return (error); sc->sc_rxfifosz = 1; sc->sc_txfifosz = 1; device_set_desc(sc->sc_dev, "Vybrid Family UART"); return (0); } static int vf_uart_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; int reg; int c; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); /* Read FIFO */ while (uart_getreg(bas, UART_S1) & UART_S1_RDRF) { if (uart_rx_full(sc)) { /* No space left in input buffer */ sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } c = uart_getreg(bas, UART_D); uart_rx_put(sc, c); } /* Reenable Data Ready interrupt */ reg = uart_getreg(bas, UART_C2); reg |= (UART_C2_RIE); uart_setreg(bas, UART_C2, reg); uart_unlock(sc->sc_hwmtx); return (0); } static int vf_uart_bus_setsig(struct uart_softc *sc, int sig) { struct uart_bas *bas; int reg; /* TODO: implement (?) */ /* XXX workaround to have working console on mount prompt */ /* Enable RX interrupt */ bas = &sc->sc_bas; if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { reg = uart_getreg(bas, UART_C2); reg |= (UART_C2_RIE); uart_setreg(bas, UART_C2, reg); } return (0); } static int vf_uart_bus_transmit(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; int i; int reg; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); /* Fill TX FIFO */ for (i = 0; i < sc->sc_txdatasz; i++) { uart_setreg(bas, UART_D, sc->sc_txbuf[i] & 0xff); uart_barrier(&sc->sc_bas); } sc->sc_txbusy = 1; /* Call me when ready */ reg = uart_getreg(bas, UART_C2); reg |= (UART_C2_TIE); uart_setreg(bas, UART_C2, reg); uart_unlock(sc->sc_hwmtx); return (0); } Index: head/sys/arm/mv/gpio.c =================================================================== --- head/sys/arm/mv/gpio.c (revision 281084) +++ head/sys/arm/mv/gpio.c (revision 281085) @@ -1,662 +1,661 @@ /*- * Copyright (c) 2006 Benno Rice. * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. * All rights reserved. * * Adapted and extended for Marvell SoCs 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. * * 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. * * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_gpio.c, rev 1 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #define GPIO_MAX_INTR_COUNT 8 #define GPIO_PINS_PER_REG 32 struct mv_gpio_softc { struct resource * res[GPIO_MAX_INTR_COUNT + 1]; void *ih_cookie[GPIO_MAX_INTR_COUNT]; bus_space_tag_t bst; bus_space_handle_t bsh; uint8_t pin_num; /* number of GPIO pins */ uint8_t irq_num; /* number of real IRQs occupied by GPIO controller */ }; extern struct resource_spec mv_gpio_res[]; static struct mv_gpio_softc *mv_gpio_softc = NULL; static uint32_t gpio_setup[MV_GPIO_MAX_NPINS]; static int mv_gpio_probe(device_t); static int mv_gpio_attach(device_t); static int mv_gpio_intr(void *); static int mv_gpio_init(void); static void mv_gpio_intr_handler(int pin); static uint32_t mv_gpio_reg_read(uint32_t reg); static void mv_gpio_reg_write(uint32_t reg, uint32_t val); static void mv_gpio_reg_set(uint32_t reg, uint32_t val); static void mv_gpio_reg_clear(uint32_t reg, uint32_t val); static void mv_gpio_blink(uint32_t pin, uint8_t enable); static void mv_gpio_polarity(uint32_t pin, uint8_t enable); static void mv_gpio_level(uint32_t pin, uint8_t enable); static void mv_gpio_edge(uint32_t pin, uint8_t enable); static void mv_gpio_out_en(uint32_t pin, uint8_t enable); static void mv_gpio_int_ack(uint32_t pin); static void mv_gpio_value_set(uint32_t pin, uint8_t val); static uint32_t mv_gpio_value_get(uint32_t pin); static device_method_t mv_gpio_methods[] = { DEVMETHOD(device_probe, mv_gpio_probe), DEVMETHOD(device_attach, mv_gpio_attach), { 0, 0 } }; static driver_t mv_gpio_driver = { "gpio", mv_gpio_methods, sizeof(struct mv_gpio_softc), }; static devclass_t mv_gpio_devclass; DRIVER_MODULE(gpio, simplebus, mv_gpio_driver, mv_gpio_devclass, 0, 0); typedef int (*gpios_phandler_t)(phandle_t, pcell_t *, int); struct gpio_ctrl_entry { const char *compat; gpios_phandler_t handler; }; int mv_handle_gpios_prop(phandle_t ctrl, pcell_t *gpios, int len); int gpio_get_config_from_dt(void); struct gpio_ctrl_entry gpio_controllers[] = { { "mrvl,gpio", &mv_handle_gpios_prop }, { NULL, NULL } }; static int mv_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "mrvl,gpio")) return (ENXIO); device_set_desc(dev, "Marvell Integrated GPIO Controller"); return (0); } static int mv_gpio_attach(device_t dev) { int error, i; struct mv_gpio_softc *sc; uint32_t dev_id, rev_id; sc = (struct mv_gpio_softc *)device_get_softc(dev); if (sc == NULL) return (ENXIO); mv_gpio_softc = sc; /* Get chip id and revision */ soc_id(&dev_id, &rev_id); if (dev_id == MV_DEV_88F5182 || dev_id == MV_DEV_88F5281 || dev_id == MV_DEV_MV78100 || dev_id == MV_DEV_MV78100_Z0 ) { sc->pin_num = 32; sc->irq_num = 4; } else if (dev_id == MV_DEV_88F6281 || dev_id == MV_DEV_88F6282) { sc->pin_num = 50; sc->irq_num = 7; } else { device_printf(dev, "unknown chip id=0x%x\n", dev_id); return (ENXIO); } error = bus_alloc_resources(dev, mv_gpio_res, sc->res); if (error) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); /* Disable and clear all interrupts */ bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_EDGE_MASK, 0); bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_LEV_MASK, 0); bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_CAUSE, 0); if (sc->pin_num > GPIO_PINS_PER_REG) { bus_space_write_4(sc->bst, sc->bsh, GPIO_HI_INT_EDGE_MASK, 0); bus_space_write_4(sc->bst, sc->bsh, GPIO_HI_INT_LEV_MASK, 0); bus_space_write_4(sc->bst, sc->bsh, GPIO_HI_INT_CAUSE, 0); } for (i = 0; i < sc->irq_num; i++) { if (bus_setup_intr(dev, sc->res[1 + i], INTR_TYPE_MISC, mv_gpio_intr, NULL, sc, &sc->ih_cookie[i]) != 0) { bus_release_resources(dev, mv_gpio_res, sc->res); device_printf(dev, "could not set up intr %d\n", i); return (ENXIO); } } return (mv_gpio_init()); } static int mv_gpio_intr(void *arg) { uint32_t int_cause, gpio_val; uint32_t int_cause_hi, gpio_val_hi = 0; int i; int_cause = mv_gpio_reg_read(GPIO_INT_CAUSE); gpio_val = mv_gpio_reg_read(GPIO_DATA_IN); gpio_val &= int_cause; if (mv_gpio_softc->pin_num > GPIO_PINS_PER_REG) { int_cause_hi = mv_gpio_reg_read(GPIO_HI_INT_CAUSE); gpio_val_hi = mv_gpio_reg_read(GPIO_HI_DATA_IN); gpio_val_hi &= int_cause_hi; } i = 0; while (gpio_val != 0) { if (gpio_val & 1) mv_gpio_intr_handler(i); gpio_val >>= 1; i++; } if (mv_gpio_softc->pin_num > GPIO_PINS_PER_REG) { i = 0; while (gpio_val_hi != 0) { if (gpio_val_hi & 1) mv_gpio_intr_handler(i + GPIO_PINS_PER_REG); gpio_val_hi >>= 1; i++; } } return (FILTER_HANDLED); } /* * GPIO interrupt handling */ static struct intr_event *gpio_events[MV_GPIO_MAX_NPINS]; int mv_gpio_setup_intrhandler(const char *name, driver_filter_t *filt, void (*hand)(void *), void *arg, int pin, int flags, void **cookiep) { struct intr_event *event; int error; if (pin < 0 || pin >= mv_gpio_softc->pin_num) return (ENXIO); event = gpio_events[pin]; if (event == NULL) { error = intr_event_create(&event, (void *)pin, 0, pin, (void (*)(void *))mv_gpio_intr_mask, (void (*)(void *))mv_gpio_intr_unmask, (void (*)(void *))mv_gpio_int_ack, NULL, "gpio%d:", pin); if (error != 0) return (error); gpio_events[pin] = event; } intr_event_add_handler(event, name, filt, hand, arg, intr_priority(flags), flags, cookiep); return (0); } void mv_gpio_intr_mask(int pin) { if (pin >= mv_gpio_softc->pin_num) return; if (gpio_setup[pin] & MV_GPIO_IN_IRQ_EDGE) mv_gpio_edge(pin, 0); else mv_gpio_level(pin, 0); } void mv_gpio_intr_unmask(int pin) { if (pin >= mv_gpio_softc->pin_num) return; if (gpio_setup[pin] & MV_GPIO_IN_IRQ_EDGE) mv_gpio_edge(pin, 1); else mv_gpio_level(pin, 1); } static void mv_gpio_intr_handler(int pin) { struct intr_event *event; event = gpio_events[pin]; if (event == NULL || TAILQ_EMPTY(&event->ie_handlers)) return; intr_event_handle(event, NULL); } static int mv_gpio_configure(uint32_t pin, uint32_t flags) { if (pin >= mv_gpio_softc->pin_num) return (EINVAL); if (flags & MV_GPIO_OUT_BLINK) mv_gpio_blink(pin, 1); if (flags & MV_GPIO_IN_POL_LOW) mv_gpio_polarity(pin, 1); if (flags & MV_GPIO_IN_IRQ_EDGE) mv_gpio_edge(pin, 1); if (flags & MV_GPIO_IN_IRQ_LEVEL) mv_gpio_level(pin, 1); gpio_setup[pin] = flags; return (0); } void mv_gpio_out(uint32_t pin, uint8_t val, uint8_t enable) { mv_gpio_value_set(pin, val); mv_gpio_out_en(pin, enable); } uint8_t mv_gpio_in(uint32_t pin) { return (mv_gpio_value_get(pin) ? 1 : 0); } static uint32_t mv_gpio_reg_read(uint32_t reg) { return (bus_space_read_4(mv_gpio_softc->bst, mv_gpio_softc->bsh, reg)); } static void mv_gpio_reg_write(uint32_t reg, uint32_t val) { bus_space_write_4(mv_gpio_softc->bst, mv_gpio_softc->bsh, reg, val); } static void mv_gpio_reg_set(uint32_t reg, uint32_t pin) { uint32_t reg_val; reg_val = mv_gpio_reg_read(reg); reg_val |= GPIO(pin); mv_gpio_reg_write(reg, reg_val); } static void mv_gpio_reg_clear(uint32_t reg, uint32_t pin) { uint32_t reg_val; reg_val = mv_gpio_reg_read(reg); reg_val &= ~(GPIO(pin)); mv_gpio_reg_write(reg, reg_val); } static void mv_gpio_out_en(uint32_t pin, uint8_t enable) { uint32_t reg; if (pin >= mv_gpio_softc->pin_num) return; if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_DATA_OUT_EN_CTRL; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_DATA_OUT_EN_CTRL; if (enable) mv_gpio_reg_clear(reg, pin); else mv_gpio_reg_set(reg, pin); } static void mv_gpio_blink(uint32_t pin, uint8_t enable) { uint32_t reg; if (pin >= mv_gpio_softc->pin_num) return; if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_BLINK_EN; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_BLINK_EN; if (enable) mv_gpio_reg_set(reg, pin); else mv_gpio_reg_clear(reg, pin); } static void mv_gpio_polarity(uint32_t pin, uint8_t enable) { uint32_t reg; if (pin >= mv_gpio_softc->pin_num) return; if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_DATA_IN_POLAR; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_DATA_IN_POLAR; if (enable) mv_gpio_reg_set(reg, pin); else mv_gpio_reg_clear(reg, pin); } static void mv_gpio_level(uint32_t pin, uint8_t enable) { uint32_t reg; if (pin >= mv_gpio_softc->pin_num) return; if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_INT_LEV_MASK; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_INT_LEV_MASK; if (enable) mv_gpio_reg_set(reg, pin); else mv_gpio_reg_clear(reg, pin); } static void mv_gpio_edge(uint32_t pin, uint8_t enable) { uint32_t reg; if (pin >= mv_gpio_softc->pin_num) return; if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_INT_EDGE_MASK; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_INT_EDGE_MASK; if (enable) mv_gpio_reg_set(reg, pin); else mv_gpio_reg_clear(reg, pin); } static void mv_gpio_int_ack(uint32_t pin) { uint32_t reg; if (pin >= mv_gpio_softc->pin_num) return; if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_INT_CAUSE; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_INT_CAUSE; mv_gpio_reg_clear(reg, pin); } static uint32_t mv_gpio_value_get(uint32_t pin) { uint32_t reg, reg_val; if (pin >= mv_gpio_softc->pin_num) return (0); if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_DATA_IN; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_DATA_IN; reg_val = mv_gpio_reg_read(reg); return (reg_val & GPIO(pin)); } static void mv_gpio_value_set(uint32_t pin, uint8_t val) { uint32_t reg; if (pin >= mv_gpio_softc->pin_num) return; if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_DATA_OUT; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_DATA_OUT; if (val) mv_gpio_reg_set(reg, pin); else mv_gpio_reg_clear(reg, pin); } int mv_handle_gpios_prop(phandle_t ctrl, pcell_t *gpios, int len) { pcell_t gpio_cells, pincnt; int inc, t, tuples, tuple_size; int dir, flags, pin; u_long gpio_ctrl, size; struct mv_gpio_softc sc; pincnt = 0; if (!OF_hasprop(ctrl, "gpio-controller")) /* Node is not a GPIO controller. */ return (ENXIO); if (OF_getprop(ctrl, "#gpio-cells", &gpio_cells, sizeof(pcell_t)) < 0) return (ENXIO); gpio_cells = fdt32_to_cpu(gpio_cells); if (gpio_cells != 3) return (ENXIO); tuple_size = gpio_cells * sizeof(pcell_t) + sizeof(phandle_t); tuples = len / tuple_size; if (fdt_regsize(ctrl, &gpio_ctrl, &size)) return (ENXIO); if (OF_getprop(ctrl, "pin-count", &pincnt, sizeof(pcell_t)) < 0) return (ENXIO); sc.pin_num = fdt32_to_cpu(pincnt); /* * Skip controller reference, since controller's phandle is given * explicitly (in a function argument). */ inc = sizeof(ihandle_t) / sizeof(pcell_t); gpios += inc; for (t = 0; t < tuples; t++) { pin = fdt32_to_cpu(gpios[0]); dir = fdt32_to_cpu(gpios[1]); flags = fdt32_to_cpu(gpios[2]); mv_gpio_configure(pin, flags); if (dir == 1) /* Input. */ mv_gpio_out_en(pin, 0); else { /* Output. */ if (flags & MV_GPIO_OUT_OPEN_DRAIN) mv_gpio_out(pin, 0, 1); if (flags & MV_GPIO_OUT_OPEN_SRC) mv_gpio_out(pin, 1, 1); } gpios += gpio_cells + inc; } return (0); } #define MAX_PINS_PER_NODE 5 #define GPIOS_PROP_CELLS 4 static int mv_gpio_init(void) { phandle_t child, parent, root, ctrl; pcell_t gpios[MAX_PINS_PER_NODE * GPIOS_PROP_CELLS]; struct gpio_ctrl_entry *e; int len, rv; root = OF_finddevice("/"); len = 0; parent = root; /* Traverse through entire tree to find nodes with 'gpios' prop */ for (child = OF_child(parent); child != 0; child = OF_peer(child)) { /* Find a 'leaf'. Start the search from this node. */ while (OF_child(child)) { parent = child; child = OF_child(child); } if ((len = OF_getproplen(child, "gpios")) > 0) { if (len > sizeof(gpios)) return (ENXIO); /* Get 'gpios' property. */ OF_getprop(child, "gpios", &gpios, len); e = (struct gpio_ctrl_entry *)&gpio_controllers; /* Find and call a handler. */ for (; e->compat; e++) { /* * First cell of 'gpios' property should * contain a ref. to a node defining GPIO * controller. */ ctrl = OF_node_from_xref(fdt32_to_cpu(gpios[0])); if (fdt_is_compatible(ctrl, e->compat)) /* Call a handler. */ if ((rv = e->handler(ctrl, (pcell_t *)&gpios, len))) return (rv); } } if (OF_peer(child) == 0) { /* No more siblings. */ child = parent; parent = OF_parent(child); } } return (0); } Index: head/sys/arm/mv/mv_ts.c =================================================================== --- head/sys/arm/mv/mv_ts.c (revision 281084) +++ head/sys/arm/mv/mv_ts.c (revision 281085) @@ -1,181 +1,180 @@ /*- * Copyright (c) 2012 Hiroki Sato * 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. * * Driver for on-die thermal sensor in 88F6282 and 88F6283. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include -#include #include #include #include #include #include static struct resource_spec mvts_res[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; struct mvts_softc { device_t sc_dev; struct resource *sc_res[sizeof(mvts_res)]; }; static int ts_probe(device_t dev) { uint32_t d, r; if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "mrvl,ts")) return (ENXIO); soc_id(&d, &r); switch (d) { case MV_DEV_88F6282: break; default: device_printf(dev, "unsupported SoC (ID: 0x%08X)!\n", d); return (ENXIO); } device_set_desc(dev, "Marvell Thermal Sensor"); return (0); } #define MV_TEMP_VALID_BIT (1 << 9) #define MV_TEMP_SENS_OFFS 10 #define MV_TEMP_SENS_MASK 0x1ff #define MV_TEMP_SENS_READ_MAX 16 #define TZ_ZEROC 2732 #define MV_TEMP_CONVERT(x) ((((322 - x) * 100000) / 13625) + TZ_ZEROC) /* * MSB LSB * 0000 0000 0000 0000 0000 0000 0000 0000 * ^- valid bit * |---------| * ^--- temperature (9 bits) */ static int ts_sysctl_handler(SYSCTL_HANDLER_ARGS) { struct mvts_softc *sc; device_t dev; uint32_t ret, ret0; u_int val; int i; dev = (device_t)arg1; sc = device_get_softc(dev); val = TZ_ZEROC; ret = bus_read_4(sc->sc_res[0], 0); if ((ret & MV_TEMP_VALID_BIT) == 0) { device_printf(dev, "temperature sensor is broken.\n"); goto ts_sysctl_handle_int; } ret0 = 0; for (i = 0; i < MV_TEMP_SENS_READ_MAX; i++) { ret = bus_read_4(sc->sc_res[0], 0); ret = (ret >> MV_TEMP_SENS_OFFS) & MV_TEMP_SENS_MASK; /* * Successive reads should returns the same value except * for the LSB when the sensor is normal. */ if (((ret0 ^ ret) & 0x1fe) == 0) break; else ret0 = ret; } if (i == MV_TEMP_SENS_READ_MAX) { device_printf(dev, "temperature sensor is unstable.\n"); goto ts_sysctl_handle_int; } val = (u_int)MV_TEMP_CONVERT(ret); ts_sysctl_handle_int: return (sysctl_handle_int(oidp, &val, 0, req)); } static int ts_attach(device_t dev) { struct mvts_softc *sc; struct sysctl_ctx_list *ctx; int error; sc = device_get_softc(dev); sc->sc_dev = dev; error = bus_alloc_resources(dev, mvts_res, sc->sc_res); if (error) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } ctx = device_get_sysctl_ctx(dev); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, dev, 0, ts_sysctl_handler, "IK", "Current Temperature"); return (0); } static int ts_detach(device_t dev) { return (0); } static device_method_t ts_methods[] = { DEVMETHOD(device_probe, ts_probe), DEVMETHOD(device_attach, ts_attach), DEVMETHOD(device_detach, ts_detach), {0, 0}, }; static driver_t ts_driver = { "mvts", ts_methods, sizeof(struct mvts_softc), }; static devclass_t ts_devclass; DRIVER_MODULE(mvts, simplebus, ts_driver, ts_devclass, 0, 0); MODULE_VERSION(mvts, 1); Index: head/sys/arm/rockchip/rk30xx_common.c =================================================================== --- head/sys/arm/rockchip/rk30xx_common.c (revision 281084) +++ head/sys/arm/rockchip/rk30xx_common.c (revision 281085) @@ -1,64 +1,63 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * 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 struct fdt_fixup_entry fdt_fixup_table[] = { { NULL, NULL } }; static int fdt_aintc_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, int *pol) { if (!fdt_is_compatible(node, "arm,gic")) return (ENXIO); *interrupt = fdt32_to_cpu(intr[0]); *trig = INTR_TRIGGER_CONFORM; *pol = INTR_POLARITY_CONFORM; return (0); } fdt_pic_decode_t fdt_pic_table[] = { &fdt_aintc_decode_ic, NULL }; Index: head/sys/arm/rockchip/rk30xx_gpio.c =================================================================== --- head/sys/arm/rockchip/rk30xx_gpio.c (revision 281084) +++ head/sys/arm/rockchip/rk30xx_gpio.c (revision 281085) @@ -1,635 +1,634 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * 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 "gpio_if.h" #include "rk30xx_grf.h" #include "rk30xx_pmu.h" /* * RK3188 has 4 banks of gpio. * 32 pins per bank * PA0 - PA7 | PB0 - PB7 * PC0 - PC7 | PD0 - PD7 */ #define RK30_GPIO_PINS 32 #define RK30_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) #define RK30_GPIO_NONE 0 #define RK30_GPIO_PULLUP 1 #define RK30_GPIO_PULLDOWN 2 struct rk30_gpio_softc { device_t sc_dev; device_t sc_busdev; 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_bank; int sc_gpio_npins; struct gpio_pin sc_gpio_pins[RK30_GPIO_PINS]; }; /* We use our base address to find out our bank number. */ static unsigned long rk30_gpio_base_addr[4] = { 0x2000a000, 0x2003c000, 0x2003e000, 0x20080000 }; static struct rk30_gpio_softc *rk30_gpio_sc = NULL; typedef int (*gpios_phandler_t)(phandle_t, pcell_t *, int); struct gpio_ctrl_entry { const char *compat; gpios_phandler_t handler; }; int rk30_gpios_prop_handle(phandle_t ctrl, pcell_t *gpios, int len); static int rk30_gpio_init(void); struct gpio_ctrl_entry gpio_controllers[] = { { "rockchip,rk30xx-gpio", &rk30_gpios_prop_handle }, { "rockchip,rk30xx-gpio", &rk30_gpios_prop_handle }, { "rockchip,rk30xx-gpio", &rk30_gpios_prop_handle }, { "rockchip,rk30xx-gpio", &rk30_gpios_prop_handle }, { NULL, NULL } }; #define RK30_GPIO_LOCK(_sc) mtx_lock(&_sc->sc_mtx) #define RK30_GPIO_UNLOCK(_sc) mtx_unlock(&_sc->sc_mtx) #define RK30_GPIO_LOCK_ASSERT(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED) #define RK30_GPIO_SWPORT_DR 0x00 #define RK30_GPIO_SWPORT_DDR 0x04 #define RK30_GPIO_INTEN 0x30 #define RK30_GPIO_INTMASK 0x34 #define RK30_GPIO_INTTYPE_LEVEL 0x38 #define RK30_GPIO_INT_POLARITY 0x3c #define RK30_GPIO_INT_STATUS 0x40 #define RK30_GPIO_INT_RAWSTATUS 0x44 #define RK30_GPIO_DEBOUNCE 0x48 #define RK30_GPIO_PORT_EOI 0x4c #define RK30_GPIO_EXT_PORT 0x50 #define RK30_GPIO_LS_SYNC 0x60 #define RK30_GPIO_WRITE(_sc, _off, _val) \ bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) #define RK30_GPIO_READ(_sc, _off) \ bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off) static uint32_t rk30_gpio_get_function(struct rk30_gpio_softc *sc, uint32_t pin) { if (RK30_GPIO_READ(sc, RK30_GPIO_SWPORT_DDR) & (1U << pin)) return (GPIO_PIN_OUTPUT); else return (GPIO_PIN_INPUT); } static void rk30_gpio_set_function(struct rk30_gpio_softc *sc, uint32_t pin, uint32_t func) { uint32_t data; /* Must be called with lock held. */ RK30_GPIO_LOCK_ASSERT(sc); data = RK30_GPIO_READ(sc, RK30_GPIO_SWPORT_DDR); if (func == GPIO_PIN_OUTPUT) data |= (1U << pin); else data &= ~(1U << pin); RK30_GPIO_WRITE(sc, RK30_GPIO_SWPORT_DDR, data); } static void rk30_gpio_set_pud(struct rk30_gpio_softc *sc, uint32_t pin, uint32_t state) { uint32_t pud; /* Must be called with lock held. */ RK30_GPIO_LOCK_ASSERT(sc); switch (state) { case GPIO_PIN_PULLUP: pud = RK30_GPIO_PULLUP; break; case GPIO_PIN_PULLDOWN: pud = RK30_GPIO_PULLDOWN; break; default: pud = RK30_GPIO_NONE; } /* * The pull up/down registers for GPIO0A and half of GPIO0B * (the first 12 pins on bank 0) are at a different location. */ if (sc->sc_bank == 0 && pin < 12) rk30_pmu_gpio_pud(pin, pud); else rk30_grf_gpio_pud(sc->sc_bank, pin, pud); } static void rk30_gpio_pin_configure(struct rk30_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { RK30_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; else pin->gp_flags |= GPIO_PIN_INPUT; rk30_gpio_set_function(sc, pin->gp_pin, pin->gp_flags); } /* 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; else pin->gp_flags |= GPIO_PIN_PULLDOWN; } rk30_gpio_set_pud(sc, pin->gp_pin, pin->gp_flags); RK30_GPIO_UNLOCK(sc); } static device_t rk30_gpio_get_bus(device_t dev) { struct rk30_gpio_softc *sc; sc = device_get_softc(dev); return (sc->sc_busdev); } static int rk30_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = RK30_GPIO_PINS - 1; return (0); } static int rk30_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct rk30_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); RK30_GPIO_LOCK(sc); *caps = sc->sc_gpio_pins[i].gp_caps; RK30_GPIO_UNLOCK(sc); return (0); } static int rk30_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct rk30_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); RK30_GPIO_LOCK(sc); *flags = sc->sc_gpio_pins[i].gp_flags; RK30_GPIO_UNLOCK(sc); return (0); } static int rk30_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct rk30_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); RK30_GPIO_LOCK(sc); memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); RK30_GPIO_UNLOCK(sc); return (0); } static int rk30_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct rk30_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); rk30_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); return (0); } static int rk30_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { int i; struct rk30_gpio_softc *sc; uint32_t data; sc = device_get_softc(dev); 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); RK30_GPIO_LOCK(sc); data = RK30_GPIO_READ(sc, RK30_GPIO_SWPORT_DR); if (value) data |= (1U << pin); else data &= ~(1U << pin); RK30_GPIO_WRITE(sc, RK30_GPIO_SWPORT_DR, data); RK30_GPIO_UNLOCK(sc); return (0); } static int rk30_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { int i; struct rk30_gpio_softc *sc; uint32_t data; sc = device_get_softc(dev); 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); RK30_GPIO_LOCK(sc); data = RK30_GPIO_READ(sc, RK30_GPIO_EXT_PORT); RK30_GPIO_UNLOCK(sc); *val = (data & (1U << pin)) ? 1 : 0; return (0); } static int rk30_gpio_pin_toggle(device_t dev, uint32_t pin) { int i; struct rk30_gpio_softc *sc; uint32_t data; sc = device_get_softc(dev); 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); RK30_GPIO_LOCK(sc); data = RK30_GPIO_READ(sc, RK30_GPIO_SWPORT_DR); if (data & (1U << pin)) data &= ~(1U << pin); else data |= (1U << pin); RK30_GPIO_WRITE(sc, RK30_GPIO_SWPORT_DR, data); RK30_GPIO_UNLOCK(sc); return (0); } static int rk30_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "rockchip,rk30xx-gpio")) return (ENXIO); device_set_desc(dev, "Rockchip RK30XX GPIO controller"); return (BUS_PROBE_DEFAULT); } static int rk30_gpio_attach(device_t dev) { struct rk30_gpio_softc *sc = device_get_softc(dev); int i, rid; phandle_t gpio; unsigned long start; if (rk30_gpio_sc) return (ENXIO); sc->sc_dev = dev; mtx_init(&sc->sc_mtx, "rk30 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"); goto fail; } 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 our base address. */ sc->sc_bank = -1; start = rman_get_start(sc->sc_mem_res); for (i = 0; i < nitems(rk30_gpio_base_addr); i++) { if (rk30_gpio_base_addr[i] == start) { sc->sc_bank = i; break; } } if (sc->sc_bank == -1) { device_printf(dev, "unsupported device unit (only GPIO0..3 are supported)\n"); goto fail; } 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"); goto fail; } /* 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; /* Initialize the software controlled pins. */ for (i = 0; i < RK30_GPIO_PINS; i++) { snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, "pin %d", i); sc->sc_gpio_pins[i].gp_pin = i; sc->sc_gpio_pins[i].gp_caps = RK30_GPIO_DEFAULT_CAPS; sc->sc_gpio_pins[i].gp_flags = rk30_gpio_get_function(sc, i); } sc->sc_gpio_npins = i; rk30_gpio_sc = sc; rk30_gpio_init(); sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) goto fail; return (0); 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); mtx_destroy(&sc->sc_mtx); return (ENXIO); } static int rk30_gpio_detach(device_t dev) { return (EBUSY); } static device_method_t rk30_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk30_gpio_probe), DEVMETHOD(device_attach, rk30_gpio_attach), DEVMETHOD(device_detach, rk30_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, rk30_gpio_get_bus), DEVMETHOD(gpio_pin_max, rk30_gpio_pin_max), DEVMETHOD(gpio_pin_getname, rk30_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, rk30_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, rk30_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, rk30_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, rk30_gpio_pin_get), DEVMETHOD(gpio_pin_set, rk30_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, rk30_gpio_pin_toggle), DEVMETHOD_END }; static devclass_t rk30_gpio_devclass; static driver_t rk30_gpio_driver = { "gpio", rk30_gpio_methods, sizeof(struct rk30_gpio_softc), }; DRIVER_MODULE(rk30_gpio, simplebus, rk30_gpio_driver, rk30_gpio_devclass, 0, 0); int rk30_gpios_prop_handle(phandle_t ctrl, pcell_t *gpios, int len) { struct rk30_gpio_softc *sc; pcell_t gpio_cells; int inc, t, tuples, tuple_size; int dir, flags, pin, i; u_long gpio_ctrl, size; sc = rk30_gpio_sc; if (sc == NULL) return ENXIO; if (OF_getprop(ctrl, "#gpio-cells", &gpio_cells, sizeof(pcell_t)) < 0) return (ENXIO); gpio_cells = fdt32_to_cpu(gpio_cells); if (gpio_cells != 2) return (ENXIO); tuple_size = gpio_cells * sizeof(pcell_t) + sizeof(phandle_t); tuples = len / tuple_size; if (fdt_regsize(ctrl, &gpio_ctrl, &size)) return (ENXIO); /* * Skip controller reference, since controller's phandle is given * explicitly (in a function argument). */ inc = sizeof(ihandle_t) / sizeof(pcell_t); gpios += inc; for (t = 0; t < tuples; t++) { pin = fdt32_to_cpu(gpios[0]); dir = fdt32_to_cpu(gpios[1]); flags = fdt32_to_cpu(gpios[2]); 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); rk30_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); if (dir == 1) { /* Input. */ rk30_gpio_pin_set(sc->sc_dev, pin, GPIO_PIN_INPUT); } else { /* Output. */ rk30_gpio_pin_set(sc->sc_dev, pin, GPIO_PIN_OUTPUT); } gpios += gpio_cells + inc; } return (0); } #define MAX_PINS_PER_NODE 5 #define GPIOS_PROP_CELLS 4 static int rk30_gpio_init(void) { phandle_t child, parent, root, ctrl; pcell_t gpios[MAX_PINS_PER_NODE * GPIOS_PROP_CELLS]; struct gpio_ctrl_entry *e; int len, rv; root = OF_finddevice("/"); len = 0; parent = root; /* Traverse through entire tree to find nodes with 'gpios' prop */ for (child = OF_child(parent); child != 0; child = OF_peer(child)) { /* Find a 'leaf'. Start the search from this node. */ while (OF_child(child)) { parent = child; child = OF_child(child); } if ((len = OF_getproplen(child, "gpios")) > 0) { if (len > sizeof(gpios)) return (ENXIO); /* Get 'gpios' property. */ OF_getprop(child, "gpios", &gpios, len); e = (struct gpio_ctrl_entry *)&gpio_controllers; /* Find and call a handler. */ for (; e->compat; e++) { /* * First cell of 'gpios' property should * contain a ref. to a node defining GPIO * controller. */ ctrl = OF_node_from_xref(fdt32_to_cpu(gpios[0])); if (fdt_is_compatible(ctrl, e->compat)) /* Call a handler. */ if ((rv = e->handler(ctrl, (pcell_t *)&gpios, len))) return (rv); } } if (OF_peer(child) == 0) { /* No more siblings. */ child = parent; parent = OF_parent(child); } } return (0); } Index: head/sys/arm/rockchip/rk30xx_grf.c =================================================================== --- head/sys/arm/rockchip/rk30xx_grf.c (revision 281084) +++ head/sys/arm/rockchip/rk30xx_grf.c (revision 281085) @@ -1,132 +1,131 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * 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. */ /* General Register File for Rockchip RK30xx */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include "rk30xx_grf.h" struct rk30_grf_softc { struct resource *res; bus_space_tag_t bst; bus_space_handle_t bsh; }; static struct rk30_grf_softc *rk30_grf_sc = NULL; #define grf_read_4(sc, reg) \ bus_space_read_4((sc)->bst, (sc)->bsh, (reg)) #define grf_write_4(sc, reg, val) \ bus_space_write_4((sc)->bst, (sc)->bsh, (reg), (val)) static int rk30_grf_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "rockchip,rk30xx-grf")) { device_set_desc(dev, "RK30XX General Register File"); return(BUS_PROBE_DEFAULT); } return (ENXIO); } static int rk30_grf_attach(device_t dev) { struct rk30_grf_softc *sc = device_get_softc(dev); int rid = 0; if (rk30_grf_sc) return (ENXIO); sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->res) { device_printf(dev, "could not allocate resource\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->res); sc->bsh = rman_get_bushandle(sc->res); rk30_grf_sc = sc; return (0); } static device_method_t rk30_grf_methods[] = { DEVMETHOD(device_probe, rk30_grf_probe), DEVMETHOD(device_attach, rk30_grf_attach), { 0, 0 } }; static driver_t rk30_grf_driver = { "rk30_grf", rk30_grf_methods, sizeof(struct rk30_grf_softc), }; static devclass_t rk30_grf_devclass; DRIVER_MODULE(rk30_grf, simplebus, rk30_grf_driver, rk30_grf_devclass, 0, 0); void rk30_grf_gpio_pud(uint32_t bank, uint32_t pin, uint32_t state) { uint32_t offset; offset = GRF_GPIO0B_PULL - 4 + (bank * 16) + ((pin / 8) * 4); pin = (7 - (pin % 8)) * 2; grf_write_4(rk30_grf_sc, offset, (0x3 << (16 + pin)) | (state << pin)); } Index: head/sys/arm/rockchip/rk30xx_pmu.c =================================================================== --- head/sys/arm/rockchip/rk30xx_pmu.c (revision 281084) +++ head/sys/arm/rockchip/rk30xx_pmu.c (revision 281085) @@ -1,132 +1,131 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * 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. */ /* PMU for Rockchip RK30xx */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include "rk30xx_pmu.h" struct rk30_pmu_softc { struct resource *res; bus_space_tag_t bst; bus_space_handle_t bsh; }; static struct rk30_pmu_softc *rk30_pmu_sc = NULL; #define pmu_read_4(sc, reg) \ bus_space_read_4((sc)->bst, (sc)->bsh, (reg)) #define pmu_write_4(sc, reg, val) \ bus_space_write_4((sc)->bst, (sc)->bsh, (reg), (val)) static int rk30_pmu_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "rockchip,rk30xx-pmu")) { device_set_desc(dev, "RK30XX PMU"); return(BUS_PROBE_DEFAULT); } return (ENXIO); } static int rk30_pmu_attach(device_t dev) { struct rk30_pmu_softc *sc = device_get_softc(dev); int rid = 0; if (rk30_pmu_sc) return (ENXIO); sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->res) { device_printf(dev, "could not allocate resource\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->res); sc->bsh = rman_get_bushandle(sc->res); rk30_pmu_sc = sc; return (0); } static device_method_t rk30_pmu_methods[] = { DEVMETHOD(device_probe, rk30_pmu_probe), DEVMETHOD(device_attach, rk30_pmu_attach), { 0, 0 } }; static driver_t rk30_pmu_driver = { "rk30_pmu", rk30_pmu_methods, sizeof(struct rk30_pmu_softc), }; static devclass_t rk30_pmu_devclass; DRIVER_MODULE(rk30_pmu, simplebus, rk30_pmu_driver, rk30_pmu_devclass, 0, 0); void rk30_pmu_gpio_pud(uint32_t pin, uint32_t state) { uint32_t offset; offset = PMU_GPIO0A_PULL + ((pin / 8) * 4); pin = (pin % 8) * 2; pmu_write_4(rk30_pmu_sc, offset, (0x3 << (16 + pin)) | (state << pin)); } Index: head/sys/arm/ti/am335x/am335x_dmtimer.c =================================================================== --- head/sys/arm/ti/am335x/am335x_dmtimer.c (revision 281084) +++ head/sys/arm/ti/am335x/am335x_dmtimer.c (revision 281085) @@ -1,696 +1,695 @@ /*- * Copyright (c) 2012 Damjan Marion * 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 "opt_ntp.h" #include #include #include #include #include -#include #include #include #define AM335X_NUM_TIMERS 8 #define DMT_TIDR 0x00 /* Identification Register */ #define DMT_TIOCP_CFG 0x10 /* OCP Configuration Reg */ #define DMT_TIOCP_RESET (1 << 0) /* TIOCP perform soft reset */ #define DMT_IQR_EOI 0x20 /* IRQ End-Of-Interrupt Reg */ #define DMT_IRQSTATUS_RAW 0x24 /* IRQSTATUS Raw Reg */ #define DMT_IRQSTATUS 0x28 /* IRQSTATUS Reg */ #define DMT_IRQENABLE_SET 0x2c /* IRQSTATUS Set Reg */ #define DMT_IRQENABLE_CLR 0x30 /* IRQSTATUS Clear Reg */ #define DMT_IRQWAKEEN 0x34 /* IRQ Wakeup Enable Reg */ #define DMT_IRQ_MAT (1 << 0) /* IRQ: Match */ #define DMT_IRQ_OVF (1 << 1) /* IRQ: Overflow */ #define DMT_IRQ_TCAR (1 << 2) /* IRQ: Capture */ #define DMT_IRQ_MASK (DMT_IRQ_TCAR | DMT_IRQ_OVF | DMT_IRQ_MAT) #define DMT_TCLR 0x38 /* Control Register */ #define DMT_TCLR_START (1 << 0) /* Start timer */ #define DMT_TCLR_AUTOLOAD (1 << 1) /* Auto-reload on overflow */ #define DMT_TCLR_PRES_MASK (7 << 2) /* Prescaler mask */ #define DMT_TCLR_PRES_ENABLE (1 << 5) /* Prescaler enable */ #define DMT_TCLR_COMP_ENABLE (1 << 6) /* Compare enable */ #define DMT_TCLR_PWM_HIGH (1 << 7) /* PWM default output high */ #define DMT_TCLR_CAPTRAN_MASK (3 << 8) /* Capture transition mask */ #define DMT_TCLR_CAPTRAN_NONE (0 << 8) /* Capture: none */ #define DMT_TCLR_CAPTRAN_LOHI (1 << 8) /* Capture lo->hi transition */ #define DMT_TCLR_CAPTRAN_HILO (2 << 8) /* Capture hi->lo transition */ #define DMT_TCLR_CAPTRAN_BOTH (3 << 8) /* Capture both transitions */ #define DMT_TCLR_TRGMODE_MASK (3 << 10) /* Trigger output mode mask */ #define DMT_TCLR_TRGMODE_NONE (0 << 10) /* Trigger off */ #define DMT_TCLR_TRGMODE_OVFL (1 << 10) /* Trigger on overflow */ #define DMT_TCLR_TRGMODE_BOTH (2 << 10) /* Trigger on match + ovflow */ #define DMT_TCLR_PWM_PTOGGLE (1 << 12) /* PWM toggles */ #define DMT_TCLR_CAP_MODE_2ND (1 << 13) /* Capture second event mode */ #define DMT_TCLR_GPO_CFG (1 << 14) /* (no descr in datasheet) */ #define DMT_TCRR 0x3C /* Counter Register */ #define DMT_TLDR 0x40 /* Load Reg */ #define DMT_TTGR 0x44 /* Trigger Reg */ #define DMT_TWPS 0x48 /* Write Posted Status Reg */ #define DMT_TMAR 0x4C /* Match Reg */ #define DMT_TCAR1 0x50 /* Capture Reg */ #define DMT_TSICR 0x54 /* Synchr. Interface Ctrl Reg */ #define DMT_TSICR_RESET (1 << 1) /* TSICR perform soft reset */ #define DMT_TCAR2 0x48 /* Capture Reg */ /* * Use timer 2 for the eventtimer. When PPS support is not compiled in, there's * no need to use a timer that has an associated capture-input pin, so use timer * 3 for timecounter. When PPS is compiled in we ignore the default and use * whichever of timers 4-7 have the capture pin configured. */ #define DEFAULT_ET_TIMER 2 #define DEFAULT_TC_TIMER 3 struct am335x_dmtimer_softc { struct resource * tmr_mem_res[AM335X_NUM_TIMERS]; struct resource * tmr_irq_res[AM335X_NUM_TIMERS]; uint32_t sysclk_freq; uint32_t tc_num; /* Which timer number is tc. */ uint32_t tc_tclr; /* Cached tc TCLR register. */ struct resource * tc_memres; /* Resources for tc timer. */ uint32_t et_num; /* Which timer number is et. */ uint32_t et_tclr; /* Cached et TCLR register. */ struct resource * et_memres; /* Resources for et timer. */ int pps_curmode; /* Edge mode now set in hw. */ struct task pps_task; /* For pps_event handling. */ struct cdev * pps_cdev; struct pps_state pps; struct timecounter tc; struct eventtimer et; }; static struct am335x_dmtimer_softc *am335x_dmtimer_sc; static struct resource_spec am335x_dmtimer_mem_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { SYS_RES_MEMORY, 2, RF_ACTIVE }, { SYS_RES_MEMORY, 3, RF_ACTIVE }, { SYS_RES_MEMORY, 4, RF_ACTIVE }, { SYS_RES_MEMORY, 5, RF_ACTIVE }, { SYS_RES_MEMORY, 6, RF_ACTIVE }, { SYS_RES_MEMORY, 7, RF_ACTIVE }, { -1, 0, 0 } }; static struct resource_spec am335x_dmtimer_irq_spec[] = { { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { SYS_RES_IRQ, 3, RF_ACTIVE }, { SYS_RES_IRQ, 4, RF_ACTIVE }, { SYS_RES_IRQ, 5, RF_ACTIVE }, { SYS_RES_IRQ, 6, RF_ACTIVE }, { SYS_RES_IRQ, 7, RF_ACTIVE }, { -1, 0, 0 } }; static inline uint32_t am335x_dmtimer_tc_read_4(struct am335x_dmtimer_softc *sc, uint32_t reg) { return (bus_read_4(sc->tc_memres, reg)); } static inline void am335x_dmtimer_tc_write_4(struct am335x_dmtimer_softc *sc, uint32_t reg, uint32_t val) { bus_write_4(sc->tc_memres, reg, val); } static inline uint32_t am335x_dmtimer_et_read_4(struct am335x_dmtimer_softc *sc, uint32_t reg) { return (bus_read_4(sc->et_memres, reg)); } static inline void am335x_dmtimer_et_write_4(struct am335x_dmtimer_softc *sc, uint32_t reg, uint32_t val) { bus_write_4(sc->et_memres, reg, val); } /* * PPS driver routines, included when the kernel is built with option PPS_SYNC. * * Note that this PPS driver does not use an interrupt. Instead it uses the * hardware's ability to latch the timer's count register in response to a * signal on an IO pin. Each of timers 4-7 have an associated pin, and this * code allows any one of those to be used. * * The timecounter routines in kern_tc.c call the pps poll routine periodically * to see if a new counter value has been latched. When a new value has been * latched, the only processing done in the poll routine is to capture the * current set of timecounter timehands (done with pps_capture()) and the * latched value from the timer. The remaining work (done by pps_event()) is * scheduled to be done later in a non-interrupt context. */ #ifdef PPS_SYNC #define PPS_CDEV_NAME "dmtpps" static void am335x_dmtimer_set_capture_mode(struct am335x_dmtimer_softc *sc, bool force_off) { int newmode; if (force_off) newmode = 0; else newmode = sc->pps.ppsparam.mode & PPS_CAPTUREBOTH; if (newmode == sc->pps_curmode) return; sc->pps_curmode = newmode; sc->tc_tclr &= ~DMT_TCLR_CAPTRAN_MASK; switch (newmode) { case PPS_CAPTUREASSERT: sc->tc_tclr |= DMT_TCLR_CAPTRAN_LOHI; break; case PPS_CAPTURECLEAR: sc->tc_tclr |= DMT_TCLR_CAPTRAN_HILO; break; default: /* It can't be BOTH, so it's disabled. */ break; } am335x_dmtimer_tc_write_4(sc, DMT_TCLR, sc->tc_tclr); } static void am335x_dmtimer_tc_poll_pps(struct timecounter *tc) { struct am335x_dmtimer_softc *sc; sc = tc->tc_priv; /* * Note that we don't have the TCAR interrupt enabled, but the hardware * still provides the status bits in the "RAW" status register even when * they're masked from generating an irq. However, when clearing the * TCAR status to re-arm the capture for the next second, we have to * write to the IRQ status register, not the RAW register. Quirky. */ if (am335x_dmtimer_tc_read_4(sc, DMT_IRQSTATUS_RAW) & DMT_IRQ_TCAR) { pps_capture(&sc->pps); sc->pps.capcount = am335x_dmtimer_tc_read_4(sc, DMT_TCAR1); am335x_dmtimer_tc_write_4(sc, DMT_IRQSTATUS, DMT_IRQ_TCAR); taskqueue_enqueue_fast(taskqueue_fast, &sc->pps_task); } } static void am335x_dmtimer_process_pps_event(void *arg, int pending) { struct am335x_dmtimer_softc *sc; sc = arg; /* This is the task function that gets enqueued by poll_pps. Once the * time has been captured in the hw interrupt context, the remaining * (more expensive) work to process the event is done later in a * non-fast-interrupt context. * * We only support capture of the rising or falling edge, not both at * once; tell the kernel to process whichever mode is currently active. */ pps_event(&sc->pps, sc->pps.ppsparam.mode & PPS_CAPTUREBOTH); } static int am335x_dmtimer_pps_open(struct cdev *dev, int flags, int fmt, struct thread *td) { struct am335x_dmtimer_softc *sc; sc = dev->si_drv1; /* Enable capture on open. Harmless if already open. */ am335x_dmtimer_set_capture_mode(sc, 0); return 0; } static int am335x_dmtimer_pps_close(struct cdev *dev, int flags, int fmt, struct thread *td) { struct am335x_dmtimer_softc *sc; sc = dev->si_drv1; /* * Disable capture on last close. Use the force-off flag to override * the configured mode and turn off the hardware capture. */ am335x_dmtimer_set_capture_mode(sc, 1); return 0; } static int am335x_dmtimer_pps_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) { struct am335x_dmtimer_softc *sc; int err; sc = dev->si_drv1; /* * The hardware has a "capture both edges" mode, but we can't do * anything useful with it in terms of PPS capture, so don't even try. */ if ((sc->pps.ppsparam.mode & PPS_CAPTUREBOTH) == PPS_CAPTUREBOTH) return (EINVAL); /* Let the kernel do the heavy lifting for ioctl. */ err = pps_ioctl(cmd, data, &sc->pps); if (err != 0) return (err); /* * The capture mode could have changed, set the hardware to whatever * mode is now current. Effectively a no-op if nothing changed. */ am335x_dmtimer_set_capture_mode(sc, 0); return (err); } static struct cdevsw am335x_dmtimer_pps_cdevsw = { .d_version = D_VERSION, .d_open = am335x_dmtimer_pps_open, .d_close = am335x_dmtimer_pps_close, .d_ioctl = am335x_dmtimer_pps_ioctl, .d_name = PPS_CDEV_NAME, }; /* * Set up the PPS cdev and the the kernel timepps stuff. * * Note that this routine cannot touch the hardware, because bus space resources * are not fully set up yet when this is called. */ static int am335x_dmtimer_pps_init(device_t dev, struct am335x_dmtimer_softc *sc) { int i, timer_num, unit; unsigned int padstate; const char * padmux; struct padinfo { char * ballname; char * muxname; int timer_num; } padinfo[] = { {"GPMC_ADVn_ALE", "timer4", 4}, {"GPMC_BEn0_CLE", "timer5", 5}, {"GPMC_WEn", "timer6", 6}, {"GPMC_OEn_REn", "timer7", 7}, }; /* * Figure out which pin the user has set up for pps. We'll use the * first timer that has an external caputure pin configured as input. * * XXX The hieroglyphic "(padstate & (0x01 << 5)))" checks that the pin * is configured for input. The right symbolic values aren't exported * yet from ti_scm.h. */ timer_num = 0; for (i = 0; i < nitems(padinfo) && timer_num == 0; ++i) { if (ti_scm_padconf_get(padinfo[i].ballname, &padmux, &padstate) == 0) { if (strcasecmp(padinfo[i].muxname, padmux) == 0 && (padstate & (0x01 << 5))) timer_num = padinfo[i].timer_num; } } if (timer_num == 0) { device_printf(dev, "No DMTimer found with capture pin " "configured as input; PPS driver disabled.\n"); return (DEFAULT_TC_TIMER); } /* * Indicate our capabilities (pretty much just capture of either edge). * Have the kernel init its part of the pps_state struct and add its * capabilities. */ sc->pps.ppscap = PPS_CAPTUREBOTH; pps_init(&sc->pps); /* * Set up to capture the PPS via timecounter polling, and init the task * that does deferred pps_event() processing after capture. */ sc->tc.tc_poll_pps = am335x_dmtimer_tc_poll_pps; TASK_INIT(&sc->pps_task, 0, am335x_dmtimer_process_pps_event, sc); /* Create the PPS cdev. */ unit = device_get_unit(dev); sc->pps_cdev = make_dev(&am335x_dmtimer_pps_cdevsw, unit, UID_ROOT, GID_WHEEL, 0600, PPS_CDEV_NAME "%d", unit); sc->pps_cdev->si_drv1 = sc; device_printf(dev, "Using DMTimer%d for PPS device /dev/%s%d\n", timer_num, PPS_CDEV_NAME, unit); return (timer_num); } #else /* PPS_SYNC */ static int am335x_dmtimer_pps_init(device_t dev, struct am335x_dmtimer_softc *sc) { /* * When PPS support is not compiled in, there's no need to use a timer * that has an associated capture-input pin, so use the default. */ return (DEFAULT_TC_TIMER); } #endif /* PPS_SYNC */ /* * End of PPS driver code. */ static unsigned am335x_dmtimer_tc_get_timecount(struct timecounter *tc) { struct am335x_dmtimer_softc *sc; sc = tc->tc_priv; return (am335x_dmtimer_tc_read_4(sc, DMT_TCRR)); } static int am335x_dmtimer_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { struct am335x_dmtimer_softc *sc; uint32_t initial_count, reload_count; sc = et->et_priv; /* * Stop the timer before changing it. This routine will often be called * while the timer is still running, to either lengthen or shorten the * current event time. We need to ensure the timer doesn't expire while * we're working with it. * * Also clear any pending interrupt status, because it's at least * theoretically possible that we're running in a primary interrupt * context now, and a timer interrupt could be pending even before we * stopped the timer. The more likely case is that we're being called * from the et_event_cb() routine dispatched from our own handler, but * it's not clear to me that that's the only case possible. */ sc->et_tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD); am335x_dmtimer_et_write_4(sc, DMT_TCLR, sc->et_tclr); am335x_dmtimer_et_write_4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); if (period != 0) { reload_count = ((uint32_t)et->et_frequency * period) >> 32; sc->et_tclr |= DMT_TCLR_AUTOLOAD; } else { reload_count = 0; } if (first != 0) initial_count = ((uint32_t)et->et_frequency * first) >> 32; else initial_count = reload_count; /* * Set auto-reload and current-count values. This timer hardware counts * up from the initial/reload value and interrupts on the zero rollover. */ am335x_dmtimer_et_write_4(sc, DMT_TLDR, 0xFFFFFFFF - reload_count); am335x_dmtimer_et_write_4(sc, DMT_TCRR, 0xFFFFFFFF - initial_count); /* Enable overflow interrupt, and start the timer. */ am335x_dmtimer_et_write_4(sc, DMT_IRQENABLE_SET, DMT_IRQ_OVF); sc->et_tclr |= DMT_TCLR_START; am335x_dmtimer_et_write_4(sc, DMT_TCLR, sc->et_tclr); return (0); } static int am335x_dmtimer_stop(struct eventtimer *et) { struct am335x_dmtimer_softc *sc; sc = et->et_priv; /* Stop timer, disable and clear interrupt. */ sc->et_tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD); am335x_dmtimer_et_write_4(sc, DMT_TCLR, sc->et_tclr); am335x_dmtimer_et_write_4(sc, DMT_IRQENABLE_CLR, DMT_IRQ_OVF); am335x_dmtimer_et_write_4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); return (0); } static int am335x_dmtimer_intr(void *arg) { struct am335x_dmtimer_softc *sc; sc = arg; /* Ack the interrupt, and invoke the callback if it's still enabled. */ am335x_dmtimer_et_write_4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); if (sc->et.et_active) sc->et.et_event_cb(&sc->et, sc->et.et_arg); return (FILTER_HANDLED); } static int am335x_dmtimer_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "ti,am335x-dmtimer")) { device_set_desc(dev, "AM335x DMTimer"); return(BUS_PROBE_DEFAULT); } return (ENXIO); } static int am335x_dmtimer_attach(device_t dev) { struct am335x_dmtimer_softc *sc; void *ihl; int err; /* * Note that if this routine returns an error status rather than running * to completion it makes no attempt to clean up allocated resources; * the system is essentially dead anyway without functional timers. */ sc = device_get_softc(dev); if (am335x_dmtimer_sc != NULL) return (EINVAL); /* Get the base clock frequency. */ err = ti_prcm_clk_get_source_freq(SYS_CLK, &sc->sysclk_freq); if (err) { device_printf(dev, "Error: could not get sysclk frequency\n"); return (ENXIO); } /* Request the memory resources. */ err = bus_alloc_resources(dev, am335x_dmtimer_mem_spec, sc->tmr_mem_res); if (err) { device_printf(dev, "Error: could not allocate mem resources\n"); return (ENXIO); } /* Request the IRQ resources. */ err = bus_alloc_resources(dev, am335x_dmtimer_irq_spec, sc->tmr_irq_res); if (err) { device_printf(dev, "Error: could not allocate irq resources\n"); return (ENXIO); } /* * Use the default eventtimer. Let the PPS init routine decide which * timer to use for the timecounter. */ sc->et_num = DEFAULT_ET_TIMER; sc->tc_num = am335x_dmtimer_pps_init(dev, sc); sc->et_memres = sc->tmr_mem_res[sc->et_num]; sc->tc_memres = sc->tmr_mem_res[sc->tc_num]; /* Enable clocks and power on the chosen devices. */ err = ti_prcm_clk_set_source(DMTIMER0_CLK + sc->et_num, SYSCLK_CLK); err |= ti_prcm_clk_enable(DMTIMER0_CLK + sc->et_num); err |= ti_prcm_clk_set_source(DMTIMER0_CLK + sc->tc_num, SYSCLK_CLK); err |= ti_prcm_clk_enable(DMTIMER0_CLK + sc->tc_num); if (err) { device_printf(dev, "Error: could not enable timer clock\n"); return (ENXIO); } /* Setup eventtimer interrupt handler. */ if (bus_setup_intr(dev, sc->tmr_irq_res[sc->et_num], INTR_TYPE_CLK, am335x_dmtimer_intr, NULL, sc, &ihl) != 0) { device_printf(dev, "Unable to setup the clock irq handler.\n"); return (ENXIO); } /* Set up timecounter, start it, register it. */ am335x_dmtimer_tc_write_4(sc, DMT_TSICR, DMT_TSICR_RESET); while (am335x_dmtimer_tc_read_4(sc, DMT_TIOCP_CFG) & DMT_TIOCP_RESET) continue; sc->tc_tclr |= DMT_TCLR_START | DMT_TCLR_AUTOLOAD; am335x_dmtimer_tc_write_4(sc, DMT_TLDR, 0); am335x_dmtimer_tc_write_4(sc, DMT_TCRR, 0); am335x_dmtimer_tc_write_4(sc, DMT_TCLR, sc->tc_tclr); sc->tc.tc_name = "AM335x Timecounter"; sc->tc.tc_get_timecount = am335x_dmtimer_tc_get_timecount; sc->tc.tc_counter_mask = ~0u; sc->tc.tc_frequency = sc->sysclk_freq; sc->tc.tc_quality = 1000; sc->tc.tc_priv = sc; tc_init(&sc->tc); sc->et.et_name = "AM335x Eventtimer"; sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT; sc->et.et_quality = 1000; sc->et.et_frequency = sc->sysclk_freq; sc->et.et_min_period = ((0x00000005LLU << 32) / sc->et.et_frequency); sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; sc->et.et_start = am335x_dmtimer_start; sc->et.et_stop = am335x_dmtimer_stop; sc->et.et_priv = sc; et_register(&sc->et); /* Store a pointer to the softc for use in DELAY(). */ am335x_dmtimer_sc = sc; return (0); } static device_method_t am335x_dmtimer_methods[] = { DEVMETHOD(device_probe, am335x_dmtimer_probe), DEVMETHOD(device_attach, am335x_dmtimer_attach), { 0, 0 } }; static driver_t am335x_dmtimer_driver = { "am335x_dmtimer", am335x_dmtimer_methods, sizeof(struct am335x_dmtimer_softc), }; static devclass_t am335x_dmtimer_devclass; DRIVER_MODULE(am335x_dmtimer, simplebus, am335x_dmtimer_driver, am335x_dmtimer_devclass, 0, 0); MODULE_DEPEND(am335x_dmtimer, am335x_prcm, 1, 1, 1); void DELAY(int usec) { struct am335x_dmtimer_softc *sc; int32_t counts; uint32_t first, last; sc = am335x_dmtimer_sc; if (sc == NULL) { for (; usec > 0; usec--) for (counts = 200; counts > 0; counts--) /* Prevent gcc from optimizing out the loop */ cpufunc_nullop(); return; } /* Get the number of times to count */ counts = (usec + 1) * (sc->sysclk_freq / 1000000); first = am335x_dmtimer_tc_read_4(sc, DMT_TCRR); while (counts > 0) { last = am335x_dmtimer_tc_read_4(sc, DMT_TCRR); if (last > first) { counts -= (int32_t)(last - first); } else { counts -= (int32_t)((0xFFFFFFFF - first) + last); } first = last; } } Index: head/sys/arm/ti/am335x/am335x_prcm.c =================================================================== --- head/sys/arm/ti/am335x/am335x_prcm.c (revision 281084) +++ head/sys/arm/ti/am335x/am335x_prcm.c (revision 281085) @@ -1,798 +1,797 @@ /*- * Copyright (c) 2012 Damjan Marion * 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 #define CM_PER 0 #define CM_PER_L4LS_CLKSTCTRL (CM_PER + 0x000) #define CM_PER_L3S_CLKSTCTRL (CM_PER + 0x004) #define CM_PER_L3_CLKSTCTRL (CM_PER + 0x00C) #define CM_PER_CPGMAC0_CLKCTRL (CM_PER + 0x014) #define CM_PER_LCDC_CLKCTRL (CM_PER + 0x018) #define CM_PER_USB0_CLKCTRL (CM_PER + 0x01C) #define CM_PER_TPTC0_CLKCTRL (CM_PER + 0x024) #define CM_PER_UART5_CLKCTRL (CM_PER + 0x038) #define CM_PER_MMC0_CLKCTRL (CM_PER + 0x03C) #define CM_PER_I2C2_CLKCTRL (CM_PER + 0x044) #define CM_PER_I2C1_CLKCTRL (CM_PER + 0x048) #define CM_PER_UART1_CLKCTRL (CM_PER + 0x06C) #define CM_PER_UART2_CLKCTRL (CM_PER + 0x070) #define CM_PER_UART3_CLKCTRL (CM_PER + 0x074) #define CM_PER_UART4_CLKCTRL (CM_PER + 0x078) #define CM_PER_TIMER7_CLKCTRL (CM_PER + 0x07C) #define CM_PER_TIMER2_CLKCTRL (CM_PER + 0x080) #define CM_PER_TIMER3_CLKCTRL (CM_PER + 0x084) #define CM_PER_TIMER4_CLKCTRL (CM_PER + 0x088) #define CM_PER_GPIO1_CLKCTRL (CM_PER + 0x0AC) #define CM_PER_GPIO2_CLKCTRL (CM_PER + 0x0B0) #define CM_PER_GPIO3_CLKCTRL (CM_PER + 0x0B4) #define CM_PER_TPCC_CLKCTRL (CM_PER + 0x0BC) #define CM_PER_EPWMSS1_CLKCTRL (CM_PER + 0x0CC) #define CM_PER_EPWMSS0_CLKCTRL (CM_PER + 0x0D4) #define CM_PER_EPWMSS2_CLKCTRL (CM_PER + 0x0D8) #define CM_PER_L3_INSTR_CLKCTRL (CM_PER + 0x0DC) #define CM_PER_L3_CLKCTRL (CM_PER + 0x0E0) #define CM_PER_PRUSS_CLKCTRL (CM_PER + 0x0E8) #define CM_PER_TIMER5_CLKCTRL (CM_PER + 0x0EC) #define CM_PER_TIMER6_CLKCTRL (CM_PER + 0x0F0) #define CM_PER_MMC1_CLKCTRL (CM_PER + 0x0F4) #define CM_PER_MMC2_CLKCTRL (CM_PER + 0x0F8) #define CM_PER_TPTC1_CLKCTRL (CM_PER + 0x0FC) #define CM_PER_TPTC2_CLKCTRL (CM_PER + 0x100) #define CM_PER_SPINLOCK0_CLKCTRL (CM_PER + 0x10C) #define CM_PER_MAILBOX0_CLKCTRL (CM_PER + 0x110) #define CM_PER_OCPWP_L3_CLKSTCTRL (CM_PER + 0x12C) #define CM_PER_OCPWP_CLKCTRL (CM_PER + 0x130) #define CM_PER_CPSW_CLKSTCTRL (CM_PER + 0x144) #define CM_PER_PRUSS_CLKSTCTRL (CM_PER + 0x140) #define CM_WKUP 0x400 #define CM_WKUP_CLKSTCTRL (CM_WKUP + 0x000) #define CM_WKUP_CONTROL_CLKCTRL (CM_WKUP + 0x004) #define CM_WKUP_GPIO0_CLKCTRL (CM_WKUP + 0x008) #define CM_WKUP_CM_L3_AON_CLKSTCTRL (CM_WKUP + 0x01C) #define CM_WKUP_CM_CLKSEL_DPLL_MPU (CM_WKUP + 0x02C) #define CM_WKUP_CM_IDLEST_DPLL_DISP (CM_WKUP + 0x048) #define CM_WKUP_CM_CLKSEL_DPLL_DISP (CM_WKUP + 0x054) #define CM_WKUP_CM_CLKDCOLDO_DPLL_PER (CM_WKUP + 0x07C) #define CM_WKUP_CM_CLKMODE_DPLL_DISP (CM_WKUP + 0x098) #define CM_WKUP_I2C0_CLKCTRL (CM_WKUP + 0x0B8) #define CM_WKUP_ADC_TSC_CLKCTRL (CM_WKUP + 0x0BC) #define CM_DPLL 0x500 #define CLKSEL_TIMER7_CLK (CM_DPLL + 0x004) #define CLKSEL_TIMER2_CLK (CM_DPLL + 0x008) #define CLKSEL_TIMER3_CLK (CM_DPLL + 0x00C) #define CLKSEL_TIMER4_CLK (CM_DPLL + 0x010) #define CLKSEL_TIMER5_CLK (CM_DPLL + 0x018) #define CLKSEL_TIMER6_CLK (CM_DPLL + 0x01C) #define CLKSEL_PRUSS_OCP_CLK (CM_DPLL + 0x030) #define CM_RTC 0x800 #define CM_RTC_RTC_CLKCTRL (CM_RTC + 0x000) #define CM_RTC_CLKSTCTRL (CM_RTC + 0x004) #define PRM_PER 0xC00 #define PRM_PER_RSTCTRL (PRM_PER + 0x00) #define PRM_DEVICE_OFFSET 0xF00 #define PRM_RSTCTRL (PRM_DEVICE_OFFSET + 0x00) struct am335x_prcm_softc { struct resource * res[2]; bus_space_tag_t bst; bus_space_handle_t bsh; }; static struct resource_spec am335x_prcm_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static struct am335x_prcm_softc *am335x_prcm_sc = NULL; static int am335x_clk_noop_activate(struct ti_clock_dev *clkdev); static int am335x_clk_generic_activate(struct ti_clock_dev *clkdev); static int am335x_clk_gpio_activate(struct ti_clock_dev *clkdev); static int am335x_clk_noop_deactivate(struct ti_clock_dev *clkdev); static int am335x_clk_generic_deactivate(struct ti_clock_dev *clkdev); static int am335x_clk_noop_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); static int am335x_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); static int am335x_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq); static int am335x_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq); static int am335x_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq); static int am335x_clk_get_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int *freq); static void am335x_prcm_reset(void); static int am335x_clk_cpsw_activate(struct ti_clock_dev *clkdev); static int am335x_clk_musb0_activate(struct ti_clock_dev *clkdev); static int am335x_clk_lcdc_activate(struct ti_clock_dev *clkdev); static int am335x_clk_pruss_activate(struct ti_clock_dev *clkdev); #define AM335X_NOOP_CLOCK_DEV(i) \ { .id = (i), \ .clk_activate = am335x_clk_noop_activate, \ .clk_deactivate = am335x_clk_noop_deactivate, \ .clk_set_source = am335x_clk_noop_set_source, \ .clk_accessible = NULL, \ .clk_get_source_freq = NULL \ } #define AM335X_GENERIC_CLOCK_DEV(i) \ { .id = (i), \ .clk_activate = am335x_clk_generic_activate, \ .clk_deactivate = am335x_clk_generic_deactivate, \ .clk_set_source = am335x_clk_generic_set_source, \ .clk_accessible = NULL, \ .clk_get_source_freq = NULL \ } #define AM335X_GPIO_CLOCK_DEV(i) \ { .id = (i), \ .clk_activate = am335x_clk_gpio_activate, \ .clk_deactivate = am335x_clk_generic_deactivate, \ .clk_set_source = am335x_clk_generic_set_source, \ .clk_accessible = NULL, \ .clk_get_source_freq = NULL \ } #define AM335X_MMCHS_CLOCK_DEV(i) \ { .id = (i), \ .clk_activate = am335x_clk_generic_activate, \ .clk_deactivate = am335x_clk_generic_deactivate, \ .clk_set_source = am335x_clk_generic_set_source, \ .clk_accessible = NULL, \ .clk_get_source_freq = am335x_clk_hsmmc_get_source_freq \ } struct ti_clock_dev ti_am335x_clk_devmap[] = { /* System clocks */ { .id = SYS_CLK, .clk_activate = NULL, .clk_deactivate = NULL, .clk_set_source = NULL, .clk_accessible = NULL, .clk_get_source_freq = am335x_clk_get_sysclk_freq, }, /* MPU (ARM) core clocks */ { .id = MPU_CLK, .clk_activate = NULL, .clk_deactivate = NULL, .clk_set_source = NULL, .clk_accessible = NULL, .clk_get_source_freq = am335x_clk_get_arm_fclk_freq, }, /* CPSW Ethernet Switch core clocks */ { .id = CPSW_CLK, .clk_activate = am335x_clk_cpsw_activate, .clk_deactivate = NULL, .clk_set_source = NULL, .clk_accessible = NULL, .clk_get_source_freq = NULL, }, /* Mentor USB HS controller core clocks */ { .id = MUSB0_CLK, .clk_activate = am335x_clk_musb0_activate, .clk_deactivate = NULL, .clk_set_source = NULL, .clk_accessible = NULL, .clk_get_source_freq = NULL, }, /* LCD controller clocks */ { .id = LCDC_CLK, .clk_activate = am335x_clk_lcdc_activate, .clk_deactivate = NULL, .clk_set_source = NULL, .clk_accessible = NULL, .clk_get_source_freq = am335x_clk_get_arm_disp_freq, }, /* UART. Uart0 clock cannot be controlled. */ AM335X_NOOP_CLOCK_DEV(UART0_CLK), AM335X_GENERIC_CLOCK_DEV(UART1_CLK), AM335X_GENERIC_CLOCK_DEV(UART2_CLK), AM335X_GENERIC_CLOCK_DEV(UART3_CLK), AM335X_GENERIC_CLOCK_DEV(UART4_CLK), AM335X_GENERIC_CLOCK_DEV(UART5_CLK), /* DMTimer */ AM335X_GENERIC_CLOCK_DEV(DMTIMER2_CLK), AM335X_GENERIC_CLOCK_DEV(DMTIMER3_CLK), AM335X_GENERIC_CLOCK_DEV(DMTIMER4_CLK), AM335X_GENERIC_CLOCK_DEV(DMTIMER5_CLK), AM335X_GENERIC_CLOCK_DEV(DMTIMER6_CLK), AM335X_GENERIC_CLOCK_DEV(DMTIMER7_CLK), /* GPIO */ AM335X_GPIO_CLOCK_DEV(GPIO0_CLK), AM335X_GPIO_CLOCK_DEV(GPIO1_CLK), AM335X_GPIO_CLOCK_DEV(GPIO2_CLK), AM335X_GPIO_CLOCK_DEV(GPIO3_CLK), /* I2C */ AM335X_GENERIC_CLOCK_DEV(I2C0_CLK), AM335X_GENERIC_CLOCK_DEV(I2C1_CLK), AM335X_GENERIC_CLOCK_DEV(I2C2_CLK), /* TSC_ADC */ AM335X_GENERIC_CLOCK_DEV(TSC_ADC_CLK), /* EDMA */ AM335X_GENERIC_CLOCK_DEV(EDMA_TPCC_CLK), AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC0_CLK), AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC1_CLK), AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC2_CLK), /* MMCHS */ AM335X_MMCHS_CLOCK_DEV(MMC0_CLK), AM335X_MMCHS_CLOCK_DEV(MMC1_CLK), AM335X_MMCHS_CLOCK_DEV(MMC2_CLK), /* PWMSS */ AM335X_GENERIC_CLOCK_DEV(PWMSS0_CLK), AM335X_GENERIC_CLOCK_DEV(PWMSS1_CLK), AM335X_GENERIC_CLOCK_DEV(PWMSS2_CLK), /* System Mailbox clock */ AM335X_GENERIC_CLOCK_DEV(MAILBOX0_CLK), /* SPINLOCK */ AM335X_GENERIC_CLOCK_DEV(SPINLOCK0_CLK), /* PRU-ICSS */ { .id = PRUSS_CLK, .clk_activate = am335x_clk_pruss_activate, .clk_deactivate = NULL, .clk_set_source = NULL, .clk_accessible = NULL, .clk_get_source_freq = NULL, }, /* RTC */ AM335X_GENERIC_CLOCK_DEV(RTC_CLK), { INVALID_CLK_IDENT, NULL, NULL, NULL, NULL } }; struct am335x_clk_details { clk_ident_t id; uint32_t clkctrl_reg; uint32_t clksel_reg; }; #define _CLK_DETAIL(i, c, s) \ { .id = (i), \ .clkctrl_reg = (c), \ .clksel_reg = (s), \ } static struct am335x_clk_details g_am335x_clk_details[] = { /* UART. UART0 clock not controllable. */ _CLK_DETAIL(UART0_CLK, 0, 0), _CLK_DETAIL(UART1_CLK, CM_PER_UART1_CLKCTRL, 0), _CLK_DETAIL(UART2_CLK, CM_PER_UART2_CLKCTRL, 0), _CLK_DETAIL(UART3_CLK, CM_PER_UART3_CLKCTRL, 0), _CLK_DETAIL(UART4_CLK, CM_PER_UART4_CLKCTRL, 0), _CLK_DETAIL(UART5_CLK, CM_PER_UART5_CLKCTRL, 0), /* DMTimer modules */ _CLK_DETAIL(DMTIMER2_CLK, CM_PER_TIMER2_CLKCTRL, CLKSEL_TIMER2_CLK), _CLK_DETAIL(DMTIMER3_CLK, CM_PER_TIMER3_CLKCTRL, CLKSEL_TIMER3_CLK), _CLK_DETAIL(DMTIMER4_CLK, CM_PER_TIMER4_CLKCTRL, CLKSEL_TIMER4_CLK), _CLK_DETAIL(DMTIMER5_CLK, CM_PER_TIMER5_CLKCTRL, CLKSEL_TIMER5_CLK), _CLK_DETAIL(DMTIMER6_CLK, CM_PER_TIMER6_CLKCTRL, CLKSEL_TIMER6_CLK), _CLK_DETAIL(DMTIMER7_CLK, CM_PER_TIMER7_CLKCTRL, CLKSEL_TIMER7_CLK), /* GPIO modules */ _CLK_DETAIL(GPIO0_CLK, CM_WKUP_GPIO0_CLKCTRL, 0), _CLK_DETAIL(GPIO1_CLK, CM_PER_GPIO1_CLKCTRL, 0), _CLK_DETAIL(GPIO2_CLK, CM_PER_GPIO2_CLKCTRL, 0), _CLK_DETAIL(GPIO3_CLK, CM_PER_GPIO3_CLKCTRL, 0), /* I2C modules */ _CLK_DETAIL(I2C0_CLK, CM_WKUP_I2C0_CLKCTRL, 0), _CLK_DETAIL(I2C1_CLK, CM_PER_I2C1_CLKCTRL, 0), _CLK_DETAIL(I2C2_CLK, CM_PER_I2C2_CLKCTRL, 0), /* TSC_ADC module */ _CLK_DETAIL(TSC_ADC_CLK, CM_WKUP_ADC_TSC_CLKCTRL, 0), /* EDMA modules */ _CLK_DETAIL(EDMA_TPCC_CLK, CM_PER_TPCC_CLKCTRL, 0), _CLK_DETAIL(EDMA_TPTC0_CLK, CM_PER_TPTC0_CLKCTRL, 0), _CLK_DETAIL(EDMA_TPTC1_CLK, CM_PER_TPTC1_CLKCTRL, 0), _CLK_DETAIL(EDMA_TPTC2_CLK, CM_PER_TPTC2_CLKCTRL, 0), /* MMCHS modules*/ _CLK_DETAIL(MMC0_CLK, CM_PER_MMC0_CLKCTRL, 0), _CLK_DETAIL(MMC1_CLK, CM_PER_MMC1_CLKCTRL, 0), _CLK_DETAIL(MMC2_CLK, CM_PER_MMC1_CLKCTRL, 0), /* PWMSS modules */ _CLK_DETAIL(PWMSS0_CLK, CM_PER_EPWMSS0_CLKCTRL, 0), _CLK_DETAIL(PWMSS1_CLK, CM_PER_EPWMSS1_CLKCTRL, 0), _CLK_DETAIL(PWMSS2_CLK, CM_PER_EPWMSS2_CLKCTRL, 0), _CLK_DETAIL(MAILBOX0_CLK, CM_PER_MAILBOX0_CLKCTRL, 0), _CLK_DETAIL(SPINLOCK0_CLK, CM_PER_SPINLOCK0_CLKCTRL, 0), /* RTC module */ _CLK_DETAIL(RTC_CLK, CM_RTC_RTC_CLKCTRL, 0), { INVALID_CLK_IDENT, 0}, }; /* Read/Write macros */ #define prcm_read_4(reg) \ bus_space_read_4(am335x_prcm_sc->bst, am335x_prcm_sc->bsh, reg) #define prcm_write_4(reg, val) \ bus_space_write_4(am335x_prcm_sc->bst, am335x_prcm_sc->bsh, reg, val) void am335x_prcm_setup_dmtimer(int); static int am335x_prcm_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "am335x,prcm")) { device_set_desc(dev, "AM335x Power and Clock Management"); return(BUS_PROBE_DEFAULT); } return (ENXIO); } static int am335x_prcm_attach(device_t dev) { struct am335x_prcm_softc *sc = device_get_softc(dev); unsigned int sysclk, fclk; if (am335x_prcm_sc) return (ENXIO); if (bus_alloc_resources(dev, am335x_prcm_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); am335x_prcm_sc = sc; ti_cpu_reset = am335x_prcm_reset; am335x_clk_get_sysclk_freq(NULL, &sysclk); am335x_clk_get_arm_fclk_freq(NULL, &fclk); device_printf(dev, "Clocks: System %u.%01u MHz, CPU %u MHz\n", sysclk/1000000, (sysclk % 1000000)/100000, fclk/1000000); return (0); } static device_method_t am335x_prcm_methods[] = { DEVMETHOD(device_probe, am335x_prcm_probe), DEVMETHOD(device_attach, am335x_prcm_attach), { 0, 0 } }; static driver_t am335x_prcm_driver = { "am335x_prcm", am335x_prcm_methods, sizeof(struct am335x_prcm_softc), }; static devclass_t am335x_prcm_devclass; DRIVER_MODULE(am335x_prcm, simplebus, am335x_prcm_driver, am335x_prcm_devclass, 0, 0); MODULE_DEPEND(am335x_prcm, ti_scm, 1, 1, 1); static struct am335x_clk_details* am335x_clk_details(clk_ident_t id) { struct am335x_clk_details *walker; for (walker = g_am335x_clk_details; walker->id != INVALID_CLK_IDENT; walker++) { if (id == walker->id) return (walker); } return NULL; } static int am335x_clk_noop_activate(struct ti_clock_dev *clkdev) { return (0); } static int am335x_clk_generic_activate(struct ti_clock_dev *clkdev) { struct am335x_prcm_softc *sc = am335x_prcm_sc; struct am335x_clk_details* clk_details; if (sc == NULL) return ENXIO; clk_details = am335x_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); /* set *_CLKCTRL register MODULEMODE[1:0] to enable(2) */ prcm_write_4(clk_details->clkctrl_reg, 2); while ((prcm_read_4(clk_details->clkctrl_reg) & 0x3) != 2) DELAY(10); return (0); } static int am335x_clk_gpio_activate(struct ti_clock_dev *clkdev) { struct am335x_prcm_softc *sc = am335x_prcm_sc; struct am335x_clk_details* clk_details; if (sc == NULL) return ENXIO; clk_details = am335x_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); /* set *_CLKCTRL register MODULEMODE[1:0] to enable(2) */ /* set *_CLKCTRL register OPTFCLKEN_GPIO_1_G DBCLK[18] to FCLK_EN(1) */ prcm_write_4(clk_details->clkctrl_reg, 2 | (1 << 18)); while ((prcm_read_4(clk_details->clkctrl_reg) & (3 | (1 << 18) )) != (2 | (1 << 18))) DELAY(10); return (0); } static int am335x_clk_noop_deactivate(struct ti_clock_dev *clkdev) { return(0); } static int am335x_clk_generic_deactivate(struct ti_clock_dev *clkdev) { struct am335x_prcm_softc *sc = am335x_prcm_sc; struct am335x_clk_details* clk_details; if (sc == NULL) return ENXIO; clk_details = am335x_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); /* set *_CLKCTRL register MODULEMODE[1:0] to disable(0) */ prcm_write_4(clk_details->clkctrl_reg, 0); while ((prcm_read_4(clk_details->clkctrl_reg) & 0x3) != 0) DELAY(10); return (0); } static int am335x_clk_noop_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) { return (0); } static int am335x_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) { struct am335x_prcm_softc *sc = am335x_prcm_sc; struct am335x_clk_details* clk_details; uint32_t reg; if (sc == NULL) return ENXIO; clk_details = am335x_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); switch (clksrc) { case EXT_CLK: reg = 0; /* SEL2: TCLKIN clock */ break; case SYSCLK_CLK: reg = 1; /* SEL1: CLK_M_OSC clock */ break; case F32KHZ_CLK: reg = 2; /* SEL3: CLK_32KHZ clock */ break; default: return (ENXIO); } prcm_write_4(clk_details->clksel_reg, reg); while ((prcm_read_4(clk_details->clksel_reg) & 0x3) != reg) DELAY(10); return (0); } static int am335x_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq) { *freq = 96000000; return (0); } static int am335x_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq) { uint32_t ctrl_status; /* Read the input clock freq from the control module */ /* control_status reg (0x40) */ if (ti_scm_reg_read_4(0x40, &ctrl_status)) return ENXIO; switch ((ctrl_status>>22) & 0x3) { case 0x0: /* 19.2Mhz */ *freq = 19200000; break; case 0x1: /* 24Mhz */ *freq = 24000000; break; case 0x2: /* 25Mhz */ *freq = 25000000; break; case 0x3: /* 26Mhz */ *freq = 26000000; break; } return (0); } #define DPLL_BYP_CLKSEL(reg) ((reg>>23) & 1) #define DPLL_DIV(reg) ((reg & 0x7f)+1) #define DPLL_MULT(reg) ((reg>>8) & 0x7FF) static int am335x_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq) { uint32_t reg; uint32_t sysclk; reg = prcm_read_4(CM_WKUP_CM_CLKSEL_DPLL_MPU); /*Check if we are running in bypass */ if (DPLL_BYP_CLKSEL(reg)) return ENXIO; am335x_clk_get_sysclk_freq(NULL, &sysclk); *freq = DPLL_MULT(reg) * (sysclk / DPLL_DIV(reg)); return(0); } static int am335x_clk_get_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int *freq) { uint32_t reg; uint32_t sysclk; reg = prcm_read_4(CM_WKUP_CM_CLKSEL_DPLL_DISP); /*Check if we are running in bypass */ if (DPLL_BYP_CLKSEL(reg)) return ENXIO; am335x_clk_get_sysclk_freq(NULL, &sysclk); *freq = DPLL_MULT(reg) * (sysclk / DPLL_DIV(reg)); return(0); } static void am335x_prcm_reset(void) { prcm_write_4(PRM_RSTCTRL, (1<<1)); } static int am335x_clk_cpsw_activate(struct ti_clock_dev *clkdev) { struct am335x_prcm_softc *sc = am335x_prcm_sc; if (sc == NULL) return ENXIO; /* set MODULENAME to ENABLE */ prcm_write_4(CM_PER_CPGMAC0_CLKCTRL, 2); /* wait for IDLEST to become Func(0) */ while(prcm_read_4(CM_PER_CPGMAC0_CLKCTRL) & (3<<16)); /*set CLKTRCTRL to SW_WKUP(2) */ prcm_write_4(CM_PER_CPSW_CLKSTCTRL, 2); /* wait for 125 MHz OCP clock to become active */ while((prcm_read_4(CM_PER_CPSW_CLKSTCTRL) & (1<<4)) == 0); return(0); } static int am335x_clk_musb0_activate(struct ti_clock_dev *clkdev) { struct am335x_prcm_softc *sc = am335x_prcm_sc; if (sc == NULL) return ENXIO; /* set ST_DPLL_CLKDCOLDO(9) to CLK_GATED(1) */ /* set DPLL_CLKDCOLDO_GATE_CTRL(8) to CLK_ENABLE(1)*/ prcm_write_4(CM_WKUP_CM_CLKDCOLDO_DPLL_PER, 0x300); /*set MODULEMODE to ENABLE(2) */ prcm_write_4(CM_PER_USB0_CLKCTRL, 2); /* wait for MODULEMODE to become ENABLE(2) */ while ((prcm_read_4(CM_PER_USB0_CLKCTRL) & 0x3) != 2) DELAY(10); /* wait for IDLEST to become Func(0) */ while(prcm_read_4(CM_PER_USB0_CLKCTRL) & (3<<16)) DELAY(10); return(0); } static int am335x_clk_lcdc_activate(struct ti_clock_dev *clkdev) { struct am335x_prcm_softc *sc = am335x_prcm_sc; if (sc == NULL) return (ENXIO); /* Bypass mode */ prcm_write_4(CM_WKUP_CM_CLKMODE_DPLL_DISP, 0x4); /* Make sure it's in bypass mode */ while (!(prcm_read_4(CM_WKUP_CM_IDLEST_DPLL_DISP) & (1 << 8))) DELAY(10); /* * For now set frequency to 99*SYSFREQ/8 which is twice as * HDMI 1080p pixel clock (minimum LCDC freq divisor is 2) */ prcm_write_4(CM_WKUP_CM_CLKSEL_DPLL_DISP, (99 << 8) | 8); /* Locked mode */ prcm_write_4(CM_WKUP_CM_CLKMODE_DPLL_DISP, 0x7); int timeout = 10000; while ((!(prcm_read_4(CM_WKUP_CM_IDLEST_DPLL_DISP) & (1 << 0))) && timeout--) DELAY(10); /*set MODULEMODE to ENABLE(2) */ prcm_write_4(CM_PER_LCDC_CLKCTRL, 2); /* wait for MODULEMODE to become ENABLE(2) */ while ((prcm_read_4(CM_PER_LCDC_CLKCTRL) & 0x3) != 2) DELAY(10); /* wait for IDLEST to become Func(0) */ while(prcm_read_4(CM_PER_LCDC_CLKCTRL) & (3<<16)) DELAY(10); return (0); } static int am335x_clk_pruss_activate(struct ti_clock_dev *clkdev) { struct am335x_prcm_softc *sc = am335x_prcm_sc; if (sc == NULL) return (ENXIO); /* Set MODULEMODE to ENABLE(2) */ prcm_write_4(CM_PER_PRUSS_CLKCTRL, 2); /* Wait for MODULEMODE to become ENABLE(2) */ while ((prcm_read_4(CM_PER_PRUSS_CLKCTRL) & 0x3) != 2) DELAY(10); /* Set CLKTRCTRL to SW_WKUP(2) */ prcm_write_4(CM_PER_PRUSS_CLKSTCTRL, 2); /* Wait for the 200 MHz OCP clock to become active */ while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<4)) == 0) DELAY(10); /* Wait for the 200 MHz IEP clock to become active */ while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<5)) == 0) DELAY(10); /* Wait for the 192 MHz UART clock to become active */ while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<6)) == 0) DELAY(10); /* Select L3F as OCP clock */ prcm_write_4(CLKSEL_PRUSS_OCP_CLK, 0); while ((prcm_read_4(CLKSEL_PRUSS_OCP_CLK) & 0x3) != 0) DELAY(10); /* Clear the RESET bit */ prcm_write_4(PRM_PER_RSTCTRL, prcm_read_4(PRM_PER_RSTCTRL) & ~2); return (0); } Index: head/sys/arm/ti/ti_common.c =================================================================== --- head/sys/arm/ti/ti_common.c (revision 281084) +++ head/sys/arm/ti/ti_common.c (revision 281085) @@ -1,96 +1,95 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include -#include #include struct fdt_fixup_entry fdt_fixup_table[] = { { NULL, NULL } }; #ifdef SOC_OMAP4 static int fdt_gic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, int *pol) { if (!fdt_is_compatible(node, "arm,gic")) return (ENXIO); *interrupt = fdt32_to_cpu(intr[0]); *trig = INTR_TRIGGER_CONFORM; *pol = INTR_POLARITY_CONFORM; return (0); } #endif #ifdef SOC_TI_AM335X static int fdt_aintc_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, int *pol) { if (!fdt_is_compatible(node, "ti,aintc")) return (ENXIO); *interrupt = fdt32_to_cpu(intr[0]); *trig = INTR_TRIGGER_CONFORM; *pol = INTR_POLARITY_CONFORM; return (0); } #endif fdt_pic_decode_t fdt_pic_table[] = { #ifdef SOC_OMAP4 &fdt_gic_decode_ic, #endif #ifdef SOC_TI_AM335X &fdt_aintc_decode_ic, #endif NULL }; Index: head/sys/arm/ti/ti_mbox.c =================================================================== --- head/sys/arm/ti/ti_mbox.c (revision 281084) +++ head/sys/arm/ti/ti_mbox.c (revision 281085) @@ -1,265 +1,264 @@ /*- * Copyright (c) 2013 Rui Paulo * 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$"); #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" #ifdef DEBUG #define DPRINTF(fmt, ...) do { \ printf("%s: ", __func__); \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(fmt, ...) #endif static device_probe_t ti_mbox_probe; static device_attach_t ti_mbox_attach; static device_detach_t ti_mbox_detach; static void ti_mbox_intr(void *); static int ti_mbox_read(device_t, int, uint32_t *); static int ti_mbox_write(device_t, int, uint32_t); struct ti_mbox_softc { struct mtx sc_mtx; struct resource *sc_mem_res; struct resource *sc_irq_res; void *sc_intr; bus_space_tag_t sc_bt; bus_space_handle_t sc_bh; }; #define TI_MBOX_LOCK(sc) mtx_lock(&(sc)->sc_mtx) #define TI_MBOX_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) static device_method_t ti_mbox_methods[] = { DEVMETHOD(device_probe, ti_mbox_probe), DEVMETHOD(device_attach, ti_mbox_attach), DEVMETHOD(device_detach, ti_mbox_detach), DEVMETHOD(mbox_read, ti_mbox_read), DEVMETHOD(mbox_write, ti_mbox_write), DEVMETHOD_END }; static driver_t ti_mbox_driver = { "ti_mbox", ti_mbox_methods, sizeof(struct ti_mbox_softc) }; static devclass_t ti_mbox_devclass; DRIVER_MODULE(ti_mbox, simplebus, ti_mbox_driver, ti_mbox_devclass, 0, 0); static __inline uint32_t ti_mbox_reg_read(struct ti_mbox_softc *sc, uint16_t reg) { return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); } static __inline void ti_mbox_reg_write(struct ti_mbox_softc *sc, uint16_t reg, uint32_t val) { bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); } static int ti_mbox_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "ti,system-mbox")) { device_set_desc(dev, "TI System Mailbox"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int ti_mbox_attach(device_t dev) { struct ti_mbox_softc *sc; int rid, delay, chan; uint32_t rev, sysconfig; if (ti_prcm_clk_enable(MAILBOX0_CLK) != 0) { device_printf(dev, "could not enable MBOX clock\n"); return (ENXIO); } sc = device_get_softc(dev); rid = 0; mtx_init(&sc->sc_mtx, "TI mbox", NULL, MTX_DEF); sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } sc->sc_bt = rman_get_bustag(sc->sc_mem_res); sc->sc_bh = 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 == NULL) { device_printf(dev, "could not allocate interrupt resource\n"); ti_mbox_detach(dev); return (ENXIO); } if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC, NULL, ti_mbox_intr, sc, &sc->sc_intr) != 0) { device_printf(dev, "unable to setup the interrupt handler\n"); ti_mbox_detach(dev); return (ENXIO); } /* * Reset the controller. */ sysconfig = ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG); DPRINTF("initial sysconfig %d\n", sysconfig); sysconfig |= TI_MBOX_SYSCONFIG_SOFTRST; delay = 100; while (ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG) & TI_MBOX_SYSCONFIG_SOFTRST) { delay--; DELAY(10); } if (delay == 0) { device_printf(dev, "controller reset failed\n"); ti_mbox_detach(dev); return (ENXIO); } /* * Enable smart idle mode. */ ti_mbox_reg_write(sc, TI_MBOX_SYSCONFIG, ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG) | TI_MBOX_SYSCONFIG_SMARTIDLE); rev = ti_mbox_reg_read(sc, TI_MBOX_REVISION); DPRINTF("rev %d\n", rev); device_printf(dev, "revision %d.%d\n", (rev >> 8) & 0x4, rev & 0x40); /* * Enable message interrupts. */ for (chan = 0; chan < 8; chan++) ti_mbox_reg_write(sc, TI_MBOX_IRQENABLE_SET(chan), 1); return (0); } static int ti_mbox_detach(device_t dev) { struct ti_mbox_softc *sc; sc = device_get_softc(dev); if (sc->sc_intr) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res), sc->sc_mem_res); return (0); } static void ti_mbox_intr(void *arg) { struct ti_mbox_softc *sc; sc = arg; DPRINTF("interrupt %p", sc); } static int ti_mbox_read(device_t dev, int chan, uint32_t *data) { struct ti_mbox_softc *sc; if (chan < 0 || chan > 7) return (EINVAL); sc = device_get_softc(dev); return (ti_mbox_reg_read(sc, TI_MBOX_MESSAGE(chan))); } static int ti_mbox_write(device_t dev, int chan, uint32_t data) { int limit = 500; struct ti_mbox_softc *sc; if (chan < 0 || chan > 7) return (EINVAL); sc = device_get_softc(dev); TI_MBOX_LOCK(sc); /* XXX implement interrupt method */ while (ti_mbox_reg_read(sc, TI_MBOX_FIFOSTATUS(chan)) == 1 && limit--) { DELAY(10); } if (limit == 0) { device_printf(dev, "FIFOSTAUS%d stuck\n", chan); TI_MBOX_UNLOCK(sc); return (EAGAIN); } ti_mbox_reg_write(sc, TI_MBOX_MESSAGE(chan), data); return (0); } Index: head/sys/arm/ti/ti_pruss.c =================================================================== --- head/sys/arm/ti/ti_pruss.c (revision 281084) +++ head/sys/arm/ti/ti_pruss.c (revision 281085) @@ -1,305 +1,304 @@ /*- * Copyright (c) 2013 Rui Paulo * 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #ifdef DEBUG #define DPRINTF(fmt, ...) do { \ printf("%s: ", __func__); \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(fmt, ...) #endif static device_probe_t ti_pruss_probe; static device_attach_t ti_pruss_attach; static device_detach_t ti_pruss_detach; static void ti_pruss_intr(void *); static d_open_t ti_pruss_open; static d_mmap_t ti_pruss_mmap; static void ti_pruss_kq_read_detach(struct knote *); static int ti_pruss_kq_read_event(struct knote *, long); static d_kqfilter_t ti_pruss_kqfilter; #define TI_PRUSS_IRQS 8 struct ti_pruss_softc { struct mtx sc_mtx; struct resource *sc_mem_res; struct resource *sc_irq_res[TI_PRUSS_IRQS]; void *sc_intr[TI_PRUSS_IRQS]; bus_space_tag_t sc_bt; bus_space_handle_t sc_bh; struct cdev *sc_pdev; struct selinfo sc_selinfo; }; static struct cdevsw ti_pruss_cdevsw = { .d_version = D_VERSION, .d_name = "ti_pruss", .d_open = ti_pruss_open, .d_mmap = ti_pruss_mmap, .d_kqfilter = ti_pruss_kqfilter, }; static device_method_t ti_pruss_methods[] = { DEVMETHOD(device_probe, ti_pruss_probe), DEVMETHOD(device_attach, ti_pruss_attach), DEVMETHOD(device_detach, ti_pruss_detach), DEVMETHOD_END }; static driver_t ti_pruss_driver = { "ti_pruss", ti_pruss_methods, sizeof(struct ti_pruss_softc) }; static devclass_t ti_pruss_devclass; DRIVER_MODULE(ti_pruss, simplebus, ti_pruss_driver, ti_pruss_devclass, 0, 0); static struct resource_spec ti_pruss_irq_spec[] = { { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { SYS_RES_IRQ, 3, RF_ACTIVE }, { SYS_RES_IRQ, 4, RF_ACTIVE }, { SYS_RES_IRQ, 5, RF_ACTIVE }, { SYS_RES_IRQ, 6, RF_ACTIVE }, { SYS_RES_IRQ, 7, RF_ACTIVE }, { -1, 0, 0 } }; static struct ti_pruss_irq_arg { int irq; struct ti_pruss_softc *sc; } ti_pruss_irq_args[TI_PRUSS_IRQS]; static __inline uint32_t ti_pruss_reg_read(struct ti_pruss_softc *sc, uint32_t reg) { return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); } static __inline void ti_pruss_reg_write(struct ti_pruss_softc *sc, uint32_t reg, uint32_t val) { bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); } static int ti_pruss_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "ti,pruss-v1") || ofw_bus_is_compatible(dev, "ti,pruss-v2")) { device_set_desc(dev, "TI Programmable Realtime Unit Subsystem"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int ti_pruss_attach(device_t dev) { struct ti_pruss_softc *sc; int rid, i; if (ti_prcm_clk_enable(PRUSS_CLK) != 0) { device_printf(dev, "could not enable PRUSS clock\n"); return (ENXIO); } sc = device_get_softc(dev); rid = 0; mtx_init(&sc->sc_mtx, "TI PRUSS", NULL, MTX_DEF); sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } sc->sc_bt = rman_get_bustag(sc->sc_mem_res); sc->sc_bh = rman_get_bushandle(sc->sc_mem_res); if (bus_alloc_resources(dev, ti_pruss_irq_spec, sc->sc_irq_res) != 0) { device_printf(dev, "could not allocate interrupt resource\n"); ti_pruss_detach(dev); return (ENXIO); } for (i = 0; i < TI_PRUSS_IRQS; i++) { ti_pruss_irq_args[i].irq = i; ti_pruss_irq_args[i].sc = sc; if (bus_setup_intr(dev, sc->sc_irq_res[i], INTR_MPSAFE | INTR_TYPE_MISC, NULL, ti_pruss_intr, &ti_pruss_irq_args[i], &sc->sc_intr[i]) != 0) { device_printf(dev, "unable to setup the interrupt handler\n"); ti_pruss_detach(dev); return (ENXIO); } } if (ti_pruss_reg_read(sc, PRUSS_AM18XX_INTC) == PRUSS_AM18XX_REV) device_printf(dev, "AM18xx PRU-ICSS\n"); else if (ti_pruss_reg_read(sc, PRUSS_AM33XX_INTC) == PRUSS_AM33XX_REV) device_printf(dev, "AM33xx PRU-ICSS\n"); sc->sc_pdev = make_dev(&ti_pruss_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "pruss%d", device_get_unit(dev)); sc->sc_pdev->si_drv1 = dev; return (0); } static int ti_pruss_detach(device_t dev) { struct ti_pruss_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < TI_PRUSS_IRQS; i++) { if (sc->sc_intr[i]) bus_teardown_intr(dev, sc->sc_irq_res[i], sc->sc_intr[i]); if (sc->sc_irq_res[i]) bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res[i]), sc->sc_irq_res[i]); } if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res), sc->sc_mem_res); if (sc->sc_pdev) destroy_dev(sc->sc_pdev); return (0); } static void ti_pruss_intr(void *arg) { struct ti_pruss_irq_arg *iap; struct ti_pruss_softc *sc; iap = arg; sc = iap->sc; DPRINTF("interrupt %p", sc); KNOTE_UNLOCKED(&sc->sc_selinfo.si_note, iap->irq); } static int ti_pruss_open(struct cdev *cdev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused) { return (0); } static int ti_pruss_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr) { device_t dev = cdev->si_drv1; struct ti_pruss_softc *sc = device_get_softc(dev); if (offset > rman_get_size(sc->sc_mem_res)) return (-1); *paddr = rman_get_start(sc->sc_mem_res) + offset; *memattr = VM_MEMATTR_UNCACHEABLE; return (0); } static struct filterops ti_pruss_kq_read = { .f_isfd = 1, .f_detach = ti_pruss_kq_read_detach, .f_event = ti_pruss_kq_read_event, }; static void ti_pruss_kq_read_detach(struct knote *kn) { struct ti_pruss_softc *sc = kn->kn_hook; knlist_remove(&sc->sc_selinfo.si_note, kn, 0); } static int ti_pruss_kq_read_event(struct knote *kn, long hint) { kn->kn_data = hint; return (hint); } static int ti_pruss_kqfilter(struct cdev *cdev, struct knote *kn) { device_t dev = cdev->si_drv1; struct ti_pruss_softc *sc = device_get_softc(dev); switch (kn->kn_filter) { case EVFILT_READ: kn->kn_hook = sc; kn->kn_fop = &ti_pruss_kq_read; knlist_add(&sc->sc_selinfo.si_note, kn, 1); break; default: return (EINVAL); } return (0); } Index: head/sys/arm/ti/ti_wdt.c =================================================================== --- head/sys/arm/ti/ti_wdt.c (revision 281084) +++ head/sys/arm/ti/ti_wdt.c (revision 281085) @@ -1,277 +1,276 @@ /*- * Copyright (c) 2014 Rui Paulo * 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #ifdef DEBUG #define DPRINTF(fmt, ...) do { \ printf("%s: ", __func__); \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(fmt, ...) #endif static device_probe_t ti_wdt_probe; static device_attach_t ti_wdt_attach; static device_detach_t ti_wdt_detach; static void ti_wdt_intr(void *); static void ti_wdt_event(void *, unsigned int, int *); struct ti_wdt_softc { struct resource *sc_mem_res; struct resource *sc_irq_res; void *sc_intr; bus_space_tag_t sc_bt; bus_space_handle_t sc_bh; eventhandler_tag sc_ev_tag; }; static device_method_t ti_wdt_methods[] = { DEVMETHOD(device_probe, ti_wdt_probe), DEVMETHOD(device_attach, ti_wdt_attach), DEVMETHOD(device_detach, ti_wdt_detach), DEVMETHOD_END }; static driver_t ti_wdt_driver = { "ti_wdt", ti_wdt_methods, sizeof(struct ti_wdt_softc) }; static devclass_t ti_wdt_devclass; DRIVER_MODULE(ti_wdt, simplebus, ti_wdt_driver, ti_wdt_devclass, 0, 0); static __inline uint32_t ti_wdt_reg_read(struct ti_wdt_softc *sc, uint32_t reg) { return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); } static __inline void ti_wdt_reg_write(struct ti_wdt_softc *sc, uint32_t reg, uint32_t val) { bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); } /* * Wait for the write to a specific synchronised register to complete. */ static __inline void ti_wdt_reg_wait(struct ti_wdt_softc *sc, uint32_t bit) { while (ti_wdt_reg_read(sc, TI_WDT_WWPS) & bit) DELAY(10); } static __inline void ti_wdt_disable(struct ti_wdt_softc *sc) { DPRINTF("disabling watchdog %p\n", sc); ti_wdt_reg_write(sc, TI_WDT_WSPR, 0xAAAA); ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); ti_wdt_reg_write(sc, TI_WDT_WSPR, 0x5555); ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); } static __inline void ti_wdt_enable(struct ti_wdt_softc *sc) { DPRINTF("enabling watchdog %p\n", sc); ti_wdt_reg_write(sc, TI_WDT_WSPR, 0xBBBB); ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); ti_wdt_reg_write(sc, TI_WDT_WSPR, 0x4444); ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); } static int ti_wdt_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "ti,omap3-wdt")) { device_set_desc(dev, "TI Watchdog Timer"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int ti_wdt_attach(device_t dev) { struct ti_wdt_softc *sc; int rid; sc = device_get_softc(dev); rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } sc->sc_bt = rman_get_bustag(sc->sc_mem_res); sc->sc_bh = rman_get_bushandle(sc->sc_mem_res); sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(dev, "could not allocate interrupt resource\n"); ti_wdt_detach(dev); return (ENXIO); } if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC, NULL, ti_wdt_intr, sc, &sc->sc_intr) != 0) { device_printf(dev, "unable to setup the interrupt handler\n"); ti_wdt_detach(dev); return (ENXIO); } /* Reset, enable interrupts and stop the watchdog. */ ti_wdt_reg_write(sc, TI_WDT_WDSC, ti_wdt_reg_read(sc, TI_WDT_WDSC) | TI_WDSC_SR); while (ti_wdt_reg_read(sc, TI_WDT_WDSC) & TI_WDSC_SR) DELAY(10); ti_wdt_reg_write(sc, TI_WDT_WIRQENSET, TI_IRQ_EN_OVF | TI_IRQ_EN_DLY); ti_wdt_disable(sc); if (bootverbose) device_printf(dev, "revision: 0x%x\n", ti_wdt_reg_read(sc, TI_WDT_WIDR)); sc->sc_ev_tag = EVENTHANDLER_REGISTER(watchdog_list, ti_wdt_event, sc, 0); return (0); } static int ti_wdt_detach(device_t dev) { struct ti_wdt_softc *sc; sc = device_get_softc(dev); if (sc->sc_ev_tag) EVENTHANDLER_DEREGISTER(watchdog_list, sc->sc_ev_tag); if (sc->sc_intr) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res), sc->sc_mem_res); return (0); } static void ti_wdt_intr(void *arg) { struct ti_wdt_softc *sc; sc = arg; DPRINTF("interrupt %p", sc); ti_wdt_reg_write(sc, TI_WDT_WIRQSTAT, TI_IRQ_EV_OVF | TI_IRQ_EV_DLY); /* TODO: handle interrupt */ } static void ti_wdt_event(void *arg, unsigned int cmd, int *error) { struct ti_wdt_softc *sc; uint8_t s; uint32_t wldr; uint32_t ptv; sc = arg; ti_wdt_disable(sc); if (cmd == WD_TO_NEVER) { *error = 0; return; } DPRINTF("cmd 0x%x\n", cmd); cmd &= WD_INTERVAL; if (cmd < WD_TO_1SEC) { *error = EINVAL; return; } s = 1 << (cmd - WD_TO_1SEC); DPRINTF("seconds %u\n", s); /* * Leave the pre-scaler with its default values: * PTV = 0 == 2**0 == 1 * PRE = 1 (enabled) * * Compute the load register value assuming a 32kHz clock. * See OVF_Rate in the WDT section of the AM335x TRM. */ ptv = 0; wldr = 0xffffffff - (s * (32768 / (1 << ptv))) + 1; DPRINTF("wldr 0x%x\n", wldr); ti_wdt_reg_write(sc, TI_WDT_WLDR, wldr); /* * Trigger a timer reload. */ ti_wdt_reg_write(sc, TI_WDT_WTGR, ti_wdt_reg_read(sc, TI_WDT_WTGR) + 1); ti_wdt_reg_wait(sc, TI_W_PEND_WTGR); ti_wdt_enable(sc); *error = 0; } Index: head/sys/arm/versatile/pl050.c =================================================================== --- head/sys/arm/versatile/pl050.c (revision 281084) +++ head/sys/arm/versatile/pl050.c (revision 281085) @@ -1,715 +1,714 @@ /* * Copyright (c) 2012 Oleksandr Tymoshenko * All rights reserved. * * Based on dev/usb/input/ukbd.c * * 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 #define KMI_LOCK() mtx_lock(&Giant) #define KMI_UNLOCK() mtx_unlock(&Giant) #ifdef INVARIANTS /* * Assert that the lock is held in all contexts * where the code can be executed. */ #define KMI_LOCK_ASSERT() mtx_assert(&Giant, MA_OWNED) /* * Assert that the lock is held in the contexts * where it really has to be so. */ #define KMI_CTX_LOCK_ASSERT() \ do { \ if (!kdb_active && panicstr == NULL) \ mtx_assert(&Giant, MA_OWNED); \ } while (0) #else #define KMI_LOCK_ASSERT() (void)0 #define KMI_CTX_LOCK_ASSERT() (void)0 #endif #define KMICR 0x00 #define KMICR_TYPE_NONPS2 (1 << 5) #define KMICR_RXINTREN (1 << 4) #define KMICR_TXINTREN (1 << 3) #define KMICR_EN (1 << 2) #define KMICR_FKMID (1 << 1) #define KMICR_FKMIC (1 << 0) #define KMISTAT 0x04 #define KMISTAT_TXEMPTY (1 << 6) #define KMISTAT_TXBUSY (1 << 5) #define KMISTAT_RXFULL (1 << 4) #define KMISTAT_RXBUSY (1 << 3) #define KMISTAT_RXPARITY (1 << 2) #define KMISTAT_KMIC (1 << 1) #define KMISTAT_KMID (1 << 0) #define KMIDATA 0x08 #define KMICLKDIV 0x0C #define KMIIR 0x10 #define KMIIR_TXINTR (1 << 1) #define KMIIR_RXINTR (1 << 0) #define KMI_DRIVER_NAME "kmi" #define KMI_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */ struct kmi_softc { keyboard_t sc_kbd; keymap_t sc_keymap; accentmap_t sc_accmap; fkeytab_t sc_fkeymap[KMI_NFKEY]; struct resource* sc_mem_res; struct resource* sc_irq_res; void* sc_intr_hl; int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ int sc_state; /* shift/lock key state */ int sc_accents; /* accent key index (> 0) */ uint32_t sc_flags; /* flags */ #define KMI_FLAG_COMPOSE 0x00000001 #define KMI_FLAG_POLLING 0x00000002 struct thread *sc_poll_thread; }; /* Read/Write macros for Timer used as timecounter */ #define pl050_kmi_read_4(sc, reg) \ bus_read_4((sc)->sc_mem_res, (reg)) #define pl050_kmi_write_4(sc, reg, val) \ bus_write_4((sc)->sc_mem_res, (reg), (val)) /* prototypes */ static void kmi_set_leds(struct kmi_softc *, uint8_t); static int kmi_set_typematic(keyboard_t *, int); static uint32_t kmi_read_char(keyboard_t *, int); static void kmi_clear_state(keyboard_t *); static int kmi_ioctl(keyboard_t *, u_long, caddr_t); static int kmi_enable(keyboard_t *); static int kmi_disable(keyboard_t *); /* early keyboard probe, not supported */ static int kmi_configure(int flags) { return (0); } /* detect a keyboard, not used */ static int kmi_probe(int unit, void *arg, int flags) { return (ENXIO); } /* reset and initialize the device, not used */ static int kmi_init(int unit, keyboard_t **kbdp, void *arg, int flags) { return (ENXIO); } /* test the interface to the device, not used */ static int kmi_test_if(keyboard_t *kbd) { return (0); } /* finish using this keyboard, not used */ static int kmi_term(keyboard_t *kbd) { return (ENXIO); } /* keyboard interrupt routine, not used */ static int kmi_intr(keyboard_t *kbd, void *arg) { return (0); } /* lock the access to the keyboard, not used */ static int kmi_lock(keyboard_t *kbd, int lock) { return (1); } /* * Enable the access to the device; until this function is called, * the client cannot read from the keyboard. */ static int kmi_enable(keyboard_t *kbd) { KMI_LOCK(); KBD_ACTIVATE(kbd); KMI_UNLOCK(); return (0); } /* disallow the access to the device */ static int kmi_disable(keyboard_t *kbd) { KMI_LOCK(); KBD_DEACTIVATE(kbd); KMI_UNLOCK(); return (0); } /* check if data is waiting */ static int kmi_check(keyboard_t *kbd) { struct kmi_softc *sc = kbd->kb_data; uint32_t reg; KMI_CTX_LOCK_ASSERT(); if (!KBD_IS_ACTIVE(kbd)) return (0); reg = pl050_kmi_read_4(sc, KMIIR); return (reg & KMIIR_RXINTR); } /* check if char is waiting */ static int kmi_check_char_locked(keyboard_t *kbd) { KMI_CTX_LOCK_ASSERT(); if (!KBD_IS_ACTIVE(kbd)) return (0); return (kmi_check(kbd)); } static int kmi_check_char(keyboard_t *kbd) { int result; KMI_LOCK(); result = kmi_check_char_locked(kbd); KMI_UNLOCK(); return (result); } /* read one byte from the keyboard if it's allowed */ /* Currently unused. */ static int kmi_read(keyboard_t *kbd, int wait) { KMI_CTX_LOCK_ASSERT(); if (!KBD_IS_ACTIVE(kbd)) return (-1); ++(kbd->kb_count); printf("Implement ME: %s\n", __func__); return (0); } /* read char from the keyboard */ static uint32_t kmi_read_char_locked(keyboard_t *kbd, int wait) { struct kmi_softc *sc = kbd->kb_data; uint32_t reg, data; KMI_CTX_LOCK_ASSERT(); if (!KBD_IS_ACTIVE(kbd)) return (NOKEY); reg = pl050_kmi_read_4(sc, KMIIR); if (reg & KMIIR_RXINTR) { data = pl050_kmi_read_4(sc, KMIDATA); return (data); } ++kbd->kb_count; return (NOKEY); } /* Currently wait is always false. */ static uint32_t kmi_read_char(keyboard_t *kbd, int wait) { uint32_t keycode; KMI_LOCK(); keycode = kmi_read_char_locked(kbd, wait); KMI_UNLOCK(); return (keycode); } /* some useful control functions */ static int kmi_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg) { struct kmi_softc *sc = kbd->kb_data; int i; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) int ival; #endif KMI_LOCK_ASSERT(); switch (cmd) { case KDGKBMODE: /* get keyboard mode */ *(int *)arg = sc->sc_mode; break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 7): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBMODE: /* set keyboard mode */ switch (*(int *)arg) { case K_XLATE: if (sc->sc_mode != K_XLATE) { /* make lock key state and LED state match */ sc->sc_state &= ~LOCK_MASK; sc->sc_state |= KBD_LED_VAL(kbd); } /* FALLTHROUGH */ case K_RAW: case K_CODE: if (sc->sc_mode != *(int *)arg) { if ((sc->sc_flags & KMI_FLAG_POLLING) == 0) kmi_clear_state(kbd); sc->sc_mode = *(int *)arg; } break; default: return (EINVAL); } break; case KDGETLED: /* get keyboard LED */ *(int *)arg = KBD_LED_VAL(kbd); break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 66): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETLED: /* set keyboard LED */ /* NOTE: lock key state in "sc_state" won't be changed */ if (*(int *)arg & ~LOCK_MASK) return (EINVAL); i = *(int *)arg; /* replace CAPS LED with ALTGR LED for ALTGR keyboards */ if (sc->sc_mode == K_XLATE && kbd->kb_keymap->n_keys > ALTGR_OFFSET) { if (i & ALKED) i |= CLKED; else i &= ~CLKED; } if (KBD_HAS_DEVICE(kbd)) kmi_set_leds(sc, i); KBD_LED_VAL(kbd) = *(int *)arg; break; case KDGKBSTATE: /* get lock key state */ *(int *)arg = sc->sc_state & LOCK_MASK; break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 20): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBSTATE: /* set lock key state */ if (*(int *)arg & ~LOCK_MASK) { return (EINVAL); } sc->sc_state &= ~LOCK_MASK; sc->sc_state |= *(int *)arg; /* set LEDs and quit */ return (kmi_ioctl(kbd, KDSETLED, arg)); case KDSETREPEAT: /* set keyboard repeat rate (new * interface) */ if (!KBD_HAS_DEVICE(kbd)) { return (0); } if (((int *)arg)[1] < 0) { return (EINVAL); } if (((int *)arg)[0] < 0) { return (EINVAL); } if (((int *)arg)[0] < 200) /* fastest possible value */ kbd->kb_delay1 = 200; else kbd->kb_delay1 = ((int *)arg)[0]; kbd->kb_delay2 = ((int *)arg)[1]; return (0); #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 67): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETRAD: /* set keyboard repeat rate (old * interface) */ return (kmi_set_typematic(kbd, *(int *)arg)); case PIO_KEYMAP: /* set keyboard translation table */ case OPIO_KEYMAP: /* set keyboard translation table * (compat) */ case PIO_KEYMAPENT: /* set keyboard translation table * entry */ case PIO_DEADKEYMAP: /* set accent key translation table */ sc->sc_accents = 0; /* FALLTHROUGH */ default: return (genkbd_commonioctl(kbd, cmd, arg)); } return (0); } static int kmi_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) { int result; /* * XXX KDGKBSTATE, KDSKBSTATE and KDSETLED can be called from any * context where printf(9) can be called, which among other things * includes interrupt filters and threads with any kinds of locks * already held. For this reason it would be dangerous to acquire * the Giant here unconditionally. On the other hand we have to * have it to handle the ioctl. * So we make our best effort to auto-detect whether we can grab * the Giant or not. Blame syscons(4) for this. */ switch (cmd) { case KDGKBSTATE: case KDSKBSTATE: case KDSETLED: if (!mtx_owned(&Giant) && !SCHEDULER_STOPPED()) return (EDEADLK); /* best I could come up with */ /* FALLTHROUGH */ default: KMI_LOCK(); result = kmi_ioctl_locked(kbd, cmd, arg); KMI_UNLOCK(); return (result); } } /* clear the internal state of the keyboard */ static void kmi_clear_state(keyboard_t *kbd) { struct kmi_softc *sc = kbd->kb_data; KMI_CTX_LOCK_ASSERT(); sc->sc_flags &= ~(KMI_FLAG_COMPOSE | KMI_FLAG_POLLING); sc->sc_state &= LOCK_MASK; /* preserve locking key state */ sc->sc_accents = 0; } /* save the internal state, not used */ static int kmi_get_state(keyboard_t *kbd, void *buf, size_t len) { return (len == 0) ? 1 : -1; } /* set the internal state, not used */ static int kmi_set_state(keyboard_t *kbd, void *buf, size_t len) { return (EINVAL); } static int kmi_poll(keyboard_t *kbd, int on) { struct kmi_softc *sc = kbd->kb_data; KMI_LOCK(); if (on) { sc->sc_flags |= KMI_FLAG_POLLING; sc->sc_poll_thread = curthread; } else { sc->sc_flags &= ~KMI_FLAG_POLLING; } KMI_UNLOCK(); return (0); } /* local functions */ static void kmi_set_leds(struct kmi_softc *sc, uint8_t leds) { KMI_LOCK_ASSERT(); /* start transfer, if not already started */ printf("Implement me: %s\n", __func__); } static int kmi_set_typematic(keyboard_t *kbd, int code) { static const int delays[] = {250, 500, 750, 1000}; static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63, 68, 76, 84, 92, 100, 110, 118, 126, 136, 152, 168, 184, 200, 220, 236, 252, 272, 304, 336, 368, 400, 440, 472, 504}; if (code & ~0x7f) { return (EINVAL); } kbd->kb_delay1 = delays[(code >> 5) & 3]; kbd->kb_delay2 = rates[code & 0x1f]; return (0); } static keyboard_switch_t kmisw = { .probe = &kmi_probe, .init = &kmi_init, .term = &kmi_term, .intr = &kmi_intr, .test_if = &kmi_test_if, .enable = &kmi_enable, .disable = &kmi_disable, .read = &kmi_read, .check = &kmi_check, .read_char = &kmi_read_char, .check_char = &kmi_check_char, .ioctl = &kmi_ioctl, .lock = &kmi_lock, .clear_state = &kmi_clear_state, .get_state = &kmi_get_state, .set_state = &kmi_set_state, .get_fkeystr = &genkbd_get_fkeystr, .poll = &kmi_poll, .diag = &genkbd_diag, }; KEYBOARD_DRIVER(kmi, kmisw, kmi_configure); static void pl050_kmi_intr(void *arg) { struct kmi_softc *sc = arg; uint32_t c; KMI_CTX_LOCK_ASSERT(); if ((sc->sc_flags & KMI_FLAG_POLLING) != 0) return; if (KBD_IS_ACTIVE(&sc->sc_kbd) && KBD_IS_BUSY(&sc->sc_kbd)) { /* let the callback function process the input */ (sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg); } else { /* read and discard the input, no one is waiting for it */ do { c = kmi_read_char_locked(&sc->sc_kbd, 0); } while (c != NOKEY); } } static int pl050_kmi_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "arm,pl050")) { device_set_desc(dev, "PL050 Keyboard/Mouse Interface"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int pl050_kmi_attach(device_t dev) { struct kmi_softc *sc = device_get_softc(dev); keyboard_t *kbd; int rid; int i; kbd = &sc->sc_kbd; rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } /* Request the IRQ resources */ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(dev, "Error: could not allocate irq resources\n"); return (ENXIO); } /* Setup and enable the timer */ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_CLK, NULL, pl050_kmi_intr, sc, &sc->sc_intr_hl) != 0) { bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_irq_res); device_printf(dev, "Unable to setup the clock irq handler.\n"); return (ENXIO); } /* TODO: clock & divisor */ pl050_kmi_write_4(sc, KMICR, KMICR_EN | KMICR_RXINTREN); kbd_init_struct(kbd, KMI_DRIVER_NAME, KB_OTHER, device_get_unit(dev), 0, 0, 0); kbd->kb_data = (void *)sc; sc->sc_keymap = key_map; sc->sc_accmap = accent_map; for (i = 0; i < KMI_NFKEY; i++) { sc->sc_fkeymap[i] = fkey_tab[i]; } kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap, sc->sc_fkeymap, KMI_NFKEY); KBD_FOUND_DEVICE(kbd); kmi_clear_state(kbd); KBD_PROBE_DONE(kbd); KBD_INIT_DONE(kbd); if (kbd_register(kbd) < 0) { goto detach; } KBD_CONFIG_DONE(kbd); #ifdef KBD_INSTALL_CDEV if (kbd_attach(kbd)) { goto detach; } #endif if (bootverbose) { genkbd_diag(kbd, bootverbose); } return (0); detach: return (ENXIO); } static device_method_t pl050_kmi_methods[] = { DEVMETHOD(device_probe, pl050_kmi_probe), DEVMETHOD(device_attach, pl050_kmi_attach), { 0, 0 } }; static driver_t pl050_kmi_driver = { "kmi", pl050_kmi_methods, sizeof(struct kmi_softc), }; static devclass_t pl050_kmi_devclass; DRIVER_MODULE(pl050_kmi, simplebus, pl050_kmi_driver, pl050_kmi_devclass, 0, 0); Index: head/sys/arm/versatile/sp804.c =================================================================== --- head/sys/arm/versatile/sp804.c (revision 281084) +++ head/sys/arm/versatile/sp804.c (revision 281085) @@ -1,362 +1,361 @@ /* * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2012 Damjan Marion * 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 #define SP804_TIMER1_LOAD 0x00 #define SP804_TIMER1_VALUE 0x04 #define SP804_TIMER1_CONTROL 0x08 #define TIMER_CONTROL_EN (1 << 7) #define TIMER_CONTROL_FREERUN (0 << 6) #define TIMER_CONTROL_PERIODIC (1 << 6) #define TIMER_CONTROL_INTREN (1 << 5) #define TIMER_CONTROL_DIV1 (0 << 2) #define TIMER_CONTROL_DIV16 (1 << 2) #define TIMER_CONTROL_DIV256 (2 << 2) #define TIMER_CONTROL_32BIT (1 << 1) #define TIMER_CONTROL_ONESHOT (1 << 0) #define SP804_TIMER1_INTCLR 0x0C #define SP804_TIMER1_RIS 0x10 #define SP804_TIMER1_MIS 0x14 #define SP804_TIMER1_BGLOAD 0x18 #define SP804_TIMER2_LOAD 0x20 #define SP804_TIMER2_VALUE 0x24 #define SP804_TIMER2_CONTROL 0x28 #define SP804_TIMER2_INTCLR 0x2C #define SP804_TIMER2_RIS 0x30 #define SP804_TIMER2_MIS 0x34 #define SP804_TIMER2_BGLOAD 0x38 #define SP804_PERIPH_ID0 0xFE0 #define SP804_PERIPH_ID1 0xFE4 #define SP804_PERIPH_ID2 0xFE8 #define SP804_PERIPH_ID3 0xFEC #define SP804_PRIMECELL_ID0 0xFF0 #define SP804_PRIMECELL_ID1 0xFF4 #define SP804_PRIMECELL_ID2 0xFF8 #define SP804_PRIMECELL_ID3 0xFFC #define DEFAULT_FREQUENCY 1000000 /* * QEMU seems to have problem with full frequency */ #define DEFAULT_DIVISOR 16 #define DEFAULT_CONTROL_DIV TIMER_CONTROL_DIV16 struct sp804_timer_softc { struct resource* mem_res; struct resource* irq_res; void* intr_hl; uint32_t sysclk_freq; bus_space_tag_t bst; bus_space_handle_t bsh; struct timecounter tc; bool et_enabled; struct eventtimer et; int timer_initialized; }; /* Read/Write macros for Timer used as timecounter */ #define sp804_timer_tc_read_4(reg) \ bus_space_read_4(sc->bst, sc->bsh, reg) #define sp804_timer_tc_write_4(reg, val) \ bus_space_write_4(sc->bst, sc->bsh, reg, val) static unsigned sp804_timer_tc_get_timecount(struct timecounter *); static unsigned sp804_timer_tc_get_timecount(struct timecounter *tc) { struct sp804_timer_softc *sc = tc->tc_priv; return 0xffffffff - sp804_timer_tc_read_4(SP804_TIMER1_VALUE); } static int sp804_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { struct sp804_timer_softc *sc = et->et_priv; uint32_t count, reg; if (first != 0) { sc->et_enabled = 1; count = ((uint32_t)et->et_frequency * first) >> 32; sp804_timer_tc_write_4(SP804_TIMER2_LOAD, count); reg = TIMER_CONTROL_32BIT | TIMER_CONTROL_INTREN | TIMER_CONTROL_PERIODIC | DEFAULT_CONTROL_DIV | TIMER_CONTROL_EN; sp804_timer_tc_write_4(SP804_TIMER2_CONTROL, reg); return (0); } if (period != 0) { panic("period"); } return (EINVAL); } static int sp804_timer_stop(struct eventtimer *et) { struct sp804_timer_softc *sc = et->et_priv; uint32_t reg; sc->et_enabled = 0; reg = sp804_timer_tc_read_4(SP804_TIMER2_CONTROL); reg &= ~(TIMER_CONTROL_EN); sp804_timer_tc_write_4(SP804_TIMER2_CONTROL, reg); return (0); } static int sp804_timer_intr(void *arg) { struct sp804_timer_softc *sc = arg; static uint32_t prev = 0; uint32_t x = 0; x = sp804_timer_tc_read_4(SP804_TIMER1_VALUE); prev =x ; sp804_timer_tc_write_4(SP804_TIMER2_INTCLR, 1); if (sc->et_enabled) { if (sc->et.et_active) { sc->et.et_event_cb(&sc->et, sc->et.et_arg); } } return (FILTER_HANDLED); } static int sp804_timer_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "arm,sp804")) { device_set_desc(dev, "SP804 System Timer"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int sp804_timer_attach(device_t dev) { struct sp804_timer_softc *sc = device_get_softc(dev); int rid = 0; int i; uint32_t id, reg; phandle_t node; pcell_t clock; 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); /* Request the IRQ resources */ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "Error: could not allocate irq resources\n"); return (ENXIO); } sc->sysclk_freq = DEFAULT_FREQUENCY; /* Get the base clock frequency */ node = ofw_bus_get_node(dev); if ((OF_getprop(node, "clock-frequency", &clock, sizeof(clock))) > 0) { sc->sysclk_freq = fdt32_to_cpu(clock); } /* Setup and enable the timer */ if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_CLK, sp804_timer_intr, NULL, 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); } sp804_timer_tc_write_4(SP804_TIMER1_CONTROL, 0); sp804_timer_tc_write_4(SP804_TIMER2_CONTROL, 0); /* * Timer 1, timecounter */ sc->tc.tc_frequency = sc->sysclk_freq; sc->tc.tc_name = "SP804 Time Counter"; sc->tc.tc_get_timecount = sp804_timer_tc_get_timecount; sc->tc.tc_poll_pps = NULL; sc->tc.tc_counter_mask = ~0u; sc->tc.tc_quality = 1000; sc->tc.tc_priv = sc; sp804_timer_tc_write_4(SP804_TIMER1_VALUE, 0xffffffff); sp804_timer_tc_write_4(SP804_TIMER1_LOAD, 0xffffffff); reg = TIMER_CONTROL_PERIODIC | TIMER_CONTROL_32BIT; sp804_timer_tc_write_4(SP804_TIMER1_CONTROL, reg); reg |= TIMER_CONTROL_EN; sp804_timer_tc_write_4(SP804_TIMER1_CONTROL, reg); tc_init(&sc->tc); /* * Timer 2, event timer */ sc->et_enabled = 0; sc->et.et_name = malloc(64, M_DEVBUF, M_NOWAIT | M_ZERO); sprintf(sc->et.et_name, "SP804 Event Timer %d", device_get_unit(dev)); sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT; sc->et.et_quality = 1000; sc->et.et_frequency = sc->sysclk_freq / DEFAULT_DIVISOR; sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency; sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; sc->et.et_start = sp804_timer_start; sc->et.et_stop = sp804_timer_stop; sc->et.et_priv = sc; et_register(&sc->et); id = 0; for (i = 3; i >= 0; i--) { id = (id << 8) | (sp804_timer_tc_read_4(SP804_PERIPH_ID0 + i*4) & 0xff); } device_printf(dev, "peripheral ID: %08x\n", id); id = 0; for (i = 3; i >= 0; i--) { id = (id << 8) | (sp804_timer_tc_read_4(SP804_PRIMECELL_ID0 + i*4) & 0xff); } device_printf(dev, "PrimeCell ID: %08x\n", id); sc->timer_initialized = 1; return (0); } static device_method_t sp804_timer_methods[] = { DEVMETHOD(device_probe, sp804_timer_probe), DEVMETHOD(device_attach, sp804_timer_attach), { 0, 0 } }; static driver_t sp804_timer_driver = { "timer", sp804_timer_methods, sizeof(struct sp804_timer_softc), }; static devclass_t sp804_timer_devclass; DRIVER_MODULE(sp804_timer, simplebus, sp804_timer_driver, sp804_timer_devclass, 0, 0); void DELAY(int usec) { int32_t counts; uint32_t first, last; device_t timer_dev; struct sp804_timer_softc *sc; int timer_initialized = 0; timer_dev = devclass_get_device(sp804_timer_devclass, 0); if (timer_dev) { sc = device_get_softc(timer_dev); if (sc) timer_initialized = sc->timer_initialized; } if (!timer_initialized) { /* * Timer is not initialized yet */ for (; usec > 0; usec--) for (counts = 200; counts > 0; counts--) /* Prevent gcc from optimizing out the loop */ cpufunc_nullop(); return; } /* Get the number of times to count */ counts = usec * ((sc->tc.tc_frequency / 1000000) + 1); first = sp804_timer_tc_get_timecount(&sc->tc); while (counts > 0) { last = sp804_timer_tc_get_timecount(&sc->tc); if (last == first) continue; if (last>first) { counts -= (int32_t)(last - first); } else { counts -= (int32_t)((0xFFFFFFFF - first) + last); } first = last; } } Index: head/sys/arm/versatile/versatile_clcd.c =================================================================== --- head/sys/arm/versatile/versatile_clcd.c (revision 281084) +++ head/sys/arm/versatile/versatile_clcd.c (revision 281085) @@ -1,959 +1,958 @@ /* * 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 #define PL110_VENDOR_ARM926PXP 1 #define MEM_SYS 0 #define MEM_CLCD 1 #define MEM_REGIONS 2 #define SYS_CLCD 0x00 #define SYS_CLCD_CLCDID_SHIFT 0x08 #define SYS_CLCD_CLCDID_MASK 0x1f #define SYS_CLCD_PWR3V5VSWITCH (1 << 4) #define SYS_CLCD_VDDPOSSWITCH (1 << 3) #define SYS_CLCD_NLCDIOON (1 << 2) #define SYS_CLCD_LCD_MODE_MASK 0x03 #define CLCD_MODE_RGB888 0x0 #define CLCD_MODE_RGB555 0x01 #define CLCD_MODE_RBG565 0x02 #define CLCD_MODE_RGB565 0x03 #define CLCDC_TIMING0 0x00 #define CLCDC_TIMING1 0x04 #define CLCDC_TIMING2 0x08 #define CLCDC_TIMING3 0x0C #define CLCDC_TIMING3 0x0C #define CLCDC_UPBASE 0x10 #define CLCDC_LPBASE 0x14 #ifdef PL110_VENDOR_ARM926PXP #define CLCDC_CONTROL 0x18 #define CLCDC_IMSC 0x1C #else #define CLCDC_IMSC 0x18 #define CLCDC_CONTROL 0x1C #endif #define CONTROL_WATERMARK (1 << 16) #define CONTROL_VCOMP_VS (0 << 12) #define CONTROL_VCOMP_BP (1 << 12) #define CONTROL_VCOMP_SAV (2 << 12) #define CONTROL_VCOMP_FP (3 << 12) #define CONTROL_PWR (1 << 11) #define CONTROL_BEPO (1 << 10) #define CONTROL_BEBO (1 << 9) #define CONTROL_BGR (1 << 8) #define CONTROL_DUAL (1 << 7) #define CONTROL_MONO8 (1 << 6) #define CONTROL_TFT (1 << 5) #define CONTROL_BW (1 << 4) #define CONTROL_BPP1 (0x00 << 1) #define CONTROL_BPP2 (0x01 << 1) #define CONTROL_BPP4 (0x02 << 1) #define CONTROL_BPP8 (0x03 << 1) #define CONTROL_BPP16 (0x04 << 1) #define CONTROL_BPP24 (0x05 << 1) #define CONTROL_EN (1 << 0) #define CLCDC_RIS 0x20 #define CLCDC_MIS 0x24 #define INTR_MBERR (1 << 4) #define INTR_VCOMP (1 << 3) #define INTR_LNB (1 << 2) #define INTR_FUF (1 << 1) #define CLCDC_ICR 0x28 #ifdef DEBUG #define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) #else #define dprintf(fmt, args...) #endif #define versatile_clcdc_sys_read_4(sc, reg) \ bus_read_4((sc)->mem_res[MEM_SYS], (reg)) #define versatile_clcdc_sys_write_4(sc, reg, val) \ bus_write_4((sc)->mem_res[MEM_SYS], (reg), (val)) #define versatile_clcdc_read_4(sc, reg) \ bus_read_4((sc)->mem_res[MEM_CLCD], (reg)) #define versatile_clcdc_write_4(sc, reg, val) \ bus_write_4((sc)->mem_res[MEM_CLCD], (reg), (val)) struct versatile_clcdc_softc { struct resource* mem_res[MEM_REGIONS]; struct mtx mtx; int width; int height; int mode; bus_dma_tag_t dma_tag; bus_dmamap_t dma_map; bus_addr_t fb_phys; uint8_t *fb_base; }; struct video_adapter_softc { /* Videoadpater part */ video_adapter_t va; int console; intptr_t fb_addr; 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 initialized; }; struct argb { uint8_t a; uint8_t r; uint8_t g; uint8_t b; }; static struct argb versatilefb_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 16 #define VERSATILE_FONT_HEIGHT 16 static struct video_adapter_softc va_softc; static struct resource_spec versatile_clcdc_mem_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { -1, 0, 0 } }; static int versatilefb_configure(int); static void versatilefb_update_margins(video_adapter_t *adp); static void versatile_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 = segs[0].ds_addr; } static int versatile_clcdc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "arm,pl110")) { device_set_desc(dev, "PL110 CLCD controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int versatile_clcdc_attach(device_t dev) { struct versatile_clcdc_softc *sc = device_get_softc(dev); struct video_adapter_softc *va_sc = &va_softc; int err; uint32_t reg; int clcdid; int dma_size; /* Request memory resources */ err = bus_alloc_resources(dev, versatile_clcdc_mem_spec, sc->mem_res); if (err) { device_printf(dev, "Error: could not allocate memory resources\n"); return (ENXIO); } reg = versatile_clcdc_sys_read_4(sc, SYS_CLCD); clcdid = (reg >> SYS_CLCD_CLCDID_SHIFT) & SYS_CLCD_CLCDID_MASK; switch (clcdid) { case 31: device_printf(dev, "QEMU VGA 640x480\n"); sc->width = 640; sc->height = 480; break; default: device_printf(dev, "Unsupported: %d\n", clcdid); goto fail; } reg &= ~SYS_CLCD_LCD_MODE_MASK; reg |= CLCD_MODE_RGB565; sc->mode = CLCD_MODE_RGB565; versatile_clcdc_sys_write_4(sc, SYS_CLCD, reg); dma_size = sc->width*sc->height*2; /* * Power on LCD */ reg |= SYS_CLCD_PWR3V5VSWITCH | SYS_CLCD_NLCDIOON; versatile_clcdc_sys_write_4(sc, SYS_CLCD, reg); /* * XXX: hardcoded timing for VGA. For other modes/panels * we need to keep table of timing register values */ /* * XXX: set SYS_OSC1 */ versatile_clcdc_write_4(sc, CLCDC_TIMING0, 0x3F1F3F9C); versatile_clcdc_write_4(sc, CLCDC_TIMING1, 0x090B61DF); versatile_clcdc_write_4(sc, CLCDC_TIMING2, 0x067F1800); /* XXX: timing 3? */ /* * Now allocate framebuffer memory */ err = bus_dma_tag_create( bus_get_dma_tag(dev), 4, 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_base, 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_base, dma_size, versatile_fb_dmamap_cb, &sc->fb_phys, BUS_DMA_NOWAIT); if (err) { device_printf(dev, "cannot load DMA map\n"); goto fail; } /* Make sure it's blank */ memset(sc->fb_base, 0x00, dma_size); versatile_clcdc_write_4(sc, CLCDC_UPBASE, sc->fb_phys); err = (sc_attach_unit(device_get_unit(dev), device_get_flags(dev) | SC_AUTODETECT_KBD)); if (err) { device_printf(dev, "failed to attach syscons\n"); goto fail; } /* * XXX: hardcoded for VGA */ reg = CONTROL_VCOMP_BP | CONTROL_TFT | CONTROL_BGR | CONTROL_EN; reg |= CONTROL_BPP16; versatile_clcdc_write_4(sc, CLCDC_CONTROL, reg); DELAY(20); reg |= CONTROL_PWR; versatile_clcdc_write_4(sc, CLCDC_CONTROL, reg); va_sc->fb_addr = (vm_offset_t)sc->fb_base; va_sc->fb_size = dma_size; va_sc->width = sc->width; va_sc->height = sc->height; va_sc->depth = 16; va_sc->stride = sc->width * 2; versatilefb_update_margins(&va_sc->va); return (0); fail: if (sc->fb_base) bus_dmamem_free(sc->dma_tag, sc->fb_base, sc->dma_map); if (sc->dma_tag) bus_dma_tag_destroy(sc->dma_tag); return (err); } static device_method_t versatile_clcdc_methods[] = { DEVMETHOD(device_probe, versatile_clcdc_probe), DEVMETHOD(device_attach, versatile_clcdc_attach), DEVMETHOD_END }; static driver_t versatile_clcdc_driver = { "clcdc", versatile_clcdc_methods, sizeof(struct versatile_clcdc_softc), }; static devclass_t versatile_clcdc_devclass; DRIVER_MODULE(versatile_clcdc, simplebus, versatile_clcdc_driver, versatile_clcdc_devclass, 0, 0); /* * Video driver routines and glue. */ static vi_probe_t versatilefb_probe; static vi_init_t versatilefb_init; static vi_get_info_t versatilefb_get_info; static vi_query_mode_t versatilefb_query_mode; static vi_set_mode_t versatilefb_set_mode; static vi_save_font_t versatilefb_save_font; static vi_load_font_t versatilefb_load_font; static vi_show_font_t versatilefb_show_font; static vi_save_palette_t versatilefb_save_palette; static vi_load_palette_t versatilefb_load_palette; static vi_set_border_t versatilefb_set_border; static vi_save_state_t versatilefb_save_state; static vi_load_state_t versatilefb_load_state; static vi_set_win_org_t versatilefb_set_win_org; static vi_read_hw_cursor_t versatilefb_read_hw_cursor; static vi_set_hw_cursor_t versatilefb_set_hw_cursor; static vi_set_hw_cursor_shape_t versatilefb_set_hw_cursor_shape; static vi_blank_display_t versatilefb_blank_display; static vi_mmap_t versatilefb_mmap; static vi_ioctl_t versatilefb_ioctl; static vi_clear_t versatilefb_clear; static vi_fill_rect_t versatilefb_fill_rect; static vi_bitblt_t versatilefb_bitblt; static vi_diag_t versatilefb_diag; static vi_save_cursor_palette_t versatilefb_save_cursor_palette; static vi_load_cursor_palette_t versatilefb_load_cursor_palette; static vi_copy_t versatilefb_copy; static vi_putp_t versatilefb_putp; static vi_putc_t versatilefb_putc; static vi_puts_t versatilefb_puts; static vi_putm_t versatilefb_putm; static video_switch_t versatilefbvidsw = { .probe = versatilefb_probe, .init = versatilefb_init, .get_info = versatilefb_get_info, .query_mode = versatilefb_query_mode, .set_mode = versatilefb_set_mode, .save_font = versatilefb_save_font, .load_font = versatilefb_load_font, .show_font = versatilefb_show_font, .save_palette = versatilefb_save_palette, .load_palette = versatilefb_load_palette, .set_border = versatilefb_set_border, .save_state = versatilefb_save_state, .load_state = versatilefb_load_state, .set_win_org = versatilefb_set_win_org, .read_hw_cursor = versatilefb_read_hw_cursor, .set_hw_cursor = versatilefb_set_hw_cursor, .set_hw_cursor_shape = versatilefb_set_hw_cursor_shape, .blank_display = versatilefb_blank_display, .mmap = versatilefb_mmap, .ioctl = versatilefb_ioctl, .clear = versatilefb_clear, .fill_rect = versatilefb_fill_rect, .bitblt = versatilefb_bitblt, .diag = versatilefb_diag, .save_cursor_palette = versatilefb_save_cursor_palette, .load_cursor_palette = versatilefb_load_cursor_palette, .copy = versatilefb_copy, .putp = versatilefb_putp, .putc = versatilefb_putc, .puts = versatilefb_puts, .putm = versatilefb_putm, }; VIDEO_DRIVER(versatilefb, versatilefbvidsw, versatilefb_configure); static vr_init_t clcdr_init; static vr_clear_t clcdr_clear; static vr_draw_border_t clcdr_draw_border; static vr_draw_t clcdr_draw; static vr_set_cursor_t clcdr_set_cursor; static vr_draw_cursor_t clcdr_draw_cursor; static vr_blink_cursor_t clcdr_blink_cursor; static vr_set_mouse_t clcdr_set_mouse; static vr_draw_mouse_t clcdr_draw_mouse; /* * We use our own renderer; this is because we must emulate a hardware * cursor. */ static sc_rndr_sw_t clcdrend = { clcdr_init, clcdr_clear, clcdr_draw_border, clcdr_draw, clcdr_set_cursor, clcdr_draw_cursor, clcdr_blink_cursor, clcdr_set_mouse, clcdr_draw_mouse }; RENDERER(versatilefb, 0, clcdrend, gfb_set); RENDERER_MODULE(versatilefb, gfb_set); static void clcdr_init(scr_stat* scp) { } static void clcdr_clear(scr_stat* scp, int c, int attr) { } static void clcdr_draw_border(scr_stat* scp, int color) { } static void clcdr_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 clcdr_set_cursor(scr_stat* scp, int base, int height, int blink) { } static void clcdr_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; uint8_t *addr; int i,j; sc = (struct video_adapter_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); /* 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++) { addr[2*j] ^= 0xff; addr[2*j + 1] ^= 0xff; } addr += sc->stride; } } static void clcdr_blink_cursor(scr_stat* scp, int at, int flip) { } static void clcdr_set_mouse(scr_stat* scp) { } static void clcdr_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 versatilefb_static_window[ROW*COL]; extern u_char dflt_font_16[]; /* * Update videoadapter settings after changing resolution */ static void versatilefb_update_margins(video_adapter_t *adp) { struct video_adapter_softc *sc; video_info_t *vi; sc = (struct video_adapter_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; } static int versatilefb_configure(int flags) { struct video_adapter_softc *va_sc; va_sc = &va_softc; if (va_sc->initialized) return (0); va_sc->width = FB_WIDTH; va_sc->height = FB_HEIGHT; va_sc->depth = FB_DEPTH; versatilefb_init(0, &va_sc->va, 0); va_sc->initialized = 1; return (0); } static int versatilefb_probe(int unit, video_adapter_t **adp, void *arg, int flags) { return (0); } static int versatilefb_init(int unit, video_adapter_t *adp, int flags) { struct video_adapter_softc *sc; video_info_t *vi; sc = (struct video_adapter_softc *)adp; vi = &adp->va_info; vid_init_struct(adp, "versatilefb", -1, unit); sc->font = dflt_font_16; vi->vi_cheight = VERSATILE_FONT_HEIGHT; vi->vi_cwidth = 8; 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; adp->va_window = (vm_offset_t) versatilefb_static_window; adp->va_flags |= V_ADP_FONT /* | V_ADP_COLOR | V_ADP_MODECHANGE */; vid_register(&sc->va); return (0); } static int versatilefb_get_info(video_adapter_t *adp, int mode, video_info_t *info) { bcopy(&adp->va_info, info, sizeof(*info)); return (0); } static int versatilefb_query_mode(video_adapter_t *adp, video_info_t *info) { return (0); } static int versatilefb_set_mode(video_adapter_t *adp, int mode) { return (0); } static int versatilefb_save_font(video_adapter_t *adp, int page, int size, int width, u_char *data, int c, int count) { return (0); } static int versatilefb_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; sc->font = data; return (0); } static int versatilefb_show_font(video_adapter_t *adp, int page) { return (0); } static int versatilefb_save_palette(video_adapter_t *adp, u_char *palette) { return (0); } static int versatilefb_load_palette(video_adapter_t *adp, u_char *palette) { return (0); } static int versatilefb_set_border(video_adapter_t *adp, int border) { return (versatilefb_blank_display(adp, border)); } static int versatilefb_save_state(video_adapter_t *adp, void *p, size_t size) { return (0); } static int versatilefb_load_state(video_adapter_t *adp, void *p) { return (0); } static int versatilefb_set_win_org(video_adapter_t *adp, off_t offset) { return (0); } static int versatilefb_read_hw_cursor(video_adapter_t *adp, int *col, int *row) { *col = *row = 0; return (0); } static int versatilefb_set_hw_cursor(video_adapter_t *adp, int col, int row) { return (0); } static int versatilefb_set_hw_cursor_shape(video_adapter_t *adp, int base, int height, int celsize, int blink) { return (0); } static int versatilefb_blank_display(video_adapter_t *adp, int mode) { struct video_adapter_softc *sc; sc = (struct video_adapter_softc *)adp; if (sc && sc->fb_addr) memset((void*)sc->fb_addr, 0, sc->fb_size); return (0); } static int versatilefb_mmap(video_adapter_t *adp, vm_ooffset_t offset, vm_paddr_t *paddr, int prot, vm_memattr_t *memattr) { struct video_adapter_softc *sc; sc = (struct video_adapter_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_addr + offset; return (0); } return (EINVAL); } static int versatilefb_ioctl(video_adapter_t *adp, u_long cmd, caddr_t data) { return (0); } static int versatilefb_clear(video_adapter_t *adp) { return (versatilefb_blank_display(adp, 0)); } static int versatilefb_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy) { return (0); } static int versatilefb_bitblt(video_adapter_t *adp, ...) { return (0); } static int versatilefb_diag(video_adapter_t *adp, int level) { return (0); } static int versatilefb_save_cursor_palette(video_adapter_t *adp, u_char *palette) { return (0); } static int versatilefb_load_cursor_palette(video_adapter_t *adp, u_char *palette) { return (0); } static int versatilefb_copy(video_adapter_t *adp, vm_offset_t src, vm_offset_t dst, int n) { return (0); } static int versatilefb_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 versatilefb_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; u_char *p; uint8_t fg, bg, color; uint16_t rgb; sc = (struct video_adapter_softc *)adp; if (sc->fb_addr == 0) return (0); if (off >= adp->va_info.vi_width * adp->va_info.vi_height) 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*VERSATILE_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; for (i = 0; i < VERSATILE_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 16: rgb = (versatilefb_palette[color].r >> 3) << 11; rgb |= (versatilefb_palette[color].g >> 2) << 5; rgb |= (versatilefb_palette[color].b >> 3); addr[2*j] = rgb & 0xff; addr[2*j + 1] = (rgb >> 8) & 0xff; default: /* Not supported yet */ break; } } addr += (sc->stride); } return (0); } static int versatilefb_puts(video_adapter_t *adp, vm_offset_t off, u_int16_t *s, int len) { int i; for (i = 0; i < len; i++) versatilefb_putc(adp, off + i, s[i] & 0xff, (s[i] & 0xff00) >> 8); return (0); } static int versatilefb_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: head/sys/arm/versatile/versatile_common.c =================================================================== --- head/sys/arm/versatile/versatile_common.c (revision 281084) +++ head/sys/arm/versatile/versatile_common.c (revision 281085) @@ -1,73 +1,72 @@ /*- * 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 __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, "arm,versatile-vic")) return (ENXIO); *interrupt = fdt32_to_cpu(intr[0]); *trig = INTR_TRIGGER_CONFORM; *pol = INTR_POLARITY_CONFORM; return (0); } fdt_pic_decode_t fdt_pic_table[] = { &fdt_intc_decode_ic, NULL }; Index: head/sys/arm/versatile/versatile_timer.c =================================================================== --- head/sys/arm/versatile/versatile_timer.c (revision 281084) +++ head/sys/arm/versatile/versatile_timer.c (revision 281085) @@ -1,58 +1,57 @@ /* * 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 void cpu_initclocks(void) { cpu_initclocks_bsp(); }