Index: head/sys/arm/allwinner/allwinner_machdep.c =================================================================== --- head/sys/arm/allwinner/allwinner_machdep.c (revision 304317) +++ head/sys/arm/allwinner/allwinner_machdep.c (nonexistent) @@ -1,276 +0,0 @@ -/*- - * Copyright (c) 2012 Ganbold Tsagaankhuu - * Copyright (c) 2015-2016 Emmanuel Vadot - * All rights reserved. - * - * This code is derived from software written for Brini by Mark Brinicombe - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 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. - * - * from: FreeBSD: //depot/projects/arm/src/sys/arm/ti/ti_machdep.c - */ - -#include "opt_ddb.h" -#include "opt_platform.h" - -#include -__FBSDID("$FreeBSD$"); - -#define _ARM32_BUS_DMA_PRIVATE -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -#include "platform_if.h" - -static u_int soc_type; -static u_int soc_family; - -static int -a10_attach(platform_t plat) -{ - soc_type = ALLWINNERSOC_A10; - soc_family = ALLWINNERSOC_SUN4I; - return (0); -} - -static int -a13_attach(platform_t plat) -{ - soc_type = ALLWINNERSOC_A13; - soc_family = ALLWINNERSOC_SUN5I; - return (0); -} - -static int -a20_attach(platform_t plat) -{ - soc_type = ALLWINNERSOC_A20; - soc_family = ALLWINNERSOC_SUN7I; - - return (0); -} - -static int -a31_attach(platform_t plat) -{ - soc_type = ALLWINNERSOC_A31; - soc_family = ALLWINNERSOC_SUN6I; - - return (0); -} - -static int -a31s_attach(platform_t plat) -{ - soc_type = ALLWINNERSOC_A31S; - soc_family = ALLWINNERSOC_SUN6I; - - return (0); -} - -static int -a83t_attach(platform_t plat) -{ - soc_type = ALLWINNERSOC_A83T; - soc_family = ALLWINNERSOC_SUN8I; - - return (0); -} - -static int -h3_attach(platform_t plat) -{ - soc_type = ALLWINNERSOC_H3; - soc_family = ALLWINNERSOC_SUN8I; - - return (0); -} - -static vm_offset_t -allwinner_lastaddr(platform_t plat) -{ - - return (devmap_lastaddr()); -} - -/* - * Set up static device mappings. - * - * This covers all the on-chip device with 1MB section mappings, which is good - * for performance (uses fewer TLB entries for device access). - * - * XXX It also covers a block of SRAM and some GPU (mali400) stuff that maybe - * shouldn't be device-mapped. The original code mapped a 4MB block, but - * perhaps a 1MB block would be more appropriate. - */ -static int -allwinner_devmap_init(platform_t plat) -{ - - devmap_add_entry(0x01C00000, 0x00400000); /* 4MB */ - - return (0); -} - -struct arm32_dma_range * -bus_dma_get_range(void) -{ - return (NULL); -} - -int -bus_dma_get_range_nb(void) -{ - return (0); -} - -void -cpu_reset() -{ - aw_wdog_watchdog_reset(); - printf("Reset failed!\n"); - while (1); -} - -#if defined(SOC_ALLWINNER_A10) -static platform_method_t a10_methods[] = { - PLATFORMMETHOD(platform_attach, a10_attach), - PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), - PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), - - PLATFORMMETHOD_END, -}; -FDT_PLATFORM_DEF(a10, "a10", 0, "allwinner,sun4i-a10", 200); -#endif - -#if defined(SOC_ALLWINNER_A13) -static platform_method_t a13_methods[] = { - PLATFORMMETHOD(platform_attach, a13_attach), - PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), - PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), - - PLATFORMMETHOD_END, -}; -FDT_PLATFORM_DEF(a13, "a13", 0, "allwinner,sun5i-a13", 200); -#endif - -#if defined(SOC_ALLWINNER_A20) -static platform_method_t a20_methods[] = { - PLATFORMMETHOD(platform_attach, a20_attach), - PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), - PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), - -#ifdef SMP - PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap), - PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), -#endif - PLATFORMMETHOD_END, -}; -FDT_PLATFORM_DEF(a20, "a20", 0, "allwinner,sun7i-a20", 200); -#endif - -#if defined(SOC_ALLWINNER_A31) -static platform_method_t a31_methods[] = { - PLATFORMMETHOD(platform_attach, a31_attach), - PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), - PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), - -#ifdef SMP - PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap), - PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), -#endif - PLATFORMMETHOD_END, -}; -FDT_PLATFORM_DEF(a31, "a31", 0, "allwinner,sun6i-a31", 200); -#endif - -#if defined(SOC_ALLWINNER_A31S) -static platform_method_t a31s_methods[] = { - PLATFORMMETHOD(platform_attach, a31s_attach), - PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), - PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), - -#ifdef SMP - PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap), - PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), -#endif - PLATFORMMETHOD_END, -}; -FDT_PLATFORM_DEF(a31s, "a31s", 0, "allwinner,sun6i-a31s", 200); -#endif - -#if defined(SOC_ALLWINNER_A83T) -static platform_method_t a83t_methods[] = { - PLATFORMMETHOD(platform_attach, a83t_attach), - PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), - PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), - -#ifdef SMP - PLATFORMMETHOD(platform_mp_start_ap, a83t_mp_start_ap), - PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), -#endif - PLATFORMMETHOD_END, -}; -FDT_PLATFORM_DEF(a83t, "a83t", 0, "allwinner,sun8i-a83t", 200); -#endif - -#if defined(SOC_ALLWINNER_H3) -static platform_method_t h3_methods[] = { - PLATFORMMETHOD(platform_attach, h3_attach), - PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), - PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), - -#ifdef SMP - PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap), - PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), -#endif - PLATFORMMETHOD_END, -}; -FDT_PLATFORM_DEF(h3, "h3", 0, "allwinner,sun8i-h3", 200); -#endif - -u_int -allwinner_soc_type(void) -{ - return (soc_type); -} - -u_int -allwinner_soc_family(void) -{ - return (soc_family); -} Property changes on: head/sys/arm/allwinner/allwinner_machdep.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/sys/arm/allwinner/allwinner_machdep.h =================================================================== --- head/sys/arm/allwinner/allwinner_machdep.h (revision 304317) +++ head/sys/arm/allwinner/allwinner_machdep.h (nonexistent) @@ -1,51 +0,0 @@ -/*- - * 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$ - * - */ - -#ifndef AW_MACHDEP_H -#define AW_MACHDEP_H - -#define ALLWINNERSOC_A10 0x10000000 -#define ALLWINNERSOC_A13 0x13000000 -#define ALLWINNERSOC_A10S 0x10000001 -#define ALLWINNERSOC_A20 0x20000000 -#define ALLWINNERSOC_H3 0x30000000 -#define ALLWINNERSOC_A31 0x31000000 -#define ALLWINNERSOC_A31S 0x31000001 -#define ALLWINNERSOC_A83T 0x83000000 - -#define ALLWINNERSOC_SUN4I 0x40000000 -#define ALLWINNERSOC_SUN5I 0x50000000 -#define ALLWINNERSOC_SUN6I 0x60000000 -#define ALLWINNERSOC_SUN7I 0x70000000 -#define ALLWINNERSOC_SUN8I 0x80000000 - -u_int allwinner_soc_type(void); -u_int allwinner_soc_family(void); - -#endif /* AW_MACHDEP_H */ Property changes on: head/sys/arm/allwinner/allwinner_machdep.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/sys/arm/allwinner/a10_ehci.c =================================================================== --- head/sys/arm/allwinner/a10_ehci.c (revision 304317) +++ head/sys/arm/allwinner/a10_ehci.c (revision 304318) @@ -1,367 +1,367 @@ /*- * 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 #include #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 USB_CONF(d) \ (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data #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; struct aw_ehci_softc { ehci_softc_t sc; clk_t clk; hwreset_t rst; phy_t phy; }; struct aw_ehci_conf { bool sdram_init; }; static const struct aw_ehci_conf a10_ehci_conf = { .sdram_init = true, }; static const struct aw_ehci_conf a31_ehci_conf = { .sdram_init = false, }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-ehci", (uintptr_t)&a10_ehci_conf }, { "allwinner,sun5i-a13-ehci", (uintptr_t)&a10_ehci_conf }, { "allwinner,sun6i-a31-ehci", (uintptr_t)&a31_ehci_conf }, { "allwinner,sun7i-a20-ehci", (uintptr_t)&a10_ehci_conf }, { "allwinner,sun8i-a83t-ehci", (uintptr_t)&a31_ehci_conf }, { "allwinner,sun8i-h3-ehci", (uintptr_t)&a31_ehci_conf }, { NULL, (uintptr_t)NULL } }; static int a10_ehci_probe(device_t self) { if (!ofw_bus_status_okay(self)) return (ENXIO); 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) { struct aw_ehci_softc *aw_sc = device_get_softc(self); ehci_softc_t *sc = &aw_sc->sc; const struct aw_ehci_conf *conf; bus_space_handle_t bsh; int err; int rid; uint32_t reg_value = 0; conf = USB_CONF(self); /* 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"); 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; /* De-assert reset */ if (hwreset_get_by_ofw_idx(self, 0, 0, &aw_sc->rst) == 0) { err = hwreset_deassert(aw_sc->rst); if (err != 0) { device_printf(self, "Could not de-assert reset\n"); goto error; } } /* Enable clock for USB */ err = clk_get_by_ofw_index(self, 0, 0, &aw_sc->clk); if (err != 0) { device_printf(self, "Could not get clock\n"); goto error; } err = clk_enable(aw_sc->clk); if (err != 0) { device_printf(self, "Could not enable clock\n"); goto error; } /* Enable USB PHY */ err = phy_get_by_ofw_name(self, 0, "usb", &aw_sc->phy); if (err != 0) { device_printf(self, "Could not get phy\n"); goto error; } err = phy_enable(self, aw_sc->phy); if (err != 0) { device_printf(self, "Could not enable phy\n"); goto error; } /* 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 */ if (conf->sdram_init) { 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: if (aw_sc->clk) clk_release(aw_sc->clk); a10_ehci_detach(self); return (ENXIO); } static int a10_ehci_detach(device_t self) { struct aw_ehci_softc *aw_sc = device_get_softc(self); ehci_softc_t *sc = &aw_sc->sc; const struct aw_ehci_conf *conf; device_t bdev; int err; uint32_t reg_value = 0; conf = USB_CONF(self); 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 */ if (conf->sdram_init) { 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 */ clk_disable(aw_sc->clk); clk_release(aw_sc->clk); /* Assert reset */ if (aw_sc->rst != NULL) { hwreset_assert(aw_sc->rst); hwreset_release(aw_sc->rst); } 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(struct aw_ehci_softc), }; 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 304317) +++ head/sys/arm/allwinner/a10_gpio.c (revision 304318) @@ -1,784 +1,784 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2012 Luiz Otavio O Souza. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #if defined(__aarch64__) #include "opt_soc.h" #endif #include "gpio_if.h" #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 #define AW_GPIO_DRV_MASK 0x3 #define AW_GPIO_PUD_MASK 0x3 #define AW_PINCTRL 1 #define AW_R_PINCTRL 2 /* Defined in a10_padconf.c */ #ifdef SOC_ALLWINNER_A10 extern const struct allwinner_padconf a10_padconf; #endif /* Defined in a13_padconf.c */ #ifdef SOC_ALLWINNER_A13 extern const struct allwinner_padconf a13_padconf; #endif /* Defined in a20_padconf.c */ #ifdef SOC_ALLWINNER_A20 extern const struct allwinner_padconf a20_padconf; #endif /* Defined in a31_padconf.c */ #ifdef SOC_ALLWINNER_A31 extern const struct allwinner_padconf a31_padconf; #endif /* Defined in a31s_padconf.c */ #ifdef SOC_ALLWINNER_A31S extern const struct allwinner_padconf a31s_padconf; #endif #if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S) extern const struct allwinner_padconf a31_r_padconf; #endif /* Defined in h3_padconf.c */ #ifdef SOC_ALLWINNER_H3 extern const struct allwinner_padconf h3_padconf; extern const struct allwinner_padconf h3_r_padconf; #endif /* Defined in a83t_padconf.c */ #ifdef SOC_ALLWINNER_A83T extern const struct allwinner_padconf a83t_padconf; extern const struct allwinner_padconf a83t_r_padconf; #endif /* Defined in a64_padconf.c */ #ifdef SOC_ALLWINNER_A64 extern const struct allwinner_padconf a64_padconf; extern const struct allwinner_padconf a64_r_padconf; #endif static struct ofw_compat_data compat_data[] = { #ifdef SOC_ALLWINNER_A10 {"allwinner,sun4i-a10-pinctrl", (uintptr_t)&a10_padconf}, #endif #ifdef SOC_ALLWINNER_A13 {"allwinner,sun5i-a13-pinctrl", (uintptr_t)&a13_padconf}, #endif #ifdef SOC_ALLWINNER_A20 {"allwinner,sun7i-a20-pinctrl", (uintptr_t)&a20_padconf}, #endif #ifdef SOC_ALLWINNER_A31 {"allwinner,sun6i-a31-pinctrl", (uintptr_t)&a31_padconf}, #endif #ifdef SOC_ALLWINNER_A31S {"allwinner,sun6i-a31s-pinctrl", (uintptr_t)&a31s_padconf}, #endif #if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S) {"allwinner,sun6i-a31-r-pinctrl", (uintptr_t)&a31_r_padconf}, #endif #ifdef SOC_ALLWINNER_A83T {"allwinner,sun8i-a83t-pinctrl", (uintptr_t)&a83t_padconf}, {"allwinner,sun8i-a83t-r-pinctrl", (uintptr_t)&a83t_r_padconf}, #endif #ifdef SOC_ALLWINNER_H3 {"allwinner,sun8i-h3-pinctrl", (uintptr_t)&h3_padconf}, {"allwinner,sun8i-h3-r-pinctrl", (uintptr_t)&h3_r_padconf}, #endif #ifdef SOC_ALLWINNER_A64 {"allwinner,sun50i-a64-pinctrl", (uintptr_t)&a64_padconf}, {"allwinner,sun50i-a64-r-pinctrl", (uintptr_t)&a64_r_padconf}, #endif {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; const struct allwinner_padconf * padconf; }; #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 #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); if (pin > sc->padconf->npins) return (0); bank = sc->padconf->pins[pin].port; pin = sc->padconf->pins[pin].pin; offset = ((pin & 0x07) << 2); func = A10_GPIO_READ(sc, A10_GPIO_GP_CFG(bank, pin >> 3)); return ((func >> offset) & 0x7); } static int a10_gpio_set_function(struct a10_gpio_softc *sc, uint32_t pin, uint32_t f) { uint32_t bank, data, offset; /* Check if the function exists in the padconf data */ if (sc->padconf->pins[pin].functions[f] == NULL) return (EINVAL); /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); bank = sc->padconf->pins[pin].port; pin = sc->padconf->pins[pin].pin; 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); return (0); } 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 = sc->padconf->pins[pin].port; pin = sc->padconf->pins[pin].pin; offset = ((pin & 0x0f) << 1); val = A10_GPIO_READ(sc, A10_GPIO_GP_PUL(bank, pin >> 4)); return ((val >> offset) & AW_GPIO_PUD_MASK); } 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 = sc->padconf->pins[pin].port; pin = sc->padconf->pins[pin].pin; offset = ((pin & 0x0f) << 1); val = A10_GPIO_READ(sc, A10_GPIO_GP_PUL(bank, pin >> 4)); val &= ~(AW_GPIO_PUD_MASK << offset); val |= (state << offset); A10_GPIO_WRITE(sc, A10_GPIO_GP_PUL(bank, pin >> 4), val); } static uint32_t a10_gpio_get_drv(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 = sc->padconf->pins[pin].port; pin = sc->padconf->pins[pin].pin; offset = ((pin & 0x0f) << 1); val = A10_GPIO_READ(sc, A10_GPIO_GP_DRV(bank, pin >> 4)); return ((val >> offset) & AW_GPIO_DRV_MASK); } static void a10_gpio_set_drv(struct a10_gpio_softc *sc, uint32_t pin, uint32_t drive) { uint32_t bank, offset, val; /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); bank = sc->padconf->pins[pin].port; pin = sc->padconf->pins[pin].pin; offset = ((pin & 0x0f) << 1); val = A10_GPIO_READ(sc, A10_GPIO_GP_DRV(bank, pin >> 4)); val &= ~(AW_GPIO_DRV_MASK << offset); val |= (drive << offset); A10_GPIO_WRITE(sc, A10_GPIO_GP_DRV(bank, pin >> 4), val); } static int a10_gpio_pin_configure(struct a10_gpio_softc *sc, uint32_t pin, uint32_t flags) { int err = 0; /* 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) err = a10_gpio_set_function(sc, pin, A10_GPIO_OUTPUT); else err = a10_gpio_set_function(sc, pin, A10_GPIO_INPUT); } if (err) return (err); /* 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); return (0); } 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) { struct a10_gpio_softc *sc; sc = device_get_softc(dev); *maxpin = sc->padconf->npins - 1; return (0); } static int a10_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct a10_gpio_softc *sc; sc = device_get_softc(dev); if (pin >= sc->padconf->npins) 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; uint32_t func; uint32_t pud; sc = device_get_softc(dev); if (pin >= sc->padconf->npins) return (EINVAL); A10_GPIO_LOCK(sc); func = a10_gpio_get_function(sc, pin); switch (func) { case A10_GPIO_INPUT: *flags = GPIO_PIN_INPUT; break; case A10_GPIO_OUTPUT: *flags = GPIO_PIN_OUTPUT; break; default: *flags = 0; break; } pud = a10_gpio_get_pud(sc, pin); switch (pud) { case A10_GPIO_PULLDOWN: *flags |= GPIO_PIN_PULLDOWN; break; case A10_GPIO_PULLUP: *flags |= GPIO_PIN_PULLUP; break; default: break; } A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct a10_gpio_softc *sc; sc = device_get_softc(dev); if (pin >= sc->padconf->npins) return (EINVAL); snprintf(name, GPIOMAXNAME - 1, "%s", sc->padconf->pins[pin].name); 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; int err; sc = device_get_softc(dev); if (pin > sc->padconf->npins) return (EINVAL); A10_GPIO_LOCK(sc); err = a10_gpio_pin_configure(sc, pin, flags); A10_GPIO_UNLOCK(sc); return (err); } static int a10_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct a10_gpio_softc *sc; uint32_t bank, data; sc = device_get_softc(dev); if (pin > sc->padconf->npins) return (EINVAL); bank = sc->padconf->pins[pin].port; pin = sc->padconf->pins[pin].pin; 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; sc = device_get_softc(dev); if (pin > sc->padconf->npins) return (EINVAL); bank = sc->padconf->pins[pin].port; pin = sc->padconf->pins[pin].pin; 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; sc = device_get_softc(dev); if (pin > sc->padconf->npins) return (EINVAL); bank = sc->padconf->pins[pin].port; pin = sc->padconf->pins[pin].pin; 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 aw_find_pinnum_by_name(struct a10_gpio_softc *sc, const char *pinname) { int i; for (i = 0; i < sc->padconf->npins; i++) if (!strcmp(pinname, sc->padconf->pins[i].name)) return i; return (-1); } static int aw_find_pin_func(struct a10_gpio_softc *sc, int pin, const char *func) { int i; for (i = 0; i < AW_MAX_FUNC_BY_PIN; i++) if (sc->padconf->pins[pin].functions[i] && !strcmp(func, sc->padconf->pins[pin].functions[i])) return (i); return (-1); } static int aw_fdt_configure_pins(device_t dev, phandle_t cfgxref) { struct a10_gpio_softc *sc; phandle_t node; const char **pinlist = NULL; char *pin_function = NULL; uint32_t pin_drive, pin_pull; int pins_nb, pin_num, pin_func, i, ret; sc = device_get_softc(dev); node = OF_node_from_xref(cfgxref); ret = 0; /* Getting all prop for configuring pins */ pins_nb = ofw_bus_string_list_to_array(node, "allwinner,pins", &pinlist); if (pins_nb <= 0) return (ENOENT); if (OF_getprop_alloc(node, "allwinner,function", sizeof(*pin_function), (void **)&pin_function) == -1) { ret = ENOENT; goto out; } if (OF_getencprop(node, "allwinner,drive", &pin_drive, sizeof(pin_drive)) == -1) { ret = ENOENT; goto out; } if (OF_getencprop(node, "allwinner,pull", &pin_pull, sizeof(pin_pull)) == -1) { ret = ENOENT; goto out; } /* Configure each pin to the correct function, drive and pull */ for (i = 0; i < pins_nb; i++) { pin_num = aw_find_pinnum_by_name(sc, pinlist[i]); if (pin_num == -1) { ret = ENOENT; goto out; } pin_func = aw_find_pin_func(sc, pin_num, pin_function); if (pin_func == -1) { ret = ENOENT; goto out; } A10_GPIO_LOCK(sc); if (a10_gpio_get_function(sc, pin_num) != pin_func) a10_gpio_set_function(sc, pin_num, pin_func); if (a10_gpio_get_drv(sc, pin_num) != pin_drive) a10_gpio_set_drv(sc, pin_num, pin_drive); if (a10_gpio_get_pud(sc, pin_num) != pin_pull && (pin_pull == SUN4I_PINCTRL_PULL_UP || pin_pull == SUN4I_PINCTRL_PULL_DOWN)) a10_gpio_set_pud(sc, pin_num, pin_pull); A10_GPIO_UNLOCK(sc); } out: OF_prop_free(pinlist); OF_prop_free(pin_function); return (ret); } static int a10_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner GPIO/Pinmux controller"); return (BUS_PROBE_DEFAULT); } static int a10_gpio_attach(device_t dev) { int rid, error; phandle_t gpio; struct a10_gpio_softc *sc; clk_t clk; hwreset_t rst; 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; /* Use the right pin data for the current SoC */ sc->padconf = (struct allwinner_padconf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) { error = hwreset_deassert(rst); if (error != 0) { device_printf(dev, "cannot de-assert reset\n"); return (error); } } if (clk_get_by_ofw_index(dev, 0, 0, &clk) == 0) { error = clk_enable(clk); if (error != 0) { device_printf(dev, "could not enable clock\n"); return (error); } } sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) goto fail; /* * Register as a pinctrl device */ fdt_pinctrl_register(dev, "allwinner,pins"); fdt_pinctrl_configure_tree(dev); 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) { struct a10_gpio_softc *sc; int i; sc = device_get_softc(bus); /* The GPIO pins are mapped as: . */ for (i = 0; i < sc->padconf->npins; i++) if (sc->padconf->pins[i].port == gpios[0] && sc->padconf->pins[i].pin == gpios[1]) { *pin = i; break; } *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), /* fdt_pinctrl interface */ DEVMETHOD(fdt_pinctrl_configure,aw_fdt_configure_pins), DEVMETHOD_END }; static devclass_t a10_gpio_devclass; static driver_t a10_gpio_driver = { "gpio", a10_gpio_methods, sizeof(struct a10_gpio_softc), }; EARLY_DRIVER_MODULE(a10_gpio, simplebus, a10_gpio_driver, a10_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); Index: head/sys/arm/allwinner/a10_mmc.c =================================================================== --- head/sys/arm/allwinner/a10_mmc.c (revision 304317) +++ head/sys/arm/allwinner/a10_mmc.c (revision 304318) @@ -1,971 +1,971 @@ /*- * 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 #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 #define CARD_ID_FREQUENCY 400000 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; clk_t a10_clk_ahb; clk_t a10_clk_mmc; hwreset_t a10_rst_ahb; 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; bus_size_t a10_fifo_reg; /* 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_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; uint32_t bus_width; phandle_t node; int error; node = ofw_bus_get_node(dev); 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); } 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); #if defined(__arm__) /* * Later chips use a different FIFO offset. Unfortunately the FDT * uses the same compatible string for old and new implementations. */ switch (allwinner_soc_family()) { case ALLWINNERSOC_SUN4I: case ALLWINNERSOC_SUN5I: case ALLWINNERSOC_SUN7I: sc->a10_fifo_reg = A10_MMC_FIFO; break; default: sc->a10_fifo_reg = A31_MMC_FIFO; break; } #else /* __aarch64__ */ sc->a10_fifo_reg = A31_MMC_FIFO; #endif /* De-assert reset */ if (hwreset_get_by_ofw_name(dev, 0, "ahb", &sc->a10_rst_ahb) == 0) { error = hwreset_deassert(sc->a10_rst_ahb); if (error != 0) { device_printf(dev, "cannot de-assert reset\n"); return (error); } } /* Activate the module clock. */ error = clk_get_by_ofw_name(dev, 0, "ahb", &sc->a10_clk_ahb); if (error != 0) { device_printf(dev, "cannot get ahb clock\n"); goto fail; } error = clk_enable(sc->a10_clk_ahb); if (error != 0) { device_printf(dev, "cannot enable ahb clock\n"); goto fail; } error = clk_get_by_ofw_name(dev, 0, "mmc", &sc->a10_clk_mmc); if (error != 0) { device_printf(dev, "cannot get mmc clock\n"); goto fail; } error = clk_set_freq(sc->a10_clk_mmc, CARD_ID_FREQUENCY, CLK_SET_ROUND_DOWN); if (error != 0) { device_printf(dev, "cannot init mmc clock\n"); goto fail; } error = clk_enable(sc->a10_clk_mmc); if (error != 0) { device_printf(dev, "cannot enable mmc clock\n"); goto fail; } 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"); /* 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"); if (OF_getencprop(node, "bus-width", &bus_width, sizeof(uint32_t)) <= 0) bus_width = 4; sc->a10_host.f_min = 400000; sc->a10_host.f_max = 50000000; sc->a10_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340; sc->a10_host.mode = mode_sd; sc->a10_host.caps = MMC_CAP_HSPEED; if (bus_width >= 4) sc->a10_host.caps |= MMC_CAP_4_BIT_DATA; if (bus_width >= 8) sc->a10_host.caps |= MMC_CAP_8_BIT_DATA; 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), A10_MMC_DMA_ALIGN, 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), A10_MMC_DMA_ALIGN, 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; if (err) return; 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, sc->a10_fifo_reg, buf[i]); else buf[i] = A10_MMC_READ_4(sc, sc->a10_fifo_reg); 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 = clk_set_freq(sc->a10_clk_mmc, ios->clock, CLK_SET_ROUND_DOWN); if (error != 0) { device_printf(sc->a10_dev, "failed to set frequency to %u Hz: %d\n", ios->clock, error); 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); MODULE_DEPEND(a10_mmc, mmc, 1, 1, 1); Index: head/sys/arm/allwinner/aw_if_dwc.c =================================================================== --- head/sys/arm/allwinner/aw_if_dwc.c (revision 304317) +++ head/sys/arm/allwinner/aw_if_dwc.c (revision 304318) @@ -1,145 +1,145 @@ /*- * Copyright (c) 2015 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 "if_dwc_if.h" static int a20_if_dwc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-gmac")) return (ENXIO); device_set_desc(dev, "A20 Gigabit Ethernet Controller"); return (BUS_PROBE_DEFAULT); } static int a20_if_dwc_init(device_t dev) { const char *tx_parent_name; char *phy_type; clk_t clk_tx, clk_tx_parent; regulator_t reg; phandle_t node; int error; node = ofw_bus_get_node(dev); /* Configure PHY for MII or RGMII mode */ if (OF_getprop_alloc(node, "phy-mode", 1, (void **)&phy_type)) { error = clk_get_by_ofw_name(dev, 0, "allwinner_gmac_tx", &clk_tx); if (error != 0) { device_printf(dev, "could not get tx clk\n"); return (error); } if (strcmp(phy_type, "rgmii") == 0) tx_parent_name = "gmac_int_tx"; else tx_parent_name = "mii_phy_tx"; error = clk_get_by_name(dev, tx_parent_name, &clk_tx_parent); if (error != 0) { device_printf(dev, "could not get clock '%s'\n", tx_parent_name); return (error); } error = clk_set_parent_by_clk(clk_tx, clk_tx_parent); if (error != 0) { device_printf(dev, "could not set tx clk parent\n"); return (error); } } /* Enable PHY regulator if applicable */ if (regulator_get_by_ofw_property(dev, 0, "phy-supply", ®) == 0) { error = regulator_enable(reg); if (error != 0) { device_printf(dev, "could not enable PHY regulator\n"); return (error); } } return (0); } static int a20_if_dwc_mac_type(device_t dev) { return (DWC_GMAC_ALT_DESC); } static int a20_if_dwc_mii_clk(device_t dev) { return (GMAC_MII_CLK_150_250M_DIV102); } static device_method_t a20_dwc_methods[] = { DEVMETHOD(device_probe, a20_if_dwc_probe), DEVMETHOD(if_dwc_init, a20_if_dwc_init), DEVMETHOD(if_dwc_mac_type, a20_if_dwc_mac_type), DEVMETHOD(if_dwc_mii_clk, a20_if_dwc_mii_clk), DEVMETHOD_END }; static devclass_t a20_dwc_devclass; extern driver_t dwc_driver; DEFINE_CLASS_1(dwc, a20_dwc_driver, a20_dwc_methods, sizeof(struct dwc_softc), dwc_driver); DRIVER_MODULE(a20_dwc, simplebus, a20_dwc_driver, a20_dwc_devclass, 0, 0); MODULE_DEPEND(a20_dwc, dwc, 1, 1, 1); Index: head/sys/arm/allwinner/aw_machdep.c =================================================================== --- head/sys/arm/allwinner/aw_machdep.c (nonexistent) +++ head/sys/arm/allwinner/aw_machdep.c (revision 304318) @@ -0,0 +1,277 @@ +/*- + * Copyright (c) 2012 Ganbold Tsagaankhuu + * Copyright (c) 2015-2016 Emmanuel Vadot + * All rights reserved. + * + * This code is derived from software written for Brini by Mark Brinicombe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 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. + * + + * from: FreeBSD: //depot/projects/arm/src/sys/arm/ti/ti_machdep.c + */ + +#include "opt_ddb.h" +#include "opt_platform.h" + +#include +__FBSDID("$FreeBSD$"); + +#define _ARM32_BUS_DMA_PRIVATE +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "platform_if.h" + +static u_int soc_type; +static u_int soc_family; + +static int +a10_attach(platform_t plat) +{ + soc_type = ALLWINNERSOC_A10; + soc_family = ALLWINNERSOC_SUN4I; + return (0); +} + +static int +a13_attach(platform_t plat) +{ + soc_type = ALLWINNERSOC_A13; + soc_family = ALLWINNERSOC_SUN5I; + return (0); +} + +static int +a20_attach(platform_t plat) +{ + soc_type = ALLWINNERSOC_A20; + soc_family = ALLWINNERSOC_SUN7I; + + return (0); +} + +static int +a31_attach(platform_t plat) +{ + soc_type = ALLWINNERSOC_A31; + soc_family = ALLWINNERSOC_SUN6I; + + return (0); +} + +static int +a31s_attach(platform_t plat) +{ + soc_type = ALLWINNERSOC_A31S; + soc_family = ALLWINNERSOC_SUN6I; + + return (0); +} + +static int +a83t_attach(platform_t plat) +{ + soc_type = ALLWINNERSOC_A83T; + soc_family = ALLWINNERSOC_SUN8I; + + return (0); +} + +static int +h3_attach(platform_t plat) +{ + soc_type = ALLWINNERSOC_H3; + soc_family = ALLWINNERSOC_SUN8I; + + return (0); +} + +static vm_offset_t +allwinner_lastaddr(platform_t plat) +{ + + return (devmap_lastaddr()); +} + +/* + * Set up static device mappings. + * + * This covers all the on-chip device with 1MB section mappings, which is good + * for performance (uses fewer TLB entries for device access). + * + * XXX It also covers a block of SRAM and some GPU (mali400) stuff that maybe + * shouldn't be device-mapped. The original code mapped a 4MB block, but + * perhaps a 1MB block would be more appropriate. + */ +static int +allwinner_devmap_init(platform_t plat) +{ + + devmap_add_entry(0x01C00000, 0x00400000); /* 4MB */ + + return (0); +} + +struct arm32_dma_range * +bus_dma_get_range(void) +{ + return (NULL); +} + +int +bus_dma_get_range_nb(void) +{ + return (0); +} + +void +cpu_reset() +{ + aw_wdog_watchdog_reset(); + printf("Reset failed!\n"); + while (1); +} + +#if defined(SOC_ALLWINNER_A10) +static platform_method_t a10_methods[] = { + PLATFORMMETHOD(platform_attach, a10_attach), + PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), + PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), + + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF(a10, "a10", 0, "allwinner,sun4i-a10", 200); +#endif + +#if defined(SOC_ALLWINNER_A13) +static platform_method_t a13_methods[] = { + PLATFORMMETHOD(platform_attach, a13_attach), + PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), + PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), + + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF(a13, "a13", 0, "allwinner,sun5i-a13", 200); +#endif + +#if defined(SOC_ALLWINNER_A20) +static platform_method_t a20_methods[] = { + PLATFORMMETHOD(platform_attach, a20_attach), + PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), + PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), + +#ifdef SMP + PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap), + PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), +#endif + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF(a20, "a20", 0, "allwinner,sun7i-a20", 200); +#endif + +#if defined(SOC_ALLWINNER_A31) +static platform_method_t a31_methods[] = { + PLATFORMMETHOD(platform_attach, a31_attach), + PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), + PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), + +#ifdef SMP + PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap), + PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), +#endif + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF(a31, "a31", 0, "allwinner,sun6i-a31", 200); +#endif + +#if defined(SOC_ALLWINNER_A31S) +static platform_method_t a31s_methods[] = { + PLATFORMMETHOD(platform_attach, a31s_attach), + PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), + PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), + +#ifdef SMP + PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap), + PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), +#endif + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF(a31s, "a31s", 0, "allwinner,sun6i-a31s", 200); +#endif + +#if defined(SOC_ALLWINNER_A83T) +static platform_method_t a83t_methods[] = { + PLATFORMMETHOD(platform_attach, a83t_attach), + PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), + PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), + +#ifdef SMP + PLATFORMMETHOD(platform_mp_start_ap, a83t_mp_start_ap), + PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), +#endif + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF(a83t, "a83t", 0, "allwinner,sun8i-a83t", 200); +#endif + +#if defined(SOC_ALLWINNER_H3) +static platform_method_t h3_methods[] = { + PLATFORMMETHOD(platform_attach, h3_attach), + PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), + PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), + +#ifdef SMP + PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap), + PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), +#endif + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF(h3, "h3", 0, "allwinner,sun8i-h3", 200); +#endif + +u_int +allwinner_soc_type(void) +{ + return (soc_type); +} + +u_int +allwinner_soc_family(void) +{ + return (soc_family); +} Property changes on: head/sys/arm/allwinner/aw_machdep.c ___________________________________________________________________ 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/arm/allwinner/aw_machdep.h =================================================================== --- head/sys/arm/allwinner/aw_machdep.h (nonexistent) +++ head/sys/arm/allwinner/aw_machdep.h (revision 304318) @@ -0,0 +1,51 @@ +/*- + * 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$ + * + */ + +#ifndef AW_MACHDEP_H +#define AW_MACHDEP_H + +#define ALLWINNERSOC_A10 0x10000000 +#define ALLWINNERSOC_A13 0x13000000 +#define ALLWINNERSOC_A10S 0x10000001 +#define ALLWINNERSOC_A20 0x20000000 +#define ALLWINNERSOC_H3 0x30000000 +#define ALLWINNERSOC_A31 0x31000000 +#define ALLWINNERSOC_A31S 0x31000001 +#define ALLWINNERSOC_A83T 0x83000000 + +#define ALLWINNERSOC_SUN4I 0x40000000 +#define ALLWINNERSOC_SUN5I 0x50000000 +#define ALLWINNERSOC_SUN6I 0x60000000 +#define ALLWINNERSOC_SUN7I 0x70000000 +#define ALLWINNERSOC_SUN8I 0x80000000 + +u_int allwinner_soc_type(void); +u_int allwinner_soc_family(void); + +#endif /* AW_MACHDEP_H */ Property changes on: head/sys/arm/allwinner/aw_machdep.h ___________________________________________________________________ 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/arm/allwinner/aw_mp.c =================================================================== --- head/sys/arm/allwinner/aw_mp.c (revision 304317) +++ head/sys/arm/allwinner/aw_mp.c (revision 304318) @@ -1,286 +1,286 @@ /*- * Copyright (c) 2014 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 ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include /* Register for all dual-core SoC */ #define A20_CPUCFG_BASE 0x01c25c00 /* Register for all quad-core SoC */ #define CPUCFG_BASE 0x01f01c00 #define CPUCFG_SIZE 0x400 #define PRCM_BASE 0x01f01400 #define PRCM_SIZE 0x800 /* Register for multi-cluster SoC */ #define CPUXCFG_BASE 0x01700000 #define CPUXCFG_SIZE 0x400 #define CPU_OFFSET 0x40 #define CPU_OFFSET_CTL 0x04 #define CPU_OFFSET_STATUS 0x08 #define CPU_RST_CTL(cpuid) ((cpuid + 1) * CPU_OFFSET) #define CPU_CTL(cpuid) (((cpuid + 1) * CPU_OFFSET) + CPU_OFFSET_CTL) #define CPU_STATUS(cpuid) (((cpuid + 1) * CPU_OFFSET) + CPU_OFFSET_STATUS) #define CPU_RESET (1 << 0) #define CPU_CORE_RESET (1 << 1) #define CPUCFG_GENCTL 0x184 #define CPUCFG_P_REG0 0x1a4 #define A20_CPU1_PWR_CLAMP 0x1b0 #define CPU_PWR_CLAMP_REG 0x140 #define CPU_PWR_CLAMP(cpu) ((cpu * 4) + CPU_PWR_CLAMP_REG) #define CPU_PWR_CLAMP_STEPS 8 #define A20_CPU1_PWROFF_REG 0x1b4 #define CPU_PWROFF 0x100 #define CPUCFG_DBGCTL0 0x1e0 #define CPUCFG_DBGCTL1 0x1e4 #define CPUS_CL_RST(cl) (0x30 + (cluster) * 0x4) #define CPUX_CL_CTRL0(cl) (0x0 + (cluster) * 0x10) #define CPUX_CL_CTRL1(cl) (0x4 + (cluster) * 0x10) #define CPUX_CL_CPU_STATUS(cl) (0x30 + (cluster) * 0x4) #define CPUX_CL_RST(cl) (0x80 + (cluster) * 0x4) #define PRCM_CL_PWROFF(cl) (0x100 + (cluster) * 0x4) #define PRCM_CL_PWR_CLAMP(cl, cpu) (0x140 + (cluster) * 0x4 + (cpu) * 0x4) void aw_mp_setmaxid(platform_t plat) { int ncpu; uint32_t reg; if (mp_ncpus != 0) return; reg = cp15_l2ctlr_get(); ncpu = CPUV7_L2CTLR_NPROC(reg); mp_ncpus = ncpu; mp_maxid = ncpu - 1; } void aw_mp_start_ap(platform_t plat) { bus_space_handle_t cpucfg; bus_space_handle_t prcm; int i, j, soc_family; uint32_t val; soc_family = allwinner_soc_family(); if (soc_family == ALLWINNERSOC_SUN7I) { if (bus_space_map(fdtbus_bs_tag, A20_CPUCFG_BASE, CPUCFG_SIZE, 0, &cpucfg) != 0) panic("Couldn't map the CPUCFG\n"); } else { if (bus_space_map(fdtbus_bs_tag, CPUCFG_BASE, CPUCFG_SIZE, 0, &cpucfg) != 0) panic("Couldn't map the CPUCFG\n"); if (bus_space_map(fdtbus_bs_tag, PRCM_BASE, PRCM_SIZE, 0, &prcm) != 0) panic("Couldn't map the PRCM\n"); } dcache_wbinv_poc_all(); bus_space_write_4(fdtbus_bs_tag, cpucfg, CPUCFG_P_REG0, pmap_kextract((vm_offset_t)mpentry)); /* * Assert nCOREPORESET low and set L1RSTDISABLE low. * Ensure DBGPWRDUP is set to LOW to prevent any external * debug access to the processor. */ for (i = 1; i < mp_ncpus; i++) bus_space_write_4(fdtbus_bs_tag, cpucfg, CPU_RST_CTL(i), 0); /* Set L1RSTDISABLE low */ val = bus_space_read_4(fdtbus_bs_tag, cpucfg, CPUCFG_GENCTL); for (i = 1; i < mp_ncpus; i++) val &= ~(1 << i); bus_space_write_4(fdtbus_bs_tag, cpucfg, CPUCFG_GENCTL, val); /* Set DBGPWRDUP low */ val = bus_space_read_4(fdtbus_bs_tag, cpucfg, CPUCFG_DBGCTL1); for (i = 1; i < mp_ncpus; i++) val &= ~(1 << i); bus_space_write_4(fdtbus_bs_tag, cpucfg, CPUCFG_DBGCTL1, val); /* Release power clamp */ for (i = 1; i < mp_ncpus; i++) for (j = 0; j <= CPU_PWR_CLAMP_STEPS; j++) { if (soc_family != ALLWINNERSOC_SUN7I) { bus_space_write_4(fdtbus_bs_tag, prcm, CPU_PWR_CLAMP(i), 0xff >> j); } else { bus_space_write_4(fdtbus_bs_tag, cpucfg, A20_CPU1_PWR_CLAMP, 0xff >> j); } } DELAY(10000); /* Clear power-off gating */ if (soc_family != ALLWINNERSOC_SUN7I) { val = bus_space_read_4(fdtbus_bs_tag, prcm, CPU_PWROFF); for (i = 0; i < mp_ncpus; i++) val &= ~(1 << i); bus_space_write_4(fdtbus_bs_tag, prcm, CPU_PWROFF, val); } else { val = bus_space_read_4(fdtbus_bs_tag, cpucfg, A20_CPU1_PWROFF_REG); val &= ~(1 << 0); bus_space_write_4(fdtbus_bs_tag, cpucfg, A20_CPU1_PWROFF_REG, val); } DELAY(1000); /* De-assert cpu core reset */ for (i = 1; i < mp_ncpus; i++) bus_space_write_4(fdtbus_bs_tag, cpucfg, CPU_RST_CTL(i), CPU_RESET | CPU_CORE_RESET); /* Assert DBGPWRDUP signal */ val = bus_space_read_4(fdtbus_bs_tag, cpucfg, CPUCFG_DBGCTL1); for (i = 1; i < mp_ncpus; i++) val |= (1 << i); bus_space_write_4(fdtbus_bs_tag, cpucfg, CPUCFG_DBGCTL1, val); armv7_sev(); bus_space_unmap(fdtbus_bs_tag, cpucfg, CPUCFG_SIZE); if (soc_family != ALLWINNERSOC_SUN7I) bus_space_unmap(fdtbus_bs_tag, prcm, PRCM_SIZE); } static void aw_mc_mp_start_cpu(bus_space_handle_t cpuscfg, bus_space_handle_t cpuxcfg, bus_space_handle_t prcm, int cluster, int cpu) { uint32_t val; int i; /* Assert core reset */ val = bus_space_read_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster)); val &= ~(1 << cpu); bus_space_write_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster), val); /* Assert power-on reset */ val = bus_space_read_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster)); val &= ~(1 << cpu); bus_space_write_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster), val); /* Disable automatic L1 cache invalidate at reset */ val = bus_space_read_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_CTRL0(cluster)); val &= ~(1 << cpu); bus_space_write_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_CTRL0(cluster), val); /* Release power clamp */ for (i = 0; i <= CPU_PWR_CLAMP_STEPS; i++) bus_space_write_4(fdtbus_bs_tag, prcm, PRCM_CL_PWR_CLAMP(cluster, cpu), 0xff >> i); while (bus_space_read_4(fdtbus_bs_tag, prcm, PRCM_CL_PWR_CLAMP(cluster, cpu)) != 0) ; /* Clear power-off gating */ val = bus_space_read_4(fdtbus_bs_tag, prcm, PRCM_CL_PWROFF(cluster)); val &= ~(1 << cpu); bus_space_write_4(fdtbus_bs_tag, prcm, PRCM_CL_PWROFF(cluster), val); /* De-assert power-on reset */ val = bus_space_read_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster)); val |= (1 << cpu); bus_space_write_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster), val); /* De-assert core reset */ val = bus_space_read_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster)); val |= (1 << cpu); bus_space_write_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster), val); } static void aw_mc_mp_start_ap(bus_space_handle_t cpuscfg, bus_space_handle_t cpuxcfg, bus_space_handle_t prcm) { int cluster, cpu; KASSERT(mp_ncpus <= 4, ("multiple clusters not yet supported")); dcache_wbinv_poc_all(); bus_space_write_4(fdtbus_bs_tag, cpuscfg, CPUCFG_P_REG0, pmap_kextract((vm_offset_t)mpentry)); cluster = 0; for (cpu = 1; cpu < mp_ncpus; cpu++) aw_mc_mp_start_cpu(cpuscfg, cpuxcfg, prcm, cluster, cpu); } void a83t_mp_start_ap(platform_t plat) { bus_space_handle_t cpuscfg, cpuxcfg, prcm; if (bus_space_map(fdtbus_bs_tag, CPUCFG_BASE, CPUCFG_SIZE, 0, &cpuscfg) != 0) panic("Couldn't map the CPUCFG\n"); if (bus_space_map(fdtbus_bs_tag, CPUXCFG_BASE, CPUXCFG_SIZE, 0, &cpuxcfg) != 0) panic("Couldn't map the CPUXCFG\n"); if (bus_space_map(fdtbus_bs_tag, PRCM_BASE, PRCM_SIZE, 0, &prcm) != 0) panic("Couldn't map the PRCM\n"); aw_mc_mp_start_ap(cpuscfg, cpuxcfg, prcm); armv7_sev(); bus_space_unmap(fdtbus_bs_tag, cpuxcfg, CPUXCFG_SIZE); bus_space_unmap(fdtbus_bs_tag, cpuscfg, CPUCFG_SIZE); bus_space_unmap(fdtbus_bs_tag, prcm, PRCM_SIZE); } Index: head/sys/arm/allwinner/aw_rtc.c =================================================================== --- head/sys/arm/allwinner/aw_rtc.c (revision 304317) +++ head/sys/arm/allwinner/aw_rtc.c (revision 304318) @@ -1,296 +1,296 @@ /*- * Copyright (c) 2016 Vladimir Belian * 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 "clock_if.h" #define LOSC_CTRL_REG 0x00 #define A10_RTC_DATE_REG 0x04 #define A10_RTC_TIME_REG 0x08 #define A31_LOSC_AUTO_SWT_STA 0x04 #define A31_RTC_DATE_REG 0x10 #define A31_RTC_TIME_REG 0x14 #define TIME_MASK 0x001f3f3f #define LOSC_OSC_SRC (1 << 0) #define LOSC_GSM (1 << 3) #define LOSC_AUTO_SW_EN (1 << 14) #define LOSC_MAGIC 0x16aa0000 #define LOSC_BUSY_MASK 0x00000380 #define IS_SUN7I (allwinner_soc_family() == ALLWINNERSOC_SUN7I) #define YEAR_MIN (IS_SUN7I ? 1970 : 2010) #define YEAR_MAX (IS_SUN7I ? 2100 : 2073) #define YEAR_OFFSET (IS_SUN7I ? 1900 : 2010) #define YEAR_MASK (IS_SUN7I ? 0xff : 0x3f) #define LEAP_BIT (IS_SUN7I ? 24 : 22) #define GET_SEC_VALUE(x) ((x) & 0x0000003f) #define GET_MIN_VALUE(x) (((x) & 0x00003f00) >> 8) #define GET_HOUR_VALUE(x) (((x) & 0x001f0000) >> 16) #define GET_DAY_VALUE(x) ((x) & 0x0000001f) #define GET_MON_VALUE(x) (((x) & 0x00000f00) >> 8) #define GET_YEAR_VALUE(x) (((x) >> 16) & YEAR_MASK) #define SET_DAY_VALUE(x) GET_DAY_VALUE(x) #define SET_MON_VALUE(x) (((x) & 0x0000000f) << 8) #define SET_YEAR_VALUE(x) (((x) & YEAR_MASK) << 16) #define SET_LEAP_VALUE(x) (((x) & 0x00000001) << LEAP_BIT) #define SET_SEC_VALUE(x) GET_SEC_VALUE(x) #define SET_MIN_VALUE(x) (((x) & 0x0000003f) << 8) #define SET_HOUR_VALUE(x) (((x) & 0x0000001f) << 16) #define HALF_OF_SEC_NS 500000000 #define RTC_RES_US 1000000 #define RTC_TIMEOUT 70 #define RTC_READ(sc, reg) bus_read_4((sc)->res, (reg)) #define RTC_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) #define IS_LEAP_YEAR(y) \ (((y) % 400) == 0 || (((y) % 100) != 0 && ((y) % 4) == 0)) #define A10_RTC 1 #define A20_RTC 2 #define A31_RTC 3 static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-rtc", A10_RTC }, { "allwinner,sun7i-a20-rtc", A20_RTC }, { "allwinner,sun6i-a31-rtc", A31_RTC }, { NULL, 0 } }; struct aw_rtc_softc { struct resource *res; bus_size_t rtc_date; bus_size_t rtc_time; }; static int aw_rtc_probe(device_t dev); static int aw_rtc_attach(device_t dev); static int aw_rtc_detach(device_t dev); static int aw_rtc_gettime(device_t dev, struct timespec *ts); static int aw_rtc_settime(device_t dev, struct timespec *ts); static device_method_t aw_rtc_methods[] = { DEVMETHOD(device_probe, aw_rtc_probe), DEVMETHOD(device_attach, aw_rtc_attach), DEVMETHOD(device_detach, aw_rtc_detach), DEVMETHOD(clock_gettime, aw_rtc_gettime), DEVMETHOD(clock_settime, aw_rtc_settime), DEVMETHOD_END }; static driver_t aw_rtc_driver = { "rtc", aw_rtc_methods, sizeof(struct aw_rtc_softc), }; static devclass_t aw_rtc_devclass; EARLY_DRIVER_MODULE(aw_rtc, simplebus, aw_rtc_driver, aw_rtc_devclass, 0, 0, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); static int aw_rtc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) return (ENXIO); device_set_desc(dev, "Allwinner RTC"); return (BUS_PROBE_DEFAULT); } static int aw_rtc_attach(device_t dev) { struct aw_rtc_softc *sc = device_get_softc(dev); bus_size_t rtc_losc_sta; uint32_t val; int rid = 0; sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->res) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { case A10_RTC: case A20_RTC: sc->rtc_date = A10_RTC_DATE_REG; sc->rtc_time = A10_RTC_TIME_REG; rtc_losc_sta = LOSC_CTRL_REG; break; case A31_RTC: sc->rtc_date = A31_RTC_DATE_REG; sc->rtc_time = A31_RTC_TIME_REG; rtc_losc_sta = A31_LOSC_AUTO_SWT_STA; break; } val = RTC_READ(sc, LOSC_CTRL_REG); val |= LOSC_AUTO_SW_EN; val |= LOSC_MAGIC | LOSC_GSM | LOSC_OSC_SRC; RTC_WRITE(sc, LOSC_CTRL_REG, val); DELAY(100); if (bootverbose) { val = RTC_READ(sc, rtc_losc_sta); if ((val & LOSC_OSC_SRC) == 0) device_printf(dev, "Using internal oscillator\n"); else device_printf(dev, "Using external oscillator\n"); } clock_register(dev, RTC_RES_US); return (0); } static int aw_rtc_detach(device_t dev) { /* can't support detach, since there's no clock_unregister function */ return (EBUSY); } static int aw_rtc_gettime(device_t dev, struct timespec *ts) { struct aw_rtc_softc *sc = device_get_softc(dev); struct clocktime ct; uint32_t rdate, rtime; rdate = RTC_READ(sc, sc->rtc_date); rtime = RTC_READ(sc, sc->rtc_time); if ((rtime & TIME_MASK) == 0) rdate = RTC_READ(sc, sc->rtc_date); ct.sec = GET_SEC_VALUE(rtime); ct.min = GET_MIN_VALUE(rtime); ct.hour = GET_HOUR_VALUE(rtime); ct.day = GET_DAY_VALUE(rdate); ct.mon = GET_MON_VALUE(rdate); ct.year = GET_YEAR_VALUE(rdate) + YEAR_OFFSET; ct.dow = -1; /* RTC resolution is 1 sec */ ct.nsec = 0; return (clock_ct_to_ts(&ct, ts)); } static int aw_rtc_settime(device_t dev, struct timespec *ts) { struct aw_rtc_softc *sc = device_get_softc(dev); struct clocktime ct; uint32_t clk, rdate, rtime; /* RTC resolution is 1 sec */ if (ts->tv_nsec >= HALF_OF_SEC_NS) ts->tv_sec++; ts->tv_nsec = 0; clock_ts_to_ct(ts, &ct); if ((ct.year < YEAR_MIN) || (ct.year > YEAR_MAX)) { device_printf(dev, "could not set time, year out of range\n"); return (EINVAL); } for (clk = 0; RTC_READ(sc, LOSC_CTRL_REG) & LOSC_BUSY_MASK; clk++) { if (clk > RTC_TIMEOUT) { device_printf(dev, "could not set time, RTC busy\n"); return (EINVAL); } DELAY(1); } /* reset time register to avoid unexpected date increment */ RTC_WRITE(sc, sc->rtc_time, 0); rdate = SET_DAY_VALUE(ct.day) | SET_MON_VALUE(ct.mon) | SET_YEAR_VALUE(ct.year - YEAR_OFFSET) | SET_LEAP_VALUE(IS_LEAP_YEAR(ct.year)); rtime = SET_SEC_VALUE(ct.sec) | SET_MIN_VALUE(ct.min) | SET_HOUR_VALUE(ct.hour); for (clk = 0; RTC_READ(sc, LOSC_CTRL_REG) & LOSC_BUSY_MASK; clk++) { if (clk > RTC_TIMEOUT) { device_printf(dev, "could not set date, RTC busy\n"); return (EINVAL); } DELAY(1); } RTC_WRITE(sc, sc->rtc_date, rdate); for (clk = 0; RTC_READ(sc, LOSC_CTRL_REG) & LOSC_BUSY_MASK; clk++) { if (clk > RTC_TIMEOUT) { device_printf(dev, "could not set time, RTC busy\n"); return (EINVAL); } DELAY(1); } RTC_WRITE(sc, sc->rtc_time, rtime); DELAY(RTC_TIMEOUT); return (0); } Index: head/sys/arm/allwinner/clk/aw_pll.c =================================================================== --- head/sys/arm/allwinner/clk/aw_pll.c (revision 304317) +++ head/sys/arm/allwinner/clk/aw_pll.c (revision 304318) @@ -1,902 +1,902 @@ /*- * Copyright (c) 2016 Jared McNeill * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Allwinner PLL clock */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "clkdev_if.h" #define AW_PLL_ENABLE (1 << 31) #define A10_PLL1_OUT_EXT_DIVP (0x3 << 16) #define A10_PLL1_OUT_EXT_DIVP_SHIFT 16 #define A10_PLL1_FACTOR_N (0x1f << 8) #define A10_PLL1_FACTOR_N_SHIFT 8 #define A10_PLL1_FACTOR_K (0x3 << 4) #define A10_PLL1_FACTOR_K_SHIFT 4 #define A10_PLL1_FACTOR_M (0x3 << 0) #define A10_PLL1_FACTOR_M_SHIFT 0 #define A10_PLL2_POST_DIV (0xf << 26) #define A10_PLL2_POST_DIV_SHIFT 26 #define A10_PLL2_FACTOR_N (0x7f << 8) #define A10_PLL2_FACTOR_N_SHIFT 8 #define A10_PLL2_PRE_DIV (0x1f << 0) #define A10_PLL2_PRE_DIV_SHIFT 0 #define A10_PLL3_MODE_SEL (0x1 << 15) #define A10_PLL3_MODE_SEL_FRACT (0 << 15) #define A10_PLL3_MODE_SEL_INT (1 << 15) #define A10_PLL3_FUNC_SET (0x1 << 14) #define A10_PLL3_FUNC_SET_270MHZ (0 << 14) #define A10_PLL3_FUNC_SET_297MHZ (1 << 14) #define A10_PLL3_FACTOR_M (0x7f << 0) #define A10_PLL3_FACTOR_M_SHIFT 0 #define A10_PLL3_REF_FREQ 3000000 #define A10_PLL5_OUT_EXT_DIVP (0x3 << 16) #define A10_PLL5_OUT_EXT_DIVP_SHIFT 16 #define A10_PLL5_FACTOR_N (0x1f << 8) #define A10_PLL5_FACTOR_N_SHIFT 8 #define A10_PLL5_FACTOR_K (0x3 << 4) #define A10_PLL5_FACTOR_K_SHIFT 4 #define A10_PLL5_FACTOR_M1 (0x3 << 2) #define A10_PLL5_FACTOR_M1_SHIFT 2 #define A10_PLL5_FACTOR_M (0x3 << 0) #define A10_PLL5_FACTOR_M_SHIFT 0 #define A10_PLL6_BYPASS_EN (1 << 30) #define A10_PLL6_SATA_CLK_EN (1 << 14) #define A10_PLL6_FACTOR_N (0x1f << 8) #define A10_PLL6_FACTOR_N_SHIFT 8 #define A10_PLL6_FACTOR_K (0x3 << 4) #define A10_PLL6_FACTOR_K_SHIFT 4 #define A10_PLL6_FACTOR_M (0x3 << 0) #define A10_PLL6_FACTOR_M_SHIFT 0 #define A10_PLL2_POST_DIV (0xf << 26) #define A13_PLL2_POST_DIV (0xf << 26) #define A13_PLL2_POST_DIV_SHIFT 26 #define A13_PLL2_FACTOR_N (0x7f << 8) #define A13_PLL2_FACTOR_N_SHIFT 8 #define A13_PLL2_PRE_DIV (0x1f << 0) #define A13_PLL2_PRE_DIV_SHIFT 0 #define A23_PLL1_FACTOR_N (0x1f << 8) #define A23_PLL1_FACTOR_N_SHIFT 8 #define A23_PLL1_FACTOR_K (0x3 << 4) #define A23_PLL1_FACTOR_K_SHIFT 4 #define A23_PLL1_FACTOR_M (0x3 << 0) #define A23_PLL1_FACTOR_M_SHIFT 0 #define A23_PLL1_FACTOR_P (0x3 << 16) #define A23_PLL1_FACTOR_P_SHIFT 16 #define A31_PLL1_LOCK (1 << 28) #define A31_PLL1_CPU_SIGMA_DELTA_EN (1 << 24) #define A31_PLL1_FACTOR_N (0x1f << 8) #define A31_PLL1_FACTOR_N_SHIFT 8 #define A31_PLL1_FACTOR_K (0x3 << 4) #define A31_PLL1_FACTOR_K_SHIFT 4 #define A31_PLL1_FACTOR_M (0x3 << 0) #define A31_PLL1_FACTOR_M_SHIFT 0 #define A31_PLL6_LOCK (1 << 28) #define A31_PLL6_BYPASS_EN (1 << 25) #define A31_PLL6_CLK_OUT_EN (1 << 24) #define A31_PLL6_24M_OUT_EN (1 << 18) #define A31_PLL6_24M_POST_DIV (0x3 << 16) #define A31_PLL6_24M_POST_DIV_SHIFT 16 #define A31_PLL6_FACTOR_N (0x1f << 8) #define A31_PLL6_FACTOR_N_SHIFT 8 #define A31_PLL6_FACTOR_K (0x3 << 4) #define A31_PLL6_FACTOR_K_SHIFT 4 #define A31_PLL6_DEFAULT_N 0x18 #define A31_PLL6_DEFAULT_K 0x1 #define A31_PLL6_TIMEOUT 10 #define A80_PLL4_CLK_OUT_EN (1 << 20) #define A80_PLL4_PLL_DIV2 (1 << 18) #define A80_PLL4_PLL_DIV1 (1 << 16) #define A80_PLL4_FACTOR_N (0xff << 8) #define A80_PLL4_FACTOR_N_SHIFT 8 #define CLKID_A10_PLL3_1X 0 #define CLKID_A10_PLL3_2X 1 #define CLKID_A10_PLL5_DDR 0 #define CLKID_A10_PLL5_OTHER 1 #define CLKID_A10_PLL6_SATA 0 #define CLKID_A10_PLL6_OTHER 1 #define CLKID_A10_PLL6 2 #define CLKID_A10_PLL6_DIV_4 3 #define CLKID_A31_PLL6 0 #define CLKID_A31_PLL6_X2 1 enum aw_pll_type { AWPLL_A10_PLL1 = 1, AWPLL_A10_PLL2, AWPLL_A10_PLL3, AWPLL_A10_PLL5, AWPLL_A10_PLL6, AWPLL_A13_PLL2, AWPLL_A23_PLL1, AWPLL_A31_PLL1, AWPLL_A31_PLL6, AWPLL_A80_PLL4, }; struct aw_pll_sc { enum aw_pll_type type; device_t clkdev; bus_addr_t reg; int id; }; struct aw_pll_funcs { int (*recalc)(struct aw_pll_sc *, uint64_t *); int (*set_freq)(struct aw_pll_sc *, uint64_t, uint64_t *, int); int (*init)(device_t, bus_addr_t, struct clknode_init_def *); }; #define PLL_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) #define PLL_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) #define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) #define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) static int a10_pll1_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, n, k, p; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); p = 1 << ((val & A10_PLL1_OUT_EXT_DIVP) >> A10_PLL1_OUT_EXT_DIVP_SHIFT); m = ((val & A10_PLL1_FACTOR_M) >> A10_PLL1_FACTOR_M_SHIFT) + 1; k = ((val & A10_PLL1_FACTOR_K) >> A10_PLL1_FACTOR_K_SHIFT) + 1; n = (val & A10_PLL1_FACTOR_N) >> A10_PLL1_FACTOR_N_SHIFT; if (n == 0) n = 1; *freq = (*freq * n * k) / (m * p); return (0); } static int a10_pll2_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, post_div, n, pre_div; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); post_div = (val & A10_PLL2_POST_DIV) >> A10_PLL2_POST_DIV_SHIFT; if (post_div == 0) post_div = 1; n = (val & A10_PLL2_FACTOR_N) >> A10_PLL2_FACTOR_N_SHIFT; if (n == 0) n = 1; pre_div = (val & A10_PLL2_PRE_DIV) >> A10_PLL2_PRE_DIV_SHIFT; if (pre_div == 0) pre_div = 1; switch (sc->id) { case SUN4I_A10_PLL2_1X: *freq = (*freq * 2 * n) / pre_div / post_div / 2; break; case SUN4I_A10_PLL2_2X: *freq = (*freq * 2 * n) / pre_div / 4; break; case SUN4I_A10_PLL2_4X: *freq = (*freq * 2 * n) / pre_div / 2; break; case SUN4I_A10_PLL2_8X: *freq = (*freq * 2 * n) / pre_div; break; default: return (EINVAL); } return (0); } static int a10_pll2_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t val, post_div, n, pre_div; if (sc->id != SUN4I_A10_PLL2_1X) return (ENXIO); /* * Audio Codec needs PLL2-1X to be either 24576000 or 22579200. * * PLL2-1X output frequency is (48MHz * n) / pre_div / post_div / 2. * 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. */ if (*fout != 24576000 && *fout != 22579200) return (EINVAL); pre_div = 21; post_div = 4; n = (*fout * pre_div * post_div * 2) / (2 * fin); DEVICE_LOCK(sc); PLL_READ(sc, &val); val &= ~(A10_PLL2_POST_DIV | A10_PLL2_FACTOR_N | A10_PLL2_PRE_DIV); val |= (post_div << A10_PLL2_POST_DIV_SHIFT); val |= (n << A10_PLL2_FACTOR_N_SHIFT); val |= (pre_div << A10_PLL2_PRE_DIV_SHIFT); PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int a10_pll3_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); if ((val & A10_PLL3_MODE_SEL) == A10_PLL3_MODE_SEL_INT) { /* In integer mode, output is 3MHz * m */ m = (val & A10_PLL3_FACTOR_M) >> A10_PLL3_FACTOR_M_SHIFT; *freq = A10_PLL3_REF_FREQ * m; } else { /* In fractional mode, output is either 270MHz or 297MHz */ if ((val & A10_PLL3_FUNC_SET) == A10_PLL3_FUNC_SET_270MHZ) *freq = 270000000; else *freq = 297000000; } if (sc->id == CLKID_A10_PLL3_2X) *freq *= 2; return (0); } static int a10_pll3_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t val, m, mode, func; m = *fout / A10_PLL3_REF_FREQ; if (sc->id == CLKID_A10_PLL3_2X) m /= 2; mode = A10_PLL3_MODE_SEL_INT; func = 0; *fout = m * A10_PLL3_REF_FREQ; if (sc->id == CLKID_A10_PLL3_2X) *fout *= 2; DEVICE_LOCK(sc); PLL_READ(sc, &val); val &= ~(A10_PLL3_MODE_SEL | A10_PLL3_FUNC_SET | A10_PLL3_FACTOR_M); val |= mode; val |= func; val |= (m << A10_PLL3_FACTOR_M_SHIFT); PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int a10_pll3_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def) { uint32_t val; /* Allow changing PLL frequency while enabled */ def->flags = CLK_NODE_GLITCH_FREE; /* Set PLL to 297MHz */ CLKDEV_DEVICE_LOCK(dev); CLKDEV_READ_4(dev, reg, &val); val &= ~(A10_PLL3_MODE_SEL | A10_PLL3_FUNC_SET | A10_PLL3_FACTOR_M); val |= A10_PLL3_MODE_SEL_FRACT; val |= A10_PLL3_FUNC_SET_297MHZ; CLKDEV_WRITE_4(dev, reg, val); CLKDEV_DEVICE_UNLOCK(dev); return (0); } static int a10_pll5_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, n, k, p; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); p = 1 << ((val & A10_PLL5_OUT_EXT_DIVP) >> A10_PLL5_OUT_EXT_DIVP_SHIFT); m = ((val & A10_PLL5_FACTOR_M) >> A10_PLL5_FACTOR_M_SHIFT) + 1; k = ((val & A10_PLL5_FACTOR_K) >> A10_PLL5_FACTOR_K_SHIFT) + 1; n = (val & A10_PLL5_FACTOR_N) >> A10_PLL5_FACTOR_N_SHIFT; if (n == 0) return (ENXIO); switch (sc->id) { case CLKID_A10_PLL5_DDR: *freq = (*freq * n * k) / m; break; case CLKID_A10_PLL5_OTHER: *freq = (*freq * n * k) / p; break; default: return (ENXIO); } return (0); } static int a10_pll6_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def) { uint32_t val, m, n, k; /* * 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. */ m = k = 0; n = 25; CLKDEV_DEVICE_LOCK(dev); CLKDEV_READ_4(dev, reg, &val); val &= ~(A10_PLL6_FACTOR_N | A10_PLL6_FACTOR_K | A10_PLL6_FACTOR_M); val &= ~A10_PLL6_BYPASS_EN; val |= A10_PLL6_SATA_CLK_EN; val |= (n << A10_PLL6_FACTOR_N_SHIFT); val |= (k << A10_PLL6_FACTOR_K_SHIFT); val |= (m << A10_PLL6_FACTOR_M_SHIFT); CLKDEV_WRITE_4(dev, reg, val); CLKDEV_DEVICE_UNLOCK(dev); return (0); } static int a10_pll6_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, k, n; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); m = ((val & A10_PLL6_FACTOR_M) >> A10_PLL6_FACTOR_M_SHIFT) + 1; k = ((val & A10_PLL6_FACTOR_K) >> A10_PLL6_FACTOR_K_SHIFT) + 1; n = (val & A10_PLL6_FACTOR_N) >> A10_PLL6_FACTOR_N_SHIFT; if (n == 0) return (ENXIO); switch (sc->id) { case CLKID_A10_PLL6_SATA: *freq = (*freq * n * k) / m / 6; break; case CLKID_A10_PLL6_OTHER: *freq = (*freq * n * k) / 2; break; case CLKID_A10_PLL6: *freq = (*freq * n * k); break; case CLKID_A10_PLL6_DIV_4: *freq = (*freq * n * k) / 4; break; default: return (ENXIO); } return (0); } static int a10_pll6_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { if (sc->id != CLKID_A10_PLL6_SATA) return (ENXIO); /* PLL6 SATA output has been set to 100MHz in a10_pll6_init */ if (*fout != 100000000) return (ERANGE); return (0); } static int a13_pll2_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, post_div, n, pre_div; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); post_div = ((val & A13_PLL2_POST_DIV) >> A13_PLL2_POST_DIV_SHIFT) + 1; if (post_div == 0) post_div = 1; n = (val & A13_PLL2_FACTOR_N) >> A13_PLL2_FACTOR_N_SHIFT; if (n == 0) n = 1; pre_div = ((val & A13_PLL2_PRE_DIV) >> A13_PLL2_PRE_DIV_SHIFT) + 1; if (pre_div == 0) pre_div = 1; switch (sc->id) { case SUN4I_A10_PLL2_1X: *freq = (*freq * 2 * n) / pre_div / post_div / 2; break; case SUN4I_A10_PLL2_2X: *freq = (*freq * 2 * n) / pre_div / 4; break; case SUN4I_A10_PLL2_4X: *freq = (*freq * 2 * n) / pre_div / 2; break; case SUN4I_A10_PLL2_8X: *freq = (*freq * 2 * n) / pre_div; break; default: return (EINVAL); } return (0); } static int a13_pll2_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t val, post_div, n, pre_div; if (sc->id != SUN4I_A10_PLL2_1X) return (ENXIO); /* * Audio Codec needs PLL2-1X to be either 24576000 or 22579200. * * PLL2-1X output frequency is (48MHz * n) / pre_div / post_div / 2. * 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. */ if (*fout != 24576000 && *fout != 22579200) return (EINVAL); pre_div = 21; post_div = 4; n = (*fout * pre_div * post_div * 2) / (2 * fin); DEVICE_LOCK(sc); PLL_READ(sc, &val); val &= ~(A13_PLL2_POST_DIV | A13_PLL2_FACTOR_N | A13_PLL2_PRE_DIV); val |= ((post_div - 1) << A13_PLL2_POST_DIV_SHIFT); val |= (n << A13_PLL2_FACTOR_N_SHIFT); val |= ((pre_div - 1) << A13_PLL2_PRE_DIV_SHIFT); PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int a23_pll1_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, n, k, p; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); m = ((val & A23_PLL1_FACTOR_M) >> A23_PLL1_FACTOR_M_SHIFT) + 1; k = ((val & A23_PLL1_FACTOR_K) >> A23_PLL1_FACTOR_K_SHIFT) + 1; n = ((val & A23_PLL1_FACTOR_N) >> A23_PLL1_FACTOR_N_SHIFT) + 1; p = ((val & A23_PLL1_FACTOR_P) >> A23_PLL1_FACTOR_P_SHIFT) + 1; *freq = (*freq * n * k) / (m * p); return (0); } static int a31_pll1_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, n, k; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); m = ((val & A31_PLL1_FACTOR_M) >> A31_PLL1_FACTOR_M_SHIFT) + 1; k = ((val & A31_PLL1_FACTOR_K) >> A31_PLL1_FACTOR_K_SHIFT) + 1; n = ((val & A31_PLL1_FACTOR_N) >> A31_PLL1_FACTOR_N_SHIFT) + 1; *freq = (*freq * n * k) / m; return (0); } static int a31_pll6_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def) { uint32_t val; int retry; if (def->id != CLKID_A31_PLL6) return (0); /* * The datasheet recommends that PLL6 output should be fixed to * 600MHz. */ CLKDEV_DEVICE_LOCK(dev); CLKDEV_READ_4(dev, reg, &val); val &= ~(A31_PLL6_FACTOR_N | A31_PLL6_FACTOR_K | A31_PLL6_BYPASS_EN); val |= (A31_PLL6_DEFAULT_N << A31_PLL6_FACTOR_N_SHIFT); val |= (A31_PLL6_DEFAULT_K << A31_PLL6_FACTOR_K_SHIFT); CLKDEV_WRITE_4(dev, reg, val); /* Wait for PLL to become stable */ for (retry = A31_PLL6_TIMEOUT; retry > 0; retry--) { CLKDEV_READ_4(dev, reg, &val); if ((val & A31_PLL6_LOCK) == A31_PLL6_LOCK) break; DELAY(1); } CLKDEV_DEVICE_UNLOCK(dev); if (retry == 0) return (ETIMEDOUT); return (0); } static int a31_pll6_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, k, n; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); k = ((val & A10_PLL6_FACTOR_K) >> A10_PLL6_FACTOR_K_SHIFT) + 1; n = ((val & A10_PLL6_FACTOR_N) >> A10_PLL6_FACTOR_N_SHIFT) + 1; switch (sc->id) { case CLKID_A31_PLL6: *freq = (*freq * n * k) / 2; break; case CLKID_A31_PLL6_X2: *freq = *freq * n * k; break; default: return (ENXIO); } return (0); } static int a80_pll4_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, n, div1, div2; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); n = (val & A80_PLL4_FACTOR_N) >> A80_PLL4_FACTOR_N_SHIFT; div1 = (val & A80_PLL4_PLL_DIV1) == 0 ? 1 : 2; div2 = (val & A80_PLL4_PLL_DIV2) == 0 ? 1 : 2; *freq = (*freq * n) / div1 / div2; return (0); } #define PLL(_type, _recalc, _set_freq, _init) \ [(_type)] = { \ .recalc = (_recalc), \ .set_freq = (_set_freq), \ .init = (_init) \ } static struct aw_pll_funcs aw_pll_func[] = { PLL(AWPLL_A10_PLL1, a10_pll1_recalc, NULL, NULL), PLL(AWPLL_A10_PLL2, a10_pll2_recalc, a10_pll2_set_freq, NULL), PLL(AWPLL_A10_PLL3, a10_pll3_recalc, a10_pll3_set_freq, a10_pll3_init), PLL(AWPLL_A10_PLL5, a10_pll5_recalc, NULL, NULL), PLL(AWPLL_A10_PLL6, a10_pll6_recalc, a10_pll6_set_freq, a10_pll6_init), PLL(AWPLL_A13_PLL2, a13_pll2_recalc, a13_pll2_set_freq, NULL), PLL(AWPLL_A23_PLL1, a23_pll1_recalc, NULL, NULL), PLL(AWPLL_A31_PLL1, a31_pll1_recalc, NULL, NULL), PLL(AWPLL_A31_PLL6, a31_pll6_recalc, NULL, a31_pll6_init), PLL(AWPLL_A80_PLL4, a80_pll4_recalc, NULL, NULL), }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-pll1-clk", AWPLL_A10_PLL1 }, { "allwinner,sun4i-a10-pll2-clk", AWPLL_A10_PLL2 }, { "allwinner,sun4i-a10-pll3-clk", AWPLL_A10_PLL3 }, { "allwinner,sun4i-a10-pll5-clk", AWPLL_A10_PLL5 }, { "allwinner,sun4i-a10-pll6-clk", AWPLL_A10_PLL6 }, { "allwinner,sun5i-a13-pll2-clk", AWPLL_A13_PLL2 }, { "allwinner,sun6i-a31-pll1-clk", AWPLL_A31_PLL1 }, { "allwinner,sun6i-a31-pll6-clk", AWPLL_A31_PLL6 }, { "allwinner,sun8i-a23-pll1-clk", AWPLL_A23_PLL1 }, { "allwinner,sun9i-a80-pll4-clk", AWPLL_A80_PLL4 }, { NULL, 0 } }; static int aw_pll_init(struct clknode *clk, device_t dev) { clknode_init_parent_idx(clk, 0); return (0); } static int aw_pll_set_gate(struct clknode *clk, bool enable) { struct aw_pll_sc *sc; uint32_t val; sc = clknode_get_softc(clk); DEVICE_LOCK(sc); PLL_READ(sc, &val); if (enable) val |= AW_PLL_ENABLE; else val &= ~AW_PLL_ENABLE; PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int aw_pll_recalc(struct clknode *clk, uint64_t *freq) { struct aw_pll_sc *sc; sc = clknode_get_softc(clk); if (aw_pll_func[sc->type].recalc == NULL) return (ENXIO); return (aw_pll_func[sc->type].recalc(sc, freq)); } static int aw_pll_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, int flags, int *stop) { struct aw_pll_sc *sc; sc = clknode_get_softc(clk); *stop = 1; if (aw_pll_func[sc->type].set_freq == NULL) return (ENXIO); return (aw_pll_func[sc->type].set_freq(sc, fin, fout, flags)); } static clknode_method_t aw_pll_clknode_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, aw_pll_init), CLKNODEMETHOD(clknode_set_gate, aw_pll_set_gate), CLKNODEMETHOD(clknode_recalc_freq, aw_pll_recalc), CLKNODEMETHOD(clknode_set_freq, aw_pll_set_freq), CLKNODEMETHOD_END }; DEFINE_CLASS_1(aw_pll_clknode, aw_pll_clknode_class, aw_pll_clknode_methods, sizeof(struct aw_pll_sc), clknode_class); static int aw_pll_create(device_t dev, bus_addr_t paddr, struct clkdom *clkdom, const char *pclkname, const char *clkname, int index) { enum aw_pll_type type; struct clknode_init_def clkdef; struct aw_pll_sc *sc; struct clknode *clk; int error; type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; memset(&clkdef, 0, sizeof(clkdef)); clkdef.id = index; clkdef.name = clkname; if (pclkname != NULL) { clkdef.parent_names = malloc(sizeof(char *), M_OFWPROP, M_WAITOK); clkdef.parent_names[0] = pclkname; clkdef.parent_cnt = 1; } else clkdef.parent_cnt = 0; if (aw_pll_func[type].init != NULL) { error = aw_pll_func[type].init(device_get_parent(dev), paddr, &clkdef); if (error != 0) { device_printf(dev, "clock %s init failed\n", clkname); return (error); } } clk = clknode_create(clkdom, &aw_pll_clknode_class, &clkdef); if (clk == NULL) { device_printf(dev, "cannot create clock node\n"); return (ENXIO); } sc = clknode_get_softc(clk); sc->clkdev = device_get_parent(dev); sc->reg = paddr; sc->type = type; sc->id = clkdef.id; clknode_register(clkdom, clk); OF_prop_free(__DECONST(char *, clkdef.parent_names)); return (0); } static int aw_pll_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner PLL Clock"); return (BUS_PROBE_DEFAULT); } static int aw_pll_attach(device_t dev) { struct clkdom *clkdom; const char **names; int index, nout, error; clk_t clk_parent; uint32_t *indices; bus_addr_t paddr; bus_size_t psize; phandle_t node; node = ofw_bus_get_node(dev); if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { device_printf(dev, "couldn't parse 'reg' property\n"); return (ENXIO); } clkdom = clkdom_create(dev); nout = clk_parse_ofw_out_names(dev, node, &names, &indices); if (nout == 0) { device_printf(dev, "no clock outputs found\n"); error = ENOENT; goto fail; } if (clk_get_by_ofw_index(dev, 0, 0, &clk_parent) != 0) clk_parent = NULL; for (index = 0; index < nout; index++) { error = aw_pll_create(dev, paddr, clkdom, clk_parent ? clk_get_name(clk_parent) : NULL, names[index], nout == 1 ? 1 : index); if (error) goto fail; } if (clkdom_finit(clkdom) != 0) { device_printf(dev, "cannot finalize clkdom initialization\n"); error = ENXIO; goto fail; } if (bootverbose) clkdom_dump(clkdom); return (0); fail: return (error); } static device_method_t aw_pll_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_pll_probe), DEVMETHOD(device_attach, aw_pll_attach), DEVMETHOD_END }; static driver_t aw_pll_driver = { "aw_pll", aw_pll_methods, 0, }; static devclass_t aw_pll_devclass; EARLY_DRIVER_MODULE(aw_pll, simplebus, aw_pll_driver, aw_pll_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Index: head/sys/arm/allwinner/files.allwinner =================================================================== --- head/sys/arm/allwinner/files.allwinner (revision 304317) +++ head/sys/arm/allwinner/files.allwinner (revision 304318) @@ -1,55 +1,55 @@ # $FreeBSD$ kern/kern_clocksource.c standard arm/allwinner/a10_ahci.c optional ahci arm/allwinner/a10_codec.c optional sound arm/allwinner/a10_common.c standard arm/allwinner/a10_dmac.c standard arm/allwinner/a10_ehci.c optional ehci arm/allwinner/aw_usbphy.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/aw_nmi.c optional intrng arm/allwinner/aw_if_dwc.c optional dwc arm/allwinner/aw_rsb.c optional rsb arm/allwinner/aw_rtc.c standard arm/allwinner/aw_wdog.c standard arm/allwinner/a20/a20_cpu_cfg.c standard -arm/allwinner/allwinner_machdep.c standard +arm/allwinner/aw_machdep.c standard arm/allwinner/aw_mp.c optional smp arm/allwinner/axp209.c optional axp209 arm/allwinner/axp81x.c optional axp81x arm/allwinner/if_awg.c optional awg arm/allwinner/if_emac.c optional emac arm/allwinner/sunxi_dma_if.m standard dev/iicbus/twsi/a10_twsi.c optional twsi dev/usb/controller/generic_ohci.c optional ohci dev/usb/controller/generic_usb_if.m optional ohci arm/allwinner/aw_sid.c standard arm/allwinner/aw_thermal.c standard #arm/allwinner/console.c standard arm/allwinner/a10_fb.c optional vt arm/allwinner/a10_hdmi.c optional hdmi arm/allwinner/a10_hdmiaudio.c optional hdmi sound arm/arm/hdmi_if.m optional hdmi arm/allwinner/aw_reset.c standard arm/allwinner/aw_ccu.c standard arm/allwinner/clk/aw_ahbclk.c standard arm/allwinner/clk/aw_apbclk.c standard arm/allwinner/clk/aw_axiclk.c standard arm/allwinner/clk/aw_codecclk.c standard arm/allwinner/clk/aw_cpuclk.c standard arm/allwinner/clk/aw_cpusclk.c standard arm/allwinner/clk/aw_debeclk.c standard arm/allwinner/clk/aw_gate.c standard arm/allwinner/clk/aw_gmacclk.c standard arm/allwinner/clk/aw_hdmiclk.c standard arm/allwinner/clk/aw_lcdclk.c standard arm/allwinner/clk/aw_modclk.c standard arm/allwinner/clk/aw_mmcclk.c standard arm/allwinner/clk/aw_oscclk.c standard arm/allwinner/clk/aw_pll.c standard arm/allwinner/clk/aw_usbclk.c standard Index: head/sys/arm/allwinner/timer.c =================================================================== --- head/sys/arm/allwinner/timer.c (revision 304317) +++ head/sys/arm/allwinner/timer.c (revision 304318) @@ -1,368 +1,368 @@ /*- * Copyright (c) 2012 Ganbold Tsagaankhuu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#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; }; 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_hardclock(void *); static int a10_timer_probe(device_t); static int a10_timer_attach(device_t); static delay_func a10_timer_delay; 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; /* 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-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) { arm_set_delay(a10_timer_delay, sc); 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); } 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; EARLY_DRIVER_MODULE(a10_timer, simplebus, a10_timer_driver, a10_timer_devclass, 0, 0, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); static void a10_timer_delay(int usec, void *arg) { struct a10_timer_softc *sc = arg; uint64_t end, now; now = timer_read_counter64(); end = now + (sc->timer0_freq / 1000000) * (usec + 1); while (now < end) now = timer_read_counter64(); }