diff options
author | gjb <gjb@FreeBSD.org> | 2016-04-11 15:24:59 +0000 |
---|---|---|
committer | gjb <gjb@FreeBSD.org> | 2016-04-11 15:24:59 +0000 |
commit | e0e3598ce13850597a66fd28d102b36881f7d610 (patch) | |
tree | f5194d1ce3fa45b67cf63080fc519fec83abc57a /sys/arm | |
parent | cbc3bd9845ba5fd58e8132f9565cfbc41433938d (diff) | |
parent | 26836fccd261358467b3d92e77ff4695af286de9 (diff) | |
download | FreeBSD-src-e0e3598ce13850597a66fd28d102b36881f7d610.zip FreeBSD-src-e0e3598ce13850597a66fd28d102b36881f7d610.tar.gz |
MFH
Sponsored by: The FreeBSD Foundation
Diffstat (limited to 'sys/arm')
58 files changed, 5667 insertions, 2111 deletions
diff --git a/sys/arm/allwinner/a10_ahci.c b/sys/arm/allwinner/a10_ahci.c index a6c5f5d..009ef5f 100644 --- a/sys/arm/allwinner/a10_ahci.c +++ b/sys/arm/allwinner/a10_ahci.c @@ -47,7 +47,7 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/ofw_bus_subr.h> #include <dev/ahci/ahci.h> -#include <arm/allwinner/a10_clk.h> +#include <dev/extres/clk/clk.h> /* * Allwinner a1x/a2x/a8x SATA attachment. This is just the AHCI register @@ -117,6 +117,8 @@ __FBSDID("$FreeBSD$"); #define AHCI_P0PHYCR 0x0078 #define AHCI_P0PHYSR 0x007C +#define PLL_FREQ 100000000 + static void inline ahci_set(struct resource *m, bus_size_t off, uint32_t set) { @@ -295,8 +297,11 @@ ahci_a10_attach(device_t dev) { int error; struct ahci_controller *ctlr; + clk_t clk_pll, clk_gate; ctlr = device_get_softc(dev); + clk_pll = clk_gate = NULL; + ctlr->quirks = AHCI_Q_NOPMP; ctlr->vendorid = 0; ctlr->deviceid = 0; @@ -307,15 +312,36 @@ ahci_a10_attach(device_t dev) &ctlr->r_rid, RF_ACTIVE))) return (ENXIO); - /* Turn on the PLL for SATA */ - a10_clk_ahci_activate(); - + /* Enable clocks */ + error = clk_get_by_ofw_index(dev, 0, &clk_pll); + if (error != 0) { + device_printf(dev, "Cannot get PLL clock\n"); + goto fail; + } + error = clk_get_by_ofw_index(dev, 1, &clk_gate); + if (error != 0) { + device_printf(dev, "Cannot get gate clock\n"); + goto fail; + } + error = clk_set_freq(clk_pll, PLL_FREQ, CLK_SET_ROUND_DOWN); + if (error != 0) { + device_printf(dev, "Cannot set PLL frequency\n"); + goto fail; + } + error = clk_enable(clk_pll); + if (error != 0) { + device_printf(dev, "Cannot enable PLL\n"); + goto fail; + } + error = clk_enable(clk_gate); + if (error != 0) { + device_printf(dev, "Cannot enable clk gate\n"); + goto fail; + } + /* Reset controller */ - if ((error = ahci_a10_ctlr_reset(dev)) != 0) { - bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, - ctlr->r_mem); - return (error); - }; + if ((error = ahci_a10_ctlr_reset(dev)) != 0) + goto fail; /* * No MSI registers on this platform. @@ -330,6 +356,14 @@ ahci_a10_attach(device_t dev) * Note: ahci_attach will release ctlr->r_mem on errors automatically */ return (ahci_attach(dev)); + +fail: + if (clk_gate != NULL) + clk_release(clk_gate); + if (clk_pll != NULL) + clk_release(clk_pll); + bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); + return (error); } static int diff --git a/sys/arm/allwinner/a10_clk.c b/sys/arm/allwinner/a10_clk.c deleted file mode 100644 index 46bd6ad..0000000 --- a/sys/arm/allwinner/a10_clk.c +++ /dev/null @@ -1,862 +0,0 @@ -/*- - * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* Simple clock driver for Allwinner A10 */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/bus.h> -#include <sys/kernel.h> -#include <sys/module.h> -#include <sys/malloc.h> -#include <sys/rman.h> -#include <machine/bus.h> - -#include <dev/ofw/openfirm.h> -#include <dev/ofw/ofw_bus_subr.h> - -#include "a10_clk.h" - -#define TCON_PLL_WORST 1000000 -#define TCON_PLL_N_MIN 1 -#define TCON_PLL_N_MAX 15 -#define TCON_PLL_M_MIN 9 -#define TCON_PLL_M_MAX 127 -#define TCON_PLLREF_SINGLE 3000 /* kHz */ -#define TCON_PLLREF_DOUBLE 6000 /* kHz */ -#define TCON_RATE_KHZ(rate_hz) ((rate_hz) / 1000) -#define TCON_RATE_HZ(rate_khz) ((rate_khz) * 1000) -#define HDMI_DEFAULT_RATE 297000000 -#define DEBE_DEFAULT_RATE 300000000 - -struct a10_ccm_softc { - struct resource *res; - bus_space_tag_t bst; - bus_space_handle_t bsh; - struct mtx mtx; - int pll6_enabled; - int ehci_cnt; - int ohci_cnt; - int usbphy_cnt; - int usb_cnt; -}; - -static struct a10_ccm_softc *a10_ccm_sc = NULL; - -static int a10_clk_usbphy_activate(struct a10_ccm_softc *sc); -static int a10_clk_usbphy_deactivate(struct a10_ccm_softc *sc); -static int a10_clk_usb_activate(struct a10_ccm_softc *sc); -static int a10_clk_usb_deactivate(struct a10_ccm_softc *sc); - -#define CCM_LOCK(sc) mtx_lock(&(sc)->mtx); -#define CCM_UNLOCK(sc) mtx_unlock(&(sc)->mtx); -#define CCM_LOCK_ASSERT(sc) mtx_assert(&(sc)->mtx, MA_OWNED) -#define ccm_read_4(sc, reg) \ - bus_space_read_4((sc)->bst, (sc)->bsh, (reg)) -#define ccm_write_4(sc, reg, val) \ - bus_space_write_4((sc)->bst, (sc)->bsh, (reg), (val)) - -static int -a10_ccm_probe(device_t dev) -{ - - if (!ofw_bus_status_okay(dev)) - return (ENXIO); - - if (ofw_bus_is_compatible(dev, "allwinner,sun4i-ccm")) { - device_set_desc(dev, "Allwinner Clock Control Module"); - return(BUS_PROBE_DEFAULT); - } - - return (ENXIO); -} - -static int -a10_ccm_attach(device_t dev) -{ - struct a10_ccm_softc *sc = device_get_softc(dev); - int rid = 0; - - if (a10_ccm_sc) - return (ENXIO); - - sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); - if (!sc->res) { - device_printf(dev, "could not allocate resource\n"); - return (ENXIO); - } - - sc->bst = rman_get_bustag(sc->res); - sc->bsh = rman_get_bushandle(sc->res); - - mtx_init(&sc->mtx, "a10_ccm", NULL, MTX_DEF); - - a10_ccm_sc = sc; - - return (0); -} - -static device_method_t a10_ccm_methods[] = { - DEVMETHOD(device_probe, a10_ccm_probe), - DEVMETHOD(device_attach, a10_ccm_attach), - { 0, 0 } -}; - -static driver_t a10_ccm_driver = { - "a10_ccm", - a10_ccm_methods, - sizeof(struct a10_ccm_softc), -}; - -static devclass_t a10_ccm_devclass; - -EARLY_DRIVER_MODULE(a10_ccm, simplebus, a10_ccm_driver, a10_ccm_devclass, 0, 0, - BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); - -int -a10_clk_ehci_activate(void) -{ - struct a10_ccm_softc *sc = a10_ccm_sc; - uint32_t reg_value; - - if (sc == NULL) - return (ENXIO); - - CCM_LOCK(sc); - - if (++sc->ehci_cnt == 1) { - /* Gating AHB clock for USB */ - reg_value = ccm_read_4(sc, CCM_AHB_GATING0); - reg_value |= CCM_AHB_GATING_EHCI0; /* AHB clock gate ehci0 */ - reg_value |= CCM_AHB_GATING_EHCI1; /* AHB clock gate ehci1 */ - ccm_write_4(sc, CCM_AHB_GATING0, reg_value); - } - - a10_clk_usb_activate(sc); - a10_clk_usbphy_activate(sc); - - CCM_UNLOCK(sc); - - return (0); -} - -int -a10_clk_ehci_deactivate(void) -{ - struct a10_ccm_softc *sc = a10_ccm_sc; - uint32_t reg_value; - - if (sc == NULL) - return (ENXIO); - - CCM_LOCK(sc); - - if (--sc->ehci_cnt == 0) { - /* Disable gating AHB clock for USB */ - reg_value = ccm_read_4(sc, CCM_AHB_GATING0); - reg_value &= ~CCM_AHB_GATING_EHCI0; /* disable AHB clock gate ehci0 */ - reg_value &= ~CCM_AHB_GATING_EHCI1; /* disable AHB clock gate ehci1 */ - ccm_write_4(sc, CCM_AHB_GATING0, reg_value); - } - - a10_clk_usb_deactivate(sc); - a10_clk_usbphy_deactivate(sc); - - CCM_UNLOCK(sc); - - return (0); -} - -int -a10_clk_ohci_activate(void) -{ - struct a10_ccm_softc *sc = a10_ccm_sc; - uint32_t reg_value; - - if (sc == NULL) - return (ENXIO); - - CCM_LOCK(sc); - - if (++sc->ohci_cnt == 1) { - /* Gating AHB clock for USB */ - reg_value = ccm_read_4(sc, CCM_AHB_GATING0); - reg_value |= CCM_AHB_GATING_OHCI0; /* AHB clock gate ohci0 */ - reg_value |= CCM_AHB_GATING_OHCI1; /* AHB clock gate ohci1 */ - ccm_write_4(sc, CCM_AHB_GATING0, reg_value); - - /* Enable clock for USB */ - reg_value = ccm_read_4(sc, CCM_USB_CLK); - reg_value |= CCM_SCLK_GATING_OHCI0; - reg_value |= CCM_SCLK_GATING_OHCI1; - ccm_write_4(sc, CCM_USB_CLK, reg_value); - } - - a10_clk_usb_activate(sc); - a10_clk_usbphy_activate(sc); - - CCM_UNLOCK(sc); - - return (0); -} - -int -a10_clk_ohci_deactivate(void) -{ - struct a10_ccm_softc *sc = a10_ccm_sc; - uint32_t reg_value; - - if (sc == NULL) - return (ENXIO); - - CCM_LOCK(sc); - - if (--sc->ohci_cnt == 0) { - /* Disable clock for USB */ - reg_value = ccm_read_4(sc, CCM_USB_CLK); - reg_value &= ~CCM_SCLK_GATING_OHCI0; - reg_value &= ~CCM_SCLK_GATING_OHCI1; - ccm_write_4(sc, CCM_USB_CLK, reg_value); - - /* Disable gating AHB clock for USB */ - reg_value = ccm_read_4(sc, CCM_AHB_GATING0); - reg_value &= ~CCM_AHB_GATING_OHCI0; /* disable AHB clock gate ohci0 */ - reg_value &= ~CCM_AHB_GATING_OHCI1; /* disable AHB clock gate ohci1 */ - ccm_write_4(sc, CCM_AHB_GATING0, reg_value); - } - - a10_clk_usb_deactivate(sc); - a10_clk_usbphy_deactivate(sc); - - CCM_UNLOCK(sc); - - return (0); -} - -static int -a10_clk_usb_activate(struct a10_ccm_softc *sc) -{ - uint32_t reg_value; - - CCM_LOCK_ASSERT(sc); - - if (++sc->usb_cnt == 1) { - /* Gating AHB clock for USB */ - reg_value = ccm_read_4(sc, CCM_AHB_GATING0); - reg_value |= CCM_AHB_GATING_USB0; /* AHB clock gate usb0 */ - ccm_write_4(sc, CCM_AHB_GATING0, reg_value); - } - - return (0); -} - -static int -a10_clk_usb_deactivate(struct a10_ccm_softc *sc) -{ - uint32_t reg_value; - - CCM_LOCK_ASSERT(sc); - - if (--sc->usb_cnt == 0) { - /* Disable gating AHB clock for USB */ - reg_value = ccm_read_4(sc, CCM_AHB_GATING0); - reg_value &= ~CCM_AHB_GATING_USB0; /* disable AHB clock gate usb0 */ - ccm_write_4(sc, CCM_AHB_GATING0, reg_value); - } - - return (0); -} - -static int -a10_clk_usbphy_activate(struct a10_ccm_softc *sc) -{ - uint32_t reg_value; - - CCM_LOCK_ASSERT(sc); - - if (++sc->usbphy_cnt == 1) { - /* Enable clock for USB */ - reg_value = ccm_read_4(sc, CCM_USB_CLK); - reg_value |= CCM_USB_PHY; /* USBPHY */ - reg_value |= CCM_USBPHY0_RESET; /* disable reset for USBPHY0 */ - reg_value |= CCM_USBPHY1_RESET; /* disable reset for USBPHY1 */ - reg_value |= CCM_USBPHY2_RESET; /* disable reset for USBPHY2 */ - ccm_write_4(sc, CCM_USB_CLK, reg_value); - } - - return (0); -} - -static int -a10_clk_usbphy_deactivate(struct a10_ccm_softc *sc) -{ - uint32_t reg_value; - - CCM_LOCK_ASSERT(sc); - - if (--sc->usbphy_cnt == 0) { - /* Disable clock for USB */ - reg_value = ccm_read_4(sc, CCM_USB_CLK); - reg_value &= ~CCM_USB_PHY; /* USBPHY */ - reg_value &= ~CCM_USBPHY0_RESET; /* reset for USBPHY0 */ - reg_value &= ~CCM_USBPHY1_RESET; /* reset for USBPHY1 */ - reg_value &= ~CCM_USBPHY2_RESET; /* reset for USBPHY2 */ - ccm_write_4(sc, CCM_USB_CLK, reg_value); - } - - return (0); -} - -int -a10_clk_emac_activate(void) -{ - struct a10_ccm_softc *sc = a10_ccm_sc; - uint32_t reg_value; - - if (sc == NULL) - return (ENXIO); - - /* Gating AHB clock for EMAC */ - reg_value = ccm_read_4(sc, CCM_AHB_GATING0); - reg_value |= CCM_AHB_GATING_EMAC; - ccm_write_4(sc, CCM_AHB_GATING0, reg_value); - - return (0); -} - -int -a10_clk_gmac_activate(phandle_t node) -{ - char *phy_type; - struct a10_ccm_softc *sc; - uint32_t reg_value; - - sc = a10_ccm_sc; - if (sc == NULL) - return (ENXIO); - - /* Gating AHB clock for GMAC */ - reg_value = ccm_read_4(sc, CCM_AHB_GATING1); - reg_value |= CCM_AHB_GATING_GMAC; - ccm_write_4(sc, CCM_AHB_GATING1, reg_value); - - /* Set GMAC mode. */ - reg_value = CCM_GMAC_CLK_MII; - if (OF_getprop_alloc(node, "phy-mode", 1, (void **)&phy_type) > 0) { - if (strcasecmp(phy_type, "rgmii") == 0) - reg_value = CCM_GMAC_CLK_RGMII | CCM_GMAC_MODE_RGMII; - else if (strcasecmp(phy_type, "rgmii-bpi") == 0) { - reg_value = CCM_GMAC_CLK_RGMII | CCM_GMAC_MODE_RGMII; - reg_value |= (3 << CCM_GMAC_CLK_DELAY_SHIFT); - } - free(phy_type, M_OFWPROP); - } - ccm_write_4(sc, CCM_GMAC_CLK, reg_value); - - return (0); -} - -static void -a10_clk_pll6_enable(void) -{ - struct a10_ccm_softc *sc; - uint32_t reg_value; - - /* - * SATA needs PLL6 to be a 100MHz clock. - * The SATA output frequency is 24MHz * n * k / m / 6. - * To get to 100MHz, k & m must be equal and n must be 25. - * For other uses the output frequency is 24MHz * n * k / 2. - */ - sc = a10_ccm_sc; - if (sc->pll6_enabled) - return; - reg_value = ccm_read_4(sc, CCM_PLL6_CFG); - reg_value &= ~CCM_PLL_CFG_BYPASS; - reg_value &= ~(CCM_PLL_CFG_FACTOR_K | CCM_PLL_CFG_FACTOR_M | - CCM_PLL_CFG_FACTOR_N); - reg_value |= (25 << CCM_PLL_CFG_FACTOR_N_SHIFT); - reg_value |= CCM_PLL6_CFG_SATA_CLKEN; - reg_value |= CCM_PLL_CFG_ENABLE; - ccm_write_4(sc, CCM_PLL6_CFG, reg_value); - sc->pll6_enabled = 1; -} - -static unsigned int -a10_clk_pll6_get_rate(void) -{ - struct a10_ccm_softc *sc; - uint32_t k, n, reg_value; - - sc = a10_ccm_sc; - reg_value = ccm_read_4(sc, CCM_PLL6_CFG); - n = ((reg_value & CCM_PLL_CFG_FACTOR_N) >> CCM_PLL_CFG_FACTOR_N_SHIFT); - k = ((reg_value & CCM_PLL_CFG_FACTOR_K) >> CCM_PLL_CFG_FACTOR_K_SHIFT) + - 1; - - return ((CCM_CLK_REF_FREQ * n * k) / 2); -} - -static int -a10_clk_pll2_set_rate(unsigned int freq) -{ - struct a10_ccm_softc *sc; - uint32_t reg_value; - unsigned int prediv, postdiv, n; - - sc = a10_ccm_sc; - if (sc == NULL) - return (ENXIO); - - reg_value = ccm_read_4(sc, CCM_PLL2_CFG); - reg_value &= ~(CCM_PLL2_CFG_PREDIV | CCM_PLL2_CFG_POSTDIV | - CCM_PLL_CFG_FACTOR_N); - - /* - * Audio Codec needs PLL2 to be either 24576000 Hz or 22579200 Hz - * - * PLL2 output frequency is 24MHz * n / prediv / postdiv. - * To get as close as possible to the desired rate, we use a - * pre-divider of 21 and a post-divider of 4. With these values, - * a multiplier of 86 or 79 gets us close to the target rates. - */ - prediv = 21; - postdiv = 4; - - switch (freq) { - case 24576000: - n = 86; - reg_value |= CCM_PLL_CFG_ENABLE; - break; - case 22579200: - n = 79; - reg_value |= CCM_PLL_CFG_ENABLE; - break; - case 0: - n = 1; - reg_value &= ~CCM_PLL_CFG_ENABLE; - break; - default: - return (EINVAL); - } - - reg_value |= (prediv << CCM_PLL2_CFG_PREDIV_SHIFT); - reg_value |= (postdiv << CCM_PLL2_CFG_POSTDIV_SHIFT); - reg_value |= (n << CCM_PLL_CFG_FACTOR_N_SHIFT); - ccm_write_4(sc, CCM_PLL2_CFG, reg_value); - - return (0); -} - -static int -a10_clk_pll3_set_rate(unsigned int freq) -{ - struct a10_ccm_softc *sc; - uint32_t reg_value; - int m; - - sc = a10_ccm_sc; - if (sc == NULL) - return (ENXIO); - - if (freq == 0) { - /* Disable PLL3 */ - ccm_write_4(sc, CCM_PLL3_CFG, 0); - return (0); - } - - m = freq / TCON_RATE_HZ(TCON_PLLREF_SINGLE); - - reg_value = CCM_PLL_CFG_ENABLE | CCM_PLL3_CFG_MODE_SEL_INT | m; - ccm_write_4(sc, CCM_PLL3_CFG, reg_value); - - return (0); -} - -static unsigned int -a10_clk_pll5x_get_rate(void) -{ - struct a10_ccm_softc *sc; - uint32_t k, n, p, reg_value; - - sc = a10_ccm_sc; - reg_value = ccm_read_4(sc, CCM_PLL5_CFG); - n = ((reg_value & CCM_PLL_CFG_FACTOR_N) >> CCM_PLL_CFG_FACTOR_N_SHIFT); - k = ((reg_value & CCM_PLL_CFG_FACTOR_K) >> CCM_PLL_CFG_FACTOR_K_SHIFT) + - 1; - p = ((reg_value & CCM_PLL5_CFG_OUT_EXT_DIV_P) >> CCM_PLL5_CFG_OUT_EXT_DIV_P_SHIFT); - - return ((CCM_CLK_REF_FREQ * n * k) >> p); -} - -int -a10_clk_ahci_activate(void) -{ - struct a10_ccm_softc *sc; - uint32_t reg_value; - - sc = a10_ccm_sc; - if (sc == NULL) - return (ENXIO); - - a10_clk_pll6_enable(); - - /* Gating AHB clock for SATA */ - reg_value = ccm_read_4(sc, CCM_AHB_GATING0); - reg_value |= CCM_AHB_GATING_SATA; - ccm_write_4(sc, CCM_AHB_GATING0, reg_value); - DELAY(1000); - - ccm_write_4(sc, CCM_SATA_CLK, CCM_PLL_CFG_ENABLE); - - return (0); -} - -int -a10_clk_mmc_activate(int devid) -{ - struct a10_ccm_softc *sc; - uint32_t reg_value; - - sc = a10_ccm_sc; - if (sc == NULL) - return (ENXIO); - - a10_clk_pll6_enable(); - - /* Gating AHB clock for SD/MMC */ - reg_value = ccm_read_4(sc, CCM_AHB_GATING0); - reg_value |= CCM_AHB_GATING_SDMMC0 << devid; - ccm_write_4(sc, CCM_AHB_GATING0, reg_value); - - return (0); -} - -int -a10_clk_mmc_cfg(int devid, int freq) -{ - struct a10_ccm_softc *sc; - uint32_t clksrc, m, n, ophase, phase, reg_value; - unsigned int pll_freq; - - sc = a10_ccm_sc; - if (sc == NULL) - return (ENXIO); - - freq /= 1000; - if (freq <= 400) { - pll_freq = CCM_CLK_REF_FREQ / 1000; - clksrc = CCM_SD_CLK_SRC_SEL_OSC24M; - ophase = 0; - phase = 0; - n = 2; - } else if (freq <= 25000) { - pll_freq = a10_clk_pll6_get_rate() / 1000; - clksrc = CCM_SD_CLK_SRC_SEL_PLL6; - ophase = 0; - phase = 5; - n = 2; - } else if (freq <= 50000) { - pll_freq = a10_clk_pll6_get_rate() / 1000; - clksrc = CCM_SD_CLK_SRC_SEL_PLL6; - ophase = 3; - phase = 5; - n = 0; - } else - return (EINVAL); - m = ((pll_freq / (1 << n)) / (freq)) - 1; - reg_value = ccm_read_4(sc, CCM_MMC0_SCLK_CFG + (devid * 4)); - reg_value &= ~CCM_SD_CLK_SRC_SEL; - reg_value |= (clksrc << CCM_SD_CLK_SRC_SEL_SHIFT); - reg_value &= ~CCM_SD_CLK_PHASE_CTR; - reg_value |= (phase << CCM_SD_CLK_PHASE_CTR_SHIFT); - reg_value &= ~CCM_SD_CLK_DIV_RATIO_N; - reg_value |= (n << CCM_SD_CLK_DIV_RATIO_N_SHIFT); - reg_value &= ~CCM_SD_CLK_OPHASE_CTR; - reg_value |= (ophase << CCM_SD_CLK_OPHASE_CTR_SHIFT); - reg_value &= ~CCM_SD_CLK_DIV_RATIO_M; - reg_value |= m; - reg_value |= CCM_PLL_CFG_ENABLE; - ccm_write_4(sc, CCM_MMC0_SCLK_CFG + (devid * 4), reg_value); - - return (0); -} - -int -a10_clk_i2c_activate(int devid) -{ - struct a10_ccm_softc *sc; - uint32_t reg_value; - - sc = a10_ccm_sc; - if (sc == NULL) - return (ENXIO); - - a10_clk_pll6_enable(); - - /* Gating APB clock for I2C/TWI */ - reg_value = ccm_read_4(sc, CCM_APB1_GATING); - if (devid == 4) - reg_value |= CCM_APB1_GATING_TWI << 15; - else - reg_value |= CCM_APB1_GATING_TWI << devid; - ccm_write_4(sc, CCM_APB1_GATING, reg_value); - - return (0); -} - -int -a10_clk_dmac_activate(void) -{ - struct a10_ccm_softc *sc; - uint32_t reg_value; - - sc = a10_ccm_sc; - if (sc == NULL) - return (ENXIO); - - /* Gating AHB clock for DMA controller */ - reg_value = ccm_read_4(sc, CCM_AHB_GATING0); - reg_value |= CCM_AHB_GATING_DMA; - ccm_write_4(sc, CCM_AHB_GATING0, reg_value); - - return (0); -} - -int -a10_clk_codec_activate(unsigned int freq) -{ - struct a10_ccm_softc *sc; - uint32_t reg_value; - - sc = a10_ccm_sc; - if (sc == NULL) - return (ENXIO); - - a10_clk_pll2_set_rate(freq); - - /* Gating APB clock for ADDA */ - reg_value = ccm_read_4(sc, CCM_APB0_GATING); - reg_value |= CCM_APB0_GATING_ADDA; - ccm_write_4(sc, CCM_APB0_GATING, reg_value); - - /* Enable audio codec clock */ - reg_value = ccm_read_4(sc, CCM_AUDIO_CODEC_CLK); - reg_value |= CCM_AUDIO_CODEC_ENABLE; - ccm_write_4(sc, CCM_AUDIO_CODEC_CLK, reg_value); - - return (0); -} - -static void -calc_tcon_pll(int f_ref, int f_out, int *pm, int *pn) -{ - int best, m, n, f_cur, diff; - - best = TCON_PLL_WORST; - for (n = TCON_PLL_N_MIN; n <= TCON_PLL_N_MAX; n++) { - for (m = TCON_PLL_M_MIN; m <= TCON_PLL_M_MAX; m++) { - f_cur = (m * f_ref) / n; - diff = f_out - f_cur; - if (diff > 0 && diff < best) { - best = diff; - *pm = m; - *pn = n; - } - } - } -} - -int -a10_clk_debe_activate(void) -{ - struct a10_ccm_softc *sc; - int pll_rate, clk_div; - uint32_t reg_value; - - sc = a10_ccm_sc; - if (sc == NULL) - return (ENXIO); - - /* Leave reset */ - reg_value = ccm_read_4(sc, CCM_BE0_SCLK); - reg_value |= CCM_BE_CLK_RESET; - ccm_write_4(sc, CCM_BE0_SCLK, reg_value); - - pll_rate = a10_clk_pll5x_get_rate(); - - clk_div = howmany(pll_rate, DEBE_DEFAULT_RATE); - - /* Set BE0 source to PLL5 (DDR external peripheral clock) */ - reg_value = CCM_BE_CLK_RESET; - reg_value |= (CCM_BE_CLK_SRC_SEL_PLL5 << CCM_BE_CLK_SRC_SEL_SHIFT); - reg_value |= (clk_div - 1); - ccm_write_4(sc, CCM_BE0_SCLK, reg_value); - - /* Gating AHB clock for BE0 */ - reg_value = ccm_read_4(sc, CCM_AHB_GATING1); - reg_value |= CCM_AHB_GATING_DE_BE0; - ccm_write_4(sc, CCM_AHB_GATING1, reg_value); - - /* Enable DRAM clock to BE0 */ - reg_value = ccm_read_4(sc, CCM_DRAM_CLK); - reg_value |= CCM_DRAM_CLK_BE0_CLK_ENABLE; - ccm_write_4(sc, CCM_DRAM_CLK, reg_value); - - /* Enable BE0 clock */ - reg_value = ccm_read_4(sc, CCM_BE0_SCLK); - reg_value |= CCM_BE_CLK_SCLK_GATING; - ccm_write_4(sc, CCM_BE0_SCLK, reg_value); - - return (0); -} - -int -a10_clk_lcd_activate(void) -{ - struct a10_ccm_softc *sc; - uint32_t reg_value; - - sc = a10_ccm_sc; - if (sc == NULL) - return (ENXIO); - - /* Clear LCD0 reset */ - reg_value = ccm_read_4(sc, CCM_LCD0_CH0_CLK); - reg_value |= CCM_LCD_CH0_RESET; - ccm_write_4(sc, CCM_LCD0_CH0_CLK, reg_value); - - /* Gating AHB clock for LCD0 */ - reg_value = ccm_read_4(sc, CCM_AHB_GATING1); - reg_value |= CCM_AHB_GATING_LCD0; - ccm_write_4(sc, CCM_AHB_GATING1, reg_value); - - return (0); -} - -int -a10_clk_tcon_activate(unsigned int freq) -{ - struct a10_ccm_softc *sc; - int m, n, m2, n2, f_single, f_double, dbl, src_sel; - - sc = a10_ccm_sc; - if (sc == NULL) - return (ENXIO); - - m = n = m2 = n2 = 0; - dbl = 0; - - calc_tcon_pll(TCON_PLLREF_SINGLE, TCON_RATE_KHZ(freq), &m, &n); - calc_tcon_pll(TCON_PLLREF_DOUBLE, TCON_RATE_KHZ(freq), &m2, &n2); - - f_single = n ? (m * TCON_PLLREF_SINGLE) / n : 0; - f_double = n2 ? (m2 * TCON_PLLREF_DOUBLE) / n2 : 0; - - if (f_double > f_single) { - dbl = 1; - m = m2; - n = n2; - } - src_sel = dbl ? CCM_LCD_CH1_SRC_SEL_PLL3_2X : CCM_LCD_CH1_SRC_SEL_PLL3; - - if (n == 0 || m == 0) - return (EINVAL); - - /* Set PLL3 to the closest possible rate */ - a10_clk_pll3_set_rate(TCON_RATE_HZ(m * TCON_PLLREF_SINGLE)); - - /* Enable LCD0 CH1 clock */ - ccm_write_4(sc, CCM_LCD0_CH1_CLK, - CCM_LCD_CH1_SCLK2_GATING | CCM_LCD_CH1_SCLK1_GATING | - (src_sel << CCM_LCD_CH1_SRC_SEL_SHIFT) | (n - 1)); - - return (0); -} - -int -a10_clk_tcon_get_config(int *pdiv, int *pdbl) -{ - struct a10_ccm_softc *sc; - uint32_t reg_value; - int src; - - sc = a10_ccm_sc; - if (sc == NULL) - return (ENXIO); - - reg_value = ccm_read_4(sc, CCM_LCD0_CH1_CLK); - - *pdiv = (reg_value & CCM_LCD_CH1_CLK_DIV_RATIO_M) + 1; - - src = (reg_value & CCM_LCD_CH1_SRC_SEL) >> CCM_LCD_CH1_SRC_SEL_SHIFT; - switch (src) { - case CCM_LCD_CH1_SRC_SEL_PLL3: - case CCM_LCD_CH1_SRC_SEL_PLL7: - *pdbl = 0; - break; - case CCM_LCD_CH1_SRC_SEL_PLL3_2X: - case CCM_LCD_CH1_SRC_SEL_PLL7_2X: - *pdbl = 1; - break; - } - - return (0); -} - -int -a10_clk_hdmi_activate(void) -{ - struct a10_ccm_softc *sc; - uint32_t reg_value; - int error; - - sc = a10_ccm_sc; - if (sc == NULL) - return (ENXIO); - - /* Set PLL3 to 297MHz */ - error = a10_clk_pll3_set_rate(HDMI_DEFAULT_RATE); - if (error != 0) - return (error); - - /* Enable HDMI clock, source PLL3 */ - reg_value = ccm_read_4(sc, CCM_HDMI_CLK); - reg_value |= CCM_HDMI_CLK_SCLK_GATING; - reg_value &= ~CCM_HDMI_CLK_SRC_SEL; - reg_value |= (CCM_HDMI_CLK_SRC_SEL_PLL3 << CCM_HDMI_CLK_SRC_SEL_SHIFT); - ccm_write_4(sc, CCM_HDMI_CLK, reg_value); - - /* Gating AHB clock for HDMI */ - reg_value = ccm_read_4(sc, CCM_AHB_GATING1); - reg_value |= CCM_AHB_GATING_HDMI; - ccm_write_4(sc, CCM_AHB_GATING1, reg_value); - - return (0); -} diff --git a/sys/arm/allwinner/a10_clk.h b/sys/arm/allwinner/a10_clk.h deleted file mode 100644 index 6b626be..0000000 --- a/sys/arm/allwinner/a10_clk.h +++ /dev/null @@ -1,247 +0,0 @@ -/*- - * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org> - * 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 _A10_CLK_H_ -#define _A10_CLK_H_ - -#define CCM_PLL1_CFG 0x0000 -#define CCM_PLL1_TUN 0x0004 -#define CCM_PLL2_CFG 0x0008 -#define CCM_PLL2_TUN 0x000c -#define CCM_PLL3_CFG 0x0010 -#define CCM_PLL3_TUN 0x0014 -#define CCM_PLL4_CFG 0x0018 -#define CCM_PLL4_TUN 0x001c -#define CCM_PLL5_CFG 0x0020 -#define CCM_PLL5_TUN 0x0024 -#define CCM_PLL6_CFG 0x0028 -#define CCM_PLL6_TUN 0x002c -#define CCM_PLL7_CFG 0x0030 -#define CCM_PLL7_TUN 0x0034 -#define CCM_PLL1_TUN2 0x0038 -#define CCM_PLL5_TUN2 0x003c -#define CCM_PLL_LOCK_DBG 0x004c -#define CCM_OSC24M_CFG 0x0050 -#define CCM_CPU_AHB_APB0_CFG 0x0054 -#define CCM_APB1_CLK_DIV 0x0058 -#define CCM_AXI_GATING 0x005c -#define CCM_AHB_GATING0 0x0060 -#define CCM_AHB_GATING1 0x0064 -#define CCM_APB0_GATING 0x0068 -#define CCM_APB1_GATING 0x006c -#define CCM_NAND_SCLK_CFG 0x0080 -#define CCM_MS_SCLK_CFG 0x0084 -#define CCM_MMC0_SCLK_CFG 0x0088 -#define CCM_MMC1_SCLK_CFG 0x008c -#define CCM_MMC2_SCLK_CFG 0x0090 -#define CCM_MMC3_SCLK_CFG 0x0094 -#define CCM_TS_CLK 0x0098 -#define CCM_SS_CLK 0x009c -#define CCM_SPI0_CLK 0x00a0 -#define CCM_SPI1_CLK 0x00a4 -#define CCM_SPI2_CLK 0x00a8 -#define CCM_PATA_CLK 0x00ac -#define CCM_IR0_CLK 0x00b0 -#define CCM_IR1_CLK 0x00b4 -#define CCM_IIS_CLK 0x00b8 -#define CCM_AC97_CLK 0x00bc -#define CCM_SPDIF_CLK 0x00c0 -#define CCM_KEYPAD_CLK 0x00c4 -#define CCM_SATA_CLK 0x00c8 -#define CCM_USB_CLK 0x00cc -#define CCM_GPS_CLK 0x00d0 -#define CCM_SPI3_CLK 0x00d4 -#define CCM_DRAM_CLK 0x0100 -#define CCM_BE0_SCLK 0x0104 -#define CCM_BE1_SCLK 0x0108 -#define CCM_FE0_CLK 0x010c -#define CCM_FE1_CLK 0x0110 -#define CCM_MP_CLK 0x0114 -#define CCM_LCD0_CH0_CLK 0x0118 -#define CCM_LCD1_CH0_CLK 0x011c -#define CCM_CSI_ISP_CLK 0x0120 -#define CCM_TVD_CLK 0x0128 -#define CCM_LCD0_CH1_CLK 0x012c -#define CCM_LCD1_CH1_CLK 0x0130 -#define CCM_CS0_CLK 0x0134 -#define CCM_CS1_CLK 0x0138 -#define CCM_VE_CLK 0x013c -#define CCM_AUDIO_CODEC_CLK 0x0140 -#define CCM_AVS_CLK 0x0144 -#define CCM_ACE_CLK 0x0148 -#define CCM_LVDS_CLK 0x014c -#define CCM_HDMI_CLK 0x0150 -#define CCM_MALI400_CLK 0x0154 -#define CCM_GMAC_CLK 0x0164 - -#define CCM_GMAC_CLK_DELAY_SHIFT 10 -#define CCM_GMAC_CLK_MODE_MASK 0x7 -#define CCM_GMAC_MODE_RGMII (1 << 2) -#define CCM_GMAC_CLK_MII 0x0 -#define CCM_GMAC_CLK_EXT_RGMII 0x1 -#define CCM_GMAC_CLK_RGMII 0x2 - -/* APB0_GATING */ -#define CCM_APB0_GATING_ADDA (1 << 0) - -/* AHB_GATING_REG0 */ -#define CCM_AHB_GATING_USB0 (1 << 0) -#define CCM_AHB_GATING_EHCI0 (1 << 1) -#define CCM_AHB_GATING_OHCI0 (1 << 2) -#define CCM_AHB_GATING_EHCI1 (1 << 3) -#define CCM_AHB_GATING_OHCI1 (1 << 4) -#define CCM_AHB_GATING_DMA (1 << 6) -#define CCM_AHB_GATING_SDMMC0 (1 << 8) -#define CCM_AHB_GATING_EMAC (1 << 17) -#define CCM_AHB_GATING_SATA (1 << 25) - -/* AHB_GATING_REG1 */ -#define CCM_AHB_GATING_GMAC (1 << 17) -#define CCM_AHB_GATING_DE_BE1 (1 << 13) -#define CCM_AHB_GATING_DE_BE0 (1 << 12) -#define CCM_AHB_GATING_HDMI (1 << 11) -#define CCM_AHB_GATING_LCD1 (1 << 5) -#define CCM_AHB_GATING_LCD0 (1 << 4) - -/* APB1_GATING_REG */ -#define CCM_APB1_GATING_TWI (1 << 0) - -/* USB */ -#define CCM_USB_PHY (1 << 8) -#define CCM_SCLK_GATING_OHCI1 (1 << 7) -#define CCM_SCLK_GATING_OHCI0 (1 << 6) -#define CCM_USBPHY2_RESET (1 << 2) -#define CCM_USBPHY1_RESET (1 << 1) -#define CCM_USBPHY0_RESET (1 << 0) - -#define CCM_PLL_CFG_ENABLE (1U << 31) -#define CCM_PLL_CFG_BYPASS (1U << 30) -#define CCM_PLL_CFG_PLL5 (1U << 25) -#define CCM_PLL_CFG_PLL6 (1U << 24) -#define CCM_PLL_CFG_FACTOR_N 0x1f00 -#define CCM_PLL_CFG_FACTOR_N_SHIFT 8 -#define CCM_PLL_CFG_FACTOR_K 0x30 -#define CCM_PLL_CFG_FACTOR_K_SHIFT 4 -#define CCM_PLL_CFG_FACTOR_M 0x3 - -#define CCM_PLL2_CFG_POSTDIV 0x3c000000 -#define CCM_PLL2_CFG_POSTDIV_SHIFT 26 -#define CCM_PLL2_CFG_PREDIV 0x1f -#define CCM_PLL2_CFG_PREDIV_SHIFT 0 - -#define CCM_PLL3_CFG_MODE_SEL_SHIFT 15 -#define CCM_PLL3_CFG_MODE_SEL_FRACT (0 << CCM_PLL3_CFG_MODE_SEL_SHIFT) -#define CCM_PLL3_CFG_MODE_SEL_INT (1 << CCM_PLL3_CFG_MODE_SEL_SHIFT) -#define CCM_PLL3_CFG_FUNC_SET_SHIFT 14 -#define CCM_PLL3_CFG_FUNC_SET_270MHZ (0 << CCM_PLL3_CFG_FUNC_SET_SHIFT) -#define CCM_PLL3_CFG_FUNC_SET_297MHZ (1 << CCM_PLL3_CFG_FUNC_SET_SHIFT) -#define CCM_PLL3_CFG_FACTOR_M 0x7f - -#define CCM_PLL5_CFG_OUT_EXT_DIV_P 0x30000 -#define CCM_PLL5_CFG_OUT_EXT_DIV_P_SHIFT 16 - -#define CCM_PLL6_CFG_SATA_CLKEN (1U << 14) - -#define CCM_SD_CLK_SRC_SEL 0x3000000 -#define CCM_SD_CLK_SRC_SEL_SHIFT 24 -#define CCM_SD_CLK_SRC_SEL_OSC24M 0 -#define CCM_SD_CLK_SRC_SEL_PLL6 1 -#define CCM_SD_CLK_PHASE_CTR 0x700000 -#define CCM_SD_CLK_PHASE_CTR_SHIFT 20 -#define CCM_SD_CLK_DIV_RATIO_N 0x30000 -#define CCM_SD_CLK_DIV_RATIO_N_SHIFT 16 -#define CCM_SD_CLK_OPHASE_CTR 0x700 -#define CCM_SD_CLK_OPHASE_CTR_SHIFT 8 -#define CCM_SD_CLK_DIV_RATIO_M 0xf - -#define CCM_AUDIO_CODEC_ENABLE (1U << 31) - -#define CCM_LCD_CH0_SCLK_GATING (1U << 31) -#define CCM_LCD_CH0_RESET (1U << 30) -#define CCM_LCD_CH0_SRC_SEL 0x03000000 -#define CCM_LCD_CH0_SRC_SEL_SHIFT 24 -#define CCM_LCD_CH0_SRC_SEL_PLL3 0 -#define CCM_LCD_CH0_SRC_SEL_PLL7 1 -#define CCM_LCD_CH0_SRC_SEL_PLL3_2X 2 -#define CCM_LCD_CH0_SRC_SEL_PLL6_2X 3 - -#define CCM_LCD_CH1_SCLK2_GATING (1U << 31) -#define CCM_LCD_CH1_SRC_SEL 0x03000000 -#define CCM_LCD_CH1_SRC_SEL_SHIFT 24 -#define CCM_LCD_CH1_SRC_SEL_PLL3 0 -#define CCM_LCD_CH1_SRC_SEL_PLL7 1 -#define CCM_LCD_CH1_SRC_SEL_PLL3_2X 2 -#define CCM_LCD_CH1_SRC_SEL_PLL7_2X 3 -#define CCM_LCD_CH1_SCLK1_GATING (1U << 15) -#define CCM_LCD_CH1_SCLK1_SRC_SEL_SHIFT 11 -#define CCM_LCD_CH1_SCLK1_SRC_SEL_SCLK2 0 -#define CCM_LCD_CH1_SCLK1_SRC_SEL_SCLK2_DIV2 1 -#define CCM_LCD_CH1_CLK_DIV_RATIO_M 0xf - -#define CCM_DRAM_CLK_BE1_CLK_ENABLE (1U << 27) -#define CCM_DRAM_CLK_BE0_CLK_ENABLE (1U << 26) - -#define CCM_BE_CLK_SCLK_GATING (1U << 31) -#define CCM_BE_CLK_RESET (1U << 30) -#define CCM_BE_CLK_SRC_SEL 0x03000000 -#define CCM_BE_CLK_SRC_SEL_SHIFT 24 -#define CCM_BE_CLK_SRC_SEL_PLL3 0 -#define CCM_BE_CLK_SRC_SEL_PLL7 1 -#define CCM_BE_CLK_SRC_SEL_PLL5 2 -#define CCM_BE_CLK_DIV_RATIO_M 0xf - -#define CCM_HDMI_CLK_SCLK_GATING (1U << 31) -#define CCM_HDMI_CLK_SRC_SEL 0x03000000 -#define CCM_HDMI_CLK_SRC_SEL_SHIFT 24 -#define CCM_HDMI_CLK_SRC_SEL_PLL3 0 -#define CCM_HDMI_CLK_SRC_SEL_PLL7 1 -#define CCM_HDMI_CLK_SRC_SEL_PLL3_2X 2 -#define CCM_HDMI_CLK_SRC_SEL_PLL7_2X 3 -#define CCM_HDMI_CLK_DIV_RATIO_M 0xf - -#define CCM_CLK_REF_FREQ 24000000U - -int a10_clk_ehci_activate(void); -int a10_clk_ehci_deactivate(void); -int a10_clk_ohci_activate(void); -int a10_clk_ohci_deactivate(void); -int a10_clk_emac_activate(void); -int a10_clk_gmac_activate(phandle_t); -int a10_clk_ahci_activate(void); -int a10_clk_mmc_activate(int); -int a10_clk_mmc_cfg(int, int); -int a10_clk_i2c_activate(int); -int a10_clk_dmac_activate(void); -int a10_clk_codec_activate(unsigned int); -int a10_clk_debe_activate(void); -int a10_clk_lcd_activate(void); -int a10_clk_tcon_activate(unsigned int); -int a10_clk_tcon_get_config(int *, int *); -int a10_clk_hdmi_activate(void); - -#endif /* _A10_CLK_H_ */ diff --git a/sys/arm/allwinner/a10_codec.c b/sys/arm/allwinner/a10_codec.c index 6646f2d..f791b18 100644 --- a/sys/arm/allwinner/a10_codec.c +++ b/sys/arm/allwinner/a10_codec.c @@ -50,7 +50,7 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> -#include <arm/allwinner/a10_clk.h> +#include <dev/extres/clk/clk.h> #include "sunxi_dma_if.h" #include "mixer_if.h" @@ -738,6 +738,7 @@ a10codec_attach(device_t dev) { struct a10codec_info *sc; char status[SND_STATUSLEN]; + clk_t clk_apb, clk_codec; uint32_t val; int error; @@ -778,6 +779,24 @@ a10codec_attach(device_t dev) goto fail; } + /* Get clocks */ + error = clk_get_by_ofw_name(dev, "apb", &clk_apb); + if (error != 0) { + device_printf(dev, "cannot find apb clock\n"); + goto fail; + } + error = clk_get_by_ofw_name(dev, "codec", &clk_codec); + if (error != 0) { + device_printf(dev, "cannot find codec clock\n"); + goto fail; + } + + /* Gating APB clock for codec */ + error = clk_enable(clk_apb); + if (error != 0) { + device_printf(dev, "cannot enable apb clock\n"); + goto fail; + } /* Activate audio codec clock. According to the A10 and A20 user * manuals, Audio_pll can be either 24.576MHz or 22.5792MHz. Most * audio sampling rates require an 24.576MHz input clock with the @@ -787,7 +806,17 @@ a10codec_attach(device_t dev) * 24.576MHz clock source and don't advertise native support for * the three sampling rates that require a 22.5792MHz input. */ - a10_clk_codec_activate(24576000); + error = clk_set_freq(clk_codec, 24576000, CLK_SET_ROUND_DOWN); + if (error != 0) { + device_printf(dev, "cannot set codec clock frequency\n"); + goto fail; + } + /* Enable audio codec clock */ + error = clk_enable(clk_codec); + if (error != 0) { + device_printf(dev, "cannot enable codec clock\n"); + goto fail; + } /* Enable DAC */ val = CODEC_READ(sc, AC_DAC_DPC); diff --git a/sys/arm/allwinner/a10_dmac.c b/sys/arm/allwinner/a10_dmac.c index 65fa352..fd5aaa6 100644 --- a/sys/arm/allwinner/a10_dmac.c +++ b/sys/arm/allwinner/a10_dmac.c @@ -46,7 +46,7 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/ofw_bus_subr.h> #include <arm/allwinner/a10_dmac.h> -#include <arm/allwinner/a10_clk.h> +#include <dev/extres/clk/clk.h> #include "sunxi_dma_if.h" @@ -111,6 +111,7 @@ a10dmac_attach(device_t dev) { struct a10dmac_softc *sc; unsigned int index; + clk_t clk; int error; sc = device_get_softc(dev); @@ -123,7 +124,16 @@ a10dmac_attach(device_t dev) mtx_init(&sc->sc_mtx, "a10 dmac", NULL, MTX_SPIN); /* Activate DMA controller clock */ - a10_clk_dmac_activate(); + error = clk_get_by_ofw_index(dev, 0, &clk); + if (error != 0) { + device_printf(dev, "cannot get clock\n"); + return (error); + } + error = clk_enable(clk); + if (error != 0) { + device_printf(dev, "cannot enable clock\n"); + return (error); + } /* Disable all interrupts and clear pending status */ DMA_WRITE(sc, AWIN_DMA_IRQ_EN_REG, 0); diff --git a/sys/arm/allwinner/a10_ehci.c b/sys/arm/allwinner/a10_ehci.c index c633de2..c325d60 100644 --- a/sys/arm/allwinner/a10_ehci.c +++ b/sys/arm/allwinner/a10_ehci.c @@ -59,8 +59,8 @@ __FBSDID("$FreeBSD$"); #include <dev/usb/controller/ehcireg.h> #include <arm/allwinner/allwinner_machdep.h> -#include <arm/allwinner/a10_clk.h> -#include <arm/allwinner/a31/a31_clk.h> +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> #define EHCI_HC_DEVSTR "Allwinner Integrated USB 2.0 controller" @@ -90,25 +90,22 @@ 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 { - int (*clk_activate)(void); - int (*clk_deactivate)(void); bool sdram_init; }; static const struct aw_ehci_conf a10_ehci_conf = { -#if defined(SOC_ALLWINNER_A10) || defined(SOC_ALLWINNER_A20) - .clk_activate = a10_clk_ehci_activate, - .clk_deactivate = a10_clk_ehci_deactivate, -#endif .sdram_init = true, }; static const struct aw_ehci_conf a31_ehci_conf = { -#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S) - .clk_activate = a31_clk_ehci_activate, - .clk_deactivate = a31_clk_ehci_deactivate, -#endif + .sdram_init = false, }; static struct ofw_compat_data compat_data[] = { @@ -136,7 +133,8 @@ a10_ehci_probe(device_t self) static int a10_ehci_attach(device_t self) { - ehci_softc_t *sc = device_get_softc(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; @@ -144,10 +142,6 @@ a10_ehci_attach(device_t self) uint32_t reg_value = 0; conf = USB_CONF(self); - if (conf->clk_activate == NULL) { - device_printf(self, "clock not supported\n"); - return (ENXIO); - } /* initialise some bus fields */ sc->sc_bus.parent = self; @@ -208,9 +202,24 @@ a10_ehci_attach(device_t self) 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 */ - if (conf->clk_activate() != 0) { - device_printf(self, "Could not activate clock\n"); + 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; } @@ -240,6 +249,8 @@ a10_ehci_attach(device_t self) return (0); error: + if (aw_sc->clk) + clk_release(aw_sc->clk); a10_ehci_detach(self); return (ENXIO); } @@ -247,7 +258,8 @@ error: static int a10_ehci_detach(device_t self) { - ehci_softc_t *sc = device_get_softc(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; @@ -305,7 +317,14 @@ a10_ehci_detach(device_t self) A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); /* Disable clock for USB */ - conf->clk_deactivate(); + 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); } diff --git a/sys/arm/allwinner/a10_fb.c b/sys/arm/allwinner/a10_fb.c index ea033f8..cf8f167 100644 --- a/sys/arm/allwinner/a10_fb.c +++ b/sys/arm/allwinner/a10_fb.c @@ -54,7 +54,8 @@ __FBSDID("$FreeBSD$"); #include <dev/videomode/videomode.h> #include <dev/videomode/edidvar.h> -#include <arm/allwinner/a10_clk.h> +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> #include "fb_if.h" #include "hdmi_if.h" @@ -66,6 +67,7 @@ __FBSDID("$FreeBSD$"); #define FB_ALIGN 0x1000 #define HDMI_ENABLE_DELAY 20000 +#define DEBE_FREQ 300000000 #define DOT_CLOCK_TO_HZ(c) ((c) * 1000) @@ -193,18 +195,68 @@ a10fb_freefb(struct a10fb_softc *sc) kmem_free(kernel_arena, sc->vaddr, sc->fbsize); } -static void +static int a10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode) { int width, height, interlace, reg; + clk_t clk_ahb, clk_dram, clk_debe; + hwreset_t rst; uint32_t val; + int error; interlace = !!(mode->flags & VID_INTERLACE); width = mode->hdisplay; height = mode->vdisplay << interlace; - /* Enable DEBE clocks */ - a10_clk_debe_activate(); + /* Leave reset */ + error = hwreset_get_by_ofw_name(sc->dev, "de_be", &rst); + if (error != 0) { + device_printf(sc->dev, "cannot find reset 'de_be'\n"); + return (error); + } + error = hwreset_deassert(rst); + if (error != 0) { + device_printf(sc->dev, "couldn't de-assert reset 'de_be'\n"); + return (error); + } + /* Gating AHB clock for BE */ + error = clk_get_by_ofw_name(sc->dev, "ahb_de_be", &clk_ahb); + if (error != 0) { + device_printf(sc->dev, "cannot find clk 'ahb_de_be'\n"); + return (error); + } + error = clk_enable(clk_ahb); + if (error != 0) { + device_printf(sc->dev, "cannot enable clk 'ahb_de_be'\n"); + return (error); + } + /* Enable DRAM clock to BE */ + error = clk_get_by_ofw_name(sc->dev, "dram_de_be", &clk_dram); + if (error != 0) { + device_printf(sc->dev, "cannot find clk 'dram_de_be'\n"); + return (error); + } + error = clk_enable(clk_dram); + if (error != 0) { + device_printf(sc->dev, "cannot enable clk 'dram_de_be'\n"); + return (error); + } + /* Set BE clock to 300MHz and enable */ + error = clk_get_by_ofw_name(sc->dev, "de_be", &clk_debe); + if (error != 0) { + device_printf(sc->dev, "cannot find clk 'de_be'\n"); + return (error); + } + error = clk_set_freq(clk_debe, DEBE_FREQ, CLK_SET_ROUND_DOWN); + if (error != 0) { + device_printf(sc->dev, "cannot set 'de_be' frequency\n"); + return (error); + } + error = clk_enable(clk_debe); + if (error != 0) { + device_printf(sc->dev, "cannot enable clk 'de_be'\n"); + return (error); + } /* Initialize all registers to 0 */ for (reg = DEBE_REG_START; reg < DEBE_REG_END; reg += DEBE_REG_WIDTH) @@ -247,14 +299,55 @@ a10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode) val = DEBE_READ(sc, DEBE_MODCTL); val |= MODCTL_START_CTL; DEBE_WRITE(sc, DEBE_MODCTL, val); + + return (0); } -static void +static int +a10fb_setup_pll(struct a10fb_softc *sc, uint64_t freq) +{ + clk_t clk_sclk1, clk_sclk2; + int error; + + error = clk_get_by_ofw_name(sc->dev, "lcd_ch1_sclk1", &clk_sclk1); + if (error != 0) { + device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk1'\n"); + return (error); + } + error = clk_get_by_ofw_name(sc->dev, "lcd_ch1_sclk2", &clk_sclk2); + if (error != 0) { + device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk2'\n"); + return (error); + } + + error = clk_set_freq(clk_sclk2, freq, 0); + if (error != 0) { + device_printf(sc->dev, "cannot set lcd ch1 frequency\n"); + return (error); + } + error = clk_enable(clk_sclk2); + if (error != 0) { + device_printf(sc->dev, "cannot enable lcd ch1 sclk2\n"); + return (error); + } + error = clk_enable(clk_sclk1); + if (error != 0) { + device_printf(sc->dev, "cannot enable lcd ch1 sclk1\n"); + return (error); + } + + return (0); +} + +static int a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode) { u_int interlace, hspw, hbp, vspw, vbp, vbl, width, height, start_delay; u_int vtotal, framerate, clk; + clk_t clk_ahb; + hwreset_t rst; uint32_t val; + int error; interlace = !!(mode->flags & VID_INTERLACE); width = mode->hdisplay; @@ -266,8 +359,28 @@ a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode) vbl = VBLANK_LEN(mode->vtotal, mode->vdisplay, interlace); start_delay = START_DELAY(vbl); - /* Enable LCD clocks */ - a10_clk_lcd_activate(); + /* Leave reset */ + error = hwreset_get_by_ofw_name(sc->dev, "lcd", &rst); + if (error != 0) { + device_printf(sc->dev, "cannot find reset 'lcd'\n"); + return (error); + } + error = hwreset_deassert(rst); + if (error != 0) { + device_printf(sc->dev, "couldn't de-assert reset 'lcd'\n"); + return (error); + } + /* Gating AHB clock for LCD */ + error = clk_get_by_ofw_name(sc->dev, "ahb_lcd", &clk_ahb); + if (error != 0) { + device_printf(sc->dev, "cannot find clk 'ahb_lcd'\n"); + return (error); + } + error = clk_enable(clk_ahb); + if (error != 0) { + device_printf(sc->dev, "cannot enable clk 'ahb_lcd'\n"); + return (error); + } /* Disable TCON and TCON1 */ TCON_WRITE(sc, TCON_GCTL, 0); @@ -322,7 +435,7 @@ a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode) TCON_WRITE(sc, TCON1_CTL, val); /* Setup PLL */ - a10_clk_tcon_activate(DOT_CLOCK_TO_HZ(mode->dot_clock)); + return (a10fb_setup_pll(sc, DOT_CLOCK_TO_HZ(mode->dot_clock))); } static void @@ -378,10 +491,14 @@ a10fb_configure(struct a10fb_softc *sc, const struct videomode *mode) } /* Setup display backend */ - a10fb_setup_debe(sc, mode); + error = a10fb_setup_debe(sc, mode); + if (error != 0) + return (error); /* Setup display timing controller */ - a10fb_setup_tcon(sc, mode); + error = a10fb_setup_tcon(sc, mode); + if (error != 0) + return (error); /* Attach framebuffer device */ sc->info.fb_name = device_get_nameunit(sc->dev); diff --git a/sys/arm/allwinner/a10_hdmi.c b/sys/arm/allwinner/a10_hdmi.c index ff86fa0..99cbc2f 100644 --- a/sys/arm/allwinner/a10_hdmi.c +++ b/sys/arm/allwinner/a10_hdmi.c @@ -49,7 +49,7 @@ __FBSDID("$FreeBSD$"); #include <dev/videomode/videomode.h> #include <dev/videomode/edidvar.h> -#include <arm/allwinner/a10_clk.h> +#include <dev/extres/clk/clk.h> #include "hdmi_if.h" @@ -204,6 +204,7 @@ __FBSDID("$FreeBSD$"); #define HDMI_VSDB_MINLEN 5 #define HDMI_OUI "\x03\x0c\x00" #define HDMI_OUI_LEN 3 +#define HDMI_DEFAULT_FREQ 297000000 struct a10hdmi_softc { struct resource *res; @@ -214,6 +215,10 @@ struct a10hdmi_softc { int has_hdmi; int has_audio; + + clk_t clk_ahb; + clk_t clk_hdmi; + clk_t clk_lcd; }; static struct resource_spec a10hdmi_spec[] = { @@ -287,7 +292,33 @@ a10hdmi_attach(device_t dev) return (ENXIO); } - a10_clk_hdmi_activate(); + /* Setup clocks */ + error = clk_get_by_ofw_name(dev, "ahb", &sc->clk_ahb); + if (error != 0) { + device_printf(dev, "cannot find ahb clock\n"); + return (error); + } + error = clk_get_by_ofw_name(dev, "hdmi", &sc->clk_hdmi); + if (error != 0) { + device_printf(dev, "cannot find hdmi clock\n"); + return (error); + } + error = clk_get_by_ofw_name(dev, "lcd", &sc->clk_lcd); + if (error != 0) { + device_printf(dev, "cannot find lcd clock\n"); + } + /* Enable HDMI clock */ + error = clk_enable(sc->clk_hdmi); + if (error != 0) { + device_printf(dev, "cannot enable hdmi clock\n"); + return (error); + } + /* Gating AHB clock for HDMI */ + error = clk_enable(sc->clk_ahb); + if (error != 0) { + device_printf(dev, "cannot enable ahb gate\n"); + return (error); + } a10hdmi_init(sc); @@ -527,6 +558,38 @@ a10hdmi_set_audiomode(device_t dev, const struct videomode *mode) } static int +a10hdmi_get_tcon_config(struct a10hdmi_softc *sc, int *div, int *dbl) +{ + uint64_t lcd_fin, lcd_fout; + clk_t clk_lcd_parent; + const char *pname; + int error; + + error = clk_get_parent(sc->clk_lcd, &clk_lcd_parent); + if (error != 0) + return (error); + + /* Get the LCD CH1 special clock 2 divider */ + error = clk_get_freq(sc->clk_lcd, &lcd_fout); + if (error != 0) + return (error); + error = clk_get_freq(clk_lcd_parent, &lcd_fin); + if (error != 0) + return (error); + *div = lcd_fin / lcd_fout; + + /* Detect LCD CH1 special clock using a 1X or 2X source */ + /* XXX */ + pname = clk_get_name(clk_lcd_parent); + if (strcmp(pname, "pll3-1x") == 0 || strcmp(pname, "pll7-1x") == 0) + *dbl = 0; + else + *dbl = 1; + + return (0); +} + +static int a10hdmi_set_videomode(device_t dev, const struct videomode *mode) { struct a10hdmi_softc *sc; @@ -543,9 +606,11 @@ a10hdmi_set_videomode(device_t dev, const struct videomode *mode) vspw = mode->vsync_end - mode->vsync_start; vbp = mode->vtotal - mode->vsync_start; - error = a10_clk_tcon_get_config(&clk_div, &clk_dbl); - if (error != 0) + error = a10hdmi_get_tcon_config(sc, &clk_div, &clk_dbl); + if (error != 0) { + device_printf(dev, "couldn't get tcon config: %d\n", error); return (error); + } /* Clear interrupt status */ HDMI_WRITE(sc, HDMI_INT_STATUS, HDMI_READ(sc, HDMI_INT_STATUS)); diff --git a/sys/arm/allwinner/a10_mmc.c b/sys/arm/allwinner/a10_mmc.c index dc341dd..1b89c0f 100644 --- a/sys/arm/allwinner/a10_mmc.c +++ b/sys/arm/allwinner/a10_mmc.c @@ -49,9 +49,9 @@ __FBSDID("$FreeBSD$"); #include <dev/mmc/mmcbrvar.h> #include <arm/allwinner/allwinner_machdep.h> -#include <arm/allwinner/a10_clk.h> #include <arm/allwinner/a10_mmc.h> -#include <arm/allwinner/a31/a31_clk.h> +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> #define A10_MMC_MEMRES 0 #define A10_MMC_IRQRES 1 @@ -60,6 +60,8 @@ __FBSDID("$FreeBSD$"); #define A10_MMC_DMA_MAX_SIZE 0x2000 #define A10_MMC_DMA_FTRGLEVEL 0x20070008 +#define CARD_ID_FREQUENCY 400000 + static int a10_mmc_pio_mode = 0; TUNABLE_INT("hw.a10.mmc.pio_mode", &a10_mmc_pio_mode); @@ -74,6 +76,9 @@ struct a10_mmc_softc { bus_space_handle_t a10_bsh; bus_space_tag_t a10_bst; device_t a10_dev; + clk_t a10_clk_ahb; + clk_t a10_clk_mmc; + hwreset_t a10_rst_ahb; int a10_bus_busy; int a10_id; int a10_resid; @@ -147,7 +152,7 @@ a10_mmc_attach(device_t dev) struct a10_mmc_softc *sc; struct sysctl_ctx_list *ctx; struct sysctl_oid_list *tree; - int clk; + int error; sc = device_get_softc(dev); sc->a10_dev = dev; @@ -170,6 +175,9 @@ a10_mmc_attach(device_t dev) device_printf(dev, "cannot setup interrupt handler\n"); return (ENXIO); } + mtx_init(&sc->a10_mtx, device_get_nameunit(sc->a10_dev), "a10_mmc", + MTX_DEF); + callout_init_mtx(&sc->a10_timeoutc, &sc->a10_mtx, 0); /* * Later chips use a different FIFO offset. Unfortunately the FDT @@ -186,30 +194,41 @@ a10_mmc_attach(device_t dev) break; } + /* De-assert reset */ + if (hwreset_get_by_ofw_name(dev, "ahb", &sc->a10_rst_ahb) == 0) { + error = hwreset_deassert(sc->a10_rst_ahb); + if (error != 0) { + device_printf(dev, "cannot de-assert reset\n"); + return (error); + } + } + /* Activate the module clock. */ - switch (allwinner_soc_type()) { -#if defined(SOC_ALLWINNER_A10) || defined(SOC_ALLWINNER_A20) - case ALLWINNERSOC_A10: - case ALLWINNERSOC_A10S: - case ALLWINNERSOC_A20: - clk = a10_clk_mmc_activate(sc->a10_id); - break; -#endif -#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S) - case ALLWINNERSOC_A31: - case ALLWINNERSOC_A31S: - clk = a31_clk_mmc_activate(sc->a10_id); - break; -#endif - default: - clk = -1; + error = clk_get_by_ofw_name(dev, "ahb", &sc->a10_clk_ahb); + if (error != 0) { + device_printf(dev, "cannot get ahb clock\n"); + goto fail; } - if (clk != 0) { - bus_teardown_intr(dev, sc->a10_res[A10_MMC_IRQRES], - sc->a10_intrhand); - bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res); - device_printf(dev, "cannot activate mmc clock\n"); - return (ENXIO); + error = clk_enable(sc->a10_clk_ahb); + if (error != 0) { + device_printf(dev, "cannot enable ahb clock\n"); + goto fail; + } + error = clk_get_by_ofw_name(dev, "mmc", &sc->a10_clk_mmc); + if (error != 0) { + device_printf(dev, "cannot get mmc clock\n"); + goto fail; + } + error = clk_set_freq(sc->a10_clk_mmc, CARD_ID_FREQUENCY, + CLK_SET_ROUND_DOWN); + if (error != 0) { + device_printf(dev, "cannot init mmc clock\n"); + goto fail; + } + error = clk_enable(sc->a10_clk_mmc); + if (error != 0) { + device_printf(dev, "cannot enable mmc clock\n"); + goto fail; } sc->a10_timeout = 10; @@ -217,9 +236,6 @@ a10_mmc_attach(device_t dev) tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "req_timeout", CTLFLAG_RW, &sc->a10_timeout, 0, "Request timeout in seconds"); - mtx_init(&sc->a10_mtx, device_get_nameunit(sc->a10_dev), "a10_mmc", - MTX_DEF); - callout_init_mtx(&sc->a10_timeoutc, &sc->a10_mtx, 0); /* Reset controller. */ if (a10_mmc_reset(sc) != 0) { @@ -826,25 +842,14 @@ a10_mmc_update_ios(device_t bus, device_t child) return (error); /* Set the MMC clock. */ - switch (allwinner_soc_type()) { -#if defined(SOC_ALLWINNER_A10) || defined(SOC_ALLWINNER_A20) - case ALLWINNERSOC_A10: - case ALLWINNERSOC_A10S: - case ALLWINNERSOC_A20: - error = a10_clk_mmc_cfg(sc->a10_id, ios->clock); - break; -#endif -#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S) - case ALLWINNERSOC_A31: - case ALLWINNERSOC_A31S: - error = a31_clk_mmc_cfg(sc->a10_id, ios->clock); - break; -#endif - default: - error = ENXIO; - } - if (error != 0) + error = clk_set_freq(sc->a10_clk_mmc, ios->clock, + CLK_SET_ROUND_DOWN); + if (error != 0) { + device_printf(sc->a10_dev, + "failed to set frequency to %u Hz: %d\n", + ios->clock, error); return (error); + } /* Enable clock. */ clkcr |= A10_MMC_CARD_CLK_ON; diff --git a/sys/arm/allwinner/a10_sramc.c b/sys/arm/allwinner/a10_sramc.c index a26afbb..22ba1f7 100644 --- a/sys/arm/allwinner/a10_sramc.c +++ b/sys/arm/allwinner/a10_sramc.c @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include "a10_sramc.h" #define SRAM_CTL1_CFG 0x04 +#define CTL1_CFG_SRAMD_MAP_USB0 (1 << 0) struct a10_sramc_softc { struct resource *res; @@ -71,7 +72,7 @@ static int a10_sramc_probe(device_t dev) { - if (ofw_bus_is_compatible(dev, "allwinner,sun4i-sramc")) { + if (ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-sram-controller")) { device_set_desc(dev, "Allwinner sramc module"); return (BUS_PROBE_DEFAULT); } @@ -113,7 +114,8 @@ static driver_t a10_sramc_driver = { static devclass_t a10_sramc_devclass; -DRIVER_MODULE(a10_sramc, simplebus, a10_sramc_driver, a10_sramc_devclass, 0, 0); +EARLY_DRIVER_MODULE(a10_sramc, simplebus, a10_sramc_driver, a10_sramc_devclass, + 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_EARLY); int a10_map_to_emac(void) @@ -131,3 +133,20 @@ a10_map_to_emac(void) return (0); } + +int +a10_map_to_otg(void) +{ + struct a10_sramc_softc *sc = a10_sramc_sc; + uint32_t reg_value; + + if (sc == NULL) + return (ENXIO); + + /* Map SRAM to OTG */ + reg_value = sramc_read_4(sc, SRAM_CTL1_CFG); + reg_value |= CTL1_CFG_SRAMD_MAP_USB0; + sramc_write_4(sc, SRAM_CTL1_CFG, reg_value); + + return (0); +} diff --git a/sys/arm/allwinner/a10_sramc.h b/sys/arm/allwinner/a10_sramc.h index 9c906df..d66a8f2 100644 --- a/sys/arm/allwinner/a10_sramc.h +++ b/sys/arm/allwinner/a10_sramc.h @@ -30,5 +30,6 @@ #define _A10_SRAMC_H_ int a10_map_to_emac(void); +int a10_map_to_otg(void); #endif diff --git a/sys/arm/allwinner/a20/a20_if_dwc.c b/sys/arm/allwinner/a20/a20_if_dwc.c index 711873d..2f88d9d 100644 --- a/sys/arm/allwinner/a20/a20_if_dwc.c +++ b/sys/arm/allwinner/a20/a20_if_dwc.c @@ -41,8 +41,8 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/ofw_bus_subr.h> #include <arm/allwinner/allwinner_machdep.h> -#include <arm/allwinner/a10_clk.h> -#include <arm/allwinner/a31/a31_clk.h> +#include <dev/extres/clk/clk.h> +#include <dev/extres/regulator/regulator.h> #include "if_dwc_if.h" @@ -62,29 +62,49 @@ a20_if_dwc_probe(device_t dev) static int a20_if_dwc_init(device_t dev) { - int clk; - - /* Activate GMAC clock and set the pin mux to rgmii. */ - switch (allwinner_soc_type()) { -#if defined(SOC_ALLWINNER_A10) || defined(SOC_ALLWINNER_A20) - case ALLWINNERSOC_A10: - case ALLWINNERSOC_A10S: - case ALLWINNERSOC_A20: - clk = a10_clk_gmac_activate(ofw_bus_get_node(dev)); - break; -#endif -#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S) - case ALLWINNERSOC_A31: - case ALLWINNERSOC_A31S: - clk = a31_clk_gmac_activate(ofw_bus_get_node(dev)); - break; -#endif - default: - clk = -1; + const char *tx_parent_name; + char *phy_type; + clk_t clk_tx, clk_tx_parent; + regulator_t reg; + phandle_t node; + int error; + + node = ofw_bus_get_node(dev); + + /* Configure PHY for MII or RGMII mode */ + if (OF_getprop_alloc(node, "phy-mode", 1, (void **)&phy_type)) { + error = clk_get_by_ofw_name(dev, "allwinner_gmac_tx", &clk_tx); + if (error != 0) { + device_printf(dev, "could not get tx clk\n"); + return (error); + } + + if (strcmp(phy_type, "rgmii") == 0) + tx_parent_name = "gmac_int_tx"; + else + tx_parent_name = "mii_phy_tx"; + + error = clk_get_by_name(dev, tx_parent_name, &clk_tx_parent); + if (error != 0) { + device_printf(dev, "could not get clock '%s'\n", + tx_parent_name); + return (error); + } + + error = clk_set_parent_by_clk(clk_tx, clk_tx_parent); + if (error != 0) { + device_printf(dev, "could not set tx clk parent\n"); + return (error); + } } - if (clk != 0) { - device_printf(dev, "could not activate gmac module\n"); - return (ENXIO); + + /* Enable PHY regulator if applicable */ + if (regulator_get_by_ofw_property(dev, "phy-supply", ®) == 0) { + error = regulator_enable(reg); + if (error != 0) { + device_printf(dev, "could not enable PHY regulator\n"); + return (error); + } } return (0); diff --git a/sys/arm/allwinner/a31/a31_clk.c b/sys/arm/allwinner/a31/a31_clk.c deleted file mode 100644 index edda8bb..0000000 --- a/sys/arm/allwinner/a31/a31_clk.c +++ /dev/null @@ -1,378 +0,0 @@ -/*- - * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org> - * Copyright (c) 2016 Emmanuel Vadot <manu@bidouilliste.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * Simple clock driver for Allwinner A31 - * Adapted from a10_clk.c -*/ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/bus.h> -#include <sys/kernel.h> -#include <sys/module.h> -#include <sys/malloc.h> -#include <machine/bus.h> - -#include <dev/ofw/openfirm.h> -#include <dev/ofw/ofw_bus_subr.h> - -#include <arm/allwinner/a31/a31_clk.h> - -struct a31_ccm_softc { - struct resource *res; - struct mtx mtx; - int pll6_enabled; - int ehci_refcnt; -}; - -static struct a31_ccm_softc *a31_ccm_sc = NULL; - -#define ccm_read_4(sc, reg) \ - bus_read_4((sc)->res, (reg)) -#define ccm_write_4(sc, reg, val) \ - bus_write_4((sc)->res, (reg), (val)) - -#define CCM_LOCK(sc) mtx_lock(&(sc)->mtx) -#define CCM_UNLOCK(sc) mtx_unlock(&(sc)->mtx) - -#define PLL6_TIMEOUT 10 - -static int -a31_ccm_probe(device_t dev) -{ - - if (!ofw_bus_status_okay(dev)) - return (ENXIO); - - if (ofw_bus_is_compatible(dev, "allwinner,sun6i-a31-ccm")) { - device_set_desc(dev, "Allwinner Clock Control Module"); - return(BUS_PROBE_DEFAULT); - } - - return (ENXIO); -} - -static int -a31_ccm_attach(device_t dev) -{ - struct a31_ccm_softc *sc = device_get_softc(dev); - int rid = 0; - - if (a31_ccm_sc) - return (ENXIO); - - sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); - if (!sc->res) { - device_printf(dev, "could not allocate resource\n"); - return (ENXIO); - } - - mtx_init(&sc->mtx, "a31 ccm", NULL, MTX_DEF); - - a31_ccm_sc = sc; - - return (0); -} - -static device_method_t a31_ccm_methods[] = { - DEVMETHOD(device_probe, a31_ccm_probe), - DEVMETHOD(device_attach, a31_ccm_attach), - { 0, 0 } -}; - -static driver_t a31_ccm_driver = { - "a31_ccm", - a31_ccm_methods, - sizeof(struct a31_ccm_softc), -}; - -static devclass_t a31_ccm_devclass; - -EARLY_DRIVER_MODULE(a31_ccm, simplebus, a31_ccm_driver, a31_ccm_devclass, 0, 0, - BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); - -static int -a31_clk_pll6_enable(void) -{ - struct a31_ccm_softc *sc; - uint32_t reg_value; - int i; - - /* Datasheet recommand to use the default 600Mhz value */ - sc = a31_ccm_sc; - if (sc->pll6_enabled) - return (0); - reg_value = ccm_read_4(sc, A31_CCM_PLL6_CFG); - reg_value |= A31_CCM_PLL_CFG_ENABLE; - ccm_write_4(sc, A31_CCM_PLL6_CFG, reg_value); - - /* Wait for PLL to be stable */ - for (i = 0; i < PLL6_TIMEOUT; i++) - if ((ccm_read_4(sc, A31_CCM_PLL6_CFG) & - A31_CCM_PLL6_CFG_REG_LOCK) == A31_CCM_PLL6_CFG_REG_LOCK) - break; - if (i == PLL6_TIMEOUT) - return (ENXIO); - sc->pll6_enabled = 1; - - return (0); -} - -static unsigned int -a31_clk_pll6_get_rate(void) -{ - struct a31_ccm_softc *sc; - uint32_t k, n, reg_value; - - sc = a31_ccm_sc; - reg_value = ccm_read_4(sc, A31_CCM_PLL6_CFG); - n = ((reg_value & A31_CCM_PLL_CFG_FACTOR_N) >> - A31_CCM_PLL_CFG_FACTOR_N_SHIFT); - k = ((reg_value & A31_CCM_PLL_CFG_FACTOR_K) >> - A31_CCM_PLL_CFG_FACTOR_K_SHIFT) + 1; - - return ((A31_CCM_CLK_REF_FREQ * n * k) / 2); -} - -int -a31_clk_gmac_activate(phandle_t node) -{ - char *phy_type; - struct a31_ccm_softc *sc; - uint32_t reg_value; - - sc = a31_ccm_sc; - if (sc == NULL) - return (ENXIO); - - if (a31_clk_pll6_enable()) - return (ENXIO); - - /* Gating AHB clock for GMAC */ - reg_value = ccm_read_4(sc, A31_CCM_AHB_GATING0); - reg_value |= A31_CCM_AHB_GATING_GMAC; - ccm_write_4(sc, A31_CCM_AHB_GATING0, reg_value); - - /* Set GMAC mode. */ - reg_value = A31_CCM_GMAC_CLK_MII; - if (OF_getprop_alloc(node, "phy-mode", 1, (void **)&phy_type) > 0) { - if (strcasecmp(phy_type, "rgmii") == 0) - reg_value = A31_CCM_GMAC_CLK_RGMII | - A31_CCM_GMAC_MODE_RGMII; - free(phy_type, M_OFWPROP); - } - ccm_write_4(sc, A31_CCM_GMAC_CLK, reg_value); - - /* Reset gmac */ - reg_value = ccm_read_4(sc, A31_CCM_AHB1_RST_REG0); - reg_value |= A31_CCM_AHB1_RST_REG0_GMAC; - ccm_write_4(sc, A31_CCM_AHB1_RST_REG0, reg_value); - - return (0); -} - -int -a31_clk_mmc_activate(int devid) -{ - struct a31_ccm_softc *sc; - uint32_t reg_value; - - sc = a31_ccm_sc; - if (sc == NULL) - return (ENXIO); - - if (a31_clk_pll6_enable()) - return (ENXIO); - - /* Gating AHB clock for SD/MMC */ - reg_value = ccm_read_4(sc, A31_CCM_AHB_GATING0); - reg_value |= A31_CCM_AHB_GATING_SDMMC0 << devid; - ccm_write_4(sc, A31_CCM_AHB_GATING0, reg_value); - - /* Soft reset */ - reg_value = ccm_read_4(sc, A31_CCM_AHB1_RST_REG0); - reg_value |= A31_CCM_AHB1_RST_REG0_SDMMC << devid; - ccm_write_4(sc, A31_CCM_AHB1_RST_REG0, reg_value); - - return (0); -} - -int -a31_clk_mmc_cfg(int devid, int freq) -{ - struct a31_ccm_softc *sc; - uint32_t clksrc, m, n, ophase, phase, reg_value; - unsigned int pll_freq; - - sc = a31_ccm_sc; - if (sc == NULL) - return (ENXIO); - - freq /= 1000; - if (freq <= 400) { - pll_freq = A31_CCM_CLK_REF_FREQ / 1000; - clksrc = A31_CCM_SD_CLK_SRC_SEL_OSC24M; - ophase = 0; - phase = 0; - n = 2; - } else if (freq <= 25000) { - pll_freq = a31_clk_pll6_get_rate() / 1000; - clksrc = A31_CCM_SD_CLK_SRC_SEL_PLL6; - ophase = 0; - phase = 5; - n = 2; - } else if (freq <= 50000) { - pll_freq = a31_clk_pll6_get_rate() / 1000; - clksrc = A31_CCM_SD_CLK_SRC_SEL_PLL6; - ophase = 3; - phase = 5; - n = 0; - } else - return (EINVAL); - m = ((pll_freq / (1 << n)) / (freq)) - 1; - reg_value = ccm_read_4(sc, A31_CCM_MMC0_SCLK_CFG + (devid * 4)); - reg_value &= ~A31_CCM_SD_CLK_SRC_SEL; - reg_value |= (clksrc << A31_CCM_SD_CLK_SRC_SEL_SHIFT); - reg_value &= ~A31_CCM_SD_CLK_PHASE_CTR; - reg_value |= (phase << A31_CCM_SD_CLK_PHASE_CTR_SHIFT); - reg_value &= ~A31_CCM_SD_CLK_DIV_RATIO_N; - reg_value |= (n << A31_CCM_SD_CLK_DIV_RATIO_N_SHIFT); - reg_value &= ~A31_CCM_SD_CLK_OPHASE_CTR; - reg_value |= (ophase << A31_CCM_SD_CLK_OPHASE_CTR_SHIFT); - reg_value &= ~A31_CCM_SD_CLK_DIV_RATIO_M; - reg_value |= m; - reg_value |= A31_CCM_PLL_CFG_ENABLE; - ccm_write_4(sc, A31_CCM_MMC0_SCLK_CFG + (devid * 4), reg_value); - - return (0); -} - -int -a31_clk_i2c_activate(int devid) -{ - struct a31_ccm_softc *sc; - uint32_t reg_value; - - sc = a31_ccm_sc; - if (sc == NULL) - return (ENXIO); - - if (a31_clk_pll6_enable()) - return (ENXIO); - - /* Gating APB clock for I2C/TWI */ - reg_value = ccm_read_4(sc, A31_CCM_APB2_GATING); - reg_value |= A31_CCM_APB2_GATING_TWI << devid; - ccm_write_4(sc, A31_CCM_APB2_GATING, reg_value); - - /* Soft reset */ - reg_value = ccm_read_4(sc, A31_CCM_APB2_RST); - reg_value |= A31_CCM_APB2_RST_TWI << devid; - ccm_write_4(sc, A31_CCM_APB2_RST, reg_value); - - return (0); -} - -int -a31_clk_ehci_activate(void) -{ - struct a31_ccm_softc *sc; - uint32_t reg_value; - - sc = a31_ccm_sc; - if (sc == NULL) - return (ENXIO); - - CCM_LOCK(sc); - if (++sc->ehci_refcnt == 1) { - /* Enable USB PHY */ - reg_value = ccm_read_4(sc, A31_CCM_USBPHY_CLK); - reg_value |= A31_CCM_USBPHY_CLK_GATING_USBPHY0; - reg_value |= A31_CCM_USBPHY_CLK_GATING_USBPHY1; - reg_value |= A31_CCM_USBPHY_CLK_GATING_USBPHY2; - reg_value |= A31_CCM_USBPHY_CLK_USBPHY1_RST; - reg_value |= A31_CCM_USBPHY_CLK_USBPHY2_RST; - ccm_write_4(sc, A31_CCM_USBPHY_CLK, reg_value); - - /* Gating AHB clock for EHCI */ - reg_value = ccm_read_4(sc, A31_CCM_AHB_GATING0); - reg_value |= A31_CCM_AHB_GATING_EHCI0; - reg_value |= A31_CCM_AHB_GATING_EHCI1; - ccm_write_4(sc, A31_CCM_AHB_GATING0, reg_value); - - /* De-assert reset */ - reg_value = ccm_read_4(sc, A31_CCM_AHB1_RST_REG0); - reg_value |= A31_CCM_AHB1_RST_REG0_EHCI0; - reg_value |= A31_CCM_AHB1_RST_REG0_EHCI1; - ccm_write_4(sc, A31_CCM_AHB1_RST_REG0, reg_value); - } - CCM_UNLOCK(sc); - - return (0); -} - -int -a31_clk_ehci_deactivate(void) -{ - struct a31_ccm_softc *sc; - uint32_t reg_value; - - sc = a31_ccm_sc; - if (sc == NULL) - return (ENXIO); - - CCM_LOCK(sc); - if (--sc->ehci_refcnt == 0) { - /* Disable USB PHY */ - reg_value = ccm_read_4(sc, A31_CCM_USBPHY_CLK); - reg_value &= ~A31_CCM_USBPHY_CLK_GATING_USBPHY0; - reg_value &= ~A31_CCM_USBPHY_CLK_GATING_USBPHY1; - reg_value &= ~A31_CCM_USBPHY_CLK_GATING_USBPHY2; - reg_value &= ~A31_CCM_USBPHY_CLK_USBPHY1_RST; - reg_value &= ~A31_CCM_USBPHY_CLK_USBPHY2_RST; - ccm_write_4(sc, A31_CCM_USBPHY_CLK, reg_value); - - /* Gating AHB clock for EHCI */ - reg_value = ccm_read_4(sc, A31_CCM_AHB_GATING0); - reg_value &= ~A31_CCM_AHB_GATING_EHCI0; - reg_value &= ~A31_CCM_AHB_GATING_EHCI1; - ccm_write_4(sc, A31_CCM_AHB_GATING0, reg_value); - - /* Assert reset */ - reg_value = ccm_read_4(sc, A31_CCM_AHB1_RST_REG0); - reg_value &= ~A31_CCM_AHB1_RST_REG0_EHCI0; - reg_value &= ~A31_CCM_AHB1_RST_REG0_EHCI1; - ccm_write_4(sc, A31_CCM_AHB1_RST_REG0, reg_value); - } - CCM_UNLOCK(sc); - - return (0); -} diff --git a/sys/arm/allwinner/a31/a31_clk.h b/sys/arm/allwinner/a31/a31_clk.h deleted file mode 100644 index 1ec6a79..0000000 --- a/sys/arm/allwinner/a31/a31_clk.h +++ /dev/null @@ -1,213 +0,0 @@ -/*- - * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org> - * Copyright (c) 2016 Emmanuel Vadot <manu@bidouilliste.com> - * 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 _A31_CLK_H_ -#define _A31_CLK_H_ - -#define A31_CCM_PLL1_CFG 0x0000 -#define A31_CCM_PLL2_CFG 0x0008 -#define A31_CCM_PLL3_CFG 0x0010 -#define A31_CCM_PLL4_CFG 0x0018 -#define A31_CCM_PLL5_CFG 0x0020 -#define A31_CCM_PLL6_CFG 0x0028 -#define A31_CCM_PLL7_CFG 0x0030 -#define A31_CCM_PLL8_CFG 0x0038 -#define A31_CCM_MIPI_PLL_CFG 0x0040 -#define A31_CCM_PLL9_CFG 0x0044 -#define A31_CCM_PLL10_CFG 0x0048 -#define A31_CCM_AXI_CFG_REG 0x0050 -#define A31_CCM_AHB1_APB1_CFG 0x0054 -#define A31_CCM_APB2_CLK_DIV 0x0058 -#define A31_CCM_AHB_GATING0 0x0060 -#define A31_CCM_AHB_GATING1 0x0064 -#define A31_CCM_APB1_GATING 0x0068 -#define A31_CCM_APB2_GATING 0x006c -#define A31_CCM_NAND0_SCLK_CFG 0x0080 -#define A31_CCM_NAND1_SCLK_CFG 0x0084 -#define A31_CCM_MMC0_SCLK_CFG 0x0088 -#define A31_CCM_MMC1_SCLK_CFG 0x008c -#define A31_CCM_MMC2_SCLK_CFG 0x0090 -#define A31_CCM_MMC3_SCLK_CFG 0x0094 -#define A31_CCM_TS_CLK 0x0098 -#define A31_CCM_SS_CLK 0x009c -#define A31_CCM_SPI0_CLK 0x00a0 -#define A31_CCM_SPI1_CLK 0x00a4 -#define A31_CCM_SPI2_CLK 0x00a8 -#define A31_CCM_SPI3_CLK 0x00ac -#define A31_CCM_DAUDIO0_CLK 0x00b0 -#define A31_CCM_DAUDIO1_CLK 0x00b4 -#define A31_CCM_USBPHY_CLK 0x00cc -#define A31_CCM_GMAC_CLK 0x00d0 -#define A31_CCM_MDFS_CLK 0x00f0 -#define A31_CCM_DRAM_CLK 0x00f4 -#define A31_CCM_DRAM_GATING 0x0100 -#define A31_CCM_BE0_SCLK 0x0104 -#define A31_CCM_BE1_SCLK 0x0108 -#define A31_CCM_FE0_CLK 0x010c -#define A31_CCM_FE1_CLK 0x0110 -#define A31_CCM_MP_CLK 0x0114 -#define A31_CCM_LCD0_CH0_CLK 0x0118 -#define A31_CCM_LCD1_CH0_CLK 0x011c -#define A31_CCM_LCD0_CH1_CLK 0x012c -#define A31_CCM_LCD1_CH1_CLK 0x0130 -#define A31_CCM_CSI0_CLK 0x0134 -#define A31_CCM_CSI1_CLK 0x0138 -#define A31_CCM_VE_CLK 0x013c -#define A31_CCM_AUDIO_CODEC_CLK 0x0140 -#define A31_CCM_AVS_CLK 0x0144 -#define A31_CCM_DIGITAL_MIC_CLK 0x0148 -#define A31_CCM_HDMI_CLK 0x0150 -#define A31_CCM_PS_CLK 0x0154 -#define A31_CCM_MBUS_SCLK_CFG0 0x015c -#define A31_CCM_MBUS_SCLK_CFG1 0x0160 -#define A31_CCM_MIPI_DSI_CLK 0x0168 -#define A31_CCM_MIPI_CSI0_CLK 0x016c -#define A31_CCM_DRC0_SCLK_CFG 0x0180 -#define A31_CCM_DRC1_SCLK_CFG 0x0184 -#define A31_CCM_DEU0_SCLK_CFG 0x0188 -#define A31_CCM_DEU1_SCLK_CFG 0x018c -#define A31_CCM_GPU_CORE_CLK 0x01a0 -#define A31_CCM_GPU_MEM_CLK 0x01a4 -#define A31_CCM_GPU_HYD_CLK 0x01a8 -#define A31_CCM_ATS_CLK 0x01b0 -#define A31_CCM_TRACE_CLK 0x01b4 -#define A31_CCM_PLL_LOCK_CFG 0x0200 -#define A31_CCM_PLL1_LOCK_CFG 0x0204 -#define A31_CCM_PLL1_BIAS 0x0220 -#define A31_CCM_PLL2_BIAS 0x0224 -#define A31_CCM_PLL3_BIAS 0x0228 -#define A31_CCM_PLL4_BIAS 0x022c -#define A31_CCM_PLL5_BIAS 0x0230 -#define A31_CCM_PLL6_BIAS 0x0234 -#define A31_CCM_PLL7_BIAS 0x0238 -#define A31_CCM_PLL8_BIAS 0x023c -#define A31_CCM_PLL9_BIAS 0x0240 -#define A31_CCM_MIPI_PLL_BIAS 0x0244 -#define A31_CCM_PLL10_BIAS 0x0248 -#define A31_CCM_PLL1_PAT_CFG 0x0280 -#define A31_CCM_PLL2_PAT_CFG 0x0284 -#define A31_CCM_PLL3_PAT_CFG 0x0288 -#define A31_CCM_PLL4_PAT_CFG 0x028c -#define A31_CCM_PLL5_PAT_CFG 0x0290 -#define A31_CCM_PLL6_PAT_CFG 0x0294 -#define A31_CCM_PLL7_PAT_CFG 0x0298 -#define A31_CCM_PLL8_PAT_CFG 0x029c -#define A31_CCM_MIPI_PLL_PAT_CFG 0x02a0 -#define A31_CCM_PLL9_PAT_CFG 0x02a4 -#define A31_CCM_PLL10_PAT_CFG 0x02a8 -#define A31_CCM_AHB1_RST_REG0 0x02c0 -#define A31_CCM_AHB1_RST_REG1 0x02c4 -#define A31_CCM_AHB1_RST_REG2 0x02c8 -#define A31_CCM_APB1_RST 0x02d0 -#define A31_CCM_APB2_RST 0x02d8 -#define A31_CCM_CLK_OUTA 0x0300 -#define A31_CCM_CLK_OUTB 0x0304 -#define A31_CCM_CLK_OUTC 0x0308 - -/* PLL6_CFG_REG */ -#define A31_CCM_PLL6_CFG_REG_LOCK (1 << 28) - -/* AHB_GATING_REG0 */ -#define A31_CCM_AHB_GATING_OHCI2 (1 << 31) -#define A31_CCM_AHB_GATING_OHCI1 (1 << 30) -#define A31_CCM_AHB_GATING_OHCI0 (1 << 29) -#define A31_CCM_AHB_GATING_EHCI1 (1 << 27) -#define A31_CCM_AHB_GATING_EHCI0 (1 << 26) -#define A31_CCM_AHB_GATING_USBDRD (1 << 24) -#define A31_CCM_AHB_GATING_GMAC (1 << 17) -#define A31_CCM_AHB_GATING_SDMMC0 (1 << 8) - -#define A31_CCM_PLL_CFG_ENABLE (1U << 31) -#define A31_CCM_PLL_CFG_BYPASS (1U << 30) -#define A31_CCM_PLL_CFG_PLL5 (1U << 25) -#define A31_CCM_PLL_CFG_PLL6 (1U << 24) -#define A31_CCM_PLL_CFG_FACTOR_N 0x1f00 -#define A31_CCM_PLL_CFG_FACTOR_N_SHIFT 8 -#define A31_CCM_PLL_CFG_FACTOR_K 0x30 -#define A31_CCM_PLL_CFG_FACTOR_K_SHIFT 4 -#define A31_CCM_PLL_CFG_FACTOR_M 0x3 - -/* APB2_GATING */ -#define A31_CCM_APB2_GATING_TWI (1 << 0) - -/* AHB1_RST_REG0 */ -#define A31_CCM_AHB1_RST_REG0_OHCI2 (1 << 31) -#define A31_CCM_AHB1_RST_REG0_OHCI1 (1 << 30) -#define A31_CCM_AHB1_RST_REG0_OHCI0 (1 << 29) -#define A31_CCM_AHB1_RST_REG0_EHCI1 (1 << 27) -#define A31_CCM_AHB1_RST_REG0_EHCI0 (1 << 26) -#define A31_CCM_AHB1_RST_REG0_GMAC (1 << 17) -#define A31_CCM_AHB1_RST_REG0_SDMMC (1 << 8) - -/* APB2_RST_REG */ -#define A31_CCM_APB2_RST_TWI (1 << 0) - - -/* GMAC */ -#define A31_CCM_GMAC_CLK_DELAY_SHIFT 10 -#define A31_CCM_GMAC_CLK_MODE_MASK 0x7 -#define A31_CCM_GMAC_MODE_RGMII (1 << 2) -#define A31_CCM_GMAC_CLK_MII 0x0 -#define A31_CCM_GMAC_CLK_EXT_RGMII 0x1 -#define A31_CCM_GMAC_CLK_RGMII 0x2 - -/* SD/MMC */ -#define A31_CCM_SD_CLK_SRC_SEL 0x3000000 -#define A31_CCM_SD_CLK_SRC_SEL_SHIFT 24 -#define A31_CCM_SD_CLK_SRC_SEL_OSC24M 0 -#define A31_CCM_SD_CLK_SRC_SEL_PLL6 1 -#define A31_CCM_SD_CLK_PHASE_CTR 0x700000 -#define A31_CCM_SD_CLK_PHASE_CTR_SHIFT 20 -#define A31_CCM_SD_CLK_DIV_RATIO_N 0x30000 -#define A31_CCM_SD_CLK_DIV_RATIO_N_SHIFT 16 -#define A31_CCM_SD_CLK_OPHASE_CTR 0x700 -#define A31_CCM_SD_CLK_OPHASE_CTR_SHIFT 8 -#define A31_CCM_SD_CLK_DIV_RATIO_M 0xf - -/* USB */ -#define A31_CCM_USBPHY_CLK_GATING_OHCI2 (1 << 18) -#define A31_CCM_USBPHY_CLK_GATING_OHCI1 (1 << 17) -#define A31_CCM_USBPHY_CLK_GATING_OHCI0 (1 << 16) -#define A31_CCM_USBPHY_CLK_GATING_USBPHY2 (1 << 10) -#define A31_CCM_USBPHY_CLK_GATING_USBPHY1 (1 << 9) -#define A31_CCM_USBPHY_CLK_GATING_USBPHY0 (1 << 8) -#define A31_CCM_USBPHY_CLK_USBPHY2_RST (1 << 2) -#define A31_CCM_USBPHY_CLK_USBPHY1_RST (1 << 1) -#define A31_CCM_USBPHY_CLK_USBPHY0_RST (1 << 0) - -#define A31_CCM_CLK_REF_FREQ 24000000U - -int a31_clk_gmac_activate(phandle_t); -int a31_clk_mmc_activate(int); -int a31_clk_mmc_cfg(int, int); -int a31_clk_i2c_activate(int); -int a31_clk_ehci_activate(void); -int a31_clk_ehci_deactivate(void); - -#endif /* _A31_CLK_H_ */ diff --git a/sys/arm/allwinner/a31/a31_padconf.c b/sys/arm/allwinner/a31/a31_padconf.c index 1a38c79..e591d57 100644 --- a/sys/arm/allwinner/a31/a31_padconf.c +++ b/sys/arm/allwinner/a31/a31_padconf.c @@ -51,19 +51,19 @@ const static struct allwinner_pins a31_pins[] = { {"PA12", 0, 12, {"gpio_in", "gpio_out", "gmac", "lcd1", "mmc3", "mmc2", NULL, NULL}}, {"PA13", 0, 13, {"gpio_in", "gpio_out", "gmac", "lcd1", "mmc3", "mmc2", NULL, NULL}}, {"PA14", 0, 14, {"gpio_in", "gpio_out", "gmac", "lcd1", "mmc3", "mmc2", NULL, NULL}}, - {"PA15", 0, 15, {"gpio_in", "gpio_out", "gmac", "lcd1", "dmic", NULL, NULL, NULL}}, + {"PA15", 0, 15, {"gpio_in", "gpio_out", "gmac", "lcd1", "clk_out_a", NULL, NULL, NULL}}, {"PA16", 0, 16, {"gpio_in", "gpio_out", "gmac", "lcd1", "dmic", NULL, NULL, NULL}}, - {"PA17", 0, 17, {"gpio_in", "gpio_out", "gmac", "lcd1", "clk_out_b", NULL, NULL, NULL}}, - {"PA18", 0, 17, {"gpio_in", "gpio_out", "gmac", "lcd1", "pwm3", NULL, NULL, NULL}}, - {"PA19", 0, 17, {"gpio_in", "gpio_out", "gmac", "lcd1", "pwm3", NULL, NULL, NULL}}, - {"PA20", 0, 17, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, - {"PA21", 0, 17, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, - {"PA22", 0, 17, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, - {"PA23", 0, 17, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, - {"PA24", 0, 17, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, - {"PA25", 0, 17, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, - {"PA26", 0, 17, {"gpio_in", "gpio_out", "gmac", "lcd1", "clk_out_c", NULL, NULL, NULL}}, - {"PA27", 0, 17, {"gpio_in", "gpio_out", "gmac", "lcd1", NULL, NULL, NULL, NULL}}, + {"PA17", 0, 17, {"gpio_in", "gpio_out", "gmac", "lcd1", "dmic", NULL, NULL, NULL}}, + {"PA18", 0, 18, {"gpio_in", "gpio_out", "gmac", "lcd1", "clk_out_b", NULL, NULL, NULL}}, + {"PA19", 0, 19, {"gpio_in", "gpio_out", "gmac", "lcd1", "pwm3", NULL, NULL, NULL}}, + {"PA20", 0, 20, {"gpio_in", "gpio_out", "gmac", "lcd1", "pwm3", NULL, NULL, NULL}}, + {"PA21", 0, 21, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, + {"PA22", 0, 22, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, + {"PA23", 0, 23, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, + {"PA24", 0, 24, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, + {"PA25", 0, 25, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, + {"PA26", 0, 26, {"gpio_in", "gpio_out", "gmac", "lcd1", "clk_out_c", NULL, NULL, NULL}}, + {"PA27", 0, 27, {"gpio_in", "gpio_out", "gmac", "lcd1", NULL, NULL, NULL, NULL}}, {"PB0", 1, 0, {"gpio_in", "gpio_out", "i2s0", "uart3", "csi", NULL, NULL, NULL}}, {"PB1", 1, 1, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, NULL, NULL}}, @@ -144,11 +144,11 @@ const static struct allwinner_pins a31_pins[] = { {"PE9", 4, 9, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, {"PE10", 4, 10, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, {"PE11", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, - {"PE12", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, - {"PE13", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, - {"PE14", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, - {"PE15", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, - {"PE16", 4, 11, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}}, + {"PE12", 4, 12, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE13", 4, 13, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE14", 4, 14, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE15", 4, 15, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE16", 4, 16, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}}, {"PF0", 5, 0, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, {"PF1", 5, 1, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, @@ -205,9 +205,9 @@ const static struct allwinner_pins a31_pins[] = { {"PH25", 7, 25, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, {"PH26", 7, 26, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, {"PH27", 7, 27, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, - {"PH28", 7, 27, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, - {"PH29", 7, 27, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, - {"PH30", 7, 27, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, + {"PH28", 7, 28, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PH29", 7, 29, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, + {"PH30", 7, 30, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, }; const struct allwinner_padconf a31_padconf = { diff --git a/sys/arm/allwinner/aw_ccu.c b/sys/arm/allwinner/aw_ccu.c new file mode 100644 index 0000000..d2ce774 --- /dev/null +++ b/sys/arm/allwinner/aw_ccu.c @@ -0,0 +1,224 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/fdt/simplebus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/extres/clk/clk.h> + +#include "clkdev_if.h" + +#define CCU_BASE 0x01c20000 +#define CCU_SIZE 0x400 + +struct aw_ccu_softc { + struct simplebus_softc sc; + bus_space_tag_t bst; + bus_space_handle_t bsh; + struct mtx mtx; +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10", 1 }, + { "allwinner,sun7i-a20", 1 }, + { "allwinner,sun6i-a31", 1 }, + { "allwinner,sun6i-a31s", 1 }, + { NULL, 0 } +}; + +static int +aw_ccu_check_addr(bus_addr_t addr) +{ + if (addr < CCU_BASE || addr >= (CCU_BASE + CCU_SIZE)) + return (EINVAL); + return (0); +} + +static int +aw_ccu_write_4(device_t dev, bus_addr_t addr, uint32_t val) +{ + struct aw_ccu_softc *sc; + + if (aw_ccu_check_addr(addr) != 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); + + return (0); +} + +static int +aw_ccu_read_4(device_t dev, bus_addr_t addr, uint32_t *val) +{ + struct aw_ccu_softc *sc; + + if (aw_ccu_check_addr(addr) != 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); + + 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; + uint32_t val; + + if (aw_ccu_check_addr(addr) != 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 &= ~clr; + val |= set; + bus_space_write_4(sc->bst, sc->bsh, addr - CCU_BASE, 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 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); + + 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); + + /* + * Map CCU 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); + } + + 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); diff --git a/sys/arm/allwinner/aw_reset.c b/sys/arm/allwinner/aw_reset.c new file mode 100644 index 0000000..3f56e9f --- /dev/null +++ b/sys/arm/allwinner/aw_reset.c @@ -0,0 +1,164 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 module software reset registers + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/extres/hwreset/hwreset.h> + +#include "hwreset_if.h" + +#define RESET_OFFSET(index) ((index / 32) * 4) +#define RESET_SHIFT(index) (index % 32) + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun6i-a31-ahb1-reset", 1 }, + { "allwinner,sun6i-a31-clock-reset", 1 }, + { NULL, 0 } +}; + +struct aw_reset_softc { + struct resource *res; + struct mtx mtx; +}; + +static struct resource_spec aw_reset_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define RESET_READ(sc, reg) bus_read_4((sc)->res, (reg)) +#define RESET_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) + +static int +aw_reset_assert(device_t dev, intptr_t id, bool reset) +{ + struct aw_reset_softc *sc; + uint32_t reg_value; + + sc = device_get_softc(dev); + + mtx_lock(&sc->mtx); + reg_value = RESET_READ(sc, RESET_OFFSET(id)); + if (reset) + reg_value &= ~(1 << RESET_SHIFT(id)); + else + reg_value |= (1 << RESET_SHIFT(id)); + RESET_WRITE(sc, RESET_OFFSET(id), reg_value); + mtx_unlock(&sc->mtx); + + return (0); +} + +static int +aw_reset_is_asserted(device_t dev, intptr_t id, bool *reset) +{ + struct aw_reset_softc *sc; + uint32_t reg_value; + + sc = device_get_softc(dev); + + mtx_lock(&sc->mtx); + reg_value = RESET_READ(sc, RESET_OFFSET(id)); + mtx_unlock(&sc->mtx); + + *reset = (reg_value & (1 << RESET_SHIFT(id))) != 0 ? false : true; + + return (0); +} + +static int +aw_reset_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 Resets"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_reset_attach(device_t dev) +{ + struct aw_reset_softc *sc; + + sc = device_get_softc(dev); + + if (bus_alloc_resources(dev, aw_reset_spec, &sc->res) != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + return (ENXIO); + } + + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + hwreset_register_ofw_provider(dev); + + return (0); +} + +static device_method_t aw_reset_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_reset_probe), + DEVMETHOD(device_attach, aw_reset_attach), + + /* Reset interface */ + DEVMETHOD(hwreset_assert, aw_reset_assert), + DEVMETHOD(hwreset_is_asserted, aw_reset_is_asserted), + + DEVMETHOD_END +}; + +static driver_t aw_reset_driver = { + "aw_reset", + aw_reset_methods, + sizeof(struct aw_reset_softc), +}; + +static devclass_t aw_reset_devclass; + +EARLY_DRIVER_MODULE(aw_reset, simplebus, aw_reset_driver, aw_reset_devclass, + 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(aw_reset, 1); diff --git a/sys/arm/allwinner/aw_usbphy.c b/sys/arm/allwinner/aw_usbphy.c index f0ee79d..25e8da0 100644 --- a/sys/arm/allwinner/aw_usbphy.c +++ b/sys/arm/allwinner/aw_usbphy.c @@ -44,10 +44,11 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> -#include "gpio_if.h" +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> +#include <dev/extres/regulator/regulator.h> #define USBPHY_NUMOFF 3 -#define GPIO_POLARITY(flags) (((flags) & 1) ? GPIO_PIN_LOW : GPIO_PIN_HIGH) static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-usb-phy", 1 }, @@ -58,89 +59,45 @@ static struct ofw_compat_data compat_data[] = { }; static int -awusbphy_gpio_set(device_t dev, phandle_t node, const char *pname) -{ - pcell_t gpio_prop[4]; - phandle_t gpio_node; - device_t gpio_dev; - uint32_t pin, flags; - ssize_t len; - int val; - - len = OF_getencprop(node, pname, gpio_prop, sizeof(gpio_prop)); - if (len == -1) - return (0); - - if (len != sizeof(gpio_prop)) { - device_printf(dev, "property %s length was %d, expected %d\n", - pname, len, sizeof(gpio_prop)); - return (ENXIO); - } - - gpio_node = OF_node_from_xref(gpio_prop[0]); - gpio_dev = OF_device_from_xref(gpio_prop[0]); - if (gpio_dev == NULL) { - device_printf(dev, "failed to get the GPIO device for %s\n", - pname); - return (ENOENT); - } - - if (GPIO_MAP_GPIOS(gpio_dev, node, gpio_node, - sizeof(gpio_prop) / sizeof(gpio_prop[0]) - 1, gpio_prop + 1, - &pin, &flags) != 0) { - device_printf(dev, "failed to map the GPIO pin for %s\n", - pname); - return (ENXIO); - } - - val = GPIO_POLARITY(flags); - - GPIO_PIN_SETFLAGS(gpio_dev, pin, GPIO_PIN_OUTPUT); - GPIO_PIN_SET(gpio_dev, pin, val); - - return (0); -} - -static int -awusbphy_supply_set(device_t dev, const char *pname) -{ - phandle_t node, reg_node; - pcell_t reg_xref; - - node = ofw_bus_get_node(dev); - - if (OF_getencprop(node, pname, ®_xref, sizeof(reg_xref)) == -1) - return (0); - - reg_node = OF_node_from_xref(reg_xref); - - return (awusbphy_gpio_set(dev, reg_node, "gpio")); -} - -static int awusbphy_init(device_t dev) { char pname[20]; - phandle_t node; int error, off; - - node = ofw_bus_get_node(dev); - - for (off = 0; off < USBPHY_NUMOFF; off++) { - snprintf(pname, sizeof(pname), "usb%d_id_det-gpio", off); - error = awusbphy_gpio_set(dev, node, pname); - if (error) + 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); + } + } - snprintf(pname, sizeof(pname), "usb%d_vbus_det-gpio", off); - error = awusbphy_gpio_set(dev, node, pname); - if (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); - error = awusbphy_supply_set(dev, pname); - if (error) + 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); diff --git a/sys/arm/allwinner/clk/aw_ahbclk.c b/sys/arm/allwinner/clk/aw_ahbclk.c new file mode 100644 index 0000000..1d3b1a4 --- /dev/null +++ b/sys/arm/allwinner/clk/aw_ahbclk.c @@ -0,0 +1,308 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk.h> + +#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 + +enum aw_ahbclk_type { + AW_A10_AHB = 1, + AW_A13_AHB, + AW_A31_AHB1, +}; + +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 }, + { 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; + 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: + 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; + default: + 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; + 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); diff --git a/sys/arm/allwinner/clk/aw_apbclk.c b/sys/arm/allwinner/clk/aw_apbclk.c new file mode 100644 index 0000000..a56387b --- /dev/null +++ b/sys/arm/allwinner/clk/aw_apbclk.c @@ -0,0 +1,283 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk.h> + +#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 + +enum aw_apbclk_type { + AW_A10_APB0 = 1, + AW_A10_APB1, +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-apb0-clk", AW_A10_APB0 }, + { "allwinner,sun4i-a10-apb1-clk", AW_A10_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: + 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; + 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); + 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; + *freq = *freq / n / m; + 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) + return (ERANGE); + + DEVICE_LOCK(sc); + APBCLK_READ(sc, &val); + val &= ~APB1_CLK_SRC_SEL; + val |= (index << 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); diff --git a/sys/arm/allwinner/clk/aw_axiclk.c b/sys/arm/allwinner/clk/aw_axiclk.c new file mode 100644 index 0000000..8f4a6d6 --- /dev/null +++ b/sys/arm/allwinner/clk/aw_axiclk.c @@ -0,0 +1,201 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 AXI clock + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk_mux.h> +#include <dev/extres/clk/clk_gate.h> + +#include "clkdev_if.h" + +#define AXI_CLK_DIV_RATIO (0x3 << 0) + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-axi-clk", 1 }, + { NULL, 0 } +}; + +struct aw_axiclk_sc { + device_t clkdev; + bus_addr_t reg; +}; + +#define AXICLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define AXICLK_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_axiclk_init(struct clknode *clk, device_t dev) +{ + clknode_init_parent_idx(clk, 0); + return (0); +} + +static int +aw_axiclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_axiclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + AXICLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + *freq = *freq / ((val & AXI_CLK_DIV_RATIO) + 1); + + return (0); +} + +static clknode_method_t aw_axiclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_axiclk_init), + CLKNODEMETHOD(clknode_recalc_freq, aw_axiclk_recalc_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_axiclk_clknode, aw_axiclk_clknode_class, + aw_axiclk_clknode_methods, sizeof(struct aw_axiclk_sc), clknode_class); + +static int +aw_axiclk_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 AXI Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_axiclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_axiclk_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; + + 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); + } + + clkdom = clkdom_create(dev); + + error = clk_get_by_ofw_index(dev, 0, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot parse clock parent\n"); + return (ENXIO); + } + + 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 *), M_OFWPROP, M_WAITOK); + def.parent_names[0] = clk_get_name(clk_parent); + def.parent_cnt = 1; + + clk = clknode_create(clkdom, &aw_axiclk_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); + + 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_axiclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_axiclk_probe), + DEVMETHOD(device_attach, aw_axiclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_axiclk_driver = { + "aw_axiclk", + aw_axiclk_methods, + 0 +}; + +static devclass_t aw_axiclk_devclass; + +EARLY_DRIVER_MODULE(aw_axiclk, simplebus, aw_axiclk_driver, + aw_axiclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); diff --git a/sys/arm/allwinner/clk/aw_codecclk.c b/sys/arm/allwinner/clk/aw_codecclk.c new file mode 100644 index 0000000..5c50f49 --- /dev/null +++ b/sys/arm/allwinner/clk/aw_codecclk.c @@ -0,0 +1,164 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 CODEC clock + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk_gate.h> +#include <dev/extres/hwreset/hwreset.h> + +#include "clkdev_if.h" + +#define SCLK_GATING_SHIFT 31 + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-codec-clk", 1 }, + { NULL, 0 } +}; + +static int +aw_codecclk_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 = SCLK_GATING_SHIFT; + def.mask = 1; + def.on_value = 1; + def.off_value = 0; + + return (clknode_gate_register(clkdom, &def)); +} + +static int +aw_codecclk_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 CODEC Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_codecclk_attach(device_t dev) +{ + struct clkdom *clkdom; + const char **names; + int 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 != 1) { + device_printf(dev, "must have exactly one output clock\n"); + error = ENOENT; + 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); + } + + error = aw_codecclk_create(dev, paddr, clkdom, + clk_get_name(clk_parent), names[0], 1); + + 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_codecclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_codecclk_probe), + DEVMETHOD(device_attach, aw_codecclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_codecclk_driver = { + "aw_codecclk", + aw_codecclk_methods, + 0 +}; + +static devclass_t aw_codecclk_devclass; + +EARLY_DRIVER_MODULE(aw_codecclk, simplebus, aw_codecclk_driver, + aw_codecclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); diff --git a/sys/arm/allwinner/clk/aw_cpuclk.c b/sys/arm/allwinner/clk/aw_cpuclk.c new file mode 100644 index 0000000..045751b --- /dev/null +++ b/sys/arm/allwinner/clk/aw_cpuclk.c @@ -0,0 +1,161 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 CPU clock + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk_mux.h> + +#define CPU_CLK_SRC_SEL_WIDTH 2 +#define CPU_CLK_SRC_SEL_SHIFT 16 + +static int +aw_cpuclk_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-cpu-clk")) + return (ENXIO); + + device_set_desc(dev, "Allwinner CPU Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_cpuclk_attach(device_t dev) +{ + struct clk_mux_def def; + struct clkdom *clkdom; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node; + int error, ncells, i; + clk_t clk; + + 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.clkdef.id = 1; + def.clkdef.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); + if (error != 0) { + device_printf(dev, "cannot get clock %d\n", i); + goto fail; + } + def.clkdef.parent_names[i] = clk_get_name(clk); + clk_release(clk); + } + def.clkdef.parent_cnt = ncells; + def.offset = paddr; + def.shift = CPU_CLK_SRC_SEL_SHIFT; + def.width = CPU_CLK_SRC_SEL_WIDTH; + + error = clk_parse_ofw_clk_name(dev, node, &def.clkdef.name); + if (error != 0) { + device_printf(dev, "cannot parse clock name\n"); + error = ENXIO; + goto fail; + } + + error = clknode_mux_register(clkdom, &def); + if (error != 0) { + device_printf(dev, "cannot register mux clock\n"); + error = ENXIO; + goto fail; + } + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + free(__DECONST(char *, def.clkdef.parent_names), M_OFWPROP); + free(__DECONST(char *, def.clkdef.name), M_OFWPROP); + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); + +fail: + free(__DECONST(char *, def.clkdef.name), M_OFWPROP); + return (error); +} + +static device_method_t aw_cpuclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_cpuclk_probe), + DEVMETHOD(device_attach, aw_cpuclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_cpuclk_driver = { + "aw_cpuclk", + aw_cpuclk_methods, + 0 +}; + +static devclass_t aw_cpuclk_devclass; + +EARLY_DRIVER_MODULE(aw_cpuclk, simplebus, aw_cpuclk_driver, + aw_cpuclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); diff --git a/sys/arm/allwinner/clk/aw_debeclk.c b/sys/arm/allwinner/clk/aw_debeclk.c new file mode 100644 index 0000000..885fad8 --- /dev/null +++ b/sys/arm/allwinner/clk/aw_debeclk.c @@ -0,0 +1,351 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 display backend clocks + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> + +#include "clkdev_if.h" +#include "hwreset_if.h" + +#define SCLK_GATING (1 << 31) +#define BE_RST (1 << 30) +#define CLK_SRC_SEL (0x3 << 24) +#define CLK_SRC_SEL_SHIFT 24 +#define CLK_SRC_SEL_MAX 2 +#define CLK_SRC_SEL_PLL3 0 +#define CLK_SRC_SEL_PLL7 1 +#define CLK_SRC_SEL_PLL5 2 +#define CLK_RATIO_M (0xf << 0) +#define CLK_RATIO_M_SHIFT 0 +#define CLK_RATIO_M_MAX 0xf + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-de-be-clk", 1 }, + { NULL, 0 } +}; + +struct aw_debeclk_softc { + device_t clkdev; + bus_addr_t reg; +}; + +#define DEBECLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define DEBECLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) +#define DEBECLK_MODIFY(sc, clr, set) \ + CLKDEV_MODIFY_4((sc)->clkdev, (sc)->reg, (clr), (set)) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +static int +aw_debeclk_hwreset_assert(device_t dev, intptr_t id, bool value) +{ + struct aw_debeclk_softc *sc; + int error; + + sc = device_get_softc(dev); + + DEVICE_LOCK(sc); + error = DEBECLK_MODIFY(sc, BE_RST, value ? 0 : BE_RST); + DEVICE_UNLOCK(sc); + + return (error); +} + +static int +aw_debeclk_hwreset_is_asserted(device_t dev, intptr_t id, bool *value) +{ + struct aw_debeclk_softc *sc; + uint32_t val; + int error; + + sc = device_get_softc(dev); + + DEVICE_LOCK(sc); + error = DEBECLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + if (error) + return (error); + + *value = (val & BE_RST) != 0 ? false : true; + + return (0); +} + +static int +aw_debeclk_init(struct clknode *clk, device_t dev) +{ + struct aw_debeclk_softc *sc; + uint32_t val, index; + + sc = clknode_get_softc(clk); + + /* Set BE source to PLL5 (DDR external peripheral clock) */ + index = CLK_SRC_SEL_PLL5; + + DEVICE_LOCK(sc); + DEBECLK_READ(sc, &val); + val &= ~CLK_SRC_SEL; + val |= (index << CLK_SRC_SEL_SHIFT); + DEBECLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_debeclk_set_mux(struct clknode *clk, int index) +{ + struct aw_debeclk_softc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + if (index < 0 || index > CLK_SRC_SEL_MAX) + return (ERANGE); + + DEVICE_LOCK(sc); + DEBECLK_READ(sc, &val); + val &= ~CLK_SRC_SEL; + val |= (index << CLK_SRC_SEL_SHIFT); + DEBECLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_debeclk_set_gate(struct clknode *clk, bool enable) +{ + struct aw_debeclk_softc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + DEBECLK_READ(sc, &val); + if (enable) + val |= SCLK_GATING; + else + val &= ~SCLK_GATING; + DEBECLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_debeclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_debeclk_softc *sc; + uint32_t val, m; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + DEBECLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1; + + *freq = *freq / m; + + return (0); +} + +static int +aw_debeclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct aw_debeclk_softc *sc; + uint32_t val, m; + + sc = clknode_get_softc(clk); + + m = howmany(fin, *fout) - 1; + + DEVICE_LOCK(sc); + DEBECLK_READ(sc, &val); + val &= ~CLK_RATIO_M; + val |= (m << CLK_RATIO_M_SHIFT); + DEBECLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + *fout = fin / (m + 1); + *stop = 1; + + return (0); +} + +static clknode_method_t aw_debeclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_debeclk_init), + CLKNODEMETHOD(clknode_set_gate, aw_debeclk_set_gate), + CLKNODEMETHOD(clknode_set_mux, aw_debeclk_set_mux), + CLKNODEMETHOD(clknode_recalc_freq, aw_debeclk_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, aw_debeclk_set_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_debeclk_clknode, aw_debeclk_clknode_class, + aw_debeclk_clknode_methods, sizeof(struct aw_debeclk_softc), clknode_class); + +static int +aw_debeclk_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 Display Engine Backend Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_debeclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_debeclk_softc *sc, *clk_sc; + struct clkdom *clkdom; + struct clknode *clk; + clk_t clk_parent; + bus_size_t psize; + phandle_t node; + int error, ncells, i; + + sc = device_get_softc(dev); + sc->clkdev = device_get_parent(dev); + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &sc->reg, &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_debeclk_clknode_class, &def); + if (clk == NULL) { + device_printf(dev, "cannot create clknode\n"); + error = ENXIO; + goto fail; + } + + clk_sc = clknode_get_softc(clk); + clk_sc->reg = sc->reg; + clk_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); + + hwreset_register_ofw_provider(dev); + + return (0); + +fail: + return (error); +} + +static device_method_t aw_debeclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_debeclk_probe), + DEVMETHOD(device_attach, aw_debeclk_attach), + + /* Reset interface */ + DEVMETHOD(hwreset_assert, aw_debeclk_hwreset_assert), + DEVMETHOD(hwreset_is_asserted, aw_debeclk_hwreset_is_asserted), + + DEVMETHOD_END +}; + +static driver_t aw_debeclk_driver = { + "aw_debeclk", + aw_debeclk_methods, + sizeof(struct aw_debeclk_softc) +}; + +static devclass_t aw_debeclk_devclass; + +EARLY_DRIVER_MODULE(aw_debeclk, simplebus, aw_debeclk_driver, + aw_debeclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); diff --git a/sys/arm/allwinner/clk/aw_gate.c b/sys/arm/allwinner/clk/aw_gate.c new file mode 100644 index 0000000..d43d021 --- /dev/null +++ b/sys/arm/allwinner/clk/aw_gate.c @@ -0,0 +1,198 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk_gate.h> + +#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" }, + + { 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); diff --git a/sys/arm/allwinner/clk/aw_gmacclk.c b/sys/arm/allwinner/clk/aw_gmacclk.c new file mode 100644 index 0000000..5e2f618 --- /dev/null +++ b/sys/arm/allwinner/clk/aw_gmacclk.c @@ -0,0 +1,259 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk_mux.h> +#include <dev/extres/clk/clk_gate.h> + +#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 CLK_IDX_MII 0 +#define CLK_IDX_RGMII 1 +#define CLK_IDX_COUNT 2 + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun7i-a20-gmac-clk", 1 }, + { NULL, 0 } +}; + +struct aw_gmacclk_sc { + device_t clkdev; + bus_addr_t reg; +}; + +#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; + 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; + break; + case CLK_IDX_RGMII: + clk_src = GMAC_CLK_SRC_RGMII; + pit = GMAC_CLK_PIT_RGMII; + 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); + 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"); + 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); + + 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); diff --git a/sys/arm/allwinner/clk/aw_hdmiclk.c b/sys/arm/allwinner/clk/aw_hdmiclk.c new file mode 100644 index 0000000..635418c --- /dev/null +++ b/sys/arm/allwinner/clk/aw_hdmiclk.c @@ -0,0 +1,315 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 HDMI clock + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk_mux.h> +#include <dev/extres/clk/clk_gate.h> + +#include "clkdev_if.h" + +#define SCLK_GATING (1 << 31) +#define CLK_SRC_SEL (0x3 << 24) +#define CLK_SRC_SEL_SHIFT 24 +#define CLK_SRC_SEL_MAX 0x3 +#define CLK_RATIO_N (0x3 << 16) +#define CLK_RATIO_N_SHIFT 16 +#define CLK_RATIO_N_MAX 0x3 +#define CLK_RATIO_M (0x1f << 0) +#define CLK_RATIO_M_SHIFT 0 +#define CLK_RATIO_M_MAX 0x1f + +#define CLK_IDX_PLL3_1X 0 + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-hdmi-clk", 1 }, + { NULL, 0 } +}; + +struct aw_hdmiclk_sc { + device_t clkdev; + bus_addr_t reg; +}; + +#define HDMICLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define HDMICLK_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_hdmiclk_init(struct clknode *clk, device_t dev) +{ + struct aw_hdmiclk_sc *sc; + uint32_t val, index; + + sc = clknode_get_softc(clk); + + /* Select PLL3(1X) clock source */ + index = CLK_IDX_PLL3_1X; + + DEVICE_LOCK(sc); + HDMICLK_READ(sc, &val); + val &= ~CLK_SRC_SEL; + val |= (index << CLK_SRC_SEL_SHIFT); + HDMICLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_hdmiclk_set_mux(struct clknode *clk, int index) +{ + struct aw_hdmiclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + if (index < 0 || index > CLK_SRC_SEL_MAX) + return (ERANGE); + + DEVICE_LOCK(sc); + HDMICLK_READ(sc, &val); + val &= ~CLK_SRC_SEL; + val |= (index << CLK_SRC_SEL_SHIFT); + HDMICLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_hdmiclk_set_gate(struct clknode *clk, bool enable) +{ + struct aw_hdmiclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + HDMICLK_READ(sc, &val); + if (enable) + val |= SCLK_GATING; + else + val &= ~SCLK_GATING; + HDMICLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_hdmiclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_hdmiclk_sc *sc; + uint32_t val, m, n; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + HDMICLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + n = 1 << ((val & CLK_RATIO_N) >> CLK_RATIO_N_SHIFT); + m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1; + + *freq = *freq / n / m; + + return (0); +} + +static int +aw_hdmiclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct aw_hdmiclk_sc *sc; + uint32_t val, m, n, best_m, best_n; + uint64_t cur_freq; + int64_t best_diff, cur_diff; + + sc = clknode_get_softc(clk); + best_n = best_m = 0; + best_diff = (int64_t)*fout; + + for (n = 0; n <= CLK_RATIO_N_MAX; n++) + for (m = 0; m <= CLK_RATIO_M_MAX; m++) { + cur_freq = fin / (1 << n) / (m + 1); + cur_diff = (int64_t)*fout - cur_freq; + if (cur_diff >= 0 && cur_diff < best_diff) { + best_diff = cur_diff; + best_m = m; + best_n = n; + } + } + + if (best_diff == (int64_t)*fout) + return (ERANGE); + + DEVICE_LOCK(sc); + HDMICLK_READ(sc, &val); + val &= ~(CLK_RATIO_N | CLK_RATIO_M); + val |= (best_n << CLK_RATIO_N_SHIFT); + val |= (best_m << CLK_RATIO_M_SHIFT); + HDMICLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + *fout = fin / (1 << best_n) / (best_m + 1); + *stop = 1; + + return (0); +} + +static clknode_method_t aw_hdmiclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_hdmiclk_init), + CLKNODEMETHOD(clknode_set_gate, aw_hdmiclk_set_gate), + CLKNODEMETHOD(clknode_set_mux, aw_hdmiclk_set_mux), + CLKNODEMETHOD(clknode_recalc_freq, aw_hdmiclk_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, aw_hdmiclk_set_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_hdmiclk_clknode, aw_hdmiclk_clknode_class, + aw_hdmiclk_clknode_methods, sizeof(struct aw_hdmiclk_sc), clknode_class); + +static int +aw_hdmiclk_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 HDMI Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_hdmiclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_hdmiclk_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; + + 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); + } + + clkdom = clkdom_create(dev); + + error = clk_get_by_ofw_index(dev, 0, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot parse clock parent\n"); + return (ENXIO); + } + + 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 *), M_OFWPROP, M_WAITOK); + def.parent_names[0] = clk_get_name(clk_parent); + def.parent_cnt = 1; + + clk = clknode_create(clkdom, &aw_hdmiclk_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); + + 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_hdmiclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_hdmiclk_probe), + DEVMETHOD(device_attach, aw_hdmiclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_hdmiclk_driver = { + "aw_hdmiclk", + aw_hdmiclk_methods, + 0 +}; + +static devclass_t aw_hdmiclk_devclass; + +EARLY_DRIVER_MODULE(aw_hdmiclk, simplebus, aw_hdmiclk_driver, + aw_hdmiclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); diff --git a/sys/arm/allwinner/clk/aw_lcdclk.c b/sys/arm/allwinner/clk/aw_lcdclk.c new file mode 100644 index 0000000..bebee01 --- /dev/null +++ b/sys/arm/allwinner/clk/aw_lcdclk.c @@ -0,0 +1,560 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 LCD clocks + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> + +#include "clkdev_if.h" +#include "hwreset_if.h" + +/* CH0 */ +#define CH0_SCLK_GATING (1 << 31) +#define CH0_LCD_RST (1 << 30) +#define CH0_CLK_SRC_SEL (0x3 << 24) +#define CH0_CLK_SRC_SEL_SHIFT 24 +#define CH0_CLK_SRC_SEL_PLL3_1X 0 +#define CH0_CLK_SRC_SEL_PLL7_1X 1 +#define CH0_CLK_SRC_SEL_PLL3_2X 2 +#define CH0_CLK_SRC_SEL_PLL6 3 + +/* CH1 */ +#define CH1_SCLK2_GATING (1 << 31) +#define CH1_SCLK2_SEL (0x3 << 24) +#define CH1_SCLK2_SEL_SHIFT 24 +#define CH1_SCLK2_SEL_PLL3_1X 0 +#define CH1_SCLK2_SEL_PLL7_1X 1 +#define CH1_SCLK2_SEL_PLL3_2X 2 +#define CH1_SCLK2_SEL_PLL7_2X 3 +#define CH1_SCLK1_GATING (1 << 15) +#define CH1_SCLK1_SEL (0x1 << 11) +#define CH1_SCLK1_SEL_SHIFT 11 +#define CH1_SCLK1_SEL_SCLK2 0 +#define CH1_SCLK1_SEL_SCLK2_DIV2 1 +#define CH1_CLK_DIV_RATIO_M (0x1f << 0) +#define CH1_CLK_DIV_RATIO_M_SHIFT 0 + +#define TCON_PLLREF 3000000ULL +#define TCON_PLL_M_MIN 1 +#define TCON_PLL_M_MAX 15 +#define TCON_PLL_N_MIN 9 +#define TCON_PLL_N_MAX 127 + +#define CLK_IDX_CH1_SCLK1 0 +#define CLK_IDX_CH1_SCLK2 1 + +#define CLK_IDX_ + +enum aw_lcdclk_type { + AW_LCD_CH0 = 1, + AW_LCD_CH1, +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-lcd-ch0-clk", AW_LCD_CH0 }, + { "allwinner,sun4i-a10-lcd-ch1-clk", AW_LCD_CH1 }, + { NULL, 0 } +}; + +struct aw_lcdclk_softc { + enum aw_lcdclk_type type; + device_t clkdev; + bus_addr_t reg; + int id; +}; + +#define LCDCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define LCDCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) +#define LCDCLK_MODIFY(sc, clr, set) \ + CLKDEV_MODIFY_4((sc)->clkdev, (sc)->reg, (clr), (set)) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +static int +aw_lcdclk_hwreset_assert(device_t dev, intptr_t id, bool value) +{ + struct aw_lcdclk_softc *sc; + int error; + + sc = device_get_softc(dev); + + if (sc->type != AW_LCD_CH0) + return (ENXIO); + + DEVICE_LOCK(sc); + error = LCDCLK_MODIFY(sc, CH0_LCD_RST, value ? 0 : CH0_LCD_RST); + DEVICE_UNLOCK(sc); + + return (error); +} + +static int +aw_lcdclk_hwreset_is_asserted(device_t dev, intptr_t id, bool *value) +{ + struct aw_lcdclk_softc *sc; + uint32_t val; + int error; + + sc = device_get_softc(dev); + + if (sc->type != AW_LCD_CH0) + return (ENXIO); + + DEVICE_LOCK(sc); + error = LCDCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + if (error) + return (error); + + *value = (val & CH0_LCD_RST) != 0 ? false : true; + + return (0); +} + +static int +aw_lcdclk_init(struct clknode *clk, device_t dev) +{ + struct aw_lcdclk_softc *sc; + uint32_t val, index; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + LCDCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + switch (sc->type) { + case AW_LCD_CH0: + index = (val & CH0_CLK_SRC_SEL) >> CH0_CLK_SRC_SEL_SHIFT; + break; + case AW_LCD_CH1: + switch (sc->id) { + case CLK_IDX_CH1_SCLK1: + index = 0; + break; + case CLK_IDX_CH1_SCLK2: + index = (val & CH1_SCLK2_SEL_SHIFT) >> + CH1_SCLK2_SEL_SHIFT; + break; + default: + return (ENXIO); + } + break; + default: + return (ENXIO); + } + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_lcdclk_set_mux(struct clknode *clk, int index) +{ + struct aw_lcdclk_softc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + switch (sc->type) { + case AW_LCD_CH0: + DEVICE_LOCK(sc); + LCDCLK_READ(sc, &val); + val &= ~CH0_CLK_SRC_SEL; + val |= (index << CH0_CLK_SRC_SEL_SHIFT); + LCDCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + break; + case AW_LCD_CH1: + switch (sc->id) { + case CLK_IDX_CH1_SCLK2: + DEVICE_LOCK(sc); + LCDCLK_READ(sc, &val); + val &= ~CH1_SCLK2_SEL; + val |= (index << CH1_SCLK2_SEL_SHIFT); + LCDCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + break; + default: + return (ENXIO); + } + break; + default: + return (ENXIO); + } + + return (0); +} + +static int +aw_lcdclk_set_gate(struct clknode *clk, bool enable) +{ + struct aw_lcdclk_softc *sc; + uint32_t val, mask; + + sc = clknode_get_softc(clk); + + switch (sc->type) { + case AW_LCD_CH0: + mask = CH0_SCLK_GATING; + break; + case AW_LCD_CH1: + mask = (sc->id == CLK_IDX_CH1_SCLK1) ? CH1_SCLK1_GATING : + CH1_SCLK2_GATING; + break; + default: + return (ENXIO); + } + + DEVICE_LOCK(sc); + LCDCLK_READ(sc, &val); + if (enable) + val |= mask; + else + val &= ~mask; + LCDCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_lcdclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_lcdclk_softc *sc; + uint32_t val, m; + + sc = clknode_get_softc(clk); + + if (sc->type != AW_LCD_CH1) + return (0); + + DEVICE_LOCK(sc); + LCDCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + m = ((val & CH1_CLK_DIV_RATIO_M) >> CH1_CLK_DIV_RATIO_M_SHIFT) + 1; + *freq = *freq / m; + + if (sc->id == CLK_IDX_CH1_SCLK1) { + if ((val & CH1_SCLK1_SEL) == CH1_SCLK1_SEL_SCLK2_DIV2) + *freq /= 2; + } + + return (0); +} + +static void +calc_tcon_pll(uint64_t fin, uint64_t fout, uint32_t *pm, uint32_t *pn) +{ + int64_t diff, fcur, best; + int m, n; + + best = fout; + for (m = TCON_PLL_M_MIN; m <= TCON_PLL_M_MAX; m++) { + for (n = TCON_PLL_N_MIN; n <= TCON_PLL_N_MAX; n++) { + fcur = (n * fin) / m; + diff = (int64_t)fout - fcur; + if (diff > 0 && diff < best) { + best = diff; + *pm = m; + *pn = n; + } + } + } +} + +static int +aw_lcdclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct aw_lcdclk_softc *sc; + uint32_t val, m, m2, n, n2, src_sel; + uint64_t fsingle, fdouble; + int error; + bool dbl; + + sc = clknode_get_softc(clk); + + switch (sc->type) { + case AW_LCD_CH0: + *stop = 0; + break; + case AW_LCD_CH1: + if (sc->id != CLK_IDX_CH1_SCLK2) + return (ENXIO); + + m = n = m2 = n2 = 0; + dbl = false; + + /* Find the frequency closes to the target dot clock, using + * both 1X and 2X PLL inputs as possible candidates. + */ + calc_tcon_pll(TCON_PLLREF, *fout, &m, &n); + calc_tcon_pll(TCON_PLLREF * 2, *fout, &m2, &n2); + + fsingle = m ? (n * TCON_PLLREF) / m : 0; + fdouble = m2 ? (n2 * TCON_PLLREF * 2) / m2 : 0; + + if (fdouble > fsingle) { + dbl = true; + m = m2; + n = n2; + } + + src_sel = dbl ? CH0_CLK_SRC_SEL_PLL3_2X : + CH0_CLK_SRC_SEL_PLL3_1X; + + /* Switch parent clock if necessary */ + if (src_sel != clknode_get_parent_idx(clk)) { + error = clknode_set_parent_by_idx(clk, src_sel); + if (error != 0) + return (error); + } + + /* Set desired parent frequency */ + fin = n * TCON_PLLREF; + + error = clknode_set_freq(clknode_get_parent(clk), fin, 0, 0); + if (error != 0) + return (error); + + error = clknode_enable(clknode_get_parent(clk)); + if (error != 0) + return (error); + + /* Fetch new input frequency */ + error = clknode_get_freq(clknode_get_parent(clk), &fin); + if (error != 0) + return (error); + + /* Set LCD divisor */ + DEVICE_LOCK(sc); + LCDCLK_READ(sc, &val); + val &= ~CH1_CLK_DIV_RATIO_M; + val |= ((m - 1) << CH1_CLK_DIV_RATIO_M_SHIFT); + LCDCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + *fout = fin / m; + *stop = 1; + + break; + } + + return (0); +} + +static clknode_method_t aw_lcdclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_lcdclk_init), + CLKNODEMETHOD(clknode_set_gate, aw_lcdclk_set_gate), + CLKNODEMETHOD(clknode_set_mux, aw_lcdclk_set_mux), + CLKNODEMETHOD(clknode_recalc_freq, aw_lcdclk_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, aw_lcdclk_set_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_lcdclk_clknode, aw_lcdclk_clknode_class, + aw_lcdclk_clknode_methods, sizeof(struct aw_lcdclk_softc), clknode_class); + +static int +aw_lcdclk_create(device_t dev, struct clkdom *clkdom, + const char **parent_names, int parent_cnt, const char *name, int index) +{ + struct aw_lcdclk_softc *sc, *clk_sc; + struct clknode_init_def def; + struct clknode *clk; + phandle_t node; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + + memset(&def, 0, sizeof(def)); + def.id = index; + def.name = name; + def.parent_names = parent_names; + def.parent_cnt = parent_cnt; + + clk = clknode_create(clkdom, &aw_lcdclk_clknode_class, &def); + if (clk == NULL) { + device_printf(dev, "cannot create clknode\n"); + return (ENXIO); + } + + clk_sc = clknode_get_softc(clk); + clk_sc->type = sc->type; + clk_sc->reg = sc->reg; + clk_sc->clkdev = sc->clkdev; + clk_sc->id = index; + + clknode_register(clkdom, clk); + + return (0); +} + +static int +aw_lcdclk_probe(device_t dev) +{ + enum aw_lcdclk_type type; + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + switch (type) { + case AW_LCD_CH0: + device_set_desc(dev, "Allwinner LCD CH0 Clock"); + break; + case AW_LCD_CH1: + device_set_desc(dev, "Allwinner LCD CH1 Clock"); + break; + default: + return (ENXIO); + } + + return (BUS_PROBE_DEFAULT); +} + +static int +aw_lcdclk_attach(device_t dev) +{ + struct aw_lcdclk_softc *sc; + struct clkdom *clkdom; + clk_t clk_parent; + bus_size_t psize; + phandle_t node; + uint32_t *indices; + const char **parent_names; + const char **names; + int error, ncells, nout, i; + + sc = device_get_softc(dev); + sc->clkdev = device_get_parent(dev); + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &sc->reg, &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); + } + + 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; + } + parent_names[i] = clk_get_name(clk_parent); + clk_release(clk_parent); + } + + nout = clk_parse_ofw_out_names(dev, node, &names, &indices); + if (nout == 0) { + device_printf(dev, "no clock outputs found\n"); + return (error); + } + + clkdom = clkdom_create(dev); + + for (i = 0; i < nout; i++) { + error = aw_lcdclk_create(dev, clkdom, parent_names, ncells, + names[i], nout == 1 ? 1 : i); + 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); + + if (sc->type == AW_LCD_CH0) + hwreset_register_ofw_provider(dev); + + free(parent_names, M_OFWPROP); + return (0); + +fail: + free(parent_names, M_OFWPROP); + return (error); +} + +static device_method_t aw_lcdclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_lcdclk_probe), + DEVMETHOD(device_attach, aw_lcdclk_attach), + + /* Reset interface */ + DEVMETHOD(hwreset_assert, aw_lcdclk_hwreset_assert), + DEVMETHOD(hwreset_is_asserted, aw_lcdclk_hwreset_is_asserted), + + DEVMETHOD_END +}; + +static driver_t aw_lcdclk_driver = { + "aw_lcdclk", + aw_lcdclk_methods, + sizeof(struct aw_lcdclk_softc) +}; + +static devclass_t aw_lcdclk_devclass; + +EARLY_DRIVER_MODULE(aw_lcdclk, simplebus, aw_lcdclk_driver, + aw_lcdclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); diff --git a/sys/arm/allwinner/clk/aw_mmcclk.c b/sys/arm/allwinner/clk/aw_mmcclk.c new file mode 100644 index 0000000..653afd9 --- /dev/null +++ b/sys/arm/allwinner/clk/aw_mmcclk.c @@ -0,0 +1,351 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 MMC clocks + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk_mux.h> +#include <dev/extres/clk/clk_gate.h> + +#include "clkdev_if.h" + +#define SCLK_GATING (1 << 31) +#define CLK_SRC_SEL (0x3 << 24) +#define CLK_SRC_SEL_SHIFT 24 +#define CLK_SRC_SEL_MAX 0x3 +#define CLK_SRC_SEL_OSC24M 0 +#define CLK_SRC_SEL_PLL6 1 +#define CLK_PHASE_CTR (0x7 << 20) +#define CLK_PHASE_CTR_SHIFT 20 +#define CLK_RATIO_N (0x3 << 16) +#define CLK_RATIO_N_SHIFT 16 +#define CLK_RATIO_N_MAX 0x3 +#define OUTPUT_CLK_PHASE_CTR (0x7 << 8) +#define OUTPUT_CLK_PHASE_CTR_SHIFT 8 +#define CLK_RATIO_M (0xf << 0) +#define CLK_RATIO_M_SHIFT 0 +#define CLK_RATIO_M_MAX 0xf + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-mmc-clk", 1 }, + { NULL, 0 } +}; + +struct aw_mmcclk_sc { + device_t clkdev; + bus_addr_t reg; +}; + +#define MODCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define MODCLK_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_mmcclk_init(struct clknode *clk, device_t dev) +{ + struct aw_mmcclk_sc *sc; + uint32_t val, index; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + index = (val & CLK_SRC_SEL) >> CLK_SRC_SEL_SHIFT; + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_mmcclk_set_mux(struct clknode *clk, int index) +{ + struct aw_mmcclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + if (index < 0 || index > CLK_SRC_SEL_MAX) + return (ERANGE); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + val &= ~CLK_SRC_SEL; + val |= (index << CLK_SRC_SEL_SHIFT); + MODCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_mmcclk_set_gate(struct clknode *clk, bool enable) +{ + struct aw_mmcclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + if (enable) + val |= SCLK_GATING; + else + val &= ~SCLK_GATING; + MODCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_mmcclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_mmcclk_sc *sc; + uint32_t val, m, n; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + n = 1 << ((val & CLK_RATIO_N) >> CLK_RATIO_N_SHIFT); + m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1; + + *freq = *freq / n / m; + + return (0); +} + +static int +aw_mmcclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct aw_mmcclk_sc *sc; + uint32_t val, m, n, phase, ophase; + int parent_idx, error; + + sc = clknode_get_softc(clk); + + /* XXX + * The ophase/phase values should be set by the MMC driver, but + * there is currently no way to do this with the clk API + */ + if (*fout <= 400000) { + parent_idx = CLK_SRC_SEL_OSC24M; + ophase = 0; + phase = 0; + n = 2; + } else if (*fout <= 25000000) { + parent_idx = CLK_SRC_SEL_PLL6; + ophase = 0; + phase = 5; + n = 2; + } else if (*fout <= 50000000) { + parent_idx = CLK_SRC_SEL_PLL6; + ophase = 3; + phase = 5; + n = 0; + } else + return (ERANGE); + + /* Switch parent clock, if necessary */ + if (parent_idx != clknode_get_parent_idx(clk)) { + error = clknode_set_parent_by_idx(clk, parent_idx); + if (error != 0) + return (error); + + /* Fetch new input frequency */ + error = clknode_get_freq(clknode_get_parent(clk), &fin); + if (error != 0) + return (error); + } + + m = ((fin / (1 << n)) / *fout) - 1; + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + val &= ~(CLK_RATIO_N | CLK_RATIO_M | CLK_PHASE_CTR | + OUTPUT_CLK_PHASE_CTR); + val |= (n << CLK_RATIO_N_SHIFT); + val |= (m << CLK_RATIO_M_SHIFT); + val |= (phase << CLK_PHASE_CTR_SHIFT); + val |= (ophase << OUTPUT_CLK_PHASE_CTR_SHIFT); + MODCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + *fout = fin / (1 << n) / (m + 1); + *stop = 1; + + return (0); +} + +static clknode_method_t aw_mmcclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_mmcclk_init), + CLKNODEMETHOD(clknode_set_gate, aw_mmcclk_set_gate), + CLKNODEMETHOD(clknode_set_mux, aw_mmcclk_set_mux), + CLKNODEMETHOD(clknode_recalc_freq, aw_mmcclk_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, aw_mmcclk_set_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_mmcclk_clknode, aw_mmcclk_clknode_class, + aw_mmcclk_clknode_methods, sizeof(struct aw_mmcclk_sc), clknode_class); + +static int +aw_mmcclk_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 MMC Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_mmcclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_mmcclk_sc *sc; + struct clkdom *clkdom; + struct clknode *clk; + const char **names; + uint32_t *indices; + clk_t clk_parent; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node; + int error, nout, 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 == 0) { + device_printf(dev, "couldn't find parent clocks\n"); + return (ENXIO); + } + + clkdom = clkdom_create(dev); + + nout = clk_parse_ofw_out_names(dev, node, &names, &indices); + if (nout == 0) { + device_printf(dev, "no output clocks found\n"); + error = ENXIO; + goto fail; + } + + memset(&def, 0, sizeof(def)); + def.name = names[0]; + def.id = 0; + 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; + def.flags = CLK_NODE_GLITCH_FREE; + + clk = clknode_create(clkdom, &aw_mmcclk_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); + + 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_mmcclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_mmcclk_probe), + DEVMETHOD(device_attach, aw_mmcclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_mmcclk_driver = { + "aw_mmcclk", + aw_mmcclk_methods, + 0 +}; + +static devclass_t aw_mmcclk_devclass; + +EARLY_DRIVER_MODULE(aw_mmcclk, simplebus, aw_mmcclk_driver, + aw_mmcclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); diff --git a/sys/arm/allwinner/clk/aw_modclk.c b/sys/arm/allwinner/clk/aw_modclk.c new file mode 100644 index 0000000..f4755ea --- /dev/null +++ b/sys/arm/allwinner/clk/aw_modclk.c @@ -0,0 +1,318 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 module clocks + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk_mux.h> +#include <dev/extres/clk/clk_gate.h> + +#include "clkdev_if.h" + +#define SCLK_GATING (1 << 31) +#define CLK_SRC_SEL (0x3 << 24) +#define CLK_SRC_SEL_SHIFT 24 +#define CLK_SRC_SEL_MAX 0x3 +#define CLK_RATIO_N (0x3 << 16) +#define CLK_RATIO_N_SHIFT 16 +#define CLK_RATIO_N_MAX 0x3 +#define CLK_RATIO_M (0x1f << 0) +#define CLK_RATIO_M_SHIFT 0 +#define CLK_RATIO_M_MAX 0x1f + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-mod0-clk", 1 }, + { NULL, 0 } +}; + +struct aw_modclk_sc { + device_t clkdev; + bus_addr_t reg; +}; + +#define MODCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define MODCLK_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_modclk_init(struct clknode *clk, device_t dev) +{ + struct aw_modclk_sc *sc; + uint32_t val, index; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + index = (val & CLK_SRC_SEL) >> CLK_SRC_SEL_SHIFT; + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_modclk_set_mux(struct clknode *clk, int index) +{ + struct aw_modclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + if (index < 0 || index > CLK_SRC_SEL_MAX) + return (ERANGE); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + val &= ~CLK_SRC_SEL; + val |= (index << CLK_SRC_SEL_SHIFT); + MODCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_modclk_set_gate(struct clknode *clk, bool enable) +{ + struct aw_modclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + if (enable) + val |= SCLK_GATING; + else + val &= ~SCLK_GATING; + MODCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_modclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_modclk_sc *sc; + uint32_t val, m, n; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + n = 1 << ((val & CLK_RATIO_N) >> CLK_RATIO_N_SHIFT); + m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1; + + *freq = *freq / n / m; + + return (0); +} + +static int +aw_modclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct aw_modclk_sc *sc; + uint32_t val, m, n, best_m, best_n; + uint64_t cur_freq; + int64_t best_diff, cur_diff; + + sc = clknode_get_softc(clk); + best_n = best_m = 0; + best_diff = (int64_t)*fout; + + for (n = 0; n <= CLK_RATIO_N_MAX; n++) + for (m = 0; m <= CLK_RATIO_M_MAX; m++) { + cur_freq = fin / (1 << n) / (m + 1); + cur_diff = (int64_t)*fout - cur_freq; + if (cur_diff >= 0 && cur_diff < best_diff) { + best_diff = cur_diff; + best_m = m; + best_n = n; + } + } + + if (best_diff == (int64_t)*fout) + return (ERANGE); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + val &= ~(CLK_RATIO_N | CLK_RATIO_M); + val |= (best_n << CLK_RATIO_N_SHIFT); + val |= (best_m << CLK_RATIO_M_SHIFT); + MODCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + *fout = fin / (1 << best_n) / (best_m + 1); + *stop = 1; + + return (0); +} + +static clknode_method_t aw_modclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_modclk_init), + CLKNODEMETHOD(clknode_set_gate, aw_modclk_set_gate), + CLKNODEMETHOD(clknode_set_mux, aw_modclk_set_mux), + CLKNODEMETHOD(clknode_recalc_freq, aw_modclk_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, aw_modclk_set_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_modclk_clknode, aw_modclk_clknode_class, + aw_modclk_clknode_methods, sizeof(struct aw_modclk_sc), clknode_class); + +static int +aw_modclk_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"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_modclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_modclk_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_modclk_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); + + 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_modclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_modclk_probe), + DEVMETHOD(device_attach, aw_modclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_modclk_driver = { + "aw_modclk", + aw_modclk_methods, + 0 +}; + +static devclass_t aw_modclk_devclass; + +EARLY_DRIVER_MODULE(aw_modclk, simplebus, aw_modclk_driver, + aw_modclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); diff --git a/sys/arm/allwinner/clk/aw_oscclk.c b/sys/arm/allwinner/clk/aw_oscclk.c new file mode 100644 index 0000000..313111f --- /dev/null +++ b/sys/arm/allwinner/clk/aw_oscclk.c @@ -0,0 +1,132 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/extres/clk/clk_fixed.h> + +static int +aw_oscclk_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-osc-clk")) + return (ENXIO); + + device_set_desc(dev, "Allwinner Oscillator Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_oscclk_attach(device_t dev) +{ + struct clk_fixed_def def; + struct clkdom *clkdom; + phandle_t node; + uint32_t freq; + int error; + + node = ofw_bus_get_node(dev); + + if (OF_getencprop(node, "clock-frequency", &freq, sizeof(freq)) <= 0) { + device_printf(dev, "missing clock-frequency property\n"); + error = ENXIO; + goto fail; + } + + clkdom = clkdom_create(dev); + + memset(&def, 0, sizeof(def)); + def.clkdef.id = 1; + def.freq = freq; + error = clk_parse_ofw_clk_name(dev, node, &def.clkdef.name); + if (error != 0) { + device_printf(dev, "cannot parse clock name\n"); + error = ENXIO; + goto fail; + } + + error = clknode_fixed_register(clkdom, &def); + if (error != 0) { + device_printf(dev, "cannot register fixed clock\n"); + error = ENXIO; + goto fail; + } + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + free(__DECONST(char *, def.clkdef.name), M_OFWPROP); + + return (0); + +fail: + free(__DECONST(char *, def.clkdef.name), M_OFWPROP); + return (error); +} + +static device_method_t aw_oscclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_oscclk_probe), + DEVMETHOD(device_attach, aw_oscclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_oscclk_driver = { + "aw_oscclk", + aw_oscclk_methods, + 0, +}; + +static devclass_t aw_oscclk_devclass; + +EARLY_DRIVER_MODULE(aw_oscclk, simplebus, aw_oscclk_driver, + aw_oscclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); diff --git a/sys/arm/allwinner/clk/aw_pll.c b/sys/arm/allwinner/clk/aw_pll.c new file mode 100644 index 0000000..2e67646 --- /dev/null +++ b/sys/arm/allwinner/clk/aw_pll.c @@ -0,0 +1,757 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk.h> + +#include <dt-bindings/clock/sun4i-a10-pll2.h> + +#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 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, +}; + +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); +} + +#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), +}; + +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 }, + { 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); diff --git a/sys/arm/allwinner/clk/aw_usbclk.c b/sys/arm/allwinner/clk/aw_usbclk.c new file mode 100644 index 0000000..bac9991 --- /dev/null +++ b/sys/arm/allwinner/clk/aw_usbclk.c @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk_gate.h> +#include <dev/extres/hwreset/hwreset.h> + +#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, +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-usb-clk", AW_A10_USBCLK }, + { "allwinner,sun6i-a31-usb-clk", AW_A31_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; + int index, nout, error; + enum aw_usbclk_type type; + uint32_t *indices; + clk_t clk_parent; + 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); + } + + 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 (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); diff --git a/sys/arm/allwinner/files.allwinner b/sys/arm/allwinner/files.allwinner index a13b637..34daf43 100644 --- a/sys/arm/allwinner/files.allwinner +++ b/sys/arm/allwinner/files.allwinner @@ -2,7 +2,6 @@ kern/kern_clocksource.c standard arm/allwinner/a10_ahci.c optional ahci -arm/allwinner/a10_clk.c standard arm/allwinner/a10_codec.c optional sound arm/allwinner/a10_common.c standard arm/allwinner/a10_dmac.c standard @@ -25,3 +24,21 @@ 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_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 diff --git a/sys/arm/allwinner/if_emac.c b/sys/arm/allwinner/if_emac.c index 268f5cb..cb9892f 100644 --- a/sys/arm/allwinner/if_emac.c +++ b/sys/arm/allwinner/if_emac.c @@ -78,11 +78,12 @@ __FBSDID("$FreeBSD$"); #include <arm/allwinner/if_emacreg.h> +#include <dev/extres/clk/clk.h> + #include "miibus_if.h" #include "gpio_if.h" -#include "a10_clk.h" #include "a10_sramc.h" struct emac_softc { @@ -94,6 +95,7 @@ struct emac_softc { struct resource *emac_res; struct resource *emac_irq; void *emac_intrhand; + clk_t emac_clk; int emac_if_flags; struct mtx emac_mtx; struct callout emac_tick_ch; @@ -110,7 +112,7 @@ static int emac_shutdown(device_t); static int emac_suspend(device_t); static int emac_resume(device_t); -static void emac_sys_setup(void); +static int emac_sys_setup(struct emac_softc *); static void emac_reset(struct emac_softc *); static void emac_init_locked(struct emac_softc *); @@ -138,14 +140,27 @@ static int sysctl_hw_emac_proc_limit(SYSCTL_HANDLER_ARGS); #define EMAC_WRITE_REG(sc, reg, val) \ bus_space_write_4(sc->emac_tag, sc->emac_handle, reg, val) -static void -emac_sys_setup(void) +static int +emac_sys_setup(struct emac_softc *sc) { + int error; /* Activate EMAC clock. */ - a10_clk_emac_activate(); + error = clk_get_by_ofw_index(sc->emac_dev, 0, &sc->emac_clk); + if (error != 0) { + device_printf(sc->emac_dev, "cannot get clock\n"); + return (error); + } + error = clk_enable(sc->emac_clk); + if (error != 0) { + device_printf(sc->emac_dev, "cannot enable clock\n"); + return (error); + } + /* Map sram. */ a10_map_to_emac(); + + return (0); } static void @@ -784,6 +799,9 @@ emac_detach(device_t dev) bus_generic_detach(sc->emac_dev); } + if (sc->emac_clk != NULL) + clk_disable(sc->emac_clk); + if (sc->emac_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->emac_res); @@ -897,7 +915,10 @@ emac_attach(device_t dev) } } /* Setup EMAC */ - emac_sys_setup(); + error = emac_sys_setup(sc); + if (error != 0) + goto fail; + emac_reset(sc); ifp = sc->emac_ifp = if_alloc(IFT_ETHER); diff --git a/sys/arm/altera/socfpga/socfpga_rstmgr.c b/sys/arm/altera/socfpga/socfpga_rstmgr.c index 36a1d75..49db05e 100644 --- a/sys/arm/altera/socfpga/socfpga_rstmgr.c +++ b/sys/arm/altera/socfpga/socfpga_rstmgr.c @@ -145,7 +145,7 @@ rstmgr_sysctl(SYSCTL_HANDLER_ARGS) break; default: return (1); - }; + } reg = READ4(sc, RSTMGR_BRGMODRST); enable = reg & bit ? 0 : 1; diff --git a/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.c b/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.c index 3daa99f..75825fb 100644 --- a/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.c +++ b/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.c @@ -621,7 +621,7 @@ spurious: stop = 1; if ((sndr & AML_SDXC_SEND_RESP_136) != 0) { start = 1; - stop = start + 4;; + stop = start + 4; } for (i = start; i < stop; i++) { pdmar = CSR_READ_4(sc, AML_SDXC_PDMA_REG); diff --git a/sys/arm/arm/disassem.c b/sys/arm/arm/disassem.c index dbe533a..b7aa099 100644 --- a/sys/arm/arm/disassem.c +++ b/sys/arm/arm/disassem.c @@ -523,7 +523,7 @@ disasm(const disasm_interface_t *di, vm_offset_t loc, int altfmt) else di->di_printf(", "); } - }; + } di->di_printf("\n"); diff --git a/sys/arm/arm/gic.c b/sys/arm/arm/gic.c index 23b9577..5c0aece 100644 --- a/sys/arm/arm/gic.c +++ b/sys/arm/arm/gic.c @@ -256,11 +256,11 @@ static void arm_gic_init_secondary(device_t dev) { struct arm_gic_softc *sc = device_get_softc(dev); - struct intr_irqsrc *isrc; - u_int irq; + u_int irq, cpu; /* Set the mask so we can find this CPU to send it IPIs */ - arm_gic_map[PCPU_GET(cpuid)] = gic_cpu_mask(sc); + cpu = PCPU_GET(cpuid); + arm_gic_map[cpu] = gic_cpu_mask(sc); for (irq = 0; irq < sc->nirqs; irq += 4) gic_d_write_4(sc, GICD_IPRIORITYR(irq >> 2), 0); @@ -280,27 +280,14 @@ arm_gic_init_secondary(device_t dev) gic_d_write_4(sc, GICD_CTLR, 0x01); /* Unmask attached SGI interrupts. */ - for (irq = GIC_FIRST_SGI; irq <= GIC_LAST_SGI; irq++) { - isrc = GIC_INTR_ISRC(sc, irq); - if (isrc != NULL && isrc->isrc_handlers != 0) { - CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); + for (irq = GIC_FIRST_SGI; irq <= GIC_LAST_SGI; irq++) + if (intr_isrc_init_on_cpu(GIC_INTR_ISRC(sc, irq), cpu)) gic_irq_unmask(sc, irq); - } - } /* Unmask attached PPI interrupts. */ - for (irq = GIC_FIRST_PPI; irq <= GIC_LAST_PPI; irq++) { - isrc = GIC_INTR_ISRC(sc, irq); - if (isrc == NULL || isrc->isrc_handlers == 0) - continue; - if (isrc->isrc_flags & INTR_ISRCF_BOUND) { - if (CPU_ISSET(PCPU_GET(cpuid), &isrc->isrc_cpu)) - gic_irq_unmask(sc, irq); - } else { - CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); + for (irq = GIC_FIRST_PPI; irq <= GIC_LAST_PPI; irq++) + if (intr_isrc_init_on_cpu(GIC_INTR_ISRC(sc, irq), cpu)) gic_irq_unmask(sc, irq); - } - } } #else static void @@ -1016,13 +1003,18 @@ arm_gic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus, static int arm_gic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp) { + struct intr_irqsrc *isrc; struct arm_gic_softc *sc = device_get_softc(dev); if (sgi_first_unused > GIC_LAST_SGI) return (ENOSPC); - *isrcp = GIC_INTR_ISRC(sc, sgi_first_unused); + isrc = GIC_INTR_ISRC(sc, sgi_first_unused); sgi_to_ipi[sgi_first_unused++] = ipi; + + CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); + + *isrcp = isrc; return (0); } #endif diff --git a/sys/arm/arm/locore.S b/sys/arm/arm/locore.S index 2aecb7e..0a6a2d3 100644 --- a/sys/arm/arm/locore.S +++ b/sys/arm/arm/locore.S @@ -1,6 +1,6 @@ /*- * Copyright (c) 2014 Ian Lepore <ian@freebsd.org> - * All rights excluded. + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/sys/arm/arm/physmem.c b/sys/arm/arm/physmem.c index d023905..f6222c3 100644 --- a/sys/arm/arm/physmem.c +++ b/sys/arm/arm/physmem.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2014 Ian Lepore <ian@freebsd.org> - * All rights excluded. + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/sys/arm/arm/vm_machdep.c b/sys/arm/arm/vm_machdep.c index fdab1c6..fec2ca0 100644 --- a/sys/arm/arm/vm_machdep.c +++ b/sys/arm/arm/vm_machdep.c @@ -148,7 +148,7 @@ cpu_fork(register struct thread *td1, register struct proc *p2, /* Setup to release spin count in fork_exit(). */ td2->td_md.md_spinlock_count = 1; - td2->td_md.md_saved_cspr = PSR_SVC32_MODE;; + td2->td_md.md_saved_cspr = PSR_SVC32_MODE; #if __ARM_ARCH >= 6 td2->td_md.md_tp = td1->td_md.md_tp; #else diff --git a/sys/arm/broadcom/bcm2835/bcm2836.c b/sys/arm/broadcom/bcm2835/bcm2836.c index 05e9cc6..f9a2fcc 100644 --- a/sys/arm/broadcom/bcm2835/bcm2836.c +++ b/sys/arm/broadcom/bcm2835/bcm2836.c @@ -525,40 +525,21 @@ bcm_lintc_setup_intr(device_t dev, struct intr_irqsrc *isrc, } #ifdef SMP -static bool -bcm_lint_init_on_ap(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli, - u_int cpu) -{ - struct intr_irqsrc *isrc; - - isrc = &bli->bli_isrc; - - KASSERT(isrc->isrc_flags & INTR_ISRCF_PPI, - ("%s: irq %d is not PPI", __func__, bli->bli_irq)); - - if (isrc->isrc_handlers == 0) - return (false); - if (isrc->isrc_flags & INTR_ISRCF_BOUND) - return (CPU_ISSET(cpu, &isrc->isrc_cpu)); - - CPU_SET(cpu, &isrc->isrc_cpu); - return (true); -} - static void bcm_lintc_init_rwreg_on_ap(struct bcm_lintc_softc *sc, u_int cpu, u_int irq, uint32_t reg, uint32_t mask) { - if (bcm_lint_init_on_ap(sc, &sc->bls_isrcs[irq], cpu)) + if (intr_isrc_init_on_cpu(&sc->bls_isrcs[irq].bli_isrc, cpu)) bcm_lintc_rwreg_set(sc, reg, mask); } static void bcm_lintc_init_pmu_on_ap(struct bcm_lintc_softc *sc, u_int cpu) { + struct intr_irqsrc *isrc = &sc->bls_isrcs[BCM_LINTC_PMU_IRQ].bli_isrc; - if (bcm_lint_init_on_ap(sc, &sc->bls_isrcs[BCM_LINTC_PMU_IRQ], cpu)) { + if (intr_isrc_init_on_cpu(isrc, cpu)) { /* Write-set register. */ bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_SET_REG, BCM_LINTC_PIRR_IRQ_EN_CORE(cpu)); diff --git a/sys/arm/conf/A10 b/sys/arm/conf/A10 index 31a5e24..fc39be7 100644 --- a/sys/arm/conf/A10 +++ b/sys/arm/conf/A10 @@ -51,6 +51,13 @@ options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed #options BOOTP_NFSV3 #options BOOTP_WIRED_TO=emac0 +# EXT_RESOURCES pseudo devices +options EXT_RESOURCES +device clk +device phy +device hwreset +device regulator + # MMC/SD/SDIO Card slot support device mmc # mmc/sd bus device mmcsd # mmc/sd flash cards diff --git a/sys/arm/conf/A20 b/sys/arm/conf/A20 index ad3b29a..8f56523 100644 --- a/sys/arm/conf/A20 +++ b/sys/arm/conf/A20 @@ -55,6 +55,13 @@ options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed #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 diff --git a/sys/arm/freescale/imx/imx6_sdma.c b/sys/arm/freescale/imx/imx6_sdma.c index ae9a339..4839270 100644 --- a/sys/arm/freescale/imx/imx6_sdma.c +++ b/sys/arm/freescale/imx/imx6_sdma.c @@ -444,7 +444,7 @@ boot_firmware(struct sdma_softc *sc) if (timeout-- <= 0) break; DELAY(10); - }; + } if (ret == 0) { device_printf(sc->dev, "SDMA failed to boot\n"); diff --git a/sys/arm/freescale/imx/imx6_ssi.c b/sys/arm/freescale/imx/imx6_ssi.c index 184de2a..9387aa3 100644 --- a/sys/arm/freescale/imx/imx6_ssi.c +++ b/sys/arm/freescale/imx/imx6_ssi.c @@ -463,7 +463,7 @@ find_sdma_controller(struct sc_info *sc) if (sdma_sc == NULL) { device_printf(sc->dev, "No sDMA found. Can't operate\n"); return (ENXIO); - }; + } sc->sdma_sc = sdma_sc; diff --git a/sys/arm/freescale/imx/imx_gpio.c b/sys/arm/freescale/imx/imx_gpio.c index cdff020..df0f2ad 100644 --- a/sys/arm/freescale/imx/imx_gpio.c +++ b/sys/arm/freescale/imx/imx_gpio.c @@ -157,45 +157,6 @@ static int imx51_gpio_pin_toggle(device_t, uint32_t pin); #ifdef ARM_INTRNG static int -gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, - struct resource *res, struct intr_map_data *data) -{ - struct imx51_gpio_softc *sc; - struct gpio_irqsrc *gi; - - sc = device_get_softc(dev); - if (isrc->isrc_handlers == 0) { - gi = (struct gpio_irqsrc *)isrc; - gi->gi_pol = INTR_POLARITY_CONFORM; - gi->gi_trig = INTR_TRIGGER_CONFORM; - - // XXX Not sure this is necessary - mtx_lock_spin(&sc->sc_mtx); - CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << gi->gi_irq)); - WRITE4(sc, IMX_GPIO_ISR_REG, (1U << gi->gi_irq)); - mtx_unlock_spin(&sc->sc_mtx); - } - return (0); -} - -/* - * this is mask_intr - */ -static void -gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) -{ - struct imx51_gpio_softc *sc; - u_int irq; - - sc = device_get_softc(dev); - irq = ((struct gpio_irqsrc *)isrc)->gi_irq; - - mtx_lock_spin(&sc->sc_mtx); - CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << irq)); - mtx_unlock_spin(&sc->sc_mtx); -} - -static int gpio_pic_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp, enum intr_polarity *polp, enum intr_trigger *trigp) { @@ -279,6 +240,28 @@ gpio_pic_map_intr(device_t dev, struct intr_map_data *data, } static int +gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct imx51_gpio_softc *sc; + struct gpio_irqsrc *gi; + + sc = device_get_softc(dev); + if (isrc->isrc_handlers == 0) { + gi = (struct gpio_irqsrc *)isrc; + gi->gi_pol = INTR_POLARITY_CONFORM; + gi->gi_trig = INTR_TRIGGER_CONFORM; + + // XXX Not sure this is necessary + mtx_lock_spin(&sc->sc_mtx); + CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << gi->gi_irq)); + WRITE4(sc, IMX_GPIO_ISR_REG, (1U << gi->gi_irq)); + mtx_unlock_spin(&sc->sc_mtx); + } + return (0); +} + +static int gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { @@ -345,6 +328,23 @@ gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, } /* + * this is mask_intr + */ +static void +gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct imx51_gpio_softc *sc; + u_int irq; + + sc = device_get_softc(dev); + irq = ((struct gpio_irqsrc *)isrc)->gi_irq; + + mtx_lock_spin(&sc->sc_mtx); + CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << irq)); + mtx_unlock_spin(&sc->sc_mtx); +} + +/* * this is unmask_intr */ static void @@ -417,7 +417,7 @@ gpio_pic_filter(void *arg) } /* - * register our isrcs into intrng to make it known about them. + * Initialize our isrcs and register them with intrng. */ static int gpio_pic_register_isrcs(struct imx51_gpio_softc *sc) @@ -451,22 +451,27 @@ static void imx51_gpio_pin_configure(struct imx51_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { + u_int newflags; mtx_lock_spin(&sc->sc_mtx); /* - * Manage input/output + * Manage input/output; other flags not supported yet. + * + * Note that changes to pin->gp_flags must be acccumulated in newflags + * and stored with a single writeback to gp_flags at the end, to enable + * unlocked reads of that value elsewhere. */ - if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { - pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); + if (flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { + newflags = pin->gp_flags & ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { - pin->gp_flags |= GPIO_PIN_OUTPUT; + newflags |= GPIO_PIN_OUTPUT; SET4(sc, IMX_GPIO_OE_REG, (1U << pin->gp_pin)); - } - else { - pin->gp_flags |= GPIO_PIN_INPUT; + } else { + newflags |= GPIO_PIN_INPUT; CLEAR4(sc, IMX_GPIO_OE_REG, (1U << pin->gp_pin)); } + pin->gp_flags = newflags; } mtx_unlock_spin(&sc->sc_mtx); @@ -497,20 +502,13 @@ static int imx51_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct imx51_gpio_softc *sc; - int i; sc = device_get_softc(dev); - for (i = 0; i < sc->gpio_npins; i++) { - if (sc->gpio_pins[i].gp_pin == pin) - break; - } - if (i >= sc->gpio_npins) + if (pin >= sc->gpio_npins) return (EINVAL); - mtx_lock_spin(&sc->sc_mtx); - *caps = sc->gpio_pins[i].gp_caps; - mtx_unlock_spin(&sc->sc_mtx); + *caps = sc->gpio_pins[pin].gp_caps; return (0); } @@ -519,20 +517,13 @@ static int imx51_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct imx51_gpio_softc *sc; - int i; sc = device_get_softc(dev); - for (i = 0; i < sc->gpio_npins; i++) { - if (sc->gpio_pins[i].gp_pin == pin) - break; - } - if (i >= sc->gpio_npins) + if (pin >= sc->gpio_npins) return (EINVAL); - mtx_lock_spin(&sc->sc_mtx); - *flags = sc->gpio_pins[i].gp_flags; - mtx_unlock_spin(&sc->sc_mtx); + *flags = sc->gpio_pins[pin].gp_flags; return (0); } @@ -541,19 +532,13 @@ static int imx51_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct imx51_gpio_softc *sc; - int i; sc = device_get_softc(dev); - for (i = 0; i < sc->gpio_npins; i++) { - if (sc->gpio_pins[i].gp_pin == pin) - break; - } - - if (i >= sc->gpio_npins) + if (pin >= sc->gpio_npins) return (EINVAL); mtx_lock_spin(&sc->sc_mtx); - memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); + memcpy(name, sc->gpio_pins[pin].gp_name, GPIOMAXNAME); mtx_unlock_spin(&sc->sc_mtx); return (0); @@ -563,18 +548,13 @@ static int imx51_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct imx51_gpio_softc *sc; - int i; sc = device_get_softc(dev); - for (i = 0; i < sc->gpio_npins; i++) { - if (sc->gpio_pins[i].gp_pin == pin) - break; - } - if (i >= sc->gpio_npins) + if (pin >= sc->gpio_npins) return (EINVAL); - imx51_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); + imx51_gpio_pin_configure(sc, &sc->gpio_pins[pin], flags); return (0); } @@ -583,22 +563,17 @@ static int imx51_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct imx51_gpio_softc *sc; - int i; sc = device_get_softc(dev); - for (i = 0; i < sc->gpio_npins; i++) { - if (sc->gpio_pins[i].gp_pin == pin) - break; - } - if (i >= sc->gpio_npins) + if (pin >= sc->gpio_npins) return (EINVAL); mtx_lock_spin(&sc->sc_mtx); if (value) - SET4(sc, IMX_GPIO_DR_REG, (1U << i)); + SET4(sc, IMX_GPIO_DR_REG, (1U << pin)); else - CLEAR4(sc, IMX_GPIO_DR_REG, (1U << i)); + CLEAR4(sc, IMX_GPIO_DR_REG, (1U << pin)); mtx_unlock_spin(&sc->sc_mtx); return (0); @@ -608,20 +583,13 @@ static int imx51_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct imx51_gpio_softc *sc; - int i; sc = device_get_softc(dev); - for (i = 0; i < sc->gpio_npins; i++) { - if (sc->gpio_pins[i].gp_pin == pin) - break; - } - if (i >= sc->gpio_npins) + if (pin >= sc->gpio_npins) return (EINVAL); - mtx_lock_spin(&sc->sc_mtx); - *val = (READ4(sc, IMX_GPIO_DR_REG) >> i) & 1; - mtx_unlock_spin(&sc->sc_mtx); + *val = (READ4(sc, IMX_GPIO_DR_REG) >> pin) & 1; return (0); } @@ -630,20 +598,15 @@ static int imx51_gpio_pin_toggle(device_t dev, uint32_t pin) { struct imx51_gpio_softc *sc; - int i; sc = device_get_softc(dev); - for (i = 0; i < sc->gpio_npins; i++) { - if (sc->gpio_pins[i].gp_pin == pin) - break; - } - if (i >= sc->gpio_npins) + if (pin >= sc->gpio_npins) return (EINVAL); mtx_lock_spin(&sc->sc_mtx); WRITE4(sc, IMX_GPIO_DR_REG, - (READ4(sc, IMX_GPIO_DR_REG) ^ (1U << i))); + (READ4(sc, IMX_GPIO_DR_REG) ^ (1U << pin))); mtx_unlock_spin(&sc->sc_mtx); return (0); diff --git a/sys/arm/freescale/vybrid/vf_ccm.c b/sys/arm/freescale/vybrid/vf_ccm.c index 8f8a3f0..82284d5 100644 --- a/sys/arm/freescale/vybrid/vf_ccm.c +++ b/sys/arm/freescale/vybrid/vf_ccm.c @@ -379,15 +379,15 @@ set_clock(struct ccm_softc *sc, char *name) reg &= ~(clk->sel_mask << clk->sel_shift); reg |= (clk->sel_val << clk->sel_shift); WRITE4(sc, clk->sel_reg, reg); - }; + } reg = READ4(sc, clk->reg); reg |= clk->enable_reg; reg &= ~(clk->div_mask << clk->div_shift); reg |= (clk->div_val << clk->div_shift); WRITE4(sc, clk->reg, reg); - }; - }; + } + } return (0); } @@ -425,8 +425,8 @@ ccm_fdt_set(struct ccm_softc *sc) fdt_config += strlen(name) + 1; len -= strlen(name) + 1; set_clock(sc, name); - }; - }; + } + } if (OF_peer(child) == 0) { /* No more siblings. */ diff --git a/sys/arm/freescale/vybrid/vf_edma.c b/sys/arm/freescale/vybrid/vf_edma.c index ed12072..9317921 100644 --- a/sys/arm/freescale/vybrid/vf_edma.c +++ b/sys/arm/freescale/vybrid/vf_edma.c @@ -161,7 +161,7 @@ channel_configure(struct edma_softc *sc, int mux_grp, int mux_src) } else { channel_first = 0; mux_num = sc->device_id * 2; - }; + } /* Take first unused eDMA channel */ ch = NULL; @@ -171,12 +171,12 @@ channel_configure(struct edma_softc *sc, int mux_grp, int mux_src) break; } ch = NULL; - }; + } if (ch == NULL) { /* Can't find free channel */ return (-1); - }; + } chnum = i; diff --git a/sys/arm/freescale/vybrid/vf_port.c b/sys/arm/freescale/vybrid/vf_port.c index 943ca88..6ff8bfc 100644 --- a/sys/arm/freescale/vybrid/vf_port.c +++ b/sys/arm/freescale/vybrid/vf_port.c @@ -171,7 +171,7 @@ port_setup(int pnum, enum ev_type pevt, void (*ih)(void *), void *ih_user) break; default: return (-1); - }; + } reg = READ4(sc, PORT_PCR(pnum)); reg &= ~(PCR_IRQC_M << PCR_IRQC_S); diff --git a/sys/arm/freescale/vybrid/vf_sai.c b/sys/arm/freescale/vybrid/vf_sai.c index 309d95e..83f689f 100644 --- a/sys/arm/freescale/vybrid/vf_sai.c +++ b/sys/arm/freescale/vybrid/vf_sai.c @@ -433,7 +433,7 @@ find_edma_controller(struct sc_info *sc) if ((len = OF_getproplen(edma_node, "device-id")) <= 0) { return (ENXIO); - }; + } OF_getprop(edma_node, "device-id", &dts_value, len); edma_device_id = fdt32_to_cpu(dts_value); @@ -447,16 +447,16 @@ find_edma_controller(struct sc_info *sc) if (edma_sc->device_id == edma_device_id) { /* found */ break; - }; + } edma_sc = NULL; - }; - }; + } + } if (edma_sc == NULL) { device_printf(sc->dev, "no eDMA. can't operate\n"); return (ENXIO); - }; + } sc->edma_sc = edma_sc; @@ -465,7 +465,7 @@ find_edma_controller(struct sc_info *sc) if (sc->edma_chnum < 0) { /* cant setup eDMA */ return (ENXIO); - }; + } return (0); }; diff --git a/sys/arm/mv/mv_pci.c b/sys/arm/mv/mv_pci.c index dc8b890..2ef6604 100644 --- a/sys/arm/mv/mv_pci.c +++ b/sys/arm/mv/mv_pci.c @@ -842,7 +842,7 @@ mv_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, default: return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, type, rid, start, end, count, flags)); - }; + } if (RMAN_IS_DEFAULT_RANGE(start, end)) { start = sc->sc_mem_base; diff --git a/sys/arm/samsung/exynos/chrome_ec.c b/sys/arm/samsung/exynos/chrome_ec.c index 2c88414..8f794b7 100644 --- a/sys/arm/samsung/exynos/chrome_ec.c +++ b/sys/arm/samsung/exynos/chrome_ec.c @@ -176,7 +176,7 @@ ec_command(uint8_t cmd, uint8_t *dout, uint8_t dout_len, for (i = 0; i < dout_len; i++) { msg_dout[i + 3] = dout[i]; - }; + } fill_checksum(msg_dout, dout_len + 3); @@ -195,7 +195,7 @@ ec_command(uint8_t cmd, uint8_t *dout, uint8_t dout_len, for (i = 0; i < dinp_len; i++) { dinp[i] = msg_dinp[i + 2]; - }; + } free(msg_dout, M_DEVBUF); free(msg_dinp, M_DEVBUF); diff --git a/sys/arm/samsung/exynos/chrome_ec_spi.c b/sys/arm/samsung/exynos/chrome_ec_spi.c index a018afa..9e54969 100644 --- a/sys/arm/samsung/exynos/chrome_ec_spi.c +++ b/sys/arm/samsung/exynos/chrome_ec_spi.c @@ -143,7 +143,7 @@ ec_command(uint8_t cmd, uint8_t *dout, uint8_t dout_len, for (i = 0; i < dout_len; i++) { msg_dout[i + 3] = dout[i]; - }; + } fill_checksum(msg_dout, dout_len + 3); @@ -177,7 +177,7 @@ ec_command(uint8_t cmd, uint8_t *dout, uint8_t dout_len, for (i = 0; i < dinp_len; i++) { dinp[i] = msg_dinp[i + 2]; - }; + } free(msg_dout, M_DEVBUF); free(msg_dinp, M_DEVBUF); diff --git a/sys/arm/samsung/exynos/chrome_kb.c b/sys/arm/samsung/exynos/chrome_kb.c index b10a5ac..6965d3c 100644 --- a/sys/arm/samsung/exynos/chrome_kb.c +++ b/sys/arm/samsung/exynos/chrome_kb.c @@ -255,12 +255,12 @@ ckb_check(keyboard_t *kbd) if (sc->sc_flags & CKB_FLAG_POLLING) { return (1); - }; + } for (i = 0; i < sc->cols; i++) if (sc->scan_local[i] != sc->scan[i]) { return (1); - }; + } if (sc->sc_repeating) return (1); @@ -356,7 +356,7 @@ ckb_read_char_locked(keyboard_t *kbd, int wait) callout_reset(&sc->sc_repeat_callout, hz / 10, ckb_repeat, sc); return (sc->sc_repeat_key); - }; + } if (sc->sc_flags & CKB_FLAG_POLLING) { for (;;) { @@ -374,7 +374,7 @@ ckb_read_char_locked(keyboard_t *kbd, int wait) } DELAY(1000); } - }; + } for (i = 0; i < sc->cols; i++) { for (j = 0; j < sc->rows; j++) { @@ -387,7 +387,7 @@ ckb_read_char_locked(keyboard_t *kbd, int wait) key = keymap_read(sc, i, j); if (key == 0) { continue; - }; + } if (newbit > 0) { /* key pressed */ @@ -841,7 +841,7 @@ chrome_kb_attach(device_t dev) for (i = 0; i < sc->cols; i++) { sc->scan_local[i] = 0; sc->scan[i] = 0; - }; + } kbd_init_struct(kbd, KBD_DRIVER_NAME, KB_OTHER, device_get_unit(dev), 0, 0, 0); @@ -866,7 +866,7 @@ chrome_kb_attach(device_t dev) if (kbd_register(kbd) < 0) { return (ENXIO); - }; + } KBD_CONFIG_DONE(kbd); return (0); diff --git a/sys/arm/samsung/exynos/exynos5_i2c.c b/sys/arm/samsung/exynos/exynos5_i2c.c index d879138..756a27d 100644 --- a/sys/arm/samsung/exynos/exynos5_i2c.c +++ b/sys/arm/samsung/exynos/exynos5_i2c.c @@ -292,7 +292,7 @@ i2c_start(device_t dev, u_char slave, int timeout) mtx_unlock(&sc->mutex); return (IIC_ENOACK); - }; + } mtx_unlock(&sc->mutex); return (IIC_NOERR); @@ -387,7 +387,7 @@ i2c_read(device_t dev, char *buf, int len, reg = READ1(sc, I2CCON); reg &= ~(ACKGEN); WRITE1(sc, I2CCON, reg); - }; + } clear_ipend(sc); @@ -444,7 +444,7 @@ i2c_write(device_t dev, const char *buf, int len, int *sent, int timeout) DPRINTF("cant i2c write: no ack\n"); mtx_unlock(&sc->mutex); return (IIC_ENOACK); - }; + } (*sent)++; } diff --git a/sys/arm/samsung/exynos/exynos5_pad.c b/sys/arm/samsung/exynos/exynos5_pad.c index ea07f5d..27d0d53 100644 --- a/sys/arm/samsung/exynos/exynos5_pad.c +++ b/sys/arm/samsung/exynos/exynos5_pad.c @@ -330,10 +330,10 @@ get_bank(struct pad_softc *sc, int gpio_number, *bank = sc->gpio_map[i]; *pin_shift = (gpio_number - n); return (0); - }; + } n += ngpio; - }; + } return (-1); } @@ -516,7 +516,7 @@ pad_attach(device_t dev) break; default: goto fail; - }; + } if (bus_alloc_resources(dev, sc->pad_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); @@ -528,7 +528,7 @@ pad_attach(device_t dev) for (i = 0; i < sc->nports; i++) { sc->bst[i] = rman_get_bustag(sc->res[i]); sc->bsh[i] = rman_get_bushandle(sc->res[i]); - }; + } sc->dev = dev; |