Index: head/sys/arm/allwinner/a10_ehci.c =================================================================== --- head/sys/arm/allwinner/a10_ehci.c (revision 299112) +++ head/sys/arm/allwinner/a10_ehci.c (revision 299113) @@ -1,353 +1,354 @@ /*- * 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 #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; bs_r_1_proto(reversed); bs_w_1_proto(reversed); struct aw_ehci_softc { ehci_softc_t sc; clk_t clk; hwreset_t rst; }; 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,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 }, { 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, &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, &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 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(ehci_softc_t), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); Index: head/sys/arm/allwinner/a10_gpio.c =================================================================== --- head/sys/arm/allwinner/a10_gpio.c (revision 299112) +++ head/sys/arm/allwinner/a10_gpio.c (revision 299113) @@ -1,691 +1,701 @@ /*- * 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 "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 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 a83t_padconf.c */ +#ifdef SOC_ALLWINNER_A83T +extern const struct allwinner_padconf a83t_padconf; +extern const struct allwinner_padconf a83t_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_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 {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)); switch ((func >> offset) & 0x7) { case A10_GPIO_INPUT: return (GPIO_PIN_INPUT); case A10_GPIO_OUTPUT: return (GPIO_PIN_OUTPUT); } return (0); } static void a10_gpio_set_function(struct a10_gpio_softc *sc, uint32_t pin, uint32_t f) { uint32_t bank, data, offset; /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); bank = 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); } 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)); switch ((val >> offset) & 0x3) { case A10_GPIO_PULLDOWN: return (GPIO_PIN_PULLDOWN); case A10_GPIO_PULLUP: return (GPIO_PIN_PULLUP); } return (0); } static void a10_gpio_set_pud(struct a10_gpio_softc *sc, uint32_t pin, uint32_t state) { uint32_t bank, offset, val; /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); bank = 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 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 void a10_gpio_pin_configure(struct a10_gpio_softc *sc, uint32_t pin, uint32_t flags) { /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); /* Manage input/output. */ if (flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { if (flags & GPIO_PIN_OUTPUT) a10_gpio_set_function(sc, pin, A10_GPIO_OUTPUT); else a10_gpio_set_function(sc, pin, A10_GPIO_INPUT); } /* Manage Pull-up/pull-down. */ if (flags & (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)) { if (flags & GPIO_PIN_PULLUP) a10_gpio_set_pud(sc, pin, A10_GPIO_PULLUP); else a10_gpio_set_pud(sc, pin, A10_GPIO_PULLDOWN); } else a10_gpio_set_pud(sc, pin, A10_GPIO_NONE); } static device_t a10_gpio_get_bus(device_t dev) { struct a10_gpio_softc *sc; sc = device_get_softc(dev); return (sc->sc_busdev); } static int a10_gpio_pin_max(device_t dev, int *maxpin) { 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; sc = device_get_softc(dev); if (pin >= sc->padconf->npins) return (EINVAL); A10_GPIO_LOCK(sc); *flags = a10_gpio_get_function(sc, pin); *flags |= a10_gpio_get_pud(sc, pin); A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { 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; sc = device_get_softc(dev); if (pin > sc->padconf->npins) return (EINVAL); A10_GPIO_LOCK(sc); a10_gpio_pin_configure(sc, pin, flags); A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct a10_gpio_softc *sc; uint32_t bank, data; 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); a10_gpio_set_function(sc, pin_num, pin_func); a10_gpio_set_drv(sc, pin_num, pin_drive); a10_gpio_set_pud(sc, pin_num, pin_pull); A10_GPIO_UNLOCK(sc); } out: free(pinlist, M_OFWPROP); free(pin_function, M_OFWPROP); 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, &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, &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_MIDDLE); Index: head/sys/arm/allwinner/a83t/a83t_padconf.c =================================================================== --- head/sys/arm/allwinner/a83t/a83t_padconf.c (nonexistent) +++ head/sys/arm/allwinner/a83t/a83t_padconf.c (revision 299113) @@ -0,0 +1,162 @@ +/*- + * 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$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include + +#ifdef SOC_ALLWINNER_A83T + +static const struct allwinner_pins a83t_pins[] = { + { "PB0", 1, 0, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint" } }, + { "PB1", 1, 1, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint" } }, + { "PB2", 1, 2, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint" } }, + { "PB3", 1, 3, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint" } }, + { "PB4", 1, 4, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } }, + { "PB5", 1, 5, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } }, + { "PB6", 1, 6, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } }, + { "PB7", 1, 7, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } }, + { "PB8", 1, 8, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } }, + { "PB9", 1, 9, { "gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, "eint" } }, + { "PB10", 1, 10, { "gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, "eint" } }, + + { "PC0", 2, 0, { "gpio_in", "gpio_out", "nand", "spi0" } }, + { "PC1", 2, 1, { "gpio_in", "gpio_out", "nand", "spi0" } }, + { "PC2", 2, 2, { "gpio_in", "gpio_out", "nand", "spi0" } }, + { "PC3", 2, 3, { "gpio_in", "gpio_out", "nand", "spi0" } }, + { "PC4", 2, 4, { "gpio_in", "gpio_out", "nand" } }, + { "PC5", 2, 5, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC6", 2, 6, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC7", 2, 7, { "gpio_in", "gpio_out", "nand" } }, + { "PC8", 2, 8, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC9", 2, 9, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC10", 2, 10, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC11", 2, 11, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC12", 2, 12, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC13", 2, 13, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC14", 2, 14, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC15", 2, 15, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC16", 2, 16, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC17", 2, 17, { "gpio_in", "gpio_out", "nand" } }, + { "PC18", 2, 18, { "gpio_in", "gpio_out", "nand" } }, + + { "PD2", 3, 2, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD3", 3, 3, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD4", 3, 4, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD5", 3, 5, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD6", 3, 6, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD7", 3, 7, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD10", 3, 10, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD11", 3, 11, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD12", 3, 12, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD13", 3, 13, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD14", 3, 14, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD15", 3, 15, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD18", 3, 18, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } }, + { "PD19", 3, 19, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } }, + { "PD20", 3, 20, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } }, + { "PD21", 3, 21, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } }, + { "PD22", 3, 22, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } }, + { "PD23", 3, 23, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } }, + { "PD24", 3, 24, { "gpio_in", "gpio_out", "lcd", "lvds" } }, + { "PD25", 3, 25, { "gpio_in", "gpio_out", "lcd", "lvds" } }, + { "PD26", 3, 26, { "gpio_in", "gpio_out", "lcd", "lvds" } }, + { "PD27", 3, 27, { "gpio_in", "gpio_out", "lcd", "lvds" } }, + { "PD28", 3, 28, { "gpio_in", "gpio_out", "pwm" } }, + { "PD29", 3, 29, { "gpio_in", "gpio_out" } }, + + { "PE0", 4, 0, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE1", 4, 1, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE2", 4, 2, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE3", 4, 3, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE4", 4, 4, { "gpio_in", "gpio_out", "csi" } }, + { "PE5", 4, 5, { "gpio_in", "gpio_out", "csi" } }, + { "PE6", 4, 6, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE7", 4, 7, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE8", 4, 8, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE9", 4, 9, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE10", 4, 10, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } }, + { "PE11", 4, 11, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } }, + { "PE12", 4, 12, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } }, + { "PE13", 4, 13, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } }, + { "PE14", 4, 14, { "gpio_in", "gpio_out", "csi", "twi2" } }, + { "PE15", 4, 15, { "gpio_in", "gpio_out", "csi", "twi2" } }, + { "PE16", 4, 16, { "gpio_in", "gpio_out" } }, + { "PE17", 4, 17, { "gpio_in", "gpio_out" } }, + { "PE18", 4, 18, { "gpio_in", "gpio_out", NULL, "owa" } }, + { "PE19", 4, 19, { "gpio_in", "gpio_out" } }, + + { "PF0", 5, 0, { "gpio_in", "gpio_out", "mmc0", "jtag" } }, + { "PF1", 5, 1, { "gpio_in", "gpio_out", "mmc0", "jtag" } }, + { "PF2", 5, 2, { "gpio_in", "gpio_out", "mmc0", "uart0" } }, + { "PF3", 5, 3, { "gpio_in", "gpio_out", "mmc0", "jtag" } }, + { "PF4", 5, 4, { "gpio_in", "gpio_out", "mmc0", "uart0" } }, + { "PF5", 5, 5, { "gpio_in", "gpio_out", "mmc0", "jtag" } }, + { "PF6", 5, 6, { "gpio_in", "gpio_out" } }, + + { "PG0", 6, 0, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } }, + { "PG1", 6, 1, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } }, + { "PG2", 6, 2, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } }, + { "PG3", 6, 3, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } }, + { "PG4", 6, 4, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } }, + { "PG5", 6, 5, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } }, + { "PG6", 6, 6, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "eint" } }, + { "PG7", 6, 7, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "eint" } }, + { "PG8", 6, 8, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "eint" } }, + { "PG9", 6, 9, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "eint" } }, + { "PG10", 6, 10, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "eint" } }, + { "PG11", 6, 11, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "eint" } }, + { "PG12", 6, 12, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "eint" } }, + { "PG13", 6, 13, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "eint" } }, + + { "PH0", 7, 0, { "gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, "eint" } }, + { "PH1", 7, 1, { "gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, "eint" } }, + { "PH2", 7, 2, { "gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, "eint" } }, + { "PH3", 7, 3, { "gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, "eint" } }, + { "PH4", 7, 4, { "gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, "eint" } }, + { "PH5", 7, 5, { "gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, "eint" } }, + { "PH6", 7, 6, { "gpio_in", "gpio_out", "hdmiddc", NULL, NULL, NULL, "eint" } }, + { "PH7", 7, 7, { "gpio_in", "gpio_out", "hdmiddc", NULL, NULL, NULL, "eint" } }, + { "PH8", 7, 8, { "gpio_in", "gpio_out", "hdmiddc", NULL, NULL, NULL, "eint" } }, + { "PH9", 7, 9, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "eint" } }, + { "PH10", 7, 10, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "eint" } }, + { "PH11", 7, 11, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "eint" } }, +}; + +const struct allwinner_padconf a83t_padconf = { + .npins = nitems(a83t_pins), + .pins = a83t_pins, +}; + +#endif /* !SOC_ALLWINNER_A83T */ Property changes on: head/sys/arm/allwinner/a83t/a83t_padconf.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/a83t/a83t_r_padconf.c =================================================================== --- head/sys/arm/allwinner/a83t/a83t_r_padconf.c (nonexistent) +++ head/sys/arm/allwinner/a83t/a83t_r_padconf.c (revision 299113) @@ -0,0 +1,62 @@ +/*- + * 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$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include + +#ifdef SOC_ALLWINNER_A83T + +static const struct allwinner_pins a83t_r_pins[] = { + { "PL0", 0, 0, { "gpio_in", "gpio_out", "s_rsb", "s_i2c", NULL, NULL, "eint" } }, + { "PL1", 0, 1, { "gpio_in", "gpio_out", "s_rsb", "s_i2c", NULL, NULL, "eint" } }, + { "PL2", 0, 2, { "gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, "eint" } }, + { "PL3", 0, 3, { "gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, "eint" } }, + { "PL4", 0, 4, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } }, + { "PL5", 0, 5, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } }, + { "PL6", 0, 6, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } }, + { "PL7", 0, 7, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } }, + { "PL8", 0, 8, { "gpio_in", "gpio_out", "s_i2c", NULL, NULL, NULL, "eint" } }, + { "PL9", 0, 9, { "gpio_in", "gpio_out", "s_i2c", NULL, NULL, NULL, "eint" } }, + { "PL10", 0, 10, { "gpio_in", "gpio_out", "s_pwm", NULL, NULL, NULL, "eint" } }, + { "PL11", 0, 11, { "gpio_in", "gpio_out", NULL, NULL, NULL, "eint" } }, + { "PL12", 0, 12, { "gpio_in", "gpio_out", "s_cir", NULL, NULL, NULL, "eint" } }, +}; + +const struct allwinner_padconf a83t_r_padconf = { + .npins = nitems(a83t_r_pins), + .pins = a83t_r_pins, +}; + +#endif /* !SOC_ALLWINNER_A83T */ Property changes on: head/sys/arm/allwinner/a83t/a83t_r_padconf.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/a83t/files.a83t =================================================================== --- head/sys/arm/allwinner/a83t/files.a83t (nonexistent) +++ head/sys/arm/allwinner/a83t/files.a83t (revision 299113) @@ -0,0 +1,4 @@ +# $FreeBSD$ + +arm/allwinner/a83t/a83t_padconf.c standard +arm/allwinner/a83t/a83t_r_padconf.c standard Property changes on: head/sys/arm/allwinner/a83t/files.a83t ___________________________________________________________________ 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/a83t/std.a83t =================================================================== --- head/sys/arm/allwinner/a83t/std.a83t (nonexistent) +++ head/sys/arm/allwinner/a83t/std.a83t (revision 299113) @@ -0,0 +1,15 @@ +# Allwinner A83T common options +#$FreeBSD$ + +cpu CPU_CORTEXA +machine arm armv6 +makeoptions CONF_CFLAGS="-march=armv7a" + +makeoptions KERNVIRTADDR=0xc0200000 +options KERNVIRTADDR=0xc0200000 + +options IPI_IRQ_START=0 +options IPI_IRQ_END=15 + +files "../allwinner/files.allwinner" +files "../allwinner/a83t/files.a83t" Property changes on: head/sys/arm/allwinner/a83t/std.a83t ___________________________________________________________________ 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/allwinner_machdep.c =================================================================== --- head/sys/arm/allwinner/allwinner_machdep.c (revision 299112) +++ head/sys/arm/allwinner/allwinner_machdep.c (revision 299113) @@ -1,209 +1,233 @@ /*- * 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 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 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_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, a20_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, a31_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, a31_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 u_int allwinner_soc_type(void) { return (soc_type); } u_int allwinner_soc_family(void) { return (soc_family); } Index: head/sys/arm/allwinner/allwinner_machdep.h =================================================================== --- head/sys/arm/allwinner/allwinner_machdep.h (revision 299112) +++ head/sys/arm/allwinner/allwinner_machdep.h (revision 299113) @@ -1,48 +1,50 @@ /*- * 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_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 */ Index: head/sys/arm/allwinner/aw_ccu.c =================================================================== --- head/sys/arm/allwinner/aw_ccu.c (revision 299112) +++ head/sys/arm/allwinner/aw_ccu.c (revision 299113) @@ -1,224 +1,299 @@ /*- * 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 oscillator clock */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include +#include #include #include #include #include "clkdev_if.h" #define CCU_BASE 0x01c20000 #define CCU_SIZE 0x400 +#define PRCM_BASE 0x01f01400 +#define PRCM_SIZE 0x200 + +#define SYSCTRL_BASE 0x01c00000 +#define SYSCTRL_SIZE 0x34 + struct aw_ccu_softc { struct simplebus_softc sc; bus_space_tag_t bst; - bus_space_handle_t bsh; + bus_space_handle_t ccu_bsh; + bus_space_handle_t prcm_bsh; + bus_space_handle_t sysctrl_bsh; struct mtx mtx; + int flags; }; +#define CLOCK_CCU (1 << 0) +#define CLOCK_PRCM (1 << 1) +#define CLOCK_SYSCTRL (1 << 2) + static struct ofw_compat_data compat_data[] = { - { "allwinner,sun4i-a10", 1 }, - { "allwinner,sun7i-a20", 1 }, - { "allwinner,sun6i-a31", 1 }, - { "allwinner,sun6i-a31s", 1 }, + { "allwinner,sun4i-a10", CLOCK_CCU }, + { "allwinner,sun7i-a20", CLOCK_CCU }, + { "allwinner,sun6i-a31", CLOCK_CCU }, + { "allwinner,sun6i-a31s", CLOCK_CCU }, + { "allwinner,sun8i-a83t", CLOCK_CCU|CLOCK_PRCM|CLOCK_SYSCTRL }, { NULL, 0 } }; static int -aw_ccu_check_addr(bus_addr_t addr) +aw_ccu_check_addr(struct aw_ccu_softc *sc, bus_addr_t addr, + bus_space_handle_t *pbsh, bus_size_t *poff) { - if (addr < CCU_BASE || addr >= (CCU_BASE + CCU_SIZE)) - return (EINVAL); - return (0); + if (addr >= CCU_BASE && addr < (CCU_BASE + CCU_SIZE) && + (sc->flags & CLOCK_CCU) != 0) { + *poff = addr - CCU_BASE; + *pbsh = sc->ccu_bsh; + return (0); + } + if (addr >= PRCM_BASE && addr < (PRCM_BASE + PRCM_SIZE) && + (sc->flags & CLOCK_PRCM) != 0) { + *poff = addr - PRCM_BASE; + *pbsh = sc->prcm_bsh; + return (0); + } + if (addr >= SYSCTRL_BASE && addr < (SYSCTRL_BASE + SYSCTRL_SIZE) && + (sc->flags & CLOCK_SYSCTRL) != 0) { + *poff = addr - SYSCTRL_BASE; + *pbsh = sc->sysctrl_bsh; + return (0); + } + return (EINVAL); } static int aw_ccu_write_4(device_t dev, bus_addr_t addr, uint32_t val) { struct aw_ccu_softc *sc; + bus_space_handle_t bsh; + bus_size_t reg; - if (aw_ccu_check_addr(addr) != 0) + sc = device_get_softc(dev); + + if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) return (EINVAL); - sc = device_get_softc(dev); mtx_assert(&sc->mtx, MA_OWNED); - bus_space_write_4(sc->bst, sc->bsh, addr - CCU_BASE, val); + bus_space_write_4(sc->bst, bsh, reg, val); return (0); } static int aw_ccu_read_4(device_t dev, bus_addr_t addr, uint32_t *val) { struct aw_ccu_softc *sc; + bus_space_handle_t bsh; + bus_size_t reg; - if (aw_ccu_check_addr(addr) != 0) + sc = device_get_softc(dev); + + if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) return (EINVAL); - sc = device_get_softc(dev); mtx_assert(&sc->mtx, MA_OWNED); - *val = bus_space_read_4(sc->bst, sc->bsh, addr - CCU_BASE); + *val = bus_space_read_4(sc->bst, bsh, reg); return (0); } static int aw_ccu_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) { struct aw_ccu_softc *sc; + bus_space_handle_t bsh; + bus_size_t reg; uint32_t val; - if (aw_ccu_check_addr(addr) != 0) + sc = device_get_softc(dev); + + if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) return (EINVAL); - sc = device_get_softc(dev); mtx_assert(&sc->mtx, MA_OWNED); - val = bus_space_read_4(sc->bst, sc->bsh, addr - CCU_BASE); + val = bus_space_read_4(sc->bst, bsh, reg); val &= ~clr; val |= set; - bus_space_write_4(sc->bst, sc->bsh, addr - CCU_BASE, val); + bus_space_write_4(sc->bst, bsh, reg, val); return (0); } static void aw_ccu_device_lock(device_t dev) { struct aw_ccu_softc *sc; sc = device_get_softc(dev); mtx_lock(&sc->mtx); } static void aw_ccu_device_unlock(device_t dev) { struct aw_ccu_softc *sc; sc = device_get_softc(dev); mtx_unlock(&sc->mtx); } +static const struct ofw_compat_data * +aw_ccu_search_compatible(void) +{ + const struct ofw_compat_data *compat; + phandle_t root; + + root = OF_finddevice("/"); + for (compat = compat_data; compat_data->ocd_str != NULL; compat++) + if (fdt_is_compatible(root, compat->ocd_str)) + break; + + return (compat); +} + static int aw_ccu_probe(device_t dev) { const char *name; - device_t pdev; name = ofw_bus_get_name(dev); if (name == NULL || strcmp(name, "clocks") != 0) return (ENXIO); - pdev = device_get_parent(dev); - if (ofw_bus_search_compatible(pdev, compat_data)->ocd_data == 0) - return (0); + if (aw_ccu_search_compatible()->ocd_data == 0) + return (ENXIO); device_set_desc(dev, "Allwinner Clock Control Unit"); return (BUS_PROBE_SPECIFIC); } static int aw_ccu_attach(device_t dev) { struct aw_ccu_softc *sc; phandle_t node, child; device_t cdev; int error; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); simplebus_init(dev, node); + sc->flags = aw_ccu_search_compatible()->ocd_data; + /* - * Map CCU registers. The DT doesn't have a "reg" property for the - * /clocks node and child nodes have conflicting "reg" properties. + * Map registers. The DT doesn't have a "reg" property + * for the /clocks node and child nodes have conflicting "reg" + * properties. */ sc->bst = bus_get_bus_tag(dev); - error = bus_space_map(sc->bst, CCU_BASE, CCU_SIZE, 0, &sc->bsh); - if (error != 0) { - device_printf(dev, "couldn't map CCU: %d\n", error); - return (error); + if (sc->flags & CLOCK_CCU) { + error = bus_space_map(sc->bst, CCU_BASE, CCU_SIZE, 0, + &sc->ccu_bsh); + if (error != 0) { + device_printf(dev, "couldn't map CCU: %d\n", error); + return (error); + } + } + if (sc->flags & CLOCK_PRCM) { + error = bus_space_map(sc->bst, PRCM_BASE, PRCM_SIZE, 0, + &sc->prcm_bsh); + if (error != 0) { + device_printf(dev, "couldn't map PRCM: %d\n", error); + return (error); + } + } + if (sc->flags & CLOCK_SYSCTRL) { + error = bus_space_map(sc->bst, SYSCTRL_BASE, SYSCTRL_SIZE, 0, + &sc->sysctrl_bsh); + if (error != 0) { + device_printf(dev, "couldn't map SYSCTRL: %d\n", error); + return (error); + } } mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); /* Attach child devices */ for (child = OF_child(node); child > 0; child = OF_peer(child)) { cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL); if (cdev != NULL) device_probe_and_attach(cdev); } return (bus_generic_attach(dev)); } static device_method_t aw_ccu_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_ccu_probe), DEVMETHOD(device_attach, aw_ccu_attach), /* clkdev interface */ DEVMETHOD(clkdev_write_4, aw_ccu_write_4), DEVMETHOD(clkdev_read_4, aw_ccu_read_4), DEVMETHOD(clkdev_modify_4, aw_ccu_modify_4), DEVMETHOD(clkdev_device_lock, aw_ccu_device_lock), DEVMETHOD(clkdev_device_unlock, aw_ccu_device_unlock), DEVMETHOD_END }; DEFINE_CLASS_1(aw_ccu, aw_ccu_driver, aw_ccu_methods, sizeof(struct aw_ccu_softc), simplebus_driver); static devclass_t aw_ccu_devclass; EARLY_DRIVER_MODULE(aw_ccu, simplebus, aw_ccu_driver, aw_ccu_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); MODULE_VERSION(aw_ccu, 1); Index: head/sys/arm/allwinner/aw_mp.c =================================================================== --- head/sys/arm/allwinner/aw_mp.c (revision 299112) +++ head/sys/arm/allwinner/aw_mp.c (revision 299113) @@ -1,204 +1,301 @@ /*- * 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 /* 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; } static void aw_common_mp_start_ap(bus_space_handle_t cpucfg, bus_space_handle_t prcm) { int i, j; uint32_t val; 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 (prcm) { 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 (prcm) { 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); } void a20_mp_start_ap(platform_t plat) { bus_space_handle_t cpucfg; if (bus_space_map(fdtbus_bs_tag, A20_CPUCFG_BASE, CPUCFG_SIZE, 0, &cpucfg) != 0) panic("Couldn't map the CPUCFG\n"); aw_common_mp_start_ap(cpucfg, 0); armv7_sev(); bus_space_unmap(fdtbus_bs_tag, cpucfg, CPUCFG_SIZE); } void a31_mp_start_ap(platform_t plat) { bus_space_handle_t cpucfg; bus_space_handle_t prcm; 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"); aw_common_mp_start_ap(cpucfg, prcm); armv7_sev(); bus_space_unmap(fdtbus_bs_tag, cpucfg, CPUCFG_SIZE); + 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_mp.h =================================================================== --- head/sys/arm/allwinner/aw_mp.h (revision 299112) +++ head/sys/arm/allwinner/aw_mp.h (revision 299113) @@ -1,35 +1,36 @@ /*- * 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. * * $FreeBSD$ */ #ifndef _AW_MP_H_ #define _AW_MP_H_ void aw_mp_setmaxid(platform_t plat); void a20_mp_start_ap(platform_t plat); void a31_mp_start_ap(platform_t plat); +void a83t_mp_start_ap(platform_t plat); #endif /* _AW_MP_H_ */ Index: head/sys/arm/allwinner/aw_usbphy.c =================================================================== --- head/sys/arm/allwinner/aw_usbphy.c (revision 299112) +++ head/sys/arm/allwinner/aw_usbphy.c (revision 299113) @@ -1,149 +1,150 @@ /*- * 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 USB PHY */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #define USBPHY_NUMOFF 3 static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-usb-phy", 1 }, { "allwinner,sun5i-a13-usb-phy", 1 }, { "allwinner,sun6i-a31-usb-phy", 1 }, { "allwinner,sun7i-a20-usb-phy", 1 }, + { "allwinner,sun8i-a83t-usb-phy", 1 }, { NULL, 0 } }; static int awusbphy_init(device_t dev) { char pname[20]; int error, off; regulator_t reg; hwreset_t rst; clk_t clk; /* Enable clocks */ for (off = 0; clk_get_by_ofw_index(dev, off, &clk) == 0; off++) { error = clk_enable(clk); if (error != 0) { device_printf(dev, "couldn't enable clock %s\n", clk_get_name(clk)); return (error); } } /* De-assert resets */ for (off = 0; hwreset_get_by_ofw_idx(dev, off, &rst) == 0; off++) { error = hwreset_deassert(rst); if (error != 0) { device_printf(dev, "couldn't de-assert reset %d\n", off); return (error); } } /* Enable regulator(s) */ for (off = 0; off < USBPHY_NUMOFF; off++) { snprintf(pname, sizeof(pname), "usb%d_vbus-supply", off); if (regulator_get_by_ofw_property(dev, pname, ®) != 0) continue; error = regulator_enable(reg); if (error != 0) { device_printf(dev, "couldn't enable regulator %s\n", pname); return (error); } } return (0); } static int awusbphy_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 USB PHY"); return (BUS_PROBE_DEFAULT); } static int awusbphy_attach(device_t dev) { int error; error = awusbphy_init(dev); if (error) device_printf(dev, "failed to initialize USB PHY, error %d\n", error); return (error); } static device_method_t awusbphy_methods[] = { /* Device interface */ DEVMETHOD(device_probe, awusbphy_probe), DEVMETHOD(device_attach, awusbphy_attach), DEVMETHOD_END }; static driver_t awusbphy_driver = { "awusbphy", awusbphy_methods, 0, }; static devclass_t awusbphy_devclass; DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, awusbphy_devclass, 0, 0); MODULE_VERSION(awusbphy, 1); Index: head/sys/arm/allwinner/clk/aw_ahbclk.c =================================================================== --- head/sys/arm/allwinner/clk/aw_ahbclk.c (revision 299112) +++ head/sys/arm/allwinner/clk/aw_ahbclk.c (revision 299113) @@ -1,308 +1,380 @@ /*- * 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 AHB clock */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "clkdev_if.h" #define A10_AHB_CLK_DIV_RATIO (0x3 << 4) #define A10_AHB_CLK_DIV_RATIO_SHIFT 4 #define A13_AHB_CLK_SRC_SEL (0x3 << 6) #define A13_AHB_CLK_SRC_SEL_MAX 3 #define A13_AHB_CLK_SRC_SEL_SHIFT 6 #define A31_AHB1_PRE_DIV (0x3 << 6) #define A31_AHB1_PRE_DIV_SHIFT 6 #define A31_AHB1_CLK_SRC_SEL (0x3 << 12) #define A31_AHB1_CLK_SRC_SEL_PLL6 3 #define A31_AHB1_CLK_SRC_SEL_MAX 3 #define A31_AHB1_CLK_SRC_SEL_SHIFT 12 +#define A83T_AHB1_CLK_SRC_SEL (0x3 << 12) +#define A83T_AHB1_CLK_SRC_SEL_ISPLL(x) ((x) & 0x2) +#define A83T_AHB1_CLK_SRC_SEL_MAX 3 +#define A83T_AHB1_CLK_SRC_SEL_SHIFT 12 +#define A83T_AHB1_PRE_DIV (0x3 << 6) +#define A83T_AHB1_PRE_DIV_SHIFT 6 +#define A83T_AHB1_CLK_DIV_RATIO (0x3 << 4) +#define A83T_AHB1_CLK_DIV_RATIO_SHIFT 4 + +#define H3_AHB2_CLK_CFG (0x3 << 0) +#define H3_AHB2_CLK_CFG_SHIFT 0 +#define H3_AHB2_CLK_CFG_AHB1 0 +#define H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2 1 +#define H3_AHB2_CLK_CFG_MAX 1 + enum aw_ahbclk_type { AW_A10_AHB = 1, AW_A13_AHB, AW_A31_AHB1, + AW_A83T_AHB1, + AW_H3_AHB2, }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-ahb-clk", AW_A10_AHB }, { "allwinner,sun5i-a13-ahb-clk", AW_A13_AHB }, { "allwinner,sun6i-a31-ahb1-clk", AW_A31_AHB1 }, + { "allwinner,sun8i-a83t-ahb1-clk", AW_A83T_AHB1 }, + { "allwinner,sun8i-h3-ahb2-clk", AW_H3_AHB2 }, { NULL, 0 } }; struct aw_ahbclk_sc { device_t clkdev; bus_addr_t reg; enum aw_ahbclk_type type; }; #define AHBCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) #define AHBCLK_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 aw_ahbclk_init(struct clknode *clk, device_t dev) { struct aw_ahbclk_sc *sc; uint32_t val, index; sc = clknode_get_softc(clk); switch (sc->type) { case AW_A10_AHB: index = 0; break; case AW_A13_AHB: DEVICE_LOCK(sc); AHBCLK_READ(sc, &val); DEVICE_UNLOCK(sc); index = (val & A13_AHB_CLK_SRC_SEL) >> A13_AHB_CLK_SRC_SEL_SHIFT; break; case AW_A31_AHB1: DEVICE_LOCK(sc); AHBCLK_READ(sc, &val); DEVICE_UNLOCK(sc); index = (val & A31_AHB1_CLK_SRC_SEL) >> A31_AHB1_CLK_SRC_SEL_SHIFT; break; + case AW_A83T_AHB1: + DEVICE_LOCK(sc); + AHBCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + index = (val & A83T_AHB1_CLK_SRC_SEL) >> + A83T_AHB1_CLK_SRC_SEL_SHIFT; + break; + case AW_H3_AHB2: + DEVICE_LOCK(sc); + AHBCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + index = (val & H3_AHB2_CLK_CFG) >> H3_AHB2_CLK_CFG_SHIFT; + break; default: return (ENXIO); } clknode_init_parent_idx(clk, index); return (0); } static int aw_ahbclk_recalc_freq(struct clknode *clk, uint64_t *freq) { struct aw_ahbclk_sc *sc; uint32_t val, src_sel, div, pre_div; sc = clknode_get_softc(clk); DEVICE_LOCK(sc); AHBCLK_READ(sc, &val); DEVICE_UNLOCK(sc); - div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >> - A10_AHB_CLK_DIV_RATIO_SHIFT); - switch (sc->type) { case AW_A31_AHB1: + div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >> + A10_AHB_CLK_DIV_RATIO_SHIFT); src_sel = (val & A31_AHB1_CLK_SRC_SEL) >> A31_AHB1_CLK_SRC_SEL_SHIFT; if (src_sel == A31_AHB1_CLK_SRC_SEL_PLL6) pre_div = ((val & A31_AHB1_PRE_DIV) >> A31_AHB1_PRE_DIV_SHIFT) + 1; else pre_div = 1; break; + case AW_A83T_AHB1: + div = 1 << ((val & A83T_AHB1_CLK_DIV_RATIO) >> + A83T_AHB1_CLK_DIV_RATIO_SHIFT); + src_sel = (val & A83T_AHB1_CLK_SRC_SEL) >> + A83T_AHB1_CLK_SRC_SEL_SHIFT; + if (A83T_AHB1_CLK_SRC_SEL_ISPLL(src_sel)) + pre_div = ((val & A83T_AHB1_PRE_DIV) >> + A83T_AHB1_PRE_DIV_SHIFT) + 1; + else + pre_div = 1; + break; + case AW_H3_AHB2: + src_sel = (val & H3_AHB2_CLK_CFG) >> H3_AHB2_CLK_CFG_SHIFT; + if (src_sel == H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2) + div = 2; + else + div = 1; + pre_div = 1; + break; default: + div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >> + A10_AHB_CLK_DIV_RATIO_SHIFT); pre_div = 1; break; } *freq = *freq / pre_div / div; return (0); } static int aw_ahbclk_set_mux(struct clknode *clk, int index) { struct aw_ahbclk_sc *sc; uint32_t val; sc = clknode_get_softc(clk); switch (sc->type) { case AW_A10_AHB: if (index != 0) return (ERANGE); break; case AW_A13_AHB: if (index < 0 || index > A13_AHB_CLK_SRC_SEL_MAX) return (ERANGE); DEVICE_LOCK(sc); AHBCLK_READ(sc, &val); val &= ~A13_AHB_CLK_SRC_SEL; val |= (index << A13_AHB_CLK_SRC_SEL_SHIFT); + AHBCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + break; + case AW_A83T_AHB1: + if (index < 0 || index > A83T_AHB1_CLK_SRC_SEL_MAX) + return (ERANGE); + DEVICE_LOCK(sc); + AHBCLK_READ(sc, &val); + val &= ~A83T_AHB1_CLK_SRC_SEL; + val |= (index << A83T_AHB1_CLK_SRC_SEL_SHIFT); + AHBCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + break; + case AW_H3_AHB2: + if (index < 0 || index > H3_AHB2_CLK_CFG) + return (ERANGE); + DEVICE_LOCK(sc); + AHBCLK_READ(sc, &val); + val &= ~H3_AHB2_CLK_CFG; + val |= (index << H3_AHB2_CLK_CFG_SHIFT); AHBCLK_WRITE(sc, val); DEVICE_UNLOCK(sc); break; default: return (ENXIO); } return (0); } static clknode_method_t aw_ahbclk_clknode_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, aw_ahbclk_init), CLKNODEMETHOD(clknode_recalc_freq, aw_ahbclk_recalc_freq), CLKNODEMETHOD(clknode_set_mux, aw_ahbclk_set_mux), CLKNODEMETHOD_END }; DEFINE_CLASS_1(aw_ahbclk_clknode, aw_ahbclk_clknode_class, aw_ahbclk_clknode_methods, sizeof(struct aw_ahbclk_sc), clknode_class); static int aw_ahbclk_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 AHB Clock"); return (BUS_PROBE_DEFAULT); } static int aw_ahbclk_attach(device_t dev) { struct clknode_init_def def; struct aw_ahbclk_sc *sc; struct clkdom *clkdom; struct clknode *clk; clk_t clk_parent; bus_addr_t paddr; bus_size_t psize; phandle_t node; int error, ncells, i; node = ofw_bus_get_node(dev); if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { device_printf(dev, "cannot parse 'reg' property\n"); return (ENXIO); } error = ofw_bus_parse_xref_list_get_length(node, "clocks", "#clock-cells", &ncells); if (error != 0) { device_printf(dev, "cannot get clock count\n"); return (error); } clkdom = clkdom_create(dev); memset(&def, 0, sizeof(def)); def.id = 1; def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); for (i = 0; i < ncells; i++) { error = clk_get_by_ofw_index(dev, i, &clk_parent); if (error != 0) { device_printf(dev, "cannot get clock %d\n", i); goto fail; } def.parent_names[i] = clk_get_name(clk_parent); clk_release(clk_parent); } def.parent_cnt = ncells; error = clk_parse_ofw_clk_name(dev, node, &def.name); if (error != 0) { device_printf(dev, "cannot parse clock name\n"); error = ENXIO; goto fail; } clk = clknode_create(clkdom, &aw_ahbclk_clknode_class, &def); if (clk == NULL) { device_printf(dev, "cannot create clknode\n"); error = ENXIO; goto fail; } sc = clknode_get_softc(clk); sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; sc->reg = paddr; sc->clkdev = device_get_parent(dev); clknode_register(clkdom, clk); 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_ahbclk_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_ahbclk_probe), DEVMETHOD(device_attach, aw_ahbclk_attach), DEVMETHOD_END }; static driver_t aw_ahbclk_driver = { "aw_ahbclk", aw_ahbclk_methods, 0 }; static devclass_t aw_ahbclk_devclass; EARLY_DRIVER_MODULE(aw_ahbclk, simplebus, aw_ahbclk_driver, aw_ahbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Index: head/sys/arm/allwinner/clk/aw_apbclk.c =================================================================== --- head/sys/arm/allwinner/clk/aw_apbclk.c (revision 299112) +++ head/sys/arm/allwinner/clk/aw_apbclk.c (revision 299113) @@ -1,283 +1,307 @@ /*- * 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 APB clock */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "clkdev_if.h" -#define APB0_CLK_RATIO (0x3 << 8) -#define APB0_CLK_RATIO_SHIFT 8 -#define APB1_CLK_SRC_SEL (0x3 << 24) -#define APB1_CLK_SRC_SEL_SHIFT 24 -#define APB1_CLK_SRC_SEL_MAX 0x3 -#define APB1_CLK_RAT_N (0x3 << 16) -#define APB1_CLK_RAT_N_SHIFT 16 -#define APB1_CLK_RAT_M (0x1f << 0) -#define APB1_CLK_RAT_M_SHIFT 0 +#define A10_APB0_CLK_RATIO (0x3 << 8) +#define A10_APB0_CLK_RATIO_SHIFT 8 +#define A10_APB1_CLK_SRC_SEL (0x3 << 24) +#define A10_APB1_CLK_SRC_SEL_SHIFT 24 +#define A10_APB1_CLK_SRC_SEL_MAX 0x3 +#define A10_APB1_CLK_RAT_N (0x3 << 16) +#define A10_APB1_CLK_RAT_N_SHIFT 16 +#define A10_APB1_CLK_RAT_M (0x1f << 0) +#define A10_APB1_CLK_RAT_M_SHIFT 0 +#define A23_APB0_CLK_RATIO (0x3 << 0) +#define A23_APB0_CLK_RATIO_SHIFT 0 +#define A83T_APB1_CLK_RATIO (0x3 << 8) +#define A83T_APB1_CLK_RATIO_SHIFT 8 enum aw_apbclk_type { AW_A10_APB0 = 1, AW_A10_APB1, + AW_A23_APB0, + AW_A83T_APB1, }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-apb0-clk", AW_A10_APB0 }, { "allwinner,sun4i-a10-apb1-clk", AW_A10_APB1 }, + { "allwinner,sun8i-a23-apb0-clk", AW_A23_APB0 }, + { "allwinner,sun8i-a83t-apb1-clk", AW_A83T_APB1 }, { NULL, 0 } }; struct aw_apbclk_sc { device_t clkdev; bus_addr_t reg; enum aw_apbclk_type type; }; #define APBCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) #define APBCLK_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 aw_apbclk_init(struct clknode *clk, device_t dev) { struct aw_apbclk_sc *sc; uint32_t val, index; sc = clknode_get_softc(clk); switch (sc->type) { case AW_A10_APB0: + case AW_A23_APB0: + case AW_A83T_APB1: index = 0; break; case AW_A10_APB1: DEVICE_LOCK(sc); APBCLK_READ(sc, &val); DEVICE_UNLOCK(sc); - index = (val & APB1_CLK_SRC_SEL) >> APB1_CLK_SRC_SEL_SHIFT; + index = (val & A10_APB1_CLK_SRC_SEL) >> + A10_APB1_CLK_SRC_SEL_SHIFT; break; default: return (ENXIO); } clknode_init_parent_idx(clk, index); return (0); } static int aw_apbclk_recalc_freq(struct clknode *clk, uint64_t *freq) { struct aw_apbclk_sc *sc; uint32_t val, div, m, n; sc = clknode_get_softc(clk); DEVICE_LOCK(sc); APBCLK_READ(sc, &val); DEVICE_UNLOCK(sc); switch (sc->type) { case AW_A10_APB0: - div = 1 << ((val & APB0_CLK_RATIO) >> APB0_CLK_RATIO_SHIFT); + div = 1 << ((val & A10_APB0_CLK_RATIO) >> + A10_APB0_CLK_RATIO_SHIFT); if (div == 1) div = 2; *freq = *freq / div; break; case AW_A10_APB1: - n = 1 << ((val & APB1_CLK_RAT_N) >> APB1_CLK_RAT_N_SHIFT); - m = ((val & APB1_CLK_RAT_N) >> APB1_CLK_RAT_M_SHIFT) + 1; + n = 1 << ((val & A10_APB1_CLK_RAT_N) >> + A10_APB1_CLK_RAT_N_SHIFT); + m = ((val & A10_APB1_CLK_RAT_N) >> + A10_APB1_CLK_RAT_M_SHIFT) + 1; *freq = *freq / n / m; break; + case AW_A23_APB0: + div = 1 << ((val & A23_APB0_CLK_RATIO) >> + A23_APB0_CLK_RATIO_SHIFT); + *freq = *freq / div; + break; + case AW_A83T_APB1: + div = ((val & A83T_APB1_CLK_RATIO) >> + A83T_APB1_CLK_RATIO_SHIFT) + 1; + *freq = *freq / div; + break; default: return (ENXIO); } return (0); } static int aw_apbclk_set_mux(struct clknode *clk, int index) { struct aw_apbclk_sc *sc; uint32_t val; sc = clknode_get_softc(clk); if (sc->type != AW_A10_APB1) return (ENXIO); - if (index < 0 || index > APB1_CLK_SRC_SEL_MAX) + if (index < 0 || index > A10_APB1_CLK_SRC_SEL_MAX) return (ERANGE); DEVICE_LOCK(sc); APBCLK_READ(sc, &val); - val &= ~APB1_CLK_SRC_SEL; - val |= (index << APB1_CLK_SRC_SEL_SHIFT); + val &= ~A10_APB1_CLK_SRC_SEL; + val |= (index << A10_APB1_CLK_SRC_SEL_SHIFT); APBCLK_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static clknode_method_t aw_apbclk_clknode_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, aw_apbclk_init), CLKNODEMETHOD(clknode_recalc_freq, aw_apbclk_recalc_freq), CLKNODEMETHOD(clknode_set_mux, aw_apbclk_set_mux), CLKNODEMETHOD_END }; DEFINE_CLASS_1(aw_apbclk_clknode, aw_apbclk_clknode_class, aw_apbclk_clknode_methods, sizeof(struct aw_apbclk_sc), clknode_class); static int aw_apbclk_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 APB Clock"); return (BUS_PROBE_DEFAULT); } static int aw_apbclk_attach(device_t dev) { struct clknode_init_def def; struct aw_apbclk_sc *sc; struct clkdom *clkdom; struct clknode *clk; clk_t clk_parent; bus_addr_t paddr; bus_size_t psize; phandle_t node; int error, ncells, i; node = ofw_bus_get_node(dev); if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { device_printf(dev, "cannot parse 'reg' property\n"); return (ENXIO); } error = ofw_bus_parse_xref_list_get_length(node, "clocks", "#clock-cells", &ncells); if (error != 0) { device_printf(dev, "cannot get clock count\n"); return (error); } clkdom = clkdom_create(dev); memset(&def, 0, sizeof(def)); error = clk_parse_ofw_clk_name(dev, node, &def.name); if (error != 0) { device_printf(dev, "cannot parse clock name\n"); error = ENXIO; goto fail; } def.id = 1; def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); for (i = 0; i < ncells; i++) { error = clk_get_by_ofw_index(dev, i, &clk_parent); if (error != 0) { device_printf(dev, "cannot get clock %d\n", i); goto fail; } def.parent_names[i] = clk_get_name(clk_parent); clk_release(clk_parent); } def.parent_cnt = ncells; clk = clknode_create(clkdom, &aw_apbclk_clknode_class, &def); if (clk == NULL) { device_printf(dev, "cannot create clknode\n"); error = ENXIO; goto fail; } sc = clknode_get_softc(clk); sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; sc->reg = paddr; sc->clkdev = device_get_parent(dev); clknode_register(clkdom, clk); 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_apbclk_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_apbclk_probe), DEVMETHOD(device_attach, aw_apbclk_attach), DEVMETHOD_END }; static driver_t aw_apbclk_driver = { "aw_apbclk", aw_apbclk_methods, 0 }; static devclass_t aw_apbclk_devclass; EARLY_DRIVER_MODULE(aw_apbclk, simplebus, aw_apbclk_driver, aw_apbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Index: head/sys/arm/allwinner/clk/aw_cpusclk.c =================================================================== --- head/sys/arm/allwinner/clk/aw_cpusclk.c (nonexistent) +++ head/sys/arm/allwinner/clk/aw_cpusclk.c (revision 299113) @@ -0,0 +1,320 @@ +/*- + * 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 CPUS clock + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "clkdev_if.h" + +#define A80_CPUS_CLK_SRC_SEL (0x3 << 16) +#define A80_CPUS_CLK_SRC_SEL_SHIFT 16 +#define A80_CPUS_CLK_SRC_SEL_X32KI 0 +#define A80_CPUS_CLK_SRC_SEL_OSC24M 1 +#define A80_CPUS_CLK_SRC_SEL_PLL_PERIPH 2 +#define A80_CPUS_CLK_SRC_SEL_PLL_AUDIO 3 +#define A80_CPUS_POST_DIV (0x1f << 8) +#define A80_CPUS_POST_DIV_SHIFT 8 +#define A80_CPUS_CLK_RATIO (0x3 << 4) +#define A80_CPUS_CLK_RATIO_SHIFT 4 + +#define A83T_CPUS_CLK_SRC_SEL (0x3 << 16) +#define A83T_CPUS_CLK_SRC_SEL_SHIFT 16 +#define A83T_CPUS_CLK_SRC_SEL_X32KI 0 +#define A83T_CPUS_CLK_SRC_SEL_OSC24M 1 +#define A83T_CPUS_CLK_SRC_SEL_PLL_PERIPH 2 +#define A83T_CPUS_CLK_SRC_SEL_INTERNAL_OSC 3 +#define A83T_CPUS_POST_DIV (0x1f << 8) +#define A83T_CPUS_POST_DIV_SHIFT 8 +#define A83T_CPUS_CLK_RATIO (0x3 << 4) +#define A83T_CPUS_CLK_RATIO_SHIFT 4 + +enum aw_cpusclk_type { + AW_A80_CPUS = 1, + AW_A83T_CPUS, +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun9i-a80-cpus-clk", AW_A80_CPUS }, + { "allwinner,sun8i-a83t-cpus-clk", AW_A83T_CPUS }, + { NULL, 0 } +}; + +struct aw_cpusclk_sc { + device_t clkdev; + bus_addr_t reg; + enum aw_cpusclk_type type; +}; + +#define CPUSCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define CPUSCLK_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 +aw_cpusclk_init(struct clknode *clk, device_t dev) +{ + struct aw_cpusclk_sc *sc; + uint32_t val, mask, shift, index; + + sc = clknode_get_softc(clk); + + switch (sc->type) { + case AW_A80_CPUS: + mask = A80_CPUS_CLK_SRC_SEL; + shift = A80_CPUS_CLK_SRC_SEL_SHIFT; + break; + case AW_A83T_CPUS: + mask = A83T_CPUS_CLK_SRC_SEL; + shift = A83T_CPUS_CLK_SRC_SEL_SHIFT; + break; + default: + return (ENXIO); + } + + DEVICE_LOCK(sc); + CPUSCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + index = (val & mask) >> shift; + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_cpusclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_cpusclk_sc *sc; + uint32_t val, src_sel, post_div, clk_ratio; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + CPUSCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + switch (sc->type) { + case AW_A80_CPUS: + src_sel = (val & A80_CPUS_CLK_SRC_SEL) >> + A80_CPUS_CLK_SRC_SEL_SHIFT; + post_div = ((val & A80_CPUS_POST_DIV) >> + A80_CPUS_POST_DIV_SHIFT) + 1; + clk_ratio = ((val & A80_CPUS_CLK_RATIO) >> + A80_CPUS_CLK_RATIO_SHIFT) + 1; + if (src_sel == A80_CPUS_CLK_SRC_SEL_PLL_PERIPH) + *freq = *freq / post_div / clk_ratio; + else + *freq = *freq / clk_ratio; + break; + case AW_A83T_CPUS: + src_sel = (val & A83T_CPUS_CLK_SRC_SEL) >> + A83T_CPUS_CLK_SRC_SEL_SHIFT; + post_div = ((val & A83T_CPUS_POST_DIV) >> + A83T_CPUS_POST_DIV_SHIFT) + 1; + clk_ratio = 1 << ((val & A83T_CPUS_CLK_RATIO) >> + A83T_CPUS_CLK_RATIO_SHIFT); + if (src_sel == A83T_CPUS_CLK_SRC_SEL_PLL_PERIPH) + *freq = *freq / post_div / clk_ratio; + else + *freq = *freq / clk_ratio; + break; + default: + return (EINVAL); + } + + return (0); +} + +static int +aw_cpusclk_set_mux(struct clknode *clk, int index) +{ + struct aw_cpusclk_sc *sc; + uint32_t mask, shift, val; + + sc = clknode_get_softc(clk); + + switch (sc->type) { + case AW_A80_CPUS: + mask = A80_CPUS_CLK_SRC_SEL; + shift = A80_CPUS_CLK_SRC_SEL_SHIFT; + break; + case AW_A83T_CPUS: + mask = A83T_CPUS_CLK_SRC_SEL; + shift = A83T_CPUS_CLK_SRC_SEL_SHIFT; + break; + default: + return (ENXIO); + } + + DEVICE_LOCK(sc); + CPUSCLK_READ(sc, &val); + val &= ~mask; + val |= (index << shift); + CPUSCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static clknode_method_t aw_cpusclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_cpusclk_init), + CLKNODEMETHOD(clknode_recalc_freq, aw_cpusclk_recalc_freq), + CLKNODEMETHOD(clknode_set_mux, aw_cpusclk_set_mux), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_cpusclk_clknode, aw_cpusclk_clknode_class, + aw_cpusclk_clknode_methods, sizeof(struct aw_cpusclk_sc), clknode_class); + +static int +aw_cpusclk_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 CPUS Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_cpusclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_cpusclk_sc *sc; + struct clkdom *clkdom; + struct clknode *clk; + clk_t clk_parent; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node; + int error, ncells, i; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + error = ofw_bus_parse_xref_list_get_length(node, "clocks", + "#clock-cells", &ncells); + if (error != 0) { + device_printf(dev, "cannot get clock count\n"); + return (error); + } + + clkdom = clkdom_create(dev); + + memset(&def, 0, sizeof(def)); + def.id = 1; + def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, + M_WAITOK); + for (i = 0; i < ncells; i++) { + error = clk_get_by_ofw_index(dev, i, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot get clock %d\n", i); + goto fail; + } + def.parent_names[i] = clk_get_name(clk_parent); + clk_release(clk_parent); + } + def.parent_cnt = ncells; + + error = clk_parse_ofw_clk_name(dev, node, &def.name); + if (error != 0) { + device_printf(dev, "cannot parse clock name\n"); + error = ENXIO; + goto fail; + } + + clk = clknode_create(clkdom, &aw_cpusclk_clknode_class, &def); + if (clk == NULL) { + device_printf(dev, "cannot create clknode\n"); + error = ENXIO; + goto fail; + } + sc = clknode_get_softc(clk); + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + sc->reg = paddr; + sc->clkdev = device_get_parent(dev); + + clknode_register(clkdom, clk); + + 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_cpusclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_cpusclk_probe), + DEVMETHOD(device_attach, aw_cpusclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_cpusclk_driver = { + "aw_cpusclk", + aw_cpusclk_methods, + 0 +}; + +static devclass_t aw_cpusclk_devclass; + +EARLY_DRIVER_MODULE(aw_cpusclk, simplebus, aw_cpusclk_driver, + aw_cpusclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: head/sys/arm/allwinner/clk/aw_cpusclk.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/clk/aw_gate.c =================================================================== --- head/sys/arm/allwinner/clk/aw_gate.c (revision 299112) +++ head/sys/arm/allwinner/clk/aw_gate.c (revision 299113) @@ -1,198 +1,206 @@ /*- * 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 clock gates */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #define GATE_OFFSET(index) ((index / 32) * 4) #define GATE_SHIFT(index) (index % 32) static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-dram-gates-clk", (uintptr_t)"Allwinner DRAM Clock Gates" }, { "allwinner,sun4i-a10-ahb-gates-clk", (uintptr_t)"Allwinner AHB Clock Gates" }, { "allwinner,sun4i-a10-apb0-gates-clk", (uintptr_t)"Allwinner APB0 Clock Gates" }, { "allwinner,sun4i-a10-apb1-gates-clk", (uintptr_t)"Allwinner APB1 Clock Gates" }, { "allwinner,sun7i-a20-ahb-gates-clk", (uintptr_t)"Allwinner AHB Clock Gates" }, { "allwinner,sun7i-a20-apb0-gates-clk", (uintptr_t)"Allwinner APB0 Clock Gates" }, { "allwinner,sun7i-a20-apb1-gates-clk", (uintptr_t)"Allwinner APB1 Clock Gates" }, { "allwinner,sun6i-a31-ahb1-gates-clk", (uintptr_t)"Allwinner AHB1 Clock Gates" }, { "allwinner,sun6i-a31-apb0-gates-clk", (uintptr_t)"Allwinner APB0 Clock Gates" }, { "allwinner,sun6i-a31-apb1-gates-clk", (uintptr_t)"Allwinner APB1 Clock Gates" }, { "allwinner,sun6i-a31-apb2-gates-clk", (uintptr_t)"Allwinner APB2 Clock Gates" }, + { "allwinner,sun8i-a83t-bus-gates-clk", + (uintptr_t)"Allwinner Bus Clock Gates" }, + { "allwinner,sun8i-a83t-apb0-gates-clk", + (uintptr_t)"Allwinner APB0 Clock Gates" }, + + { "allwinner,sun9i-a80-apbs-gates-clk", + (uintptr_t)"Allwinner APBS Clock Gates" }, + { NULL, 0 } }; static int aw_gate_create(device_t dev, bus_addr_t paddr, struct clkdom *clkdom, const char *pclkname, const char *clkname, int index) { const char *parent_names[1] = { pclkname }; struct clk_gate_def def; memset(&def, 0, sizeof(def)); def.clkdef.id = index; def.clkdef.name = clkname; def.clkdef.parent_names = parent_names; def.clkdef.parent_cnt = 1; def.offset = paddr + GATE_OFFSET(index); def.shift = GATE_SHIFT(index); def.mask = 1; def.on_value = 1; def.off_value = 0; return (clknode_gate_register(clkdom, &def)); } static int aw_gate_probe(device_t dev) { const char *d; if (!ofw_bus_status_okay(dev)) return (ENXIO); d = (const char *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (d == NULL) return (ENXIO); device_set_desc(dev, d); return (BUS_PROBE_DEFAULT); } static int aw_gate_attach(device_t dev) { struct clkdom *clkdom; const char **names; int index, nout, error; uint32_t *indices; clk_t clk_parent; bus_addr_t paddr; bus_size_t psize; phandle_t node; node = ofw_bus_get_node(dev); indices = NULL; if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { device_printf(dev, "cannot 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 (indices == NULL) { device_printf(dev, "no clock-indices property\n"); error = ENXIO; goto fail; } error = clk_get_by_ofw_index(dev, 0, &clk_parent); if (error != 0) { device_printf(dev, "cannot parse clock parent\n"); return (ENXIO); } for (index = 0; index < nout; index++) { error = aw_gate_create(dev, paddr, clkdom, clk_get_name(clk_parent), names[index], indices[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_gate_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_gate_probe), DEVMETHOD(device_attach, aw_gate_attach), DEVMETHOD_END }; static driver_t aw_gate_driver = { "aw_gate", aw_gate_methods, 0 }; static devclass_t aw_gate_devclass; EARLY_DRIVER_MODULE(aw_gate, simplebus, aw_gate_driver, aw_gate_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Index: head/sys/arm/allwinner/clk/aw_gmacclk.c =================================================================== --- head/sys/arm/allwinner/clk/aw_gmacclk.c (revision 299112) +++ head/sys/arm/allwinner/clk/aw_gmacclk.c (revision 299113) @@ -1,259 +1,302 @@ /*- * 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 GMAC clock */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "clkdev_if.h" #define GMAC_CLK_PIT (0x1 << 2) #define GMAC_CLK_PIT_SHIFT 2 #define GMAC_CLK_PIT_MII 0 #define GMAC_CLK_PIT_RGMII 1 #define GMAC_CLK_SRC (0x3 << 0) #define GMAC_CLK_SRC_SHIFT 0 #define GMAC_CLK_SRC_MII 0 #define GMAC_CLK_SRC_EXT_RGMII 1 #define GMAC_CLK_SRC_RGMII 2 +#define EMAC_TXC_DIV_CFG (1 << 15) +#define EMAC_TXC_DIV_CFG_SHIFT 15 +#define EMAC_TXC_DIV_CFG_125MHZ 0 +#define EMAC_TXC_DIV_CFG_25MHZ 1 +#define EMAC_PHY_SELECT (1 << 16) +#define EMAC_PHY_SELECT_SHIFT 16 +#define EMAC_PHY_SELECT_INT 0 +#define EMAC_PHY_SELECT_EXT 1 +#define EMAC_ETXDC (0x7 << 10) +#define EMAC_ETXDC_SHIFT 10 +#define EMAC_ERXDC (0x1f << 5) +#define EMAC_ERXDC_SHIFT 5 + #define CLK_IDX_MII 0 #define CLK_IDX_RGMII 1 #define CLK_IDX_COUNT 2 +enum aw_gmacclk_type { + GMACCLK_A20 = 1, + GMACCLK_A83T, +}; + static struct ofw_compat_data compat_data[] = { - { "allwinner,sun7i-a20-gmac-clk", 1 }, + { "allwinner,sun7i-a20-gmac-clk", GMACCLK_A20 }, + { "allwinner,sun8i-a83t-emac-clk", GMACCLK_A83T }, { NULL, 0 } }; struct aw_gmacclk_sc { device_t clkdev; bus_addr_t reg; + enum aw_gmacclk_type type; + + int rx_delay; + int tx_delay; }; #define GMACCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) #define GMACCLK_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 aw_gmacclk_init(struct clknode *clk, device_t dev) { struct aw_gmacclk_sc *sc; uint32_t val, index; sc = clknode_get_softc(clk); DEVICE_LOCK(sc); GMACCLK_READ(sc, &val); DEVICE_UNLOCK(sc); switch ((val & GMAC_CLK_SRC) >> GMAC_CLK_SRC_SHIFT) { case GMAC_CLK_SRC_MII: index = CLK_IDX_MII; break; case GMAC_CLK_SRC_RGMII: index = CLK_IDX_RGMII; break; default: return (ENXIO); } clknode_init_parent_idx(clk, index); return (0); } static int aw_gmacclk_set_mux(struct clknode *clk, int index) { struct aw_gmacclk_sc *sc; - uint32_t val, clk_src, pit; + uint32_t val, clk_src, pit, txc_div; int error; sc = clknode_get_softc(clk); error = 0; switch (index) { case CLK_IDX_MII: clk_src = GMAC_CLK_SRC_MII; pit = GMAC_CLK_PIT_MII; + txc_div = EMAC_TXC_DIV_CFG_25MHZ; break; case CLK_IDX_RGMII: clk_src = GMAC_CLK_SRC_RGMII; pit = GMAC_CLK_PIT_RGMII; + txc_div = EMAC_TXC_DIV_CFG_125MHZ; break; default: return (ENXIO); } DEVICE_LOCK(sc); GMACCLK_READ(sc, &val); val &= ~(GMAC_CLK_SRC | GMAC_CLK_PIT); val |= (clk_src << GMAC_CLK_SRC_SHIFT); val |= (pit << GMAC_CLK_PIT_SHIFT); + if (sc->type == GMACCLK_A83T) { + val &= ~EMAC_TXC_DIV_CFG; + val |= (txc_div << EMAC_TXC_DIV_CFG_SHIFT); + val &= ~EMAC_PHY_SELECT; + val |= (EMAC_PHY_SELECT_EXT << EMAC_PHY_SELECT_SHIFT); + if (sc->tx_delay >= 0) { + val &= ~EMAC_ETXDC; + val |= (sc->tx_delay << EMAC_ETXDC_SHIFT); + } + if (sc->rx_delay >= 0) { + val &= ~EMAC_ERXDC; + val |= (sc->rx_delay << EMAC_ERXDC_SHIFT); + } + } GMACCLK_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static clknode_method_t aw_gmacclk_clknode_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, aw_gmacclk_init), CLKNODEMETHOD(clknode_set_mux, aw_gmacclk_set_mux), CLKNODEMETHOD_END }; DEFINE_CLASS_1(aw_gmacclk_clknode, aw_gmacclk_clknode_class, aw_gmacclk_clknode_methods, sizeof(struct aw_gmacclk_sc), clknode_class); static int aw_gmacclk_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 Module Clock"); + device_set_desc(dev, "Allwinner GMAC Clock"); return (BUS_PROBE_DEFAULT); } static int aw_gmacclk_attach(device_t dev) { struct clknode_init_def def; struct aw_gmacclk_sc *sc; struct clkdom *clkdom; struct clknode *clk; clk_t clk_parent; bus_addr_t paddr; bus_size_t psize; phandle_t node; int error, ncells, i; node = ofw_bus_get_node(dev); if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { device_printf(dev, "cannot parse 'reg' property\n"); return (ENXIO); } error = ofw_bus_parse_xref_list_get_length(node, "clocks", "#clock-cells", &ncells); if (error != 0 || ncells != CLK_IDX_COUNT) { device_printf(dev, "couldn't find parent clocks\n"); return (ENXIO); } clkdom = clkdom_create(dev); memset(&def, 0, sizeof(def)); error = clk_parse_ofw_clk_name(dev, node, &def.name); if (error != 0) { device_printf(dev, "cannot parse clock name\n"); error = ENXIO; goto fail; } def.id = 1; def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); for (i = 0; i < ncells; i++) { error = clk_get_by_ofw_index(dev, i, &clk_parent); if (error != 0) { device_printf(dev, "cannot get clock %d\n", error); goto fail; } def.parent_names[i] = clk_get_name(clk_parent); clk_release(clk_parent); } def.parent_cnt = ncells; clk = clknode_create(clkdom, &aw_gmacclk_clknode_class, &def); if (clk == NULL) { device_printf(dev, "cannot create clknode\n"); error = ENXIO; goto fail; } sc = clknode_get_softc(clk); sc->reg = paddr; sc->clkdev = device_get_parent(dev); + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + sc->tx_delay = sc->rx_delay = -1; + OF_getencprop(node, "tx-delay", &sc->tx_delay, sizeof(sc->tx_delay)); + OF_getencprop(node, "rx-delay", &sc->rx_delay, sizeof(sc->rx_delay)); clknode_register(clkdom, clk); 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_gmacclk_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_gmacclk_probe), DEVMETHOD(device_attach, aw_gmacclk_attach), DEVMETHOD_END }; static driver_t aw_gmacclk_driver = { "aw_gmacclk", aw_gmacclk_methods, 0 }; static devclass_t aw_gmacclk_devclass; EARLY_DRIVER_MODULE(aw_gmacclk, simplebus, aw_gmacclk_driver, aw_gmacclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Index: head/sys/arm/allwinner/clk/aw_pll.c =================================================================== --- head/sys/arm/allwinner/clk/aw_pll.c (revision 299112) +++ head/sys/arm/allwinner/clk/aw_pll.c (revision 299113) @@ -1,757 +1,784 @@ /*- * 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 "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 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_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 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_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,sun6i-a31-pll1-clk", AWPLL_A31_PLL1 }, { "allwinner,sun6i-a31-pll6-clk", AWPLL_A31_PLL6 }, + { "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); free(__DECONST(char *, clkdef.parent_names), M_OFWPROP); 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, &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/clk/aw_usbclk.c =================================================================== --- head/sys/arm/allwinner/clk/aw_usbclk.c (revision 299112) +++ head/sys/arm/allwinner/clk/aw_usbclk.c (revision 299113) @@ -1,246 +1,259 @@ /*- * 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 USB clocks */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "clkdev_if.h" #include "hwreset_if.h" #define A10_SCLK_GATING_USBPHY (1 << 8) #define A10_SCLK_GATING_OHCI1 (1 << 7) #define A10_SCLK_GATING_OHCI0 (1 << 6) #define USBPHY2_RST (1 << 2) #define USBPHY1_RST (1 << 1) #define USBPHY0_RST (1 << 0) enum aw_usbclk_type { AW_A10_USBCLK = 1, AW_A31_USBCLK, + AW_A83T_USBCLK, }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-usb-clk", AW_A10_USBCLK }, { "allwinner,sun6i-a31-usb-clk", AW_A31_USBCLK }, + { "allwinner,sun8i-a83t-usb-clk", AW_A83T_USBCLK }, { NULL, 0 } }; /* Clock indices for A10, as there is no clock-indices property in the DT */ static uint32_t aw_usbclk_indices_a10[] = { 6, 7, 8 }; struct aw_usbclk_softc { bus_addr_t reg; }; static int aw_usbclk_hwreset_assert(device_t dev, intptr_t id, bool value) { struct aw_usbclk_softc *sc; uint32_t mask; device_t pdev; int error; sc = device_get_softc(dev); pdev = device_get_parent(dev); mask = USBPHY0_RST << id; CLKDEV_DEVICE_LOCK(pdev); error = CLKDEV_MODIFY_4(pdev, sc->reg, mask, value ? 0 : mask); CLKDEV_DEVICE_UNLOCK(pdev); return (error); } static int aw_usbclk_hwreset_is_asserted(device_t dev, intptr_t id, bool *value) { struct aw_usbclk_softc *sc; uint32_t mask, val; device_t pdev; int error; sc = device_get_softc(dev); pdev = device_get_parent(dev); mask = USBPHY0_RST << id; CLKDEV_DEVICE_LOCK(pdev); error = CLKDEV_READ_4(pdev, sc->reg, &val); CLKDEV_DEVICE_UNLOCK(pdev); if (error) return (error); *value = (val & mask) != 0 ? false : true; return (0); } static int aw_usbclk_create(device_t dev, bus_addr_t paddr, struct clkdom *clkdom, const char *pclkname, const char *clkname, int index) { const char *parent_names[1] = { pclkname }; struct clk_gate_def def; memset(&def, 0, sizeof(def)); def.clkdef.id = index; def.clkdef.name = clkname; def.clkdef.parent_names = parent_names; def.clkdef.parent_cnt = 1; def.offset = paddr; def.shift = index; def.mask = 1; def.on_value = 1; def.off_value = 0; return (clknode_gate_register(clkdom, &def)); } static int aw_usbclk_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 USB Clocks"); return (BUS_PROBE_DEFAULT); } static int aw_usbclk_attach(device_t dev) { struct aw_usbclk_softc *sc; struct clkdom *clkdom; const char **names; + const char *pname; int index, nout, error; enum aw_usbclk_type type; uint32_t *indices; - clk_t clk_parent; + clk_t clk_parent, clk_parent_pll; bus_size_t psize; phandle_t node; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); indices = NULL; type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (ofw_reg_to_paddr(node, 0, &sc->reg, &psize, NULL) != 0) { device_printf(dev, "cannot 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 (indices == NULL && type == AW_A10_USBCLK) indices = aw_usbclk_indices_a10; error = clk_get_by_ofw_index(dev, 0, &clk_parent); if (error != 0) { device_printf(dev, "cannot parse clock parent\n"); return (ENXIO); } + if (type == AW_A83T_USBCLK) { + error = clk_get_by_ofw_index(dev, 1, &clk_parent_pll); + if (error != 0) { + device_printf(dev, "cannot parse pll clock parent\n"); + return (ENXIO); + } + } for (index = 0; index < nout; index++) { - error = aw_usbclk_create(dev, sc->reg, clkdom, - clk_get_name(clk_parent), names[index], - indices != NULL ? indices[index] : index); + if (strcmp(names[index], "usb_hsic_pll") == 0) + pname = clk_get_name(clk_parent_pll); + else + pname = clk_get_name(clk_parent); + error = aw_usbclk_create(dev, sc->reg, clkdom, pname, + names[index], indices != NULL ? indices[index] : 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); hwreset_register_ofw_provider(dev); return (0); fail: return (error); } static device_method_t aw_usbclk_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_usbclk_probe), DEVMETHOD(device_attach, aw_usbclk_attach), /* Reset interface */ DEVMETHOD(hwreset_assert, aw_usbclk_hwreset_assert), DEVMETHOD(hwreset_is_asserted, aw_usbclk_hwreset_is_asserted), DEVMETHOD_END }; static driver_t aw_usbclk_driver = { "aw_usbclk", aw_usbclk_methods, sizeof(struct aw_usbclk_softc) }; static devclass_t aw_usbclk_devclass; EARLY_DRIVER_MODULE(aw_usbclk, simplebus, aw_usbclk_driver, aw_usbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Index: head/sys/arm/allwinner/files.allwinner =================================================================== --- head/sys/arm/allwinner/files.allwinner (revision 299112) +++ head/sys/arm/allwinner/files.allwinner (revision 299113) @@ -1,48 +1,49 @@ # $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_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_mp.c optional smp arm/allwinner/axp209.c optional axp209 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 #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/std.allwinner =================================================================== --- head/sys/arm/allwinner/std.allwinner (revision 299112) +++ head/sys/arm/allwinner/std.allwinner (revision 299113) @@ -1,16 +1,17 @@ # Allwinner common options #$FreeBSD$ cpu CPU_CORTEXA machine arm armv6 makeoptions CONF_CFLAGS="-march=armv7a" makeoptions KERNVIRTADDR=0xc0200000 options KERNVIRTADDR=0xc0200000 options IPI_IRQ_START=0 options IPI_IRQ_END=15 files "../allwinner/files.allwinner" files "../allwinner/a20/files.a20" files "../allwinner/a31/files.a31" +files "../allwinner/a83t/files.a83t" Index: head/sys/arm/conf/ALLWINNER =================================================================== --- head/sys/arm/conf/ALLWINNER (revision 299112) +++ head/sys/arm/conf/ALLWINNER (revision 299113) @@ -1,144 +1,146 @@ # # ALLWINNER -- Custom configuration for the Allwinner A20 and A31 ARM SoC # # For more information on this file, please read the config(5) manual page, # and/or the handbook section on Kernel Configuration Files: # # http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the ../../conf/NOTES and NOTES files. # If you are in doubt as to the purpose or necessity of a line, check first # in NOTES. # # $FreeBSD$ ident ALLWINNER include "std.armv6" include "../allwinner/std.allwinner" options INTRNG options SOC_ALLWINNER_A20 options SOC_ALLWINNER_A31 options SOC_ALLWINNER_A31S +options SOC_ALLWINNER_A83T options HZ=100 options SCHED_ULE # ULE scheduler options SMP # Enable multiple cores options PLATFORM options PLATFORM_SMP options MULTIDELAY # Debugging for use in -current makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols options ALT_BREAK_TO_DEBUGGER #options VERBOSE_SYSINIT # Enable verbose sysinit messages options KDB # Enable kernel debugger support # For minimum debugger support (stable branch) use: #options KDB_TRACE # Print a stack trace for a panic # For full debugger support use this instead: options DDB # Enable the kernel debugger options INVARIANTS # Enable calls of extra sanity checking options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS options WITNESS # Enable checks to detect deadlocks and cycles options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed #options DIAGNOSTIC # NFS root from boopt/dhcp #options BOOTP #options BOOTP_NFSROOT #options BOOTP_COMPAT #options BOOTP_NFSV3 #options BOOTP_WIRED_TO=dwc0 # EXT_RESOURCES pseudo devices options EXT_RESOURCES device clk device phy device hwreset device regulator # Interrupt controller device gic # ARM Generic Timer device generic_timer # MMC/SD/SDIO Card slot support device mmc # mmc/sd bus device mmcsd # mmc/sd flash cards # ATA controllers device ahci # AHCI-compatible SATA controllers #device ata # Legacy ATA/SATA controllers # Console and misc device uart device uart_snps device pty device snp device md device random # Entropy device # I2C support device iicbus device iic device twsi device axp209 # AXP209 Power Management Unit # GPIO device gpio device gpioled device scbus # SCSI bus (required for ATA/SCSI) device da # Direct Access (disks) device pass # Passthrough device (direct ATA/SCSI access) # USB support options USB_HOST_ALIGN=64 # Align usb buffers to cache line size. device usb options USB_DEBUG #options USB_REQ_DEBUG #options USB_VERBOSE #device uhci #device ohci device ehci device umass # Ethernet device loop device ether device mii device bpf #device emac # 10/100 integrated EMAC controller device dwc # 10/100/1000 integrated GMAC controller +device awg # 10/100/1000 integrated EMAC controller # USB ethernet support, requires miibus device miibus # Sound support device sound # Framebuffer support device vt device kbdmux device ums device ukbd device videomode device hdmi # Pinmux device fdt_pinctrl # Flattened Device Tree options FDT # Configure using FDT/DTB data makeoptions MODULES_EXTRA=dtb/allwinner Index: head/sys/conf/options.arm =================================================================== --- head/sys/conf/options.arm (revision 299112) +++ head/sys/conf/options.arm (revision 299113) @@ -1,77 +1,78 @@ #$FreeBSD$ ARMV6 opt_global.h ARM_CACHE_LOCK_ENABLE opt_global.h ARM_KERN_DIRECTMAP opt_vm.h ARM_L2_PIPT opt_global.h ARM_MANY_BOARD opt_global.h NKPT2PG opt_pmap.h ARM_WANT_TP_ADDRESS opt_global.h COUNTS_PER_SEC opt_timer.h CPU_ARM9 opt_global.h CPU_ARM9E opt_global.h CPU_ARM1176 opt_global.h CPU_CORTEXA opt_global.h CPU_KRAIT opt_global.h CPU_FA526 opt_global.h CPU_MV_PJ4B opt_global.h CPU_XSCALE_81342 opt_global.h CPU_XSCALE_IXP425 opt_global.h CPU_XSCALE_IXP435 opt_global.h CPU_XSCALE_PXA2X0 opt_global.h DEV_GIC opt_global.h DEV_PMU opt_global.h EFI opt_platform.h FLASHADDR opt_global.h GIC_DEFAULT_ICFGR_INIT opt_global.h INTRNG opt_global.h IPI_IRQ_START opt_smp.h IPI_IRQ_END opt_smp.h FREEBSD_BOOT_LOADER opt_global.h IXP4XX_FLASH_SIZE opt_global.h KERNBASE opt_global.h KERNVIRTADDR opt_global.h LINUX_BOOT_ABI opt_global.h LOADERRAMADDR opt_global.h MULTIDELAY opt_global.h PHYSADDR opt_global.h PLATFORM opt_global.h PLATFORM_SMP opt_global.h SOCDEV_PA opt_global.h SOCDEV_VA opt_global.h PV_STATS opt_pmap.h QEMU_WORKAROUNDS opt_global.h SOC_ALLWINNER_A10 opt_global.h SOC_ALLWINNER_A20 opt_global.h SOC_ALLWINNER_A31 opt_global.h SOC_ALLWINNER_A31S opt_global.h +SOC_ALLWINNER_A83T opt_global.h SOC_BCM2835 opt_global.h SOC_BCM2836 opt_global.h SOC_IMX51 opt_global.h SOC_IMX53 opt_global.h SOC_IMX6 opt_global.h SOC_MV_ARMADAXP opt_global.h SOC_MV_ARMADA38X opt_global.h SOC_MV_DISCOVERY opt_global.h SOC_MV_DOVE opt_global.h SOC_MV_FREY opt_global.h SOC_MV_KIRKWOOD opt_global.h SOC_MV_LOKIPLUS opt_global.h SOC_MV_ORION opt_global.h SOC_OMAP3 opt_global.h SOC_OMAP4 opt_global.h SOC_TI_AM335X opt_global.h SOC_TEGRA2 opt_global.h XSCALE_CACHE_READ_WRITE_ALLOCATE opt_global.h XSACLE_DISABLE_CCNT opt_timer.h VERBOSE_INIT_ARM opt_global.h VM_MAXUSER_ADDRESS opt_global.h AT91_ATE_USE_RMII opt_at91.h AT91_MACB_USE_RMII opt_at91.h AT91_MCI_ALLOW_OVERCLOCK opt_at91.h AT91_MCI_HAS_4WIRE opt_at91.h AT91_MCI_SLOT_B opt_at91.h GFB_DEBUG opt_gfb.h GFB_NO_FONT_LOADING opt_gfb.h GFB_NO_MODE_CHANGE opt_gfb.h AT91C_MAIN_CLOCK opt_at91.h VFP opt_global.h