Index: head/sys/arm/allwinner/a10_clk.c =================================================================== --- head/sys/arm/allwinner/a10_clk.c (revision 295463) +++ head/sys/arm/allwinner/a10_clk.c (revision 295464) @@ -1,443 +1,444 @@ /*- * 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 "a10_clk.h" struct a10_ccm_softc { struct resource *res; bus_space_tag_t bst; bus_space_handle_t bsh; int pll6_enabled; }; 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); +EARLY_DRIVER_MODULE(a10_ccm, simplebus, a10_ccm_driver, a10_ccm_devclass, 0, 0, + BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); 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); } int a10_clk_gmac_activate(phandle_t node) { char *phy_type; struct a10_ccm_softc *sc; uint32_t reg_value; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); /* Gating AHB clock for GMAC */ reg_value = ccm_read_4(sc, CCM_AHB_GATING1); reg_value |= CCM_AHB_GATING_GMAC; ccm_write_4(sc, CCM_AHB_GATING1, reg_value); /* Set GMAC mode. */ reg_value = CCM_GMAC_CLK_MII; - if (OF_getprop_alloc(node, "phy-type", 1, (void **)&phy_type) > 0) { + if (OF_getprop_alloc(node, "phy-mode", 1, (void **)&phy_type) > 0) { if (strcasecmp(phy_type, "rgmii") == 0) reg_value = CCM_GMAC_CLK_RGMII | CCM_GMAC_MODE_RGMII; else if (strcasecmp(phy_type, "rgmii-bpi") == 0) { reg_value = CCM_GMAC_CLK_RGMII | CCM_GMAC_MODE_RGMII; reg_value |= (3 << CCM_GMAC_CLK_DELAY_SHIFT); } free(phy_type, M_OFWPROP); } ccm_write_4(sc, CCM_GMAC_CLK, reg_value); return (0); } static void a10_clk_pll6_enable(void) { struct a10_ccm_softc *sc; uint32_t reg_value; /* * SATA needs PLL6 to be a 100MHz clock. * The SATA output frequency is 24MHz * n * k / m / 6. * To get to 100MHz, k & m must be equal and n must be 25. * For other uses the output frequency is 24MHz * n * k / 2. */ sc = a10_ccm_sc; if (sc->pll6_enabled) return; reg_value = ccm_read_4(sc, CCM_PLL6_CFG); reg_value &= ~CCM_PLL_CFG_BYPASS; reg_value &= ~(CCM_PLL_CFG_FACTOR_K | CCM_PLL_CFG_FACTOR_M | CCM_PLL_CFG_FACTOR_N); reg_value |= (25 << CCM_PLL_CFG_FACTOR_N_SHIFT); reg_value |= CCM_PLL6_CFG_SATA_CLKEN; reg_value |= CCM_PLL_CFG_ENABLE; ccm_write_4(sc, CCM_PLL6_CFG, reg_value); sc->pll6_enabled = 1; } static unsigned int a10_clk_pll6_get_rate(void) { struct a10_ccm_softc *sc; uint32_t k, n, reg_value; sc = a10_ccm_sc; reg_value = ccm_read_4(sc, CCM_PLL6_CFG); n = ((reg_value & CCM_PLL_CFG_FACTOR_N) >> CCM_PLL_CFG_FACTOR_N_SHIFT); k = ((reg_value & CCM_PLL_CFG_FACTOR_K) >> CCM_PLL_CFG_FACTOR_K_SHIFT) + 1; return ((CCM_CLK_REF_FREQ * n * k) / 2); } static int a10_clk_pll2_set_rate(unsigned int freq) { struct a10_ccm_softc *sc; uint32_t reg_value; unsigned int prediv, postdiv, n; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); reg_value = ccm_read_4(sc, CCM_PLL2_CFG); reg_value &= ~(CCM_PLL2_CFG_PREDIV | CCM_PLL2_CFG_POSTDIV | CCM_PLL_CFG_FACTOR_N); /* * Audio Codec needs PLL2 to be either 24576000 Hz or 22579200 Hz * * PLL2 output frequency is 24MHz * n / prediv / postdiv. * To get as close as possible to the desired rate, we use a * pre-divider of 21 and a post-divider of 4. With these values, * a multiplier of 86 or 79 gets us close to the target rates. */ prediv = 21; postdiv = 4; switch (freq) { case 24576000: n = 86; reg_value |= CCM_PLL_CFG_ENABLE; break; case 22579200: n = 79; reg_value |= CCM_PLL_CFG_ENABLE; break; case 0: n = 1; reg_value &= ~CCM_PLL_CFG_ENABLE; break; default: return (EINVAL); } reg_value |= (prediv << CCM_PLL2_CFG_PREDIV_SHIFT); reg_value |= (postdiv << CCM_PLL2_CFG_POSTDIV_SHIFT); reg_value |= (n << CCM_PLL_CFG_FACTOR_N_SHIFT); ccm_write_4(sc, CCM_PLL2_CFG, reg_value); return (0); } int a10_clk_ahci_activate(void) { struct a10_ccm_softc *sc; uint32_t reg_value; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); a10_clk_pll6_enable(); /* Gating AHB clock for SATA */ reg_value = ccm_read_4(sc, CCM_AHB_GATING0); reg_value |= CCM_AHB_GATING_SATA; ccm_write_4(sc, CCM_AHB_GATING0, reg_value); DELAY(1000); ccm_write_4(sc, CCM_SATA_CLK, CCM_PLL_CFG_ENABLE); return (0); } int a10_clk_mmc_activate(int devid) { struct a10_ccm_softc *sc; uint32_t reg_value; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); a10_clk_pll6_enable(); /* Gating AHB clock for SD/MMC */ reg_value = ccm_read_4(sc, CCM_AHB_GATING0); reg_value |= CCM_AHB_GATING_SDMMC0 << devid; ccm_write_4(sc, CCM_AHB_GATING0, reg_value); return (0); } int a10_clk_mmc_cfg(int devid, int freq) { struct a10_ccm_softc *sc; uint32_t clksrc, m, n, ophase, phase, reg_value; unsigned int pll_freq; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); freq /= 1000; if (freq <= 400) { pll_freq = CCM_CLK_REF_FREQ / 1000; clksrc = CCM_SD_CLK_SRC_SEL_OSC24M; ophase = 0; phase = 0; n = 2; } else if (freq <= 25000) { pll_freq = a10_clk_pll6_get_rate() / 1000; clksrc = CCM_SD_CLK_SRC_SEL_PLL6; ophase = 0; phase = 5; n = 2; } else if (freq <= 50000) { pll_freq = a10_clk_pll6_get_rate() / 1000; clksrc = CCM_SD_CLK_SRC_SEL_PLL6; ophase = 3; phase = 5; n = 0; } else return (EINVAL); m = ((pll_freq / (1 << n)) / (freq)) - 1; reg_value = ccm_read_4(sc, CCM_MMC0_SCLK_CFG + (devid * 4)); reg_value &= ~CCM_SD_CLK_SRC_SEL; reg_value |= (clksrc << CCM_SD_CLK_SRC_SEL_SHIFT); reg_value &= ~CCM_SD_CLK_PHASE_CTR; reg_value |= (phase << CCM_SD_CLK_PHASE_CTR_SHIFT); reg_value &= ~CCM_SD_CLK_DIV_RATIO_N; reg_value |= (n << CCM_SD_CLK_DIV_RATIO_N_SHIFT); reg_value &= ~CCM_SD_CLK_OPHASE_CTR; reg_value |= (ophase << CCM_SD_CLK_OPHASE_CTR_SHIFT); reg_value &= ~CCM_SD_CLK_DIV_RATIO_M; reg_value |= m; reg_value |= CCM_PLL_CFG_ENABLE; ccm_write_4(sc, CCM_MMC0_SCLK_CFG + (devid * 4), reg_value); return (0); } int a10_clk_dmac_activate(void) { struct a10_ccm_softc *sc; uint32_t reg_value; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); /* Gating AHB clock for DMA controller */ reg_value = ccm_read_4(sc, CCM_AHB_GATING0); reg_value |= CCM_AHB_GATING_DMA; ccm_write_4(sc, CCM_AHB_GATING0, reg_value); return (0); } int a10_clk_codec_activate(unsigned int freq) { struct a10_ccm_softc *sc; uint32_t reg_value; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); a10_clk_pll2_set_rate(freq); /* Gating APB clock for ADDA */ reg_value = ccm_read_4(sc, CCM_APB0_GATING); reg_value |= CCM_APB0_GATING_ADDA; ccm_write_4(sc, CCM_APB0_GATING, reg_value); /* Enable audio codec clock */ reg_value = ccm_read_4(sc, CCM_AUDIO_CODEC_CLK); reg_value |= CCM_AUDIO_CODEC_ENABLE; ccm_write_4(sc, CCM_AUDIO_CODEC_CLK, reg_value); return (0); } Index: head/sys/arm/allwinner/a10_common.c =================================================================== --- head/sys/arm/allwinner/a10_common.c (revision 295463) +++ head/sys/arm/allwinner/a10_common.c (revision 295464) @@ -1,72 +1,72 @@ /*- * 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 struct fdt_fixup_entry fdt_fixup_table[] = { { NULL, NULL } }; #ifndef ARM_INTRNG 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")) + if (fdt_is_compatible(node, "allwinner,sun4i-a10-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 }; #endif /* ARM_INTRNG */ Index: head/sys/arm/allwinner/a10_ehci.c =================================================================== --- head/sys/arm/allwinner/a10_ehci.c (revision 295463) +++ head/sys/arm/allwinner/a10_ehci.c (revision 295464) @@ -1,305 +1,311 @@ /*- * 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. */ /* * Allwinner A10 attachment driver for the USB Enhanced Host Controller. */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #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 "a10_clk.h" #define EHCI_HC_DEVSTR "Allwinner Integrated USB 2.0 controller" #define SW_USB_PMU_IRQ_ENABLE 0x800 #define SW_SDRAM_REG_HPCR_USB1 (0x250 + ((1 << 2) * 4)) #define SW_SDRAM_REG_HPCR_USB2 (0x250 + ((1 << 2) * 5)) #define SW_SDRAM_BP_HPCR_ACCESS (1 << 0) #define SW_ULPI_BYPASS (1 << 0) #define SW_AHB_INCRX_ALIGN (1 << 8) #define SW_AHB_INCR4 (1 << 9) #define SW_AHB_INCR8 (1 << 10) #define GPIO_USB1_PWR 230 #define GPIO_USB2_PWR 227 #define A10_READ_4(sc, reg) \ bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) #define A10_WRITE_4(sc, reg, data) \ bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data) static device_attach_t a10_ehci_attach; static device_detach_t a10_ehci_detach; bs_r_1_proto(reversed); bs_w_1_proto(reversed); +static struct ofw_compat_data compat_data[] = { + {"allwinner,sun4i-a10-ehci", 1}, + {"allwinner,sun7i-a20-ehci", 1}, + {NULL, 0} +}; + static int a10_ehci_probe(device_t self) { if (!ofw_bus_status_okay(self)) return (ENXIO); - if (!ofw_bus_is_compatible(self, "allwinner,usb-ehci")) + if (ofw_bus_search_compatible(self, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(self, EHCI_HC_DEVSTR); return (BUS_PROBE_DEFAULT); } static int a10_ehci_attach(device_t self) { ehci_softc_t *sc = device_get_softc(self); bus_space_handle_t bsh; device_t sc_gpio_dev; int err; int rid; uint32_t reg_value = 0; /* initialise some bus fields */ sc->sc_bus.parent = self; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { return (ENOMEM); } sc->sc_bus.usbrev = USB_REV_2_0; rid = 0; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map memory\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); bsh = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); if (bus_space_subregion(sc->sc_io_tag, bsh, 0x00, sc->sc_io_size, &sc->sc_io_hdl) != 0) panic("%s: unable to subregion USB host registers", device_get_name(self)); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(self, "Could not allocate irq\n"); goto error; } sc->sc_bus.bdev = device_add_child(self, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); sprintf(sc->sc_vendor, "Allwinner"); /* Get the GPIO device, we need this to give power to USB */ sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0); if (sc_gpio_dev == NULL) { device_printf(self, "Error: failed to get the GPIO device\n"); goto error; } err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } sc->sc_flags |= EHCI_SCFLG_DONTRESET; /* Enable clock for USB */ a10_clk_usb_activate(); /* Give power to USB */ GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB2_PWR, GPIO_PIN_OUTPUT); GPIO_PIN_SET(sc_gpio_dev, GPIO_USB2_PWR, GPIO_PIN_HIGH); /* Give power to USB */ GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB1_PWR, GPIO_PIN_OUTPUT); GPIO_PIN_SET(sc_gpio_dev, GPIO_USB1_PWR, GPIO_PIN_HIGH); /* Enable passby */ reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); reg_value |= SW_AHB_INCR8; /* AHB INCR8 enable */ reg_value |= SW_AHB_INCR4; /* AHB burst type INCR4 enable */ reg_value |= SW_AHB_INCRX_ALIGN; /* AHB INCRX align enable */ reg_value |= SW_ULPI_BYPASS; /* ULPI bypass enable */ A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); /* Configure port */ reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); reg_value |= SW_SDRAM_BP_HPCR_ACCESS; A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); err = ehci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed err=%d\n", err); goto error; } return (0); error: a10_ehci_detach(self); return (ENXIO); } static int a10_ehci_detach(device_t self) { ehci_softc_t *sc = device_get_softc(self); device_t bdev; int err; uint32_t reg_value = 0; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(self); if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ehci_detach() after ehci_init() */ ehci_detach(sc); err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, 0, sc->sc_io_res); sc->sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); /* Disable configure port */ reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); reg_value &= ~SW_SDRAM_BP_HPCR_ACCESS; A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); /* Disable passby */ reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); reg_value &= ~SW_AHB_INCR8; /* AHB INCR8 disable */ reg_value &= ~SW_AHB_INCR4; /* AHB burst type INCR4 disable */ reg_value &= ~SW_AHB_INCRX_ALIGN; /* AHB INCRX align disable */ reg_value &= ~SW_ULPI_BYPASS; /* ULPI bypass disable */ A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); /* Disable clock for USB */ a10_clk_usb_deactivate(); return (0); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, a10_ehci_probe), DEVMETHOD(device_attach, a10_ehci_attach), DEVMETHOD(device_detach, a10_ehci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ehci_driver = { .name = "ehci", .methods = ehci_methods, .size = sizeof(ehci_softc_t), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); Index: head/sys/arm/allwinner/a10_gpio.c =================================================================== --- head/sys/arm/allwinner/a10_gpio.c (revision 295463) +++ head/sys/arm/allwinner/a10_gpio.c (revision 295464) @@ -1,514 +1,522 @@ /*- * 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 "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 +static struct ofw_compat_data compat_data[] = { + {"allwinner,sun4i-a10-pinctrl", 1}, + {"allwinner,sun7i-a20-pinctrl", 1}, + {NULL, 0} +}; + 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; }; #define A10_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) #define A10_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) #define A10_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define A10_GPIO_GP_CFG(_bank, _idx) 0x00 + ((_bank) * 0x24) + ((_idx) << 2) #define A10_GPIO_GP_DAT(_bank) 0x10 + ((_bank) * 0x24) #define A10_GPIO_GP_DRV(_bank, _idx) 0x14 + ((_bank) * 0x24) + ((_idx) << 2) #define A10_GPIO_GP_PUL(_bank, _idx) 0x1c + ((_bank) * 0x24) + ((_idx) << 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; /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); bank = pin / 32; pin = pin % 32; offset = ((pin & 0x07) << 2); func = A10_GPIO_READ(sc, A10_GPIO_GP_CFG(bank, pin >> 3)); switch ((func >> offset) & 0x7) { 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, data, offset; /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); bank = pin / 32; pin = pin % 32; offset = ((pin & 0x07) << 2); data = A10_GPIO_READ(sc, A10_GPIO_GP_CFG(bank, pin >> 3)); data &= ~(7 << offset); data |= (f << offset); A10_GPIO_WRITE(sc, A10_GPIO_GP_CFG(bank, pin >> 3), data); } static uint32_t a10_gpio_get_pud(struct a10_gpio_softc *sc, uint32_t pin) { uint32_t bank, offset, val; /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); bank = pin / 32; pin = pin % 32; offset = ((pin & 0x0f) << 1); val = A10_GPIO_READ(sc, A10_GPIO_GP_PUL(bank, pin >> 4)); switch ((val >> offset) & 0x3) { case A10_GPIO_PULLDOWN: return (GPIO_PIN_PULLDOWN); case A10_GPIO_PULLUP: return (GPIO_PIN_PULLUP); } return (0); } static void a10_gpio_set_pud(struct a10_gpio_softc *sc, uint32_t pin, uint32_t state) { uint32_t bank, offset, val; /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); bank = pin / 32; pin = pin % 32; offset = ((pin & 0x0f) << 1); val = A10_GPIO_READ(sc, A10_GPIO_GP_PUL(bank, pin >> 4)); val &= ~(0x03 << offset); val |= (state << offset); A10_GPIO_WRITE(sc, A10_GPIO_GP_PUL(bank, pin >> 4), val); } static void a10_gpio_pin_configure(struct a10_gpio_softc *sc, uint32_t pin, uint32_t flags) { /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); /* Manage input/output. */ if (flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { if (flags & GPIO_PIN_OUTPUT) a10_gpio_set_function(sc, pin, A10_GPIO_OUTPUT); else a10_gpio_set_function(sc, pin, A10_GPIO_INPUT); } /* Manage Pull-up/pull-down. */ if (flags & (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)) { if (flags & GPIO_PIN_PULLUP) a10_gpio_set_pud(sc, pin, A10_GPIO_PULLUP); else a10_gpio_set_pud(sc, pin, A10_GPIO_PULLDOWN); } else a10_gpio_set_pud(sc, pin, A10_GPIO_NONE); } 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) { if (pin >= A10_GPIO_PINS) return (EINVAL); *caps = A10_GPIO_DEFAULT_CAPS; return (0); } static int a10_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct a10_gpio_softc *sc; if (pin >= A10_GPIO_PINS) return (EINVAL); sc = device_get_softc(dev); A10_GPIO_LOCK(sc); *flags = a10_gpio_get_function(sc, pin); *flags |= a10_gpio_get_pud(sc, pin); A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { uint32_t bank; if (pin >= A10_GPIO_PINS) return (EINVAL); bank = pin / 32; snprintf(name, GPIOMAXNAME - 1, "pin %d (P%c%d)", pin, bank + 'A', pin % 32); name[GPIOMAXNAME - 1] = '\0'; return (0); } static int a10_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct a10_gpio_softc *sc; if (pin >= A10_GPIO_PINS) return (EINVAL); sc = device_get_softc(dev); A10_GPIO_LOCK(sc); a10_gpio_pin_configure(sc, pin, flags); A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct a10_gpio_softc *sc; uint32_t bank, data; if (pin >= A10_GPIO_PINS) return (EINVAL); bank = pin / 32; pin = pin % 32; sc = device_get_softc(dev); A10_GPIO_LOCK(sc); data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); if (value) data |= (1 << pin); else data &= ~(1 << pin); 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; uint32_t bank, reg_data; if (pin >= A10_GPIO_PINS) return (EINVAL); bank = pin / 32; pin = pin % 32; sc = device_get_softc(dev); A10_GPIO_LOCK(sc); reg_data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); A10_GPIO_UNLOCK(sc); *val = (reg_data & (1 << pin)) ? 1 : 0; return (0); } static int a10_gpio_pin_toggle(device_t dev, uint32_t pin) { struct a10_gpio_softc *sc; uint32_t bank, data; if (pin >= A10_GPIO_PINS) return (EINVAL); bank = pin / 32; pin = pin % 32; sc = device_get_softc(dev); A10_GPIO_LOCK(sc); data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); if (data & (1 << pin)) data &= ~(1 << pin); else data |= (1 << pin); 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")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner GPIO controller"); return (BUS_PROBE_DEFAULT); } static int a10_gpio_attach(device_t dev) { int rid; phandle_t gpio; struct a10_gpio_softc *sc; sc = device_get_softc(dev); sc->sc_dev = dev; mtx_init(&sc->sc_mtx, "a10 gpio", "gpio", MTX_SPIN); 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; 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 phandle_t a10_gpio_get_node(device_t dev, device_t bus) { /* We only have one child, the GPIO bus, which needs our own node. */ return (ofw_bus_get_node(dev)); } static int a10_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) { /* The GPIO pins are mapped as: . */ *pin = gpios[0] * 32 + gpios[1]; *flags = gpios[gcells - 1]; return (0); } 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(gpio_map_gpios, a10_gpio_map_gpios), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, a10_gpio_get_node), 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); +EARLY_DRIVER_MODULE(a10_gpio, simplebus, a10_gpio_driver, a10_gpio_devclass, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); + int a10_gpio_ethernet_activate(uint32_t func) { int i; struct a10_gpio_softc *sc = a10_gpio_sc; if (sc == NULL) return (ENXIO); /* Configure pin mux settings for MII. */ A10_GPIO_LOCK(sc); for (i = 0; i <= 17; i++) a10_gpio_set_function(sc, i, func); A10_GPIO_UNLOCK(sc); return (0); } Index: head/sys/arm/allwinner/a10_mmc.c =================================================================== --- head/sys/arm/allwinner/a10_mmc.c (revision 295463) +++ head/sys/arm/allwinner/a10_mmc.c (revision 295464) @@ -1,886 +1,893 @@ /*- * Copyright (c) 2013 Alexander Fedorov * 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 A10_MMC_MEMRES 0 #define A10_MMC_IRQRES 1 #define A10_MMC_RESSZ 2 #define A10_MMC_DMA_SEGS 16 #define A10_MMC_DMA_MAX_SIZE 0x2000 #define A10_MMC_DMA_FTRGLEVEL 0x20070008 static int a10_mmc_pio_mode = 0; TUNABLE_INT("hw.a10.mmc.pio_mode", &a10_mmc_pio_mode); +static struct ofw_compat_data compat_data[] = { + {"allwinner,sun4i-a10-mmc", 1}, + {"allwinner,sun5i-a13-mmc", 1}, + {NULL, 0} +}; + struct a10_mmc_softc { bus_space_handle_t a10_bsh; bus_space_tag_t a10_bst; device_t a10_dev; int a10_bus_busy; int a10_id; int a10_resid; int a10_timeout; struct callout a10_timeoutc; struct mmc_host a10_host; struct mmc_request * a10_req; struct mtx a10_mtx; struct resource * a10_res[A10_MMC_RESSZ]; uint32_t a10_intr; uint32_t a10_intr_wait; void * a10_intrhand; /* Fields required for DMA access. */ bus_addr_t a10_dma_desc_phys; bus_dmamap_t a10_dma_map; bus_dma_tag_t a10_dma_tag; void * a10_dma_desc; bus_dmamap_t a10_dma_buf_map; bus_dma_tag_t a10_dma_buf_tag; int a10_dma_inuse; int a10_dma_map_err; }; static struct resource_spec a10_mmc_res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0, 0 } }; static int a10_mmc_probe(device_t); static int a10_mmc_attach(device_t); static int a10_mmc_detach(device_t); static int a10_mmc_setup_dma(struct a10_mmc_softc *); static int a10_mmc_reset(struct a10_mmc_softc *); static void a10_mmc_intr(void *); static int a10_mmc_update_clock(struct a10_mmc_softc *); static int a10_mmc_update_ios(device_t, device_t); static int a10_mmc_request(device_t, device_t, struct mmc_request *); static int a10_mmc_get_ro(device_t, device_t); static int a10_mmc_acquire_host(device_t, device_t); static int a10_mmc_release_host(device_t, device_t); #define A10_MMC_LOCK(_sc) mtx_lock(&(_sc)->a10_mtx) #define A10_MMC_UNLOCK(_sc) mtx_unlock(&(_sc)->a10_mtx) #define A10_MMC_READ_4(_sc, _reg) \ bus_space_read_4((_sc)->a10_bst, (_sc)->a10_bsh, _reg) #define A10_MMC_WRITE_4(_sc, _reg, _value) \ bus_space_write_4((_sc)->a10_bst, (_sc)->a10_bsh, _reg, _value) static int a10_mmc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-mmc")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); + device_set_desc(dev, "Allwinner Integrated MMC/SD controller"); return (BUS_PROBE_DEFAULT); } static int a10_mmc_attach(device_t dev) { device_t child; struct a10_mmc_softc *sc; struct sysctl_ctx_list *ctx; struct sysctl_oid_list *tree; sc = device_get_softc(dev); sc->a10_dev = dev; sc->a10_req = NULL; sc->a10_id = device_get_unit(dev); if (sc->a10_id > 3) { device_printf(dev, "only 4 hosts are supported (0-3)\n"); return (ENXIO); } if (bus_alloc_resources(dev, a10_mmc_res_spec, sc->a10_res) != 0) { device_printf(dev, "cannot allocate device resources\n"); return (ENXIO); } sc->a10_bst = rman_get_bustag(sc->a10_res[A10_MMC_MEMRES]); sc->a10_bsh = rman_get_bushandle(sc->a10_res[A10_MMC_MEMRES]); if (bus_setup_intr(dev, sc->a10_res[A10_MMC_IRQRES], INTR_TYPE_MISC | INTR_MPSAFE, NULL, a10_mmc_intr, sc, &sc->a10_intrhand)) { bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res); device_printf(dev, "cannot setup interrupt handler\n"); return (ENXIO); } /* Activate the module clock. */ if (a10_clk_mmc_activate(sc->a10_id) != 0) { bus_teardown_intr(dev, sc->a10_res[A10_MMC_IRQRES], sc->a10_intrhand); bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res); device_printf(dev, "cannot activate mmc clock\n"); return (ENXIO); } sc->a10_timeout = 10; ctx = device_get_sysctl_ctx(dev); tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "req_timeout", CTLFLAG_RW, &sc->a10_timeout, 0, "Request timeout in seconds"); mtx_init(&sc->a10_mtx, device_get_nameunit(sc->a10_dev), "a10_mmc", MTX_DEF); callout_init_mtx(&sc->a10_timeoutc, &sc->a10_mtx, 0); /* Reset controller. */ if (a10_mmc_reset(sc) != 0) { device_printf(dev, "cannot reset the controller\n"); goto fail; } if (a10_mmc_pio_mode == 0 && a10_mmc_setup_dma(sc) != 0) { device_printf(sc->a10_dev, "Couldn't setup DMA!\n"); a10_mmc_pio_mode = 1; } if (bootverbose) device_printf(sc->a10_dev, "DMA status: %s\n", a10_mmc_pio_mode ? "disabled" : "enabled"); sc->a10_host.f_min = 400000; sc->a10_host.f_max = 52000000; sc->a10_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340; sc->a10_host.caps = MMC_CAP_4_BIT_DATA | MMC_CAP_HSPEED; sc->a10_host.mode = mode_sd; child = device_add_child(dev, "mmc", -1); if (child == NULL) { device_printf(dev, "attaching MMC bus failed!\n"); goto fail; } if (device_probe_and_attach(child) != 0) { device_printf(dev, "attaching MMC child failed!\n"); device_delete_child(dev, child); goto fail; } return (0); fail: callout_drain(&sc->a10_timeoutc); mtx_destroy(&sc->a10_mtx); bus_teardown_intr(dev, sc->a10_res[A10_MMC_IRQRES], sc->a10_intrhand); bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res); return (ENXIO); } static int a10_mmc_detach(device_t dev) { return (EBUSY); } static void a10_dma_desc_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err) { struct a10_mmc_softc *sc; sc = (struct a10_mmc_softc *)arg; if (err) { sc->a10_dma_map_err = err; return; } sc->a10_dma_desc_phys = segs[0].ds_addr; } static int a10_mmc_setup_dma(struct a10_mmc_softc *sc) { int dma_desc_size, error; /* Allocate the DMA descriptor memory. */ dma_desc_size = sizeof(struct a10_mmc_dma_desc) * A10_MMC_DMA_SEGS; error = bus_dma_tag_create(bus_get_dma_tag(sc->a10_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, dma_desc_size, 1, dma_desc_size, 0, NULL, NULL, &sc->a10_dma_tag); if (error) return (error); error = bus_dmamem_alloc(sc->a10_dma_tag, &sc->a10_dma_desc, BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->a10_dma_map); if (error) return (error); error = bus_dmamap_load(sc->a10_dma_tag, sc->a10_dma_map, sc->a10_dma_desc, dma_desc_size, a10_dma_desc_cb, sc, 0); if (error) return (error); if (sc->a10_dma_map_err) return (sc->a10_dma_map_err); /* Create the DMA map for data transfers. */ error = bus_dma_tag_create(bus_get_dma_tag(sc->a10_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, A10_MMC_DMA_MAX_SIZE * A10_MMC_DMA_SEGS, A10_MMC_DMA_SEGS, A10_MMC_DMA_MAX_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->a10_dma_buf_tag); if (error) return (error); error = bus_dmamap_create(sc->a10_dma_buf_tag, 0, &sc->a10_dma_buf_map); if (error) return (error); return (0); } static void a10_dma_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err) { int i; struct a10_mmc_dma_desc *dma_desc; struct a10_mmc_softc *sc; sc = (struct a10_mmc_softc *)arg; sc->a10_dma_map_err = err; dma_desc = sc->a10_dma_desc; /* Note nsegs is guaranteed to be zero if err is non-zero. */ for (i = 0; i < nsegs; i++) { dma_desc[i].buf_size = segs[i].ds_len; dma_desc[i].buf_addr = segs[i].ds_addr; dma_desc[i].config = A10_MMC_DMA_CONFIG_CH | A10_MMC_DMA_CONFIG_OWN; if (i == 0) dma_desc[i].config |= A10_MMC_DMA_CONFIG_FD; if (i < (nsegs - 1)) { dma_desc[i].config |= A10_MMC_DMA_CONFIG_DIC; dma_desc[i].next = sc->a10_dma_desc_phys + ((i + 1) * sizeof(struct a10_mmc_dma_desc)); } else { dma_desc[i].config |= A10_MMC_DMA_CONFIG_LD | A10_MMC_DMA_CONFIG_ER; dma_desc[i].next = 0; } } } static int a10_mmc_prepare_dma(struct a10_mmc_softc *sc) { bus_dmasync_op_t sync_op; int error; struct mmc_command *cmd; uint32_t val; cmd = sc->a10_req->cmd; if (cmd->data->len > A10_MMC_DMA_MAX_SIZE * A10_MMC_DMA_SEGS) return (EFBIG); error = bus_dmamap_load(sc->a10_dma_buf_tag, sc->a10_dma_buf_map, cmd->data->data, cmd->data->len, a10_dma_cb, sc, BUS_DMA_NOWAIT); if (error) return (error); if (sc->a10_dma_map_err) return (sc->a10_dma_map_err); sc->a10_dma_inuse = 1; if (cmd->data->flags & MMC_DATA_WRITE) sync_op = BUS_DMASYNC_PREWRITE; else sync_op = BUS_DMASYNC_PREREAD; bus_dmamap_sync(sc->a10_dma_buf_tag, sc->a10_dma_buf_map, sync_op); bus_dmamap_sync(sc->a10_dma_tag, sc->a10_dma_map, BUS_DMASYNC_PREWRITE); val = A10_MMC_READ_4(sc, A10_MMC_IMASK); val &= ~(A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ); A10_MMC_WRITE_4(sc, A10_MMC_IMASK, val); val = A10_MMC_READ_4(sc, A10_MMC_GCTRL); val &= ~A10_MMC_ACCESS_BY_AHB; val |= A10_MMC_DMA_ENABLE; A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, val); val |= A10_MMC_DMA_RESET; A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, val); A10_MMC_WRITE_4(sc, A10_MMC_DMAC, A10_MMC_IDMAC_SOFT_RST); A10_MMC_WRITE_4(sc, A10_MMC_DMAC, A10_MMC_IDMAC_IDMA_ON | A10_MMC_IDMAC_FIX_BURST); val = A10_MMC_READ_4(sc, A10_MMC_IDIE); val &= ~(A10_MMC_IDMAC_RECEIVE_INT | A10_MMC_IDMAC_TRANSMIT_INT); if (cmd->data->flags & MMC_DATA_WRITE) val |= A10_MMC_IDMAC_TRANSMIT_INT; else val |= A10_MMC_IDMAC_RECEIVE_INT; A10_MMC_WRITE_4(sc, A10_MMC_IDIE, val); A10_MMC_WRITE_4(sc, A10_MMC_DLBA, sc->a10_dma_desc_phys); A10_MMC_WRITE_4(sc, A10_MMC_FTRGL, A10_MMC_DMA_FTRGLEVEL); return (0); } static int a10_mmc_reset(struct a10_mmc_softc *sc) { int timeout; A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, A10_MMC_READ_4(sc, A10_MMC_GCTRL) | A10_MMC_RESET); timeout = 1000; while (--timeout > 0) { if ((A10_MMC_READ_4(sc, A10_MMC_GCTRL) & A10_MMC_RESET) == 0) break; DELAY(100); } if (timeout == 0) return (ETIMEDOUT); /* Set the timeout. */ A10_MMC_WRITE_4(sc, A10_MMC_TIMEOUT, 0xffffffff); /* Clear pending interrupts. */ A10_MMC_WRITE_4(sc, A10_MMC_RINTR, 0xffffffff); A10_MMC_WRITE_4(sc, A10_MMC_IDST, 0xffffffff); /* Unmask interrupts. */ A10_MMC_WRITE_4(sc, A10_MMC_IMASK, A10_MMC_CMD_DONE | A10_MMC_INT_ERR_BIT | A10_MMC_DATA_OVER | A10_MMC_AUTOCMD_DONE); /* Enable interrupts and AHB access. */ A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, A10_MMC_READ_4(sc, A10_MMC_GCTRL) | A10_MMC_INT_ENABLE); return (0); } static void a10_mmc_req_done(struct a10_mmc_softc *sc) { struct mmc_command *cmd; struct mmc_request *req; cmd = sc->a10_req->cmd; if (cmd->error != MMC_ERR_NONE) { /* Reset the controller. */ a10_mmc_reset(sc); a10_mmc_update_clock(sc); } if (sc->a10_dma_inuse == 0) { /* Reset the FIFO. */ A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, A10_MMC_READ_4(sc, A10_MMC_GCTRL) | A10_MMC_FIFO_RESET); } req = sc->a10_req; callout_stop(&sc->a10_timeoutc); sc->a10_req = NULL; sc->a10_intr = 0; sc->a10_resid = 0; sc->a10_dma_inuse = 0; sc->a10_dma_map_err = 0; sc->a10_intr_wait = 0; req->done(req); } static void a10_mmc_req_ok(struct a10_mmc_softc *sc) { int timeout; struct mmc_command *cmd; uint32_t status; timeout = 1000; while (--timeout > 0) { status = A10_MMC_READ_4(sc, A10_MMC_STAS); if ((status & A10_MMC_CARD_DATA_BUSY) == 0) break; DELAY(1000); } cmd = sc->a10_req->cmd; if (timeout == 0) { cmd->error = MMC_ERR_FAILED; a10_mmc_req_done(sc); return; } if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_136) { cmd->resp[0] = A10_MMC_READ_4(sc, A10_MMC_RESP3); cmd->resp[1] = A10_MMC_READ_4(sc, A10_MMC_RESP2); cmd->resp[2] = A10_MMC_READ_4(sc, A10_MMC_RESP1); cmd->resp[3] = A10_MMC_READ_4(sc, A10_MMC_RESP0); } else cmd->resp[0] = A10_MMC_READ_4(sc, A10_MMC_RESP0); } /* All data has been transferred ? */ if (cmd->data != NULL && (sc->a10_resid << 2) < cmd->data->len) cmd->error = MMC_ERR_FAILED; a10_mmc_req_done(sc); } static void a10_mmc_timeout(void *arg) { struct a10_mmc_softc *sc; sc = (struct a10_mmc_softc *)arg; if (sc->a10_req != NULL) { device_printf(sc->a10_dev, "controller timeout\n"); sc->a10_req->cmd->error = MMC_ERR_TIMEOUT; a10_mmc_req_done(sc); } else device_printf(sc->a10_dev, "Spurious timeout - no active request\n"); } static int a10_mmc_pio_transfer(struct a10_mmc_softc *sc, struct mmc_data *data) { int i, write; uint32_t bit, *buf; buf = (uint32_t *)data->data; write = (data->flags & MMC_DATA_WRITE) ? 1 : 0; bit = write ? A10_MMC_FIFO_FULL : A10_MMC_FIFO_EMPTY; for (i = sc->a10_resid; i < (data->len >> 2); i++) { if ((A10_MMC_READ_4(sc, A10_MMC_STAS) & bit)) return (1); if (write) A10_MMC_WRITE_4(sc, A10_MMC_FIFO, buf[i]); else buf[i] = A10_MMC_READ_4(sc, A10_MMC_FIFO); sc->a10_resid = i + 1; } return (0); } static void a10_mmc_intr(void *arg) { bus_dmasync_op_t sync_op; struct a10_mmc_softc *sc; struct mmc_data *data; uint32_t idst, imask, rint; sc = (struct a10_mmc_softc *)arg; A10_MMC_LOCK(sc); rint = A10_MMC_READ_4(sc, A10_MMC_RINTR); idst = A10_MMC_READ_4(sc, A10_MMC_IDST); imask = A10_MMC_READ_4(sc, A10_MMC_IMASK); if (idst == 0 && imask == 0 && rint == 0) { A10_MMC_UNLOCK(sc); return; } #ifdef DEBUG device_printf(sc->a10_dev, "idst: %#x, imask: %#x, rint: %#x\n", idst, imask, rint); #endif if (sc->a10_req == NULL) { device_printf(sc->a10_dev, "Spurious interrupt - no active request, rint: 0x%08X\n", rint); goto end; } if (rint & A10_MMC_INT_ERR_BIT) { device_printf(sc->a10_dev, "error rint: 0x%08X\n", rint); if (rint & A10_MMC_RESP_TIMEOUT) sc->a10_req->cmd->error = MMC_ERR_TIMEOUT; else sc->a10_req->cmd->error = MMC_ERR_FAILED; a10_mmc_req_done(sc); goto end; } if (idst & A10_MMC_IDMAC_ERROR) { device_printf(sc->a10_dev, "error idst: 0x%08x\n", idst); sc->a10_req->cmd->error = MMC_ERR_FAILED; a10_mmc_req_done(sc); goto end; } sc->a10_intr |= rint; data = sc->a10_req->cmd->data; if (data != NULL && sc->a10_dma_inuse == 1 && (idst & A10_MMC_IDMAC_COMPLETE)) { if (data->flags & MMC_DATA_WRITE) sync_op = BUS_DMASYNC_POSTWRITE; else sync_op = BUS_DMASYNC_POSTREAD; bus_dmamap_sync(sc->a10_dma_buf_tag, sc->a10_dma_buf_map, sync_op); bus_dmamap_sync(sc->a10_dma_tag, sc->a10_dma_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->a10_dma_buf_tag, sc->a10_dma_buf_map); sc->a10_resid = data->len >> 2; } else if (data != NULL && sc->a10_dma_inuse == 0 && (rint & (A10_MMC_DATA_OVER | A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ)) != 0) a10_mmc_pio_transfer(sc, data); if ((sc->a10_intr & sc->a10_intr_wait) == sc->a10_intr_wait) a10_mmc_req_ok(sc); end: A10_MMC_WRITE_4(sc, A10_MMC_IDST, idst); A10_MMC_WRITE_4(sc, A10_MMC_RINTR, rint); A10_MMC_UNLOCK(sc); } static int a10_mmc_request(device_t bus, device_t child, struct mmc_request *req) { int blksz; struct a10_mmc_softc *sc; struct mmc_command *cmd; uint32_t cmdreg, val; sc = device_get_softc(bus); A10_MMC_LOCK(sc); if (sc->a10_req) { A10_MMC_UNLOCK(sc); return (EBUSY); } sc->a10_req = req; cmd = req->cmd; cmdreg = A10_MMC_START; if (cmd->opcode == MMC_GO_IDLE_STATE) cmdreg |= A10_MMC_SEND_INIT_SEQ; if (cmd->flags & MMC_RSP_PRESENT) cmdreg |= A10_MMC_RESP_EXP; if (cmd->flags & MMC_RSP_136) cmdreg |= A10_MMC_LONG_RESP; if (cmd->flags & MMC_RSP_CRC) cmdreg |= A10_MMC_CHECK_RESP_CRC; sc->a10_intr = 0; sc->a10_resid = 0; sc->a10_intr_wait = A10_MMC_CMD_DONE; cmd->error = MMC_ERR_NONE; if (cmd->data != NULL) { sc->a10_intr_wait |= A10_MMC_DATA_OVER; cmdreg |= A10_MMC_DATA_EXP | A10_MMC_WAIT_PREOVER; if (cmd->data->flags & MMC_DATA_MULTI) { cmdreg |= A10_MMC_SEND_AUTOSTOP; sc->a10_intr_wait |= A10_MMC_AUTOCMD_DONE; } if (cmd->data->flags & MMC_DATA_WRITE) cmdreg |= A10_MMC_WRITE; blksz = min(cmd->data->len, MMC_SECTOR_SIZE); A10_MMC_WRITE_4(sc, A10_MMC_BLKSZ, blksz); A10_MMC_WRITE_4(sc, A10_MMC_BCNTR, cmd->data->len); if (a10_mmc_pio_mode == 0) a10_mmc_prepare_dma(sc); /* Enable PIO access if sc->a10_dma_inuse is not set. */ if (sc->a10_dma_inuse == 0) { val = A10_MMC_READ_4(sc, A10_MMC_GCTRL); val &= ~A10_MMC_DMA_ENABLE; val |= A10_MMC_ACCESS_BY_AHB; A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, val); val = A10_MMC_READ_4(sc, A10_MMC_IMASK); val |= A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ; A10_MMC_WRITE_4(sc, A10_MMC_IMASK, val); } } A10_MMC_WRITE_4(sc, A10_MMC_CARG, cmd->arg); A10_MMC_WRITE_4(sc, A10_MMC_CMDR, cmdreg | cmd->opcode); callout_reset(&sc->a10_timeoutc, sc->a10_timeout * hz, a10_mmc_timeout, sc); A10_MMC_UNLOCK(sc); return (0); } static int a10_mmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) { struct a10_mmc_softc *sc; sc = device_get_softc(bus); switch (which) { default: return (EINVAL); case MMCBR_IVAR_BUS_MODE: *(int *)result = sc->a10_host.ios.bus_mode; break; case MMCBR_IVAR_BUS_WIDTH: *(int *)result = sc->a10_host.ios.bus_width; break; case MMCBR_IVAR_CHIP_SELECT: *(int *)result = sc->a10_host.ios.chip_select; break; case MMCBR_IVAR_CLOCK: *(int *)result = sc->a10_host.ios.clock; break; case MMCBR_IVAR_F_MIN: *(int *)result = sc->a10_host.f_min; break; case MMCBR_IVAR_F_MAX: *(int *)result = sc->a10_host.f_max; break; case MMCBR_IVAR_HOST_OCR: *(int *)result = sc->a10_host.host_ocr; break; case MMCBR_IVAR_MODE: *(int *)result = sc->a10_host.mode; break; case MMCBR_IVAR_OCR: *(int *)result = sc->a10_host.ocr; break; case MMCBR_IVAR_POWER_MODE: *(int *)result = sc->a10_host.ios.power_mode; break; case MMCBR_IVAR_VDD: *(int *)result = sc->a10_host.ios.vdd; break; case MMCBR_IVAR_CAPS: *(int *)result = sc->a10_host.caps; break; case MMCBR_IVAR_MAX_DATA: *(int *)result = 65535; break; } return (0); } static int a10_mmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value) { struct a10_mmc_softc *sc; sc = device_get_softc(bus); switch (which) { default: return (EINVAL); case MMCBR_IVAR_BUS_MODE: sc->a10_host.ios.bus_mode = value; break; case MMCBR_IVAR_BUS_WIDTH: sc->a10_host.ios.bus_width = value; break; case MMCBR_IVAR_CHIP_SELECT: sc->a10_host.ios.chip_select = value; break; case MMCBR_IVAR_CLOCK: sc->a10_host.ios.clock = value; break; case MMCBR_IVAR_MODE: sc->a10_host.mode = value; break; case MMCBR_IVAR_OCR: sc->a10_host.ocr = value; break; case MMCBR_IVAR_POWER_MODE: sc->a10_host.ios.power_mode = value; break; case MMCBR_IVAR_VDD: sc->a10_host.ios.vdd = value; break; /* These are read-only */ case MMCBR_IVAR_CAPS: case MMCBR_IVAR_HOST_OCR: case MMCBR_IVAR_F_MIN: case MMCBR_IVAR_F_MAX: case MMCBR_IVAR_MAX_DATA: return (EINVAL); } return (0); } static int a10_mmc_update_clock(struct a10_mmc_softc *sc) { uint32_t cmdreg; int retry; cmdreg = A10_MMC_START | A10_MMC_UPCLK_ONLY | A10_MMC_WAIT_PREOVER; A10_MMC_WRITE_4(sc, A10_MMC_CMDR, cmdreg); retry = 0xfffff; while (--retry > 0) { if ((A10_MMC_READ_4(sc, A10_MMC_CMDR) & A10_MMC_START) == 0) { A10_MMC_WRITE_4(sc, A10_MMC_RINTR, 0xffffffff); return (0); } DELAY(10); } A10_MMC_WRITE_4(sc, A10_MMC_RINTR, 0xffffffff); device_printf(sc->a10_dev, "timeout updating clock\n"); return (ETIMEDOUT); } static int a10_mmc_update_ios(device_t bus, device_t child) { int error; struct a10_mmc_softc *sc; struct mmc_ios *ios; uint32_t clkcr; sc = device_get_softc(bus); clkcr = A10_MMC_READ_4(sc, A10_MMC_CLKCR); if (clkcr & A10_MMC_CARD_CLK_ON) { /* Disable clock. */ clkcr &= ~A10_MMC_CARD_CLK_ON; A10_MMC_WRITE_4(sc, A10_MMC_CLKCR, clkcr); error = a10_mmc_update_clock(sc); if (error != 0) return (error); } ios = &sc->a10_host.ios; if (ios->clock) { /* Reset the divider. */ clkcr &= ~A10_MMC_CLKCR_DIV; A10_MMC_WRITE_4(sc, A10_MMC_CLKCR, clkcr); error = a10_mmc_update_clock(sc); if (error != 0) return (error); /* Set the MMC clock. */ error = a10_clk_mmc_cfg(sc->a10_id, ios->clock); if (error != 0) return (error); /* Enable clock. */ clkcr |= A10_MMC_CARD_CLK_ON; A10_MMC_WRITE_4(sc, A10_MMC_CLKCR, clkcr); error = a10_mmc_update_clock(sc); if (error != 0) return (error); } /* Set the bus width. */ switch (ios->bus_width) { case bus_width_1: A10_MMC_WRITE_4(sc, A10_MMC_WIDTH, A10_MMC_WIDTH1); break; case bus_width_4: A10_MMC_WRITE_4(sc, A10_MMC_WIDTH, A10_MMC_WIDTH4); break; case bus_width_8: A10_MMC_WRITE_4(sc, A10_MMC_WIDTH, A10_MMC_WIDTH8); break; } return (0); } static int a10_mmc_get_ro(device_t bus, device_t child) { return (0); } static int a10_mmc_acquire_host(device_t bus, device_t child) { struct a10_mmc_softc *sc; int error; sc = device_get_softc(bus); A10_MMC_LOCK(sc); while (sc->a10_bus_busy) { error = msleep(sc, &sc->a10_mtx, PCATCH, "mmchw", 0); if (error != 0) { A10_MMC_UNLOCK(sc); return (error); } } sc->a10_bus_busy++; A10_MMC_UNLOCK(sc); return (0); } static int a10_mmc_release_host(device_t bus, device_t child) { struct a10_mmc_softc *sc; sc = device_get_softc(bus); A10_MMC_LOCK(sc); sc->a10_bus_busy--; wakeup(sc); A10_MMC_UNLOCK(sc); return (0); } static device_method_t a10_mmc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, a10_mmc_probe), DEVMETHOD(device_attach, a10_mmc_attach), DEVMETHOD(device_detach, a10_mmc_detach), /* Bus interface */ DEVMETHOD(bus_read_ivar, a10_mmc_read_ivar), DEVMETHOD(bus_write_ivar, a10_mmc_write_ivar), DEVMETHOD(bus_print_child, bus_generic_print_child), /* MMC bridge interface */ DEVMETHOD(mmcbr_update_ios, a10_mmc_update_ios), DEVMETHOD(mmcbr_request, a10_mmc_request), DEVMETHOD(mmcbr_get_ro, a10_mmc_get_ro), DEVMETHOD(mmcbr_acquire_host, a10_mmc_acquire_host), DEVMETHOD(mmcbr_release_host, a10_mmc_release_host), DEVMETHOD_END }; static devclass_t a10_mmc_devclass; static driver_t a10_mmc_driver = { "a10_mmc", a10_mmc_methods, sizeof(struct a10_mmc_softc), }; DRIVER_MODULE(a10_mmc, simplebus, a10_mmc_driver, a10_mmc_devclass, 0, 0); DRIVER_MODULE(mmc, a10_mmc, mmc_driver, mmc_devclass, NULL, NULL); Index: head/sys/arm/allwinner/a10_wdog.c =================================================================== --- head/sys/arm/allwinner/a10_wdog.c (revision 295463) +++ head/sys/arm/allwinner/a10_wdog.c (revision 295464) @@ -1,207 +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 #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")) { + if (ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-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 295463) +++ head/sys/arm/allwinner/a20/a20_cpu_cfg.c (revision 295464) @@ -1,137 +1,138 @@ /*- * 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 "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); +EARLY_DRIVER_MODULE(a20_cpu_cfg, simplebus, a20_cpu_cfg_driver, a20_cpu_cfg_devclass, 0, 0, + BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); 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/aintc.c =================================================================== --- head/sys/arm/allwinner/aintc.c (revision 295463) +++ head/sys/arm/allwinner/aintc.c (revision 295464) @@ -1,215 +1,222 @@ /*- * 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 /** * Interrupt controller registers * */ #define SW_INT_VECTOR_REG 0x00 #define SW_INT_BASE_ADR_REG 0x04 #define SW_INT_PROTECTION_REG 0x08 #define SW_INT_NMI_CTRL_REG 0x0c #define SW_INT_IRQ_PENDING_REG0 0x10 #define SW_INT_IRQ_PENDING_REG1 0x14 #define SW_INT_IRQ_PENDING_REG2 0x18 #define SW_INT_FIQ_PENDING_REG0 0x20 #define SW_INT_FIQ_PENDING_REG1 0x24 #define SW_INT_FIQ_PENDING_REG2 0x28 #define SW_INT_SELECT_REG0 0x30 #define SW_INT_SELECT_REG1 0x34 #define SW_INT_SELECT_REG2 0x38 #define SW_INT_ENABLE_REG0 0x40 #define SW_INT_ENABLE_REG1 0x44 #define SW_INT_ENABLE_REG2 0x48 #define SW_INT_MASK_REG0 0x50 #define SW_INT_MASK_REG1 0x54 #define SW_INT_MASK_REG2 0x58 #define SW_INT_IRQNO_ENMI 0 #define SW_INT_IRQ_PENDING_REG(_b) (0x10 + ((_b) * 4)) #define SW_INT_FIQ_PENDING_REG(_b) (0x20 + ((_b) * 4)) #define SW_INT_SELECT_REG(_b) (0x30 + ((_b) * 4)) #define SW_INT_ENABLE_REG(_b) (0x40 + ((_b) * 4)) #define SW_INT_MASK_REG(_b) (0x50 + ((_b) * 4)) +static struct ofw_compat_data compat_data[] = { + {"allwinner,sun4i-a10-ic", 1}, + {"allwinner,sun7i-a20-sc-nmi", 1}, + {NULL, 0} +}; + struct a10_aintc_softc { device_t sc_dev; struct resource * aintc_res; bus_space_tag_t aintc_bst; bus_space_handle_t aintc_bsh; uint8_t ver; }; static struct a10_aintc_softc *a10_aintc_sc = NULL; #define aintc_read_4(reg) \ bus_space_read_4(a10_aintc_sc->aintc_bst, a10_aintc_sc->aintc_bsh, reg) #define aintc_write_4(reg, val) \ bus_space_write_4(a10_aintc_sc->aintc_bst, a10_aintc_sc->aintc_bsh, reg, val) static int a10_aintc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-ic")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "A10 AINTC Interrupt Controller"); return (BUS_PROBE_DEFAULT); } static int a10_aintc_attach(device_t dev) { struct a10_aintc_softc *sc = device_get_softc(dev); int rid = 0; int i; sc->sc_dev = dev; if (a10_aintc_sc) return (ENXIO); sc->aintc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->aintc_res) { device_printf(dev, "could not allocate resource\n"); return (ENXIO); } sc->aintc_bst = rman_get_bustag(sc->aintc_res); sc->aintc_bsh = rman_get_bushandle(sc->aintc_res); a10_aintc_sc = sc; /* Disable & clear all interrupts */ for (i = 0; i < 3; i++) { aintc_write_4(SW_INT_ENABLE_REG(i), 0); aintc_write_4(SW_INT_MASK_REG(i), 0xffffffff); } /* enable protection mode*/ aintc_write_4(SW_INT_PROTECTION_REG, 0x01); /* config the external interrupt source type*/ aintc_write_4(SW_INT_NMI_CTRL_REG, 0x00); return (0); } static device_method_t a10_aintc_methods[] = { DEVMETHOD(device_probe, a10_aintc_probe), DEVMETHOD(device_attach, a10_aintc_attach), { 0, 0 } }; static driver_t a10_aintc_driver = { "aintc", a10_aintc_methods, sizeof(struct a10_aintc_softc), }; static devclass_t a10_aintc_devclass; -DRIVER_MODULE(aintc, simplebus, a10_aintc_driver, a10_aintc_devclass, 0, 0); +EARLY_DRIVER_MODULE(aintc, simplebus, a10_aintc_driver, a10_aintc_devclass, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_FIRST); int arm_get_next_irq(int last_irq) { uint32_t value; int i, b; for (i = 0; i < 3; i++) { value = aintc_read_4(SW_INT_IRQ_PENDING_REG(i)); for (b = 0; b < 32; b++) if (value & (1 << b)) { return (i * 32 + b); } } return (-1); } void arm_mask_irq(uintptr_t nb) { uint32_t bit, block, value; bit = (nb % 32); block = (nb / 32); value = aintc_read_4(SW_INT_ENABLE_REG(block)); value &= ~(1 << bit); aintc_write_4(SW_INT_ENABLE_REG(block), value); value = aintc_read_4(SW_INT_MASK_REG(block)); value |= (1 << bit); aintc_write_4(SW_INT_MASK_REG(block), value); } void arm_unmask_irq(uintptr_t nb) { uint32_t bit, block, value; bit = (nb % 32); block = (nb / 32); value = aintc_read_4(SW_INT_ENABLE_REG(block)); value |= (1 << bit); aintc_write_4(SW_INT_ENABLE_REG(block), value); value = aintc_read_4(SW_INT_MASK_REG(block)); value &= ~(1 << bit); aintc_write_4(SW_INT_MASK_REG(block), value); if(nb == SW_INT_IRQNO_ENMI) /* must clear pending bit when enabled */ aintc_write_4(SW_INT_IRQ_PENDING_REG(0), (1 << SW_INT_IRQNO_ENMI)); } Index: head/sys/arm/allwinner/files.a10 =================================================================== --- head/sys/arm/allwinner/files.a10 (revision 295463) +++ head/sys/arm/allwinner/files.a10 (revision 295464) @@ -1,3 +1,4 @@ # $FreeBSD$ arm/allwinner/aintc.c standard +arm/allwinner/timer.c standard Index: head/sys/arm/allwinner/files.allwinner =================================================================== --- head/sys/arm/allwinner/files.allwinner (revision 295463) +++ head/sys/arm/allwinner/files.allwinner (revision 295464) @@ -1,16 +1,15 @@ # $FreeBSD$ kern/kern_clocksource.c standard arm/allwinner/a10_ahci.c optional ahci arm/allwinner/a10_clk.c standard arm/allwinner/a10_common.c standard arm/allwinner/a10_ehci.c optional ehci arm/allwinner/a10_gpio.c optional gpio arm/allwinner/a10_mmc.c optional mmc arm/allwinner/a10_sramc.c standard arm/allwinner/a10_wdog.c standard arm/allwinner/a20/a20_cpu_cfg.c standard arm/allwinner/allwinner_machdep.c standard arm/allwinner/if_emac.c optional emac -arm/allwinner/timer.c standard #arm/allwinner/console.c standard Index: head/sys/arm/allwinner/if_emac.c =================================================================== --- head/sys/arm/allwinner/if_emac.c (revision 295463) +++ head/sys/arm/allwinner/if_emac.c (revision 295464) @@ -1,1161 +1,1161 @@ /*- * 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$ */ /* A10/A20 EMAC driver */ #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 #ifdef INET #include #include #include #include #endif #include #include #include #include #include #include #include #include #include "miibus_if.h" #include "gpio_if.h" #include "a10_clk.h" #include "a10_sramc.h" #include "a10_gpio.h" struct emac_softc { struct ifnet *emac_ifp; device_t emac_dev; device_t emac_miibus; bus_space_handle_t emac_handle; bus_space_tag_t emac_tag; struct resource *emac_res; struct resource *emac_irq; void *emac_intrhand; int emac_if_flags; struct mtx emac_mtx; struct callout emac_tick_ch; int emac_watchdog_timer; int emac_rx_process_limit; int emac_link; uint32_t emac_fifo_mask; }; static int emac_probe(device_t); static int emac_attach(device_t); static int emac_detach(device_t); static int emac_shutdown(device_t); static int emac_suspend(device_t); static int emac_resume(device_t); static void emac_sys_setup(void); static void emac_reset(struct emac_softc *); static void emac_init_locked(struct emac_softc *); static void emac_start_locked(struct ifnet *); static void emac_init(void *); static void emac_stop_locked(struct emac_softc *); static void emac_intr(void *); static int emac_ioctl(struct ifnet *, u_long, caddr_t); static void emac_rxeof(struct emac_softc *, int); static void emac_txeof(struct emac_softc *, uint32_t); static int emac_miibus_readreg(device_t, int, int); static int emac_miibus_writereg(device_t, int, int, int); static void emac_miibus_statchg(device_t); static int emac_ifmedia_upd(struct ifnet *); static void emac_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int); static int sysctl_hw_emac_proc_limit(SYSCTL_HANDLER_ARGS); #define EMAC_READ_REG(sc, reg) \ bus_space_read_4(sc->emac_tag, sc->emac_handle, reg) #define EMAC_WRITE_REG(sc, reg, val) \ bus_space_write_4(sc->emac_tag, sc->emac_handle, reg, val) static void emac_sys_setup(void) { /* Activate EMAC clock. */ a10_clk_emac_activate(); /* Set the pin mux to EMAC (mii). */ a10_gpio_ethernet_activate(A10_GPIO_FUNC_MII); /* Map sram. */ a10_map_to_emac(); } static void emac_get_hwaddr(struct emac_softc *sc, uint8_t *hwaddr) { uint32_t val0, val1, rnd; /* * Try to get MAC address from running hardware. * If there is something non-zero there just use it. * * Otherwise set the address to a convenient locally assigned address, * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally * assigned bit set, and the broadcast/multicast bit clear. */ val0 = EMAC_READ_REG(sc, EMAC_MAC_A0); val1 = EMAC_READ_REG(sc, EMAC_MAC_A1); if ((val0 | val1) != 0 && (val0 | val1) != 0xffffff) { hwaddr[0] = (val1 >> 16) & 0xff; hwaddr[1] = (val1 >> 8) & 0xff; hwaddr[2] = (val1 >> 0) & 0xff; hwaddr[3] = (val0 >> 16) & 0xff; hwaddr[4] = (val0 >> 8) & 0xff; hwaddr[5] = (val0 >> 0) & 0xff; } else { rnd = arc4random() & 0x00ffffff; hwaddr[0] = 'b'; hwaddr[1] = 's'; hwaddr[2] = 'd'; hwaddr[3] = (rnd >> 16) & 0xff; hwaddr[4] = (rnd >> 8) & 0xff; hwaddr[5] = (rnd >> 0) & 0xff; } if (bootverbose) printf("MAC address: %s\n", ether_sprintf(hwaddr)); } static void emac_set_rx_mode(struct emac_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; uint32_t h, hashes[2]; uint32_t rcr = 0; EMAC_ASSERT_LOCKED(sc); ifp = sc->emac_ifp; rcr = EMAC_READ_REG(sc, EMAC_RX_CTL); /* Unicast packet and DA filtering */ rcr |= EMAC_RX_UCAD; rcr |= EMAC_RX_DAF; hashes[0] = 0; hashes[1] = 0; if (ifp->if_flags & IFF_ALLMULTI) { hashes[0] = 0xffffffff; hashes[1] = 0xffffffff; } else { if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &sc->emac_ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; hashes[h >> 5] |= 1 << (h & 0x1f); } if_maddr_runlock(ifp); } rcr |= EMAC_RX_MCO; rcr |= EMAC_RX_MHF; EMAC_WRITE_REG(sc, EMAC_RX_HASH0, hashes[0]); EMAC_WRITE_REG(sc, EMAC_RX_HASH1, hashes[1]); if (ifp->if_flags & IFF_BROADCAST) { rcr |= EMAC_RX_BCO; rcr |= EMAC_RX_MCO; } if (ifp->if_flags & IFF_PROMISC) rcr |= EMAC_RX_PA; else rcr |= EMAC_RX_UCAD; EMAC_WRITE_REG(sc, EMAC_RX_CTL, rcr); } static void emac_reset(struct emac_softc *sc) { EMAC_WRITE_REG(sc, EMAC_CTL, 0); DELAY(200); EMAC_WRITE_REG(sc, EMAC_CTL, 1); DELAY(200); } static void emac_drain_rxfifo(struct emac_softc *sc) { uint32_t data; while (EMAC_READ_REG(sc, EMAC_RX_FBC) > 0) data = EMAC_READ_REG(sc, EMAC_RX_IO_DATA); } static void emac_txeof(struct emac_softc *sc, uint32_t status) { struct ifnet *ifp; EMAC_ASSERT_LOCKED(sc); ifp = sc->emac_ifp; status &= (EMAC_TX_FIFO0 | EMAC_TX_FIFO1); sc->emac_fifo_mask &= ~status; if (status == (EMAC_TX_FIFO0 | EMAC_TX_FIFO1)) if_inc_counter(ifp, IFCOUNTER_OPACKETS, 2); else if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* Unarm watchdog timer if no TX */ sc->emac_watchdog_timer = 0; } static void emac_rxeof(struct emac_softc *sc, int count) { struct ifnet *ifp; struct mbuf *m, *m0; uint32_t reg_val, rxcount; int16_t len; uint16_t status; int i; ifp = sc->emac_ifp; for (; count > 0 && (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0; count--) { /* * Race warning: The first packet might arrive with * the interrupts disabled, but the second will fix */ rxcount = EMAC_READ_REG(sc, EMAC_RX_FBC); if (!rxcount) { /* Had one stuck? */ rxcount = EMAC_READ_REG(sc, EMAC_RX_FBC); if (!rxcount) return; } /* Check packet header */ reg_val = EMAC_READ_REG(sc, EMAC_RX_IO_DATA); if (reg_val != EMAC_PACKET_HEADER) { /* Packet header is wrong */ if (bootverbose) if_printf(ifp, "wrong packet header\n"); /* Disable RX */ reg_val = EMAC_READ_REG(sc, EMAC_CTL); reg_val &= ~EMAC_CTL_RX_EN; EMAC_WRITE_REG(sc, EMAC_CTL, reg_val); /* Flush RX FIFO */ reg_val = EMAC_READ_REG(sc, EMAC_RX_CTL); reg_val |= EMAC_RX_FLUSH_FIFO; EMAC_WRITE_REG(sc, EMAC_RX_CTL, reg_val); for (i = 100; i > 0; i--) { DELAY(100); if ((EMAC_READ_REG(sc, EMAC_RX_CTL) & EMAC_RX_FLUSH_FIFO) == 0) break; } if (i == 0) { device_printf(sc->emac_dev, "flush FIFO timeout\n"); /* Reinitialize controller */ emac_init_locked(sc); return; } /* Enable RX */ reg_val = EMAC_READ_REG(sc, EMAC_CTL); reg_val |= EMAC_CTL_RX_EN; EMAC_WRITE_REG(sc, EMAC_CTL, reg_val); return; } /* Get packet size and status */ reg_val = EMAC_READ_REG(sc, EMAC_RX_IO_DATA); len = reg_val & 0xffff; status = (reg_val >> 16) & 0xffff; if (len < 64 || (status & EMAC_PKT_OK) == 0) { if (bootverbose) if_printf(ifp, "bad packet: len = %i status = %i\n", len, status); if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); emac_drain_rxfifo(sc); continue; } #if 0 if (status & (EMAC_CRCERR | EMAC_LENERR)) { good_packet = 0; if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); if (status & EMAC_CRCERR) if_printf(ifp, "crc error\n"); if (status & EMAC_LENERR) if_printf(ifp, "length error\n"); } #endif m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { emac_drain_rxfifo(sc); return; } m->m_len = m->m_pkthdr.len = MCLBYTES; /* Copy entire frame to mbuf first. */ bus_space_read_multi_4(sc->emac_tag, sc->emac_handle, EMAC_RX_IO_DATA, mtod(m, uint32_t *), roundup2(len, 4) / 4); m->m_pkthdr.rcvif = ifp; m->m_len = m->m_pkthdr.len = len - ETHER_CRC_LEN; /* * Emac controller needs strict aligment, so to avoid * copying over an entire frame to align, we allocate * a new mbuf and copy ethernet header + IP header to * the new mbuf. The new mbuf is prepended into the * existing mbuf chain. */ if (m->m_len <= (MHLEN - ETHER_HDR_LEN)) { bcopy(m->m_data, m->m_data + ETHER_HDR_LEN, m->m_len); m->m_data += ETHER_HDR_LEN; } else if (m->m_len <= (MCLBYTES - ETHER_HDR_LEN) && m->m_len > (MHLEN - ETHER_HDR_LEN)) { MGETHDR(m0, M_NOWAIT, MT_DATA); if (m0 != NULL) { len = ETHER_HDR_LEN + m->m_pkthdr.l2hlen; bcopy(m->m_data, m0->m_data, len); m->m_data += len; m->m_len -= len; m0->m_len = len; M_MOVE_PKTHDR(m0, m); m0->m_next = m; m = m0; } else { if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); m_freem(m); m = NULL; continue; } } else if (m->m_len > EMAC_MAC_MAXF) { if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); m_freem(m); m = NULL; continue; } if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); EMAC_UNLOCK(sc); (*ifp->if_input)(ifp, m); EMAC_LOCK(sc); } } static void emac_watchdog(struct emac_softc *sc) { struct ifnet *ifp; EMAC_ASSERT_LOCKED(sc); if (sc->emac_watchdog_timer == 0 || --sc->emac_watchdog_timer) return; ifp = sc->emac_ifp; if (sc->emac_link == 0) { if (bootverbose) if_printf(sc->emac_ifp, "watchdog timeout " "(missed link)\n"); } else if_printf(sc->emac_ifp, "watchdog timeout -- resetting\n"); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; emac_init_locked(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) emac_start_locked(ifp); } static void emac_tick(void *arg) { struct emac_softc *sc; struct mii_data *mii; sc = (struct emac_softc *)arg; mii = device_get_softc(sc->emac_miibus); mii_tick(mii); emac_watchdog(sc); callout_reset(&sc->emac_tick_ch, hz, emac_tick, sc); } static void emac_init(void *xcs) { struct emac_softc *sc; sc = (struct emac_softc *)xcs; EMAC_LOCK(sc); emac_init_locked(sc); EMAC_UNLOCK(sc); } static void emac_init_locked(struct emac_softc *sc) { struct ifnet *ifp; struct mii_data *mii; uint32_t reg_val; uint8_t *eaddr; EMAC_ASSERT_LOCKED(sc); ifp = sc->emac_ifp; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) return; /* Flush RX FIFO */ reg_val = EMAC_READ_REG(sc, EMAC_RX_CTL); reg_val |= EMAC_RX_FLUSH_FIFO; EMAC_WRITE_REG(sc, EMAC_RX_CTL, reg_val); DELAY(1); /* Soft reset MAC */ reg_val = EMAC_READ_REG(sc, EMAC_MAC_CTL0); reg_val &= (~EMAC_MAC_CTL0_SOFT_RST); EMAC_WRITE_REG(sc, EMAC_MAC_CTL0, reg_val); /* Set MII clock */ reg_val = EMAC_READ_REG(sc, EMAC_MAC_MCFG); reg_val &= (~(0xf << 2)); reg_val |= (0xd << 2); EMAC_WRITE_REG(sc, EMAC_MAC_MCFG, reg_val); /* Clear RX counter */ EMAC_WRITE_REG(sc, EMAC_RX_FBC, 0); /* Disable all interrupt and clear interrupt status */ EMAC_WRITE_REG(sc, EMAC_INT_CTL, 0); reg_val = EMAC_READ_REG(sc, EMAC_INT_STA); EMAC_WRITE_REG(sc, EMAC_INT_STA, reg_val); DELAY(1); /* Set up TX */ reg_val = EMAC_READ_REG(sc, EMAC_TX_MODE); reg_val |= EMAC_TX_AB_M; reg_val &= EMAC_TX_TM; EMAC_WRITE_REG(sc, EMAC_TX_MODE, reg_val); /* Set up RX */ reg_val = EMAC_READ_REG(sc, EMAC_RX_CTL); reg_val |= EMAC_RX_SETUP; reg_val &= EMAC_RX_TM; EMAC_WRITE_REG(sc, EMAC_RX_CTL, reg_val); /* Set up MAC CTL0. */ reg_val = EMAC_READ_REG(sc, EMAC_MAC_CTL0); reg_val |= EMAC_MAC_CTL0_SETUP; EMAC_WRITE_REG(sc, EMAC_MAC_CTL0, reg_val); /* Set up MAC CTL1. */ reg_val = EMAC_READ_REG(sc, EMAC_MAC_CTL1); reg_val |= EMAC_MAC_CTL1_SETUP; EMAC_WRITE_REG(sc, EMAC_MAC_CTL1, reg_val); /* Set up IPGT */ EMAC_WRITE_REG(sc, EMAC_MAC_IPGT, EMAC_MAC_IPGT_FD); /* Set up IPGR */ EMAC_WRITE_REG(sc, EMAC_MAC_IPGR, EMAC_MAC_NBTB_IPG2 | (EMAC_MAC_NBTB_IPG1 << 8)); /* Set up Collison window */ EMAC_WRITE_REG(sc, EMAC_MAC_CLRT, EMAC_MAC_RM | (EMAC_MAC_CW << 8)); /* Set up Max Frame Length */ EMAC_WRITE_REG(sc, EMAC_MAC_MAXF, EMAC_MAC_MFL); /* Setup ethernet address */ eaddr = IF_LLADDR(ifp); EMAC_WRITE_REG(sc, EMAC_MAC_A1, eaddr[0] << 16 | eaddr[1] << 8 | eaddr[2]); EMAC_WRITE_REG(sc, EMAC_MAC_A0, eaddr[3] << 16 | eaddr[4] << 8 | eaddr[5]); /* Setup rx filter */ emac_set_rx_mode(sc); /* Enable RX/TX0/RX Hlevel interrupt */ reg_val = EMAC_READ_REG(sc, EMAC_INT_CTL); reg_val |= EMAC_INT_EN; EMAC_WRITE_REG(sc, EMAC_INT_CTL, reg_val); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->emac_link = 0; /* Switch to the current media. */ mii = device_get_softc(sc->emac_miibus); mii_mediachg(mii); callout_reset(&sc->emac_tick_ch, hz, emac_tick, sc); } static void emac_start(struct ifnet *ifp) { struct emac_softc *sc; sc = ifp->if_softc; EMAC_LOCK(sc); emac_start_locked(ifp); EMAC_UNLOCK(sc); } static void emac_start_locked(struct ifnet *ifp) { struct emac_softc *sc; struct mbuf *m, *m0; uint32_t fifo, reg; sc = ifp->if_softc; if (ifp->if_drv_flags & IFF_DRV_OACTIVE) return; if (sc->emac_fifo_mask == (EMAC_TX_FIFO0 | EMAC_TX_FIFO1)) return; if (sc->emac_link == 0) return; IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) return; /* Select channel */ if (sc->emac_fifo_mask & EMAC_TX_FIFO0) fifo = 1; else fifo = 0; sc->emac_fifo_mask |= (1 << fifo); if (sc->emac_fifo_mask == (EMAC_TX_FIFO0 | EMAC_TX_FIFO1)) ifp->if_drv_flags |= IFF_DRV_OACTIVE; EMAC_WRITE_REG(sc, EMAC_TX_INS, fifo); /* * Emac controller wants 4 byte aligned TX buffers. * We have to copy pretty much all the time. */ if (m->m_next != NULL || (mtod(m, uintptr_t) & 3) != 0) { m0 = m_defrag(m, M_NOWAIT); if (m0 == NULL) { m_freem(m); m = NULL; return; } m = m0; } /* Write data */ bus_space_write_multi_4(sc->emac_tag, sc->emac_handle, EMAC_TX_IO_DATA, mtod(m, uint32_t *), roundup2(m->m_len, 4) / 4); /* Send the data lengh. */ reg = (fifo == 0) ? EMAC_TX_PL0 : EMAC_TX_PL1; EMAC_WRITE_REG(sc, reg, m->m_len); /* Start translate from fifo to phy. */ reg = (fifo == 0) ? EMAC_TX_CTL0 : EMAC_TX_CTL1; EMAC_WRITE_REG(sc, reg, EMAC_READ_REG(sc, reg) | 1); /* Set timeout */ sc->emac_watchdog_timer = 5; /* Data have been sent to hardware, it is okay to free the mbuf now. */ BPF_MTAP(ifp, m); m_freem(m); } static void emac_stop_locked(struct emac_softc *sc) { struct ifnet *ifp; uint32_t reg_val; EMAC_ASSERT_LOCKED(sc); ifp = sc->emac_ifp; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->emac_link = 0; /* Disable all interrupt and clear interrupt status */ EMAC_WRITE_REG(sc, EMAC_INT_CTL, 0); reg_val = EMAC_READ_REG(sc, EMAC_INT_STA); EMAC_WRITE_REG(sc, EMAC_INT_STA, reg_val); /* Disable RX/TX */ reg_val = EMAC_READ_REG(sc, EMAC_CTL); reg_val &= ~(EMAC_CTL_RST | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN); EMAC_WRITE_REG(sc, EMAC_CTL, reg_val); callout_stop(&sc->emac_tick_ch); } static void emac_intr(void *arg) { struct emac_softc *sc; struct ifnet *ifp; uint32_t reg_val; sc = (struct emac_softc *)arg; EMAC_LOCK(sc); /* Disable all interrupts */ EMAC_WRITE_REG(sc, EMAC_INT_CTL, 0); /* Get EMAC interrupt status */ reg_val = EMAC_READ_REG(sc, EMAC_INT_STA); /* Clear ISR status */ EMAC_WRITE_REG(sc, EMAC_INT_STA, reg_val); /* Received incoming packet */ if (reg_val & EMAC_INT_STA_RX) emac_rxeof(sc, sc->emac_rx_process_limit); /* Transmit Interrupt check */ if (reg_val & EMAC_INT_STA_TX) { emac_txeof(sc, reg_val); ifp = sc->emac_ifp; if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) emac_start_locked(ifp); } /* Re-enable interrupt mask */ reg_val = EMAC_READ_REG(sc, EMAC_INT_CTL); reg_val |= EMAC_INT_EN; EMAC_WRITE_REG(sc, EMAC_INT_CTL, reg_val); EMAC_UNLOCK(sc); } static int emac_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct emac_softc *sc; struct mii_data *mii; struct ifreq *ifr; int error = 0; sc = ifp->if_softc; ifr = (struct ifreq *)data; switch (command) { case SIOCSIFFLAGS: EMAC_LOCK(sc); if (ifp->if_flags & IFF_UP) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { if ((ifp->if_flags ^ sc->emac_if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) emac_set_rx_mode(sc); } else emac_init_locked(sc); } else { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) emac_stop_locked(sc); } sc->emac_if_flags = ifp->if_flags; EMAC_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: EMAC_LOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) { emac_set_rx_mode(sc); } EMAC_UNLOCK(sc); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->emac_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; default: error = ether_ioctl(ifp, command, data); break; } return (error); } static int emac_probe(device_t dev) { - if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-emac")) + if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-emac")) return (ENXIO); device_set_desc(dev, "A10/A20 EMAC ethernet controller"); return (BUS_PROBE_DEFAULT); } static int emac_detach(device_t dev) { struct emac_softc *sc; sc = device_get_softc(dev); sc->emac_ifp->if_drv_flags &= ~IFF_DRV_RUNNING; if (device_is_attached(dev)) { ether_ifdetach(sc->emac_ifp); EMAC_LOCK(sc); emac_stop_locked(sc); EMAC_UNLOCK(sc); callout_drain(&sc->emac_tick_ch); } if (sc->emac_intrhand != NULL) bus_teardown_intr(sc->emac_dev, sc->emac_irq, sc->emac_intrhand); if (sc->emac_miibus != NULL) { device_delete_child(sc->emac_dev, sc->emac_miibus); bus_generic_detach(sc->emac_dev); } if (sc->emac_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->emac_res); if (sc->emac_irq != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->emac_irq); if (sc->emac_ifp != NULL) if_free(sc->emac_ifp); if (mtx_initialized(&sc->emac_mtx)) mtx_destroy(&sc->emac_mtx); return (0); } static int emac_shutdown(device_t dev) { return (emac_suspend(dev)); } static int emac_suspend(device_t dev) { struct emac_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); EMAC_LOCK(sc); ifp = sc->emac_ifp; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) emac_stop_locked(sc); EMAC_UNLOCK(sc); return (0); } static int emac_resume(device_t dev) { struct emac_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); EMAC_LOCK(sc); ifp = sc->emac_ifp; if ((ifp->if_flags & IFF_UP) != 0) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; emac_init_locked(sc); } EMAC_UNLOCK(sc); return (0); } static int emac_attach(device_t dev) { struct emac_softc *sc; struct ifnet *ifp; int error, rid; uint8_t eaddr[ETHER_ADDR_LEN]; sc = device_get_softc(dev); sc->emac_dev = dev; error = 0; mtx_init(&sc->emac_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->emac_tick_ch, &sc->emac_mtx, 0); rid = 0; sc->emac_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->emac_res == NULL) { device_printf(dev, "unable to map memory\n"); error = ENXIO; goto fail; } sc->emac_tag = rman_get_bustag(sc->emac_res); sc->emac_handle = rman_get_bushandle(sc->emac_res); rid = 0; sc->emac_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->emac_irq == NULL) { device_printf(dev, "cannot allocate IRQ resources.\n"); error = ENXIO; goto fail; } /* Create device sysctl node. */ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "process_limit", CTLTYPE_INT | CTLFLAG_RW, &sc->emac_rx_process_limit, 0, sysctl_hw_emac_proc_limit, "I", "max number of Rx events to process"); sc->emac_rx_process_limit = EMAC_PROC_DEFAULT; error = resource_int_value(device_get_name(dev), device_get_unit(dev), "process_limit", &sc->emac_rx_process_limit); if (error == 0) { if (sc->emac_rx_process_limit < EMAC_PROC_MIN || sc->emac_rx_process_limit > EMAC_PROC_MAX) { device_printf(dev, "process_limit value out of range; " "using default: %d\n", EMAC_PROC_DEFAULT); sc->emac_rx_process_limit = EMAC_PROC_DEFAULT; } } /* Setup EMAC */ emac_sys_setup(); emac_reset(sc); ifp = sc->emac_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "unable to allocate ifp\n"); error = ENOSPC; goto fail; } ifp->if_softc = sc; /* Setup MII */ error = mii_attach(dev, &sc->emac_miibus, ifp, emac_ifmedia_upd, emac_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); if (error != 0) { device_printf(dev, "PHY probe failed\n"); goto fail; } if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_start = emac_start; ifp->if_ioctl = emac_ioctl; ifp->if_init = emac_init; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); /* Get MAC address */ emac_get_hwaddr(sc, eaddr); ether_ifattach(ifp, eaddr); /* VLAN capability setup. */ ifp->if_capabilities |= IFCAP_VLAN_MTU; ifp->if_capenable = ifp->if_capabilities; /* Tell the upper layer we support VLAN over-sized frames. */ ifp->if_hdrlen = sizeof(struct ether_vlan_header); error = bus_setup_intr(dev, sc->emac_irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, emac_intr, sc, &sc->emac_intrhand); if (error != 0) { device_printf(dev, "could not set up interrupt handler.\n"); ether_ifdetach(ifp); goto fail; } fail: if (error != 0) emac_detach(dev); return (error); } static boolean_t emac_miibus_iowait(struct emac_softc *sc) { uint32_t timeout; for (timeout = 100; timeout != 0; --timeout) { DELAY(100); if ((EMAC_READ_REG(sc, EMAC_MAC_MIND) & 0x1) == 0) return (true); } return (false); } /* * The MII bus interface */ static int emac_miibus_readreg(device_t dev, int phy, int reg) { struct emac_softc *sc; int rval; sc = device_get_softc(dev); /* Issue phy address and reg */ EMAC_WRITE_REG(sc, EMAC_MAC_MADR, (phy << 8) | reg); /* Pull up the phy io line */ EMAC_WRITE_REG(sc, EMAC_MAC_MCMD, 0x1); if (!emac_miibus_iowait(sc)) { device_printf(dev, "timeout waiting for mii read\n"); return (0); } /* Push down the phy io line */ EMAC_WRITE_REG(sc, EMAC_MAC_MCMD, 0x0); /* Read data */ rval = EMAC_READ_REG(sc, EMAC_MAC_MRDD); return (rval); } static int emac_miibus_writereg(device_t dev, int phy, int reg, int data) { struct emac_softc *sc; sc = device_get_softc(dev); /* Issue phy address and reg */ EMAC_WRITE_REG(sc, EMAC_MAC_MADR, (phy << 8) | reg); /* Write data */ EMAC_WRITE_REG(sc, EMAC_MAC_MWTD, data); /* Pull up the phy io line */ EMAC_WRITE_REG(sc, EMAC_MAC_MCMD, 0x1); if (!emac_miibus_iowait(sc)) { device_printf(dev, "timeout waiting for mii write\n"); return (0); } /* Push down the phy io line */ EMAC_WRITE_REG(sc, EMAC_MAC_MCMD, 0x0); return (0); } static void emac_miibus_statchg(device_t dev) { struct emac_softc *sc; struct mii_data *mii; struct ifnet *ifp; uint32_t reg_val; sc = device_get_softc(dev); mii = device_get_softc(sc->emac_miibus); ifp = sc->emac_ifp; if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; sc->emac_link = 0; if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID)) { switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: sc->emac_link = 1; break; default: break; } } /* Program MACs with resolved speed/duplex. */ if (sc->emac_link != 0) { reg_val = EMAC_READ_REG(sc, EMAC_MAC_IPGT); if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { reg_val &= ~EMAC_MAC_IPGT_HD; reg_val |= EMAC_MAC_IPGT_FD; } else { reg_val &= ~EMAC_MAC_IPGT_FD; reg_val |= EMAC_MAC_IPGT_HD; } EMAC_WRITE_REG(sc, EMAC_MAC_IPGT, reg_val); /* Enable RX/TX */ reg_val = EMAC_READ_REG(sc, EMAC_CTL); reg_val |= EMAC_CTL_RST | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN; EMAC_WRITE_REG(sc, EMAC_CTL, reg_val); } else { /* Disable RX/TX */ reg_val = EMAC_READ_REG(sc, EMAC_CTL); reg_val &= ~(EMAC_CTL_RST | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN); EMAC_WRITE_REG(sc, EMAC_CTL, reg_val); } } static int emac_ifmedia_upd(struct ifnet *ifp) { struct emac_softc *sc; struct mii_data *mii; struct mii_softc *miisc; int error; sc = ifp->if_softc; mii = device_get_softc(sc->emac_miibus); EMAC_LOCK(sc); LIST_FOREACH(miisc, &mii->mii_phys, mii_list) PHY_RESET(miisc); error = mii_mediachg(mii); EMAC_UNLOCK(sc); return (error); } static void emac_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct emac_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->emac_miibus); EMAC_LOCK(sc); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; EMAC_UNLOCK(sc); } static device_method_t emac_methods[] = { /* Device interface */ DEVMETHOD(device_probe, emac_probe), DEVMETHOD(device_attach, emac_attach), DEVMETHOD(device_detach, emac_detach), DEVMETHOD(device_shutdown, emac_shutdown), DEVMETHOD(device_suspend, emac_suspend), DEVMETHOD(device_resume, emac_resume), /* bus interface, for miibus */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, emac_miibus_readreg), DEVMETHOD(miibus_writereg, emac_miibus_writereg), DEVMETHOD(miibus_statchg, emac_miibus_statchg), DEVMETHOD_END }; static driver_t emac_driver = { "emac", emac_methods, sizeof(struct emac_softc) }; static devclass_t emac_devclass; DRIVER_MODULE(emac, simplebus, emac_driver, emac_devclass, 0, 0); DRIVER_MODULE(miibus, emac, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(emac, miibus, 1, 1, 1); MODULE_DEPEND(emac, ether, 1, 1, 1); static int sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high) { int error, value; if (arg1 == NULL) return (EINVAL); value = *(int *)arg1; error = sysctl_handle_int(oidp, &value, 0, req); if (error || req->newptr == NULL) return (error); if (value < low || value > high) return (EINVAL); *(int *)arg1 = value; return (0); } static int sysctl_hw_emac_proc_limit(SYSCTL_HANDLER_ARGS) { return (sysctl_int_range(oidp, arg1, arg2, req, EMAC_PROC_MIN, EMAC_PROC_MAX)); } Index: head/sys/arm/allwinner/timer.c =================================================================== --- head/sys/arm/allwinner/timer.c (revision 295463) +++ head/sys/arm/allwinner/timer.c (revision 295464) @@ -1,376 +1,374 @@ /*- * 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 "a20/a20_cpu_cfg.h" +#include /** * 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; + u_int soc_family; 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 + if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-timer")) return (ENXIO); + soc_family = allwinner_soc_family(); + if (soc_family != ALLWINNERSOC_SUN4I && + soc_family != ALLWINNERSOC_SUN5I) + 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); +EARLY_DRIVER_MODULE(a10_timer, simplebus, a10_timer_driver, a10_timer_devclass, 0, 0, + BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); 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/conf/A10 =================================================================== --- head/sys/arm/conf/A10 (nonexistent) +++ head/sys/arm/conf/A10 (revision 295464) @@ -0,0 +1,105 @@ +# +# A10 -- Custom configuration for the AllWinner A10 SoC +# +# For more information on this file, please read the config(5) manual page, +# and/or the handbook section on Kernel Configuration Files: +# +# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html +# +# The handbook is also available locally in /usr/share/doc/handbook +# if you've installed the doc distribution, otherwise always see the +# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the +# latest information. +# +# An exhaustive list of options and more detailed explanations of the +# device lines is also present in the ../../conf/NOTES and NOTES files. +# If you are in doubt as to the purpose or necessity of a line, check first +# in NOTES. +# +# $FreeBSD$ + +ident A10 + +include "std.armv6" +include "../allwinner/std.a10" + +options HZ=100 +options SCHED_4BSD # 4BSD scheduler +options PLATFORM + +# Debugging for use in -current +makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols +options ALT_BREAK_TO_DEBUGGER +#options VERBOSE_SYSINIT # Enable verbose sysinit messages +options KDB # Enable kernel debugger support +# For minimum debugger support (stable branch) use: +#options KDB_TRACE # Print a stack trace for a panic +# For full debugger support use this instead: +options DDB # Enable the kernel debugger +options INVARIANTS # Enable calls of extra sanity checking +options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS +options WITNESS # Enable checks to detect deadlocks and cycles +options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed +#options DIAGNOSTIC + +# NFS root from boopt/dhcp +#options BOOTP +#options BOOTP_NFSROOT +#options BOOTP_COMPAT +#options BOOTP_NFSV3 +#options BOOTP_WIRED_TO=emac0 + +# MMC/SD/SDIO Card slot support +device mmc # mmc/sd bus +device mmcsd # mmc/sd flash cards + +# ATA controllers +device ahci # AHCI-compatible SATA controllers +#device ata # Legacy ATA/SATA controllers + +# Console and misc +device uart +device uart_ns8250 +device pty +device snp +device md +device random # Entropy device + +# I2C support +#device iicbus +#device iic + +# GPIO +device gpio +device gpioled + +device scbus # SCSI bus (required for ATA/SCSI) +device da # Direct Access (disks) +device pass # Passthrough device (direct ATA/SCSI access) + +# USB support +options USB_HOST_ALIGN=64 # Align usb buffers to cache line size. +device usb +options USB_DEBUG +#options USB_REQ_DEBUG +#options USB_VERBOSE +#device uhci +#device ohci +device ehci + +device umass + +# Ethernet +device loop +device ether +device mii +device bpf + +device emac + +# USB ethernet support, requires miibus +device miibus + +# Flattened Device Tree +options FDT # Configure using FDT/DTB data +makeoptions MODULES_EXTRA=dtb/allwinner Property changes on: head/sys/arm/conf/A10 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/arm/conf/A20 =================================================================== --- head/sys/arm/conf/A20 (revision 295463) +++ head/sys/arm/conf/A20 (revision 295464) @@ -1,115 +1,115 @@ # # A20 -- Custom configuration for the Allwinner A20 ARM SoC # # For more information on this file, please read the config(5) manual page, # and/or the handbook section on Kernel Configuration Files: # # http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the ../../conf/NOTES and NOTES files. # If you are in doubt as to the purpose or necessity of a line, check first # in NOTES. # # $FreeBSD$ ident A20 include "std.armv6" include "../allwinner/a20/std.a20" options ARM_INTRNG options HZ=100 options SCHED_ULE # ULE scheduler options SMP # Enable multiple cores options PLATFORM # Debugging for use in -current makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols options ALT_BREAK_TO_DEBUGGER #options VERBOSE_SYSINIT # Enable verbose sysinit messages options KDB # Enable kernel debugger support # For minimum debugger support (stable branch) use: #options KDB_TRACE # Print a stack trace for a panic # For full debugger support use this instead: options DDB # Enable the kernel debugger options INVARIANTS # Enable calls of extra sanity checking options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS options WITNESS # Enable checks to detect deadlocks and cycles options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed #options DIAGNOSTIC # NFS root from boopt/dhcp #options BOOTP #options BOOTP_NFSROOT #options BOOTP_COMPAT #options BOOTP_NFSV3 #options BOOTP_WIRED_TO=dwc0 -# Boot device is 2nd slice on MMC/SD card -options ROOTDEVNAME=\"ufs:/dev/da0s2\" - # Interrupt controller device gic + +# ARM Generic Timer +device generic_timer # MMC/SD/SDIO Card slot support device mmc # mmc/sd bus device mmcsd # mmc/sd flash cards # ATA controllers device ahci # AHCI-compatible SATA controllers #device ata # Legacy ATA/SATA controllers # Console and misc device uart device uart_ns8250 device pty device snp device md device random # Entropy device # I2C support #device iicbus #device iic # GPIO device gpio device gpioled device scbus # SCSI bus (required for ATA/SCSI) device da # Direct Access (disks) device pass # Passthrough device (direct ATA/SCSI access) # USB support options USB_HOST_ALIGN=64 # Align usb buffers to cache line size. device usb options USB_DEBUG #options USB_REQ_DEBUG #options USB_VERBOSE #device uhci #device ohci device ehci device umass # Ethernet device loop device ether device mii device bpf #device emac # 10/100 integrated EMAC controller device dwc # 10/100/1000 integrated GMAC controller # USB ethernet support, requires miibus device miibus # Flattened Device Tree options FDT # Configure using FDT/DTB data makeoptions MODULES_EXTRA=dtb/allwinner Index: head/sys/arm64/arm64/swtch.S =================================================================== --- head/sys/arm64/arm64/swtch.S (revision 295463) +++ head/sys/arm64/arm64/swtch.S (revision 295464) @@ -1,321 +1,320 @@ /*- * Copyright (c) 2014 Andrew Turner * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Andrew Turner 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 "assym.s" #include "opt_kstack_pages.h" #include "opt_sched.h" #include __FBSDID("$FreeBSD$"); .macro clear_step_flag pcbflags, tmp tbz \pcbflags, #PCB_SINGLE_STEP_SHIFT, 999f mrs \tmp, mdscr_el1 bic \tmp, \tmp, #1 msr mdscr_el1, \tmp isb 999: .endm .macro set_step_flag pcbflags, tmp tbz \pcbflags, #PCB_SINGLE_STEP_SHIFT, 999f mrs \tmp, mdscr_el1 orr \tmp, \tmp, #1 msr mdscr_el1, \tmp isb 999: .endm /* * void cpu_throw(struct thread *old, struct thread *new) */ ENTRY(cpu_throw) /* Of old == NULL skip disabling stepping */ cbz x0, 1f /* If we were single stepping, disable it */ ldr x4, [x0, #TD_PCB] ldr w5, [x4, #PCB_FLAGS] clear_step_flag w5, x6 1: #ifdef VFP /* Backup the new thread pointer around a call to C code */ mov x19, x1 bl vfp_discard mov x1, x19 #endif /* Store the new curthread */ str x1, [x18, #PC_CURTHREAD] /* And the new pcb */ ldr x4, [x1, #TD_PCB] str x4, [x18, #PC_CURPCB] /* * TODO: We may need to flush the cache here. */ /* Switch to the new pmap */ ldr x5, [x4, #PCB_L1ADDR] msr ttbr0_el1, x5 isb /* Invalidate the TLB */ dsb sy tlbi vmalle1is dsb sy isb /* If we are single stepping, enable it */ ldr w5, [x4, #PCB_FLAGS] set_step_flag w5, x6 /* Restore the registers */ ldp x5, x6, [x4, #PCB_SP] mov sp, x5 msr tpidr_el0, x6 ldp x8, x9, [x4, #PCB_REGS + 8 * 8] ldp x10, x11, [x4, #PCB_REGS + 10 * 8] ldp x12, x13, [x4, #PCB_REGS + 12 * 8] ldp x14, x15, [x4, #PCB_REGS + 14 * 8] ldp x16, x17, [x4, #PCB_REGS + 16 * 8] ldr x19, [x4, #PCB_REGS + 19 * 8] ldp x20, x21, [x4, #PCB_REGS + 20 * 8] ldp x22, x23, [x4, #PCB_REGS + 22 * 8] ldp x24, x25, [x4, #PCB_REGS + 24 * 8] ldp x26, x27, [x4, #PCB_REGS + 26 * 8] ldp x28, x29, [x4, #PCB_REGS + 28 * 8] ldr x30, [x4, #PCB_REGS + 30 * 8] ret END(cpu_throw) /* * void cpu_switch(struct thread *old, struct thread *new, struct mtx *mtx) * * x0 = old * x1 = new * x2 = mtx * x3 to x7, x16 and x17 are caller saved */ ENTRY(cpu_switch) - /* Store the new curthread */ - str x1, [x18, #PC_CURTHREAD] - /* And the new pcb */ - ldr x4, [x1, #TD_PCB] - str x4, [x18, #PC_CURPCB] - /* * Save the old context. */ ldr x4, [x0, #TD_PCB] /* Store the callee-saved registers */ stp x8, x9, [x4, #PCB_REGS + 8 * 8] stp x10, x11, [x4, #PCB_REGS + 10 * 8] stp x12, x13, [x4, #PCB_REGS + 12 * 8] stp x14, x15, [x4, #PCB_REGS + 14 * 8] stp x16, x17, [x4, #PCB_REGS + 16 * 8] stp x18, x19, [x4, #PCB_REGS + 18 * 8] stp x20, x21, [x4, #PCB_REGS + 20 * 8] stp x22, x23, [x4, #PCB_REGS + 22 * 8] stp x24, x25, [x4, #PCB_REGS + 24 * 8] stp x26, x27, [x4, #PCB_REGS + 26 * 8] stp x28, x29, [x4, #PCB_REGS + 28 * 8] str x30, [x4, #PCB_REGS + 30 * 8] /* And the old stack pointer */ mov x5, sp mrs x6, tpidr_el0 stp x5, x6, [x4, #PCB_SP] /* If we were single stepping, disable it */ ldr w5, [x4, #PCB_FLAGS] clear_step_flag w5, x6 #ifdef VFP mov x19, x0 mov x20, x1 mov x21, x2 /* Load the pcb address */ mov x1, x4 bl vfp_save_state mov x2, x21 mov x1, x20 mov x0, x19 #endif + /* Store the new curthread */ + str x1, [x18, #PC_CURTHREAD] + /* - * Restore the saved context. + * Restore the saved context and save it as the curpcb. */ ldr x4, [x1, #TD_PCB] + str x4, [x18, #PC_CURPCB] + /* * TODO: We may need to flush the cache here if switching * to a user process. */ /* Switch to the new pmap */ ldr x5, [x4, #PCB_L1ADDR] msr ttbr0_el1, x5 isb /* Invalidate the TLB */ dsb sy tlbi vmalle1is dsb sy isb /* * Release the old thread. This doesn't need to be a store-release * as the above dsb instruction will provide release semantics. */ str x2, [x0, #TD_LOCK] #if defined(SCHED_ULE) && defined(SMP) /* Spin if TD_LOCK points to a blocked_lock */ ldr x2, =_C_LABEL(blocked_lock) 1: ldar x3, [x1, #TD_LOCK] cmp x3, x2 b.eq 1b #endif /* If we are single stepping, enable it */ ldr w5, [x4, #PCB_FLAGS] set_step_flag w5, x6 /* Restore the registers */ ldp x5, x6, [x4, #PCB_SP] mov sp, x5 msr tpidr_el0, x6 ldp x8, x9, [x4, #PCB_REGS + 8 * 8] ldp x10, x11, [x4, #PCB_REGS + 10 * 8] ldp x12, x13, [x4, #PCB_REGS + 12 * 8] ldp x14, x15, [x4, #PCB_REGS + 14 * 8] ldp x16, x17, [x4, #PCB_REGS + 16 * 8] ldr x19, [x4, #PCB_REGS + 19 * 8] ldp x20, x21, [x4, #PCB_REGS + 20 * 8] ldp x22, x23, [x4, #PCB_REGS + 22 * 8] ldp x24, x25, [x4, #PCB_REGS + 24 * 8] ldp x26, x27, [x4, #PCB_REGS + 26 * 8] ldp x28, x29, [x4, #PCB_REGS + 28 * 8] ldr x30, [x4, #PCB_REGS + 30 * 8] str xzr, [x4, #PCB_REGS + 18 * 8] ret .Lcpu_switch_panic_str: .asciz "cpu_switch: %p\0" END(cpu_switch) ENTRY(fork_trampoline) mov x0, x8 mov x1, x9 mov x2, sp mov fp, #0 /* Stack traceback stops here. */ bl _C_LABEL(fork_exit) /* Restore sp and lr */ ldp x0, x1, [sp] msr sp_el0, x0 mov lr, x1 /* Restore the registers other than x0 and x1 */ ldp x2, x3, [sp, #TF_X + 2 * 8] ldp x4, x5, [sp, #TF_X + 4 * 8] ldp x6, x7, [sp, #TF_X + 6 * 8] ldp x8, x9, [sp, #TF_X + 8 * 8] ldp x10, x11, [sp, #TF_X + 10 * 8] ldp x12, x13, [sp, #TF_X + 12 * 8] ldp x14, x15, [sp, #TF_X + 14 * 8] ldp x16, x17, [sp, #TF_X + 16 * 8] ldr x19, [sp, #TF_X + 19 * 8] ldp x20, x21, [sp, #TF_X + 20 * 8] ldp x22, x23, [sp, #TF_X + 22 * 8] ldp x24, x25, [sp, #TF_X + 24 * 8] ldp x26, x27, [sp, #TF_X + 26 * 8] ldp x28, x29, [sp, #TF_X + 28 * 8] /* Skip x30 as it was restored above as lr */ /* * Disable interrupts to avoid * overwriting spsr_el1 by an IRQ exception. */ msr daifset, #2 /* Restore elr and spsr */ ldp x0, x1, [sp, #16] msr elr_el1, x0 msr spsr_el1, x1 /* Finally x0 and x1 */ ldp x0, x1, [sp, #TF_X + 0 * 8] ldr x18, [sp, #TF_X + 18 * 8] /* * No need for interrupts reenabling since PSR * will be set to the desired value anyway. */ eret END(fork_trampoline) ENTRY(savectx) /* Store the callee-saved registers */ stp x8, x9, [x0, #PCB_REGS + 8 * 8] stp x10, x11, [x0, #PCB_REGS + 10 * 8] stp x12, x13, [x0, #PCB_REGS + 12 * 8] stp x14, x15, [x0, #PCB_REGS + 14 * 8] stp x16, x17, [x0, #PCB_REGS + 16 * 8] stp x18, x19, [x0, #PCB_REGS + 18 * 8] stp x20, x21, [x0, #PCB_REGS + 20 * 8] stp x22, x23, [x0, #PCB_REGS + 22 * 8] stp x24, x25, [x0, #PCB_REGS + 24 * 8] stp x26, x27, [x0, #PCB_REGS + 26 * 8] stp x28, x29, [x0, #PCB_REGS + 28 * 8] str x30, [x0, #PCB_REGS + 30 * 8] /* And the old stack pointer */ mov x5, sp mrs x6, tpidr_el0 stp x5, x6, [x0, #PCB_SP] /* Store the VFP registers */ #ifdef VFP mov x28, lr mov x1, x0 /* move pcb to the correct register */ mov x0, xzr /* td = NULL */ bl vfp_save_state mov lr, x28 #endif ret END(savectx) Index: head/sys/boot/fdt/dts/arm/bananapi.dts =================================================================== --- head/sys/boot/fdt/dts/arm/bananapi.dts (revision 295463) +++ head/sys/boot/fdt/dts/arm/bananapi.dts (revision 295464) @@ -1,91 +1,92 @@ /*- * 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$ */ /dts-v1/; -/include/ "sun7i-a20.dtsi" +#include "sun7i-a20.dtsi" + #include / { model = "LeMaker Banana Pi"; compatible = "lemaker,bananapi", "allwinner,sun7i-a20"; memory { device_type = "memory"; reg = < 0x40000000 0x40000000 >; /* 1GB RAM */ }; aliases { soc = &SOC; UART0 = &UART0; }; SOC: a20 { usb1: usb@01c14000 { status = "okay"; }; usb2: usb@01c1c000 { status = "okay"; }; UART0: serial@01c28000 { status = "okay"; }; mmc0: mmc@01c0f000 { status = "okay"; }; gmac@01c50000 { - phy-type = "rgmii-bpi"; + phy-mode = "rgmii-bpi"; status = "okay"; }; ahci: sata@01c18000 { status = "okay"; }; }; leds { compatible = "gpio-leds"; green { label = "bananapi:green:usr"; gpios = <&pio 7 24 GPIO_ACTIVE_HIGH>; }; }; chosen { bootargs = "-v"; stdin = "UART0"; stdout = "UART0"; }; }; Index: head/sys/boot/fdt/dts/arm/cubieboard2.dts =================================================================== --- head/sys/boot/fdt/dts/arm/cubieboard2.dts (revision 295463) +++ head/sys/boot/fdt/dts/arm/cubieboard2.dts (revision 295464) @@ -1,99 +1,41 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu + * Copyright (c) 2016 Emmanuel Vadot * 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$ */ -/dts-v1/; +#include "sun7i-a20-cubieboard2.dts" -/include/ "sun7i-a20.dtsi" - -#include - / { - model = "Cubietech Cubieboard2"; - - memory { - device_type = "memory"; - reg = < 0x40000000 0x40000000 >; /* 1GB RAM */ - }; - - aliases { - soc = &SOC; - UART0 = &UART0; - }; - - SOC: a20 { - - usb1: usb@01c14000 { - status = "okay"; + soc@01c00000 { + ccm@01c20000 { + compatible = "allwinner,sun4i-ccm"; + #address-cells = <1>; + #size-cells = <1>; + reg = < 0x01c20000 0x400 >; }; - - usb2: usb@01c1c000 { - status = "okay"; - }; - - UART0: serial@01c28000 { - status = "okay"; - }; - - mmc0: mmc@01c0f000 { - status = "okay"; - }; - - emac@01c0b000 { - status = "okay"; - }; - - gmac@01c50000 { - status = "okay"; - }; - - ahci: sata@01c18000 { - status = "okay"; - }; }; - - leds { - compatible = "gpio-leds"; - - blue { - label = "cubieboard2:blue:usr"; - gpios = <&pio 7 21 GPIO_ACTIVE_HIGH>; - }; - - green { - label = "cubieboard2:green:usr"; - gpios = <&pio 7 20 GPIO_ACTIVE_HIGH>; - }; - }; - - chosen { - bootargs = "-v"; - stdin = "UART0"; - stdout = "UART0"; - }; }; - Index: head/sys/boot/fdt/dts/arm/olimex-a20-som-evb.dts =================================================================== --- head/sys/boot/fdt/dts/arm/olimex-a20-som-evb.dts (nonexistent) +++ head/sys/boot/fdt/dts/arm/olimex-a20-som-evb.dts (revision 295464) @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2015 Emmanuel Vadot + * 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 "sun7i-a20-olimex-som-evb.dts" + +/ { + soc@01c00000 { + ccm@01c20000 { + compatible = "allwinner,sun4i-ccm"; + #address-cells = <1>; + #size-cells = <1>; + reg = < 0x01c20000 0x400 >; + }; + }; +}; Property changes on: head/sys/boot/fdt/dts/arm/olimex-a20-som-evb.dts ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/boot/fdt/dts/arm/olinuxino-lime.dts =================================================================== --- head/sys/boot/fdt/dts/arm/olinuxino-lime.dts (nonexistent) +++ head/sys/boot/fdt/dts/arm/olinuxino-lime.dts (revision 295464) @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2015 Emmanuel Vadot + * 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 "sun4i-a10-olinuxino-lime.dts" + +/ { + soc@01c00000 { + ccm@01c20000 { + compatible = "allwinner,sun4i-ccm"; + #address-cells = <1>; + #size-cells = <1>; + reg = < 0x01c20000 0x400 >; + }; + }; +}; Property changes on: head/sys/boot/fdt/dts/arm/olinuxino-lime.dts ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/boot/fdt/dts/arm/sun4i-a10.dtsi =================================================================== --- head/sys/boot/fdt/dts/arm/sun4i-a10.dtsi (revision 295463) +++ head/sys/boot/fdt/dts/arm/sun4i-a10.dtsi (revision 295464) @@ -1,140 +1,140 @@ /*- * Copyright (c) 2014 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$ */ / { compatible = "allwinner,sun4i-a10"; #address-cells = <1>; #size-cells = <1>; interrupt-parent = <&AINTC>; aliases { soc = &SOC; }; SOC: a10 { #address-cells = <1>; #size-cells = <1>; compatible = "simple-bus"; ranges; bus-frequency = <0>; AINTC: interrupt-controller@01c20400 { - compatible = "allwinner,sun4i-ic"; + compatible = "allwinner,sun4i-a10-ic"; interrupt-controller; #address-cells = <0>; #interrupt-cells = <1>; reg = < 0x01c20400 0x400 >; }; sramc@01c00000 { compatible = "allwinner,sun4i-sramc"; #address-cells = <1>; #size-cells = <1>; reg = < 0x01c00000 0x1000 >; }; ccm@01c20000 { compatible = "allwinner,sun4i-ccm"; #address-cells = <1>; #size-cells = <1>; reg = < 0x01c20000 0x400 >; }; timer@01c20c00 { - compatible = "allwinner,sun4i-timer"; + compatible = "allwinner,sun4i-a10-timer"; reg = <0x01c20c00 0x90>; interrupts = < 22 >; interrupt-parent = <&AINTC>; clock-frequency = < 24000000 >; }; watchdog@01c20c90 { compatible = "allwinner,sun4i-wdt"; reg = <0x01c20c90 0x08>; }; GPIO: gpio@01c20800 { #gpio-cells = <3>; - compatible = "allwinner,sun4i-gpio"; + compatible = "allwinner,sun4i-a10-pinctrl"; gpio-controller; reg =< 0x01c20800 0x400 >; interrupts = < 28 >; interrupt-parent = <&AINTC>; }; usb1: usb@01c14000 { - compatible = "allwinner,usb-ehci", "usb-ehci"; + compatible = "allwinner,sun4i-a10-ehci", "generic-ehci"; reg = <0x01c14000 0x1000>; interrupts = < 39 >; interrupt-parent = <&AINTC>; }; usb2: usb@01c1c000 { - compatible = "allwinner,usb-ehci", "usb-ehci"; + compatible = "allwinner,sun4i-a10-ehci", "generic-ehci"; reg = <0x01c1c000 0x1000>; interrupts = < 40 >; interrupt-parent = <&AINTC>; }; mmc0: mmc@01c0f000 { compatible = "allwinner,sun4i-a10-mmc"; reg = <0x01c0f000 0x1000>; interrupts = <32>; interrupt-parent = <&AINTC>; status = "disabled"; }; sata@01c18000 { compatible = "allwinner,sun4i-a10-ahci"; reg = <0x01c18000 0x1000>; interrupts = <56>; interrupt-parent = <&AINTC>; status = "disabled"; }; UART0: serial@01c28000 { compatible = "snps,dw-apb-uart"; reg = <0x01c28000 0x400>; reg-shift = <2>; interrupts = <1>; interrupt-parent = <&AINTC>; current-speed = <115200>; clock-frequency = < 24000000 >; }; emac@01c0b000 { - compatible = "allwinner,sun4i-emac"; + compatible = "allwinner,sun4i-a10-emac"; reg = <0x01c0b000 0x1000>; interrupts = <55>; interrupt-parent = <&AINTC>; }; }; }; Index: head/sys/boot/fdt/dts/arm/sun7i-a20.dtsi =================================================================== --- head/sys/boot/fdt/dts/arm/sun7i-a20.dtsi (revision 295463) +++ head/sys/boot/fdt/dts/arm/sun7i-a20.dtsi (revision 295464) @@ -1,160 +1,178 @@ /*- * Copyright (c) 2014 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 + / { compatible = "allwinner,sun7i-a20"; #address-cells = <1>; #size-cells = <1>; interrupt-parent = <&GIC>; aliases { soc = &SOC; }; + timer { + compatible = "arm,armv7-timer"; + interrupts = , + , + , + ; + }; + SOC: a20 { #address-cells = <1>; #size-cells = <1>; compatible = "simple-bus"; ranges; bus-frequency = <0>; GIC: interrupt-controller@01c81000 { compatible = "arm,gic"; reg = <0x01c81000 0x1000>, /* Distributor Registers */ - <0x01c82000 0x0100>; /* CPU Interface Registers */ + <0x01c82000 0x0100>, /* CPU Interface Registers */ + <0x01c84000 0x2000>, + <0x01c86000 0x2000>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <3>; + interrupts = ; }; sramc@01c00000 { compatible = "allwinner,sun4i-sramc"; #address-cells = <1>; #size-cells = <1>; reg = < 0x01c00000 0x1000 >; }; cpu-cfg@01c25c00 { compatible = "allwinner,sun7i-cpu-cfg"; #address-cells = <1>; #size-cells = <1>; reg = < 0x01c25c00 0x400 >; }; ccm@01c20000 { compatible = "allwinner,sun4i-ccm"; #address-cells = <1>; #size-cells = <1>; reg = < 0x01c20000 0x400 >; }; timer@01c20c00 { - compatible = "allwinner,sun7i-timer"; + compatible = "allwinner,sun4i-a10-timer"; reg = <0x01c20c00 0x90>; - interrupts = < 22 >; + interrupts = , + , + , + , + , + ; interrupt-parent = <&GIC>; clock-frequency = < 24000000 >; }; watchdog@01c20c90 { - compatible = "allwinner,sun4i-wdt"; + compatible = "allwinner,sun4i-a10-wdt"; reg = <0x01c20c90 0x10>; }; pio: gpio@01c20800 { #gpio-cells = <3>; - compatible = "allwinner,sun4i-gpio"; + compatible = "allwinner,sun7i-a20-pinctrl"; gpio-controller; reg =< 0x01c20800 0x400 >; - interrupts = < 28 >; + interrupts = ; + interrupt-controller; + #interrupt-cells = <2>; interrupt-parent = <&GIC>; }; usb1: usb@01c14000 { - compatible = "allwinner,usb-ehci", "usb-ehci"; + compatible = "allwinner,sun7i-a20-ehci", "generic-ehci"; reg = <0x01c14000 0x1000>; - interrupts = < 39 >; + interrupts = ; interrupt-parent = <&GIC>; }; usb2: usb@01c1c000 { - compatible = "allwinner,usb-ehci", "usb-ehci"; + compatible = "allwinner,sun7i-a20-ehci", "generic-ehci"; reg = <0x01c1c000 0x1000>; - interrupts = < 40 >; + interrupts = ; interrupt-parent = <&GIC>; }; mmc0: mmc@01c0f000 { - compatible = "allwinner,sun4i-a10-mmc"; + compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c0f000 0x1000>; - interrupts = <32>; - interrupt-parent = <&GIC>; + interrupts = ; status = "disabled"; }; sata@01c18000 { compatible = "allwinner,sun4i-a10-ahci"; reg = <0x01c18000 0x1000>; - interrupts = <56>; + interrupts = ; interrupt-parent = <&GIC>; status = "disabled"; }; UART0: serial@01c28000 { compatible = "snps,dw-apb-uart"; reg = <0x01c28000 0x400>; reg-shift = <2>; - interrupts = <1>; - interrupt-parent = <&GIC>; + interrupts = ; current-speed = <115200>; clock-frequency = < 24000000 >; }; emac@01c0b000 { - compatible = "allwinner,sun4i-emac"; + compatible = "allwinner,sun4i-a10-emac"; reg = <0x01c0b000 0x1000>; - interrupts = <55>; + interrupts = ; interrupt-parent = <&GIC>; status = "disabled"; }; gmac@01c50000 { compatible = "allwinner,sun7i-a20-gmac"; reg = <0x01c50000 0x10000>; - interrupts = <85>; + interrupts = ; interrupt-parent = <&GIC>; snps,pbl = <2>; snps,fixed-burst; snps,force_sf_dma_mode; status = "disabled"; #address-cells = <1>; #size-cells = <0>; }; }; }; Index: head/sys/modules/dtb/allwinner/Makefile =================================================================== --- head/sys/modules/dtb/allwinner/Makefile (revision 295463) +++ head/sys/modules/dtb/allwinner/Makefile (revision 295464) @@ -1,8 +1,10 @@ # $FreeBSD$ # All the dts files for allwinner systems we support. DTS= \ bananapi.dts \ cubieboard.dts \ - cubieboard2.dts + cubieboard2.dts \ + olimex-a20-som-evb.dts \ + olinuxino-lime.dts .include