diff options
Diffstat (limited to 'sys/arm')
-rw-r--r-- | sys/arm/allwinner/a10_ahci.c | 382 | ||||
-rw-r--r-- | sys/arm/allwinner/a10_clk.c | 68 | ||||
-rw-r--r-- | sys/arm/allwinner/a10_clk.h | 17 | ||||
-rw-r--r-- | sys/arm/allwinner/a10_gpio.c | 6 | ||||
-rw-r--r-- | sys/arm/allwinner/a10_gpio.h | 5 | ||||
-rw-r--r-- | sys/arm/allwinner/a10_mmc.c | 238 | ||||
-rw-r--r-- | sys/arm/allwinner/a10_mmc.h | 21 | ||||
-rw-r--r-- | sys/arm/allwinner/files.allwinner | 1 | ||||
-rw-r--r-- | sys/arm/allwinner/if_emac.c | 13 | ||||
-rw-r--r-- | sys/arm/arm/genassym.c | 2 | ||||
-rw-r--r-- | sys/arm/conf/CUBIEBOARD | 4 | ||||
-rw-r--r-- | sys/arm/conf/CUBIEBOARD2 | 4 |
12 files changed, 712 insertions, 49 deletions
diff --git a/sys/arm/allwinner/a10_ahci.c b/sys/arm/allwinner/a10_ahci.c new file mode 100644 index 0000000..37856fb --- /dev/null +++ b/sys/arm/allwinner/a10_ahci.c @@ -0,0 +1,382 @@ +/*- + * Copyright (c) 2014-2015 M. Warner Losh <imp@freebsd.org> + * Copyright (c) 2015 Luiz Otavio O Souza <loos@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. + * + * The magic-bit-bang sequence used in this code may be based on a linux + * platform driver in the Allwinner SDK from Allwinner Technology Co., Ltd. + * www.allwinnertech.com, by Daniel Wang <danielwang@allwinnertech.com> + * though none of the original code was copied. + */ + +#include "opt_bus.h" + +#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 <sys/gpio.h> + +#include <machine/bus.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/ahci/ahci.h> +#include <arm/allwinner/a10_clk.h> +#include "gpio_if.h" + +/* + * Allwinner a1x/a2x/a8x SATA attachment. This is just the AHCI register + * set with a few extra implementation-specific registers that need to + * be accounted for. There's only one PHY in the system, and it needs + * to be trained to bring the link up. In addition, there's some DMA + * specific things that need to be done as well. These things are also + * just about completely undocumented, except in ugly code in the Linux + * SDK Allwinner releases. + */ + +/* BITx -- Unknown bit that needs to be set/cleared at position x */ +/* UFx -- Uknown multi-bit field frobbed during init */ +#define AHCI_BISTAFR 0x00A0 +#define AHCI_BISTCR 0x00A4 +#define AHCI_BISTFCTR 0x00A8 +#define AHCI_BISTSR 0x00AC +#define AHCI_BISTDECR 0x00B0 +#define AHCI_DIAGNR 0x00B4 +#define AHCI_DIAGNR1 0x00B8 +#define AHCI_OOBR 0x00BC +#define AHCI_PHYCS0R 0x00C0 +/* Bits 0..17 are a mystery */ +#define PHYCS0R_BIT18 (1 << 18) +#define PHYCS0R_POWER_ENABLE (1 << 19) +#define PHYCS0R_UF1_MASK (7 << 20) /* Unknown Field 1 */ +#define PHYCS0R_UF1_INIT (3 << 20) +#define PHYCS0R_BIT23 (1 << 23) +#define PHYCS0R_UF2_MASK (7 << 24) /* Uknown Field 2 */ +#define PHYCS0R_UF2_INIT (5 << 24) +/* Bit 27 mystery */ +#define PHYCS0R_POWER_STATUS_MASK (7 << 28) +#define PHYCS0R_PS_GOOD (2 << 28) +/* Bit 31 mystery */ +#define AHCI_PHYCS1R 0x00C4 +/* Bits 0..5 are a mystery */ +#define PHYCS1R_UF1_MASK (3 << 6) +#define PHYCS1R_UF1_INIT (2 << 6) +#define PHYCS1R_UF2_MASK (0x1f << 8) +#define PHYCS1R_UF2_INIT (6 << 8) +/* Bits 13..14 are a mystery */ +#define PHYCS1R_BIT15 (1 << 15) +#define PHYCS1R_UF3_MASK (3 << 16) +#define PHYCS1R_UF3_INIT (2 << 16) +/* Bit 18 mystery */ +#define PHYCS1R_HIGHZ (1 << 19) +/* Bits 20..27 mystery */ +#define PHYCS1R_BIT28 (1 << 28) +/* Bits 29..31 mystery */ +#define AHCI_PHYCS2R 0x00C8 +/* bits 0..4 mystery */ +#define PHYCS2R_UF1_MASK (0x1f << 5) +#define PHYCS2R_UF1_INIT (0x19 << 5) +/* Bits 10..23 mystery */ +#define PHYCS2R_CALIBRATE (1 << 24) +/* Bits 25..31 mystery */ +#define AHCI_TIMER1MS 0x00E0 +#define AHCI_GPARAM1R 0x00E8 +#define AHCI_GPARAM2R 0x00EC +#define AHCI_PPARAMR 0x00F0 +#define AHCI_TESTR 0x00F4 +#define AHCI_VERSIONR 0x00F8 +#define AHCI_IDR 0x00FC +#define AHCI_RWCR 0x00FC + +#define AHCI_P0DMACR 0x0070 +#define AHCI_P0PHYCR 0x0078 +#define AHCI_P0PHYSR 0x007C + +/* Kludge for CUBIEBOARD (and Banana PI too) */ +#define GPIO_AHCI_PWR 40 + +static void inline +ahci_set(struct resource *m, bus_size_t off, uint32_t set) +{ + uint32_t val = ATA_INL(m, off); + + val |= set; + ATA_OUTL(m, off, val); +} + +static void inline +ahci_clr(struct resource *m, bus_size_t off, uint32_t clr) +{ + uint32_t val = ATA_INL(m, off); + + val &= ~clr; + ATA_OUTL(m, off, val); +} + +static void inline +ahci_mask_set(struct resource *m, bus_size_t off, uint32_t mask, uint32_t set) +{ + uint32_t val = ATA_INL(m, off); + + val &= mask; + val |= set; + ATA_OUTL(m, off, val); +} + +/* + * Should this be phy_reset or phy_init + */ +#define PHY_RESET_TIMEOUT 1000 +static void +ahci_a10_phy_reset(device_t dev) +{ + uint32_t to, val; + struct ahci_controller *ctlr = device_get_softc(dev); + + /* + * Here start the the magic -- most of the comments are based + * on guesswork, names of routines and printf error + * messages. The code works, but it will do that even if the + * comments are 100% BS. + */ + + /* + * Lock out other access while we initialize. Or at least that + * seems to be the case based on Linux SDK #defines. Maybe this + * put things into reset? + */ + ATA_OUTL(ctlr->r_mem, AHCI_RWCR, 0); + DELAY(100); + + /* + * Set bit 19 in PHYCS1R. Guessing this disables driving the PHY + * port for a bit while we reset things. + */ + ahci_set(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_HIGHZ); + + /* + * Frob PHYCS0R... + */ + ahci_mask_set(ctlr->r_mem, AHCI_PHYCS0R, + ~PHYCS0R_UF2_MASK, + PHYCS0R_UF2_INIT | PHYCS0R_BIT23 | PHYCS0R_BIT18); + + /* + * Set three fields in PHYCS1R + */ + ahci_mask_set(ctlr->r_mem, AHCI_PHYCS1R, + ~(PHYCS1R_UF1_MASK | PHYCS1R_UF2_MASK | PHYCS1R_UF3_MASK), + PHYCS1R_UF1_INIT | PHYCS1R_UF2_INIT | PHYCS1R_UF3_INIT); + + /* + * Two more mystery bits in PHYCS1R. -- can these be combined above? + */ + ahci_set(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_BIT15 | PHYCS1R_BIT28); + + /* + * Now clear that first mysery bit. Perhaps this starts + * driving the PHY again so we can power it up and start + * talking to the SATA drive, if any below. + */ + ahci_clr(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_HIGHZ); + + /* + * Frob PHYCS0R again... + */ + ahci_mask_set(ctlr->r_mem, AHCI_PHYCS0R, + ~PHYCS0R_UF1_MASK, PHYCS0R_UF1_INIT); + + /* + * Frob PHYCS2R, because 25 means something? + */ + ahci_mask_set(ctlr->r_mem, AHCI_PHYCS2R, ~PHYCS2R_UF1_MASK, + PHYCS2R_UF1_INIT); + + DELAY(100); /* WAG */ + + /* + * Turn on the power to the PHY and wait for it to report back + * good? + */ + ahci_set(ctlr->r_mem, AHCI_PHYCS0R, PHYCS0R_POWER_ENABLE); + for (to = PHY_RESET_TIMEOUT; to > 0; to--) { + val = ATA_INL(ctlr->r_mem, AHCI_PHYCS0R); + if ((val & PHYCS0R_POWER_STATUS_MASK) == PHYCS0R_PS_GOOD) + break; + DELAY(10); + } + if (to == 0 && bootverbose) + device_printf(dev, "PHY Power Failed PHYCS0R = %#x\n", val); + + /* + * Calibrate the clocks between the device and the host. This appears + * to be an automated process that clears the bit when it is done. + */ + ahci_set(ctlr->r_mem, AHCI_PHYCS2R, PHYCS2R_CALIBRATE); + for (to = PHY_RESET_TIMEOUT; to > 0; to--) { + val = ATA_INL(ctlr->r_mem, AHCI_PHYCS2R); + if ((val & PHYCS2R_CALIBRATE) == 0) + break; + DELAY(10); + } + if (to == 0 && bootverbose) + device_printf(dev, "PHY Cal Failed PHYCS2R %#x\n", val); + + /* + * OK, let things settle down a bit. + */ + DELAY(1000); + + /* + * Go back into normal mode now that we've calibrated the PHY. + */ + ATA_OUTL(ctlr->r_mem, AHCI_RWCR, 7); +} + +static void +ahci_a10_ch_start(struct ahci_channel *ch) +{ + uint32_t reg; + + /* + * Magical values from Allwinner SDK, setup the DMA before start + * operations on this channel. + */ + reg = ATA_INL(ch->r_mem, AHCI_P0DMACR); + reg &= ~0xff00; + reg |= 0x4400; + ATA_OUTL(ch->r_mem, AHCI_P0DMACR, reg); +} + +static int +ahci_a10_ctlr_reset(device_t dev) +{ + + ahci_a10_phy_reset(dev); + + return (ahci_ctlr_reset(dev)); +} + +static int +ahci_a10_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-ahci")) + return (ENXIO); + device_set_desc(dev, "Allwinner Integrated AHCI controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +ahci_a10_attach(device_t dev) +{ + device_t gpio; + int error; + struct ahci_controller *ctlr; + + ctlr = device_get_softc(dev); + ctlr->quirks = AHCI_Q_NOPMP; + ctlr->vendorid = 0; + ctlr->deviceid = 0; + ctlr->subvendorid = 0; + ctlr->subdeviceid = 0; + ctlr->r_rid = 0; + if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &ctlr->r_rid, RF_ACTIVE))) + return (ENXIO); + + /* Turn on the PLL for SATA */ + a10_clk_ahci_activate(); + + /* Apply power to the drive, if any */ + gpio = devclass_get_device(devclass_find("gpio"), 0); + if (gpio == NULL) { + device_printf(dev, + "GPIO device not yet present (SATA won't work).\n"); + bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, + ctlr->r_mem); + return (ENXIO); + } + GPIO_PIN_SETFLAGS(gpio, GPIO_AHCI_PWR, GPIO_PIN_OUTPUT); + GPIO_PIN_SET(gpio, GPIO_AHCI_PWR, GPIO_PIN_HIGH); + DELAY(10000); + + /* 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); + }; + + /* + * No MSI registers on this platform. + */ + ctlr->msi = 0; + ctlr->numirqs = 1; + + /* Channel start callback(). */ + ctlr->ch_start = ahci_a10_ch_start; + + /* + * Note: ahci_attach will release ctlr->r_mem on errors automatically + */ + return (ahci_attach(dev)); +} + +static int +ahci_a10_detach(device_t dev) +{ + + return (ahci_detach(dev)); +} + +devclass_t ahci_devclass; + +static device_method_t ahci_ata_methods[] = { + DEVMETHOD(device_probe, ahci_a10_probe), + DEVMETHOD(device_attach, ahci_a10_attach), + DEVMETHOD(device_detach, ahci_a10_detach), + DEVMETHOD(bus_print_child, ahci_print_child), + DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), + DEVMETHOD(bus_release_resource, ahci_release_resource), + DEVMETHOD(bus_setup_intr, ahci_setup_intr), + DEVMETHOD(bus_teardown_intr,ahci_teardown_intr), + DEVMETHOD(bus_child_location_str, ahci_child_location_str), + DEVMETHOD_END +}; + +static driver_t ahci_ata_driver = { + "ahci", + ahci_ata_methods, + sizeof(struct ahci_controller) +}; + +DRIVER_MODULE(ahci, simplebus, ahci_ata_driver, ahci_devclass, 0, 0); diff --git a/sys/arm/allwinner/a10_clk.c b/sys/arm/allwinner/a10_clk.c index 0c3634f..2f41d6f 100644 --- a/sys/arm/allwinner/a10_clk.c +++ b/sys/arm/allwinner/a10_clk.c @@ -36,26 +36,18 @@ __FBSDID("$FreeBSD$"); #include <sys/module.h> #include <sys/malloc.h> #include <sys/rman.h> -#include <sys/timeet.h> -#include <sys/timetc.h> -#include <sys/watchdog.h> #include <machine/bus.h> -#include <machine/cpu.h> -#include <machine/intr.h> -#include <dev/fdt/fdt_common.h> #include <dev/ofw/openfirm.h> -#include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> -#include <machine/bus.h> - #include "a10_clk.h" struct a10_ccm_softc { struct resource *res; bus_space_tag_t bst; bus_space_handle_t bsh; + int pll6_enabled; }; static struct a10_ccm_softc *a10_ccm_sc = NULL; @@ -190,6 +182,38 @@ a10_clk_emac_activate(void) return (0); } +int +a10_clk_gmac_activate(phandle_t node) +{ + char *phy_type; + struct a10_ccm_softc *sc; + uint32_t reg_value; + + sc = a10_ccm_sc; + if (sc == NULL) + return (ENXIO); + + /* Gating AHB clock for GMAC */ + reg_value = ccm_read_4(sc, CCM_AHB_GATING1); + reg_value |= CCM_AHB_GATING_GMAC; + ccm_write_4(sc, CCM_AHB_GATING1, reg_value); + + /* Set GMAC mode. */ + reg_value = CCM_GMAC_CLK_MII; + if (OF_getprop_alloc(node, "phy-type", 1, (void **)&phy_type) > 0) { + if (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) { @@ -203,6 +227,8 @@ a10_clk_pll6_enable(void) * 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 | @@ -211,6 +237,7 @@ a10_clk_pll6_enable(void) 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 @@ -229,6 +256,29 @@ a10_clk_pll6_get_rate(void) } 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; diff --git a/sys/arm/allwinner/a10_clk.h b/sys/arm/allwinner/a10_clk.h index f9b88c1..e5aa8c2 100644 --- a/sys/arm/allwinner/a10_clk.h +++ b/sys/arm/allwinner/a10_clk.h @@ -29,8 +29,6 @@ #ifndef _A10_CLK_H_ #define _A10_CLK_H_ -#define CCMU_BASE 0xe1c20000 - #define CCM_PLL1_CFG 0x0000 #define CCM_PLL1_TUN 0x0004 #define CCM_PLL2_CFG 0x0008 @@ -99,12 +97,25 @@ #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 +/* AHB_GATING_REG0 */ #define CCM_AHB_GATING_USB0 (1 << 0) #define CCM_AHB_GATING_EHCI0 (1 << 1) #define CCM_AHB_GATING_EHCI1 (1 << 3) #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_USB_PHY (1 << 8) #define CCM_USB0_RESET (1 << 0) @@ -140,6 +151,8 @@ int a10_clk_usb_activate(void); int a10_clk_usb_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); diff --git a/sys/arm/allwinner/a10_gpio.c b/sys/arm/allwinner/a10_gpio.c index b2dca00..c461013 100644 --- a/sys/arm/allwinner/a10_gpio.c +++ b/sys/arm/allwinner/a10_gpio.c @@ -524,8 +524,9 @@ static driver_t a10_gpio_driver = { DRIVER_MODULE(a10_gpio, simplebus, a10_gpio_driver, a10_gpio_devclass, 0, 0); int -a10_emac_gpio_config(uint32_t pin) +a10_gpio_ethernet_activate(uint32_t func) { + int i; struct a10_gpio_softc *sc = a10_gpio_sc; if (sc == NULL) @@ -533,7 +534,8 @@ a10_emac_gpio_config(uint32_t pin) /* Configure pin mux settings for MII. */ A10_GPIO_LOCK(sc); - a10_gpio_set_function(sc, pin, A10_GPIO_PULLDOWN); + for (i = 0; i <= 17; i++) + a10_gpio_set_function(sc, i, func); A10_GPIO_UNLOCK(sc); return (0); diff --git a/sys/arm/allwinner/a10_gpio.h b/sys/arm/allwinner/a10_gpio.h index 0e1dc86..e78b18f 100644 --- a/sys/arm/allwinner/a10_gpio.h +++ b/sys/arm/allwinner/a10_gpio.h @@ -29,6 +29,9 @@ #ifndef _A10_GPIO_H_ #define _A10_GPIO_H_ -int a10_emac_gpio_config(uint32_t pin); +#define A10_GPIO_FUNC_MII 2 +#define A10_GPIO_FUNC_RGMII 5 + +int a10_gpio_ethernet_activate(uint32_t); #endif diff --git a/sys/arm/allwinner/a10_mmc.c b/sys/arm/allwinner/a10_mmc.c index 85ce980..c728e3b 100644 --- a/sys/arm/allwinner/a10_mmc.c +++ b/sys/arm/allwinner/a10_mmc.c @@ -54,6 +54,13 @@ __FBSDID("$FreeBSD$"); #define A10_MMC_MEMRES 0 #define A10_MMC_IRQRES 1 #define A10_MMC_RESSZ 2 +#define A10_MMC_DMA_SEGS 16 +#define A10_MMC_DMA_MAX_SIZE 0x2000 +#define A10_MMC_DMA_FTRGLEVEL 0x20070008 + +static int a10_mmc_pio_mode = 0; + +TUNABLE_INT("hw.a10.mmc.pio_mode", &a10_mmc_pio_mode); struct a10_mmc_softc { bus_space_handle_t a10_bsh; @@ -71,6 +78,16 @@ struct a10_mmc_softc { uint32_t a10_intr; uint32_t a10_intr_wait; void * a10_intrhand; + + /* Fields required for DMA access. */ + bus_addr_t a10_dma_desc_phys; + bus_dmamap_t a10_dma_map; + bus_dma_tag_t a10_dma_tag; + void * a10_dma_desc; + bus_dmamap_t a10_dma_buf_map; + bus_dma_tag_t a10_dma_buf_tag; + int a10_dma_inuse; + int a10_dma_map_err; }; static struct resource_spec a10_mmc_res_spec[] = { @@ -82,6 +99,7 @@ static struct resource_spec a10_mmc_res_spec[] = { static int a10_mmc_probe(device_t); static int a10_mmc_attach(device_t); static int a10_mmc_detach(device_t); +static int a10_mmc_setup_dma(struct a10_mmc_softc *); static int a10_mmc_reset(struct a10_mmc_softc *); static void a10_mmc_intr(void *); static int a10_mmc_update_clock(struct a10_mmc_softc *); @@ -166,6 +184,14 @@ a10_mmc_attach(device_t dev) goto fail; } + if (a10_mmc_pio_mode == 0 && a10_mmc_setup_dma(sc) != 0) { + device_printf(sc->a10_dev, "Couldn't setup DMA!\n"); + a10_mmc_pio_mode = 1; + } + if (bootverbose) + device_printf(sc->a10_dev, "DMA status: %s\n", + a10_mmc_pio_mode ? "disabled" : "enabled"); + sc->a10_host.f_min = 400000; sc->a10_host.f_max = 52000000; sc->a10_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340; @@ -201,6 +227,140 @@ a10_mmc_detach(device_t dev) return (EBUSY); } +static void +a10_dma_desc_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err) +{ + struct a10_mmc_softc *sc; + + sc = (struct a10_mmc_softc *)arg; + if (err) { + sc->a10_dma_map_err = err; + return; + } + sc->a10_dma_desc_phys = segs[0].ds_addr; +} + +static int +a10_mmc_setup_dma(struct a10_mmc_softc *sc) +{ + int dma_desc_size, error; + + /* Allocate the DMA descriptor memory. */ + dma_desc_size = sizeof(struct a10_mmc_dma_desc) * A10_MMC_DMA_SEGS; + error = bus_dma_tag_create(bus_get_dma_tag(sc->a10_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + dma_desc_size, 1, dma_desc_size, 0, NULL, NULL, &sc->a10_dma_tag); + if (error) + return (error); + error = bus_dmamem_alloc(sc->a10_dma_tag, &sc->a10_dma_desc, + BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->a10_dma_map); + if (error) + return (error); + + error = bus_dmamap_load(sc->a10_dma_tag, sc->a10_dma_map, + sc->a10_dma_desc, dma_desc_size, a10_dma_desc_cb, sc, 0); + if (error) + return (error); + if (sc->a10_dma_map_err) + return (sc->a10_dma_map_err); + + /* Create the DMA map for data transfers. */ + error = bus_dma_tag_create(bus_get_dma_tag(sc->a10_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + A10_MMC_DMA_MAX_SIZE * A10_MMC_DMA_SEGS, A10_MMC_DMA_SEGS, + A10_MMC_DMA_MAX_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL, + &sc->a10_dma_buf_tag); + if (error) + return (error); + error = bus_dmamap_create(sc->a10_dma_buf_tag, 0, + &sc->a10_dma_buf_map); + if (error) + return (error); + + return (0); +} + +static void +a10_dma_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err) +{ + int i; + struct a10_mmc_dma_desc *dma_desc; + struct a10_mmc_softc *sc; + + sc = (struct a10_mmc_softc *)arg; + sc->a10_dma_map_err = err; + dma_desc = sc->a10_dma_desc; + /* Note nsegs is guaranteed to be zero if err is non-zero. */ + for (i = 0; i < nsegs; i++) { + dma_desc[i].buf_size = segs[i].ds_len; + dma_desc[i].buf_addr = segs[i].ds_addr; + dma_desc[i].config = A10_MMC_DMA_CONFIG_CH | + A10_MMC_DMA_CONFIG_OWN; + if (i == 0) + dma_desc[i].config |= A10_MMC_DMA_CONFIG_FD; + if (i < (nsegs - 1)) { + dma_desc[i].config |= A10_MMC_DMA_CONFIG_DIC; + dma_desc[i].next = sc->a10_dma_desc_phys + + ((i + 1) * sizeof(struct a10_mmc_dma_desc)); + } else { + dma_desc[i].config |= A10_MMC_DMA_CONFIG_LD | + A10_MMC_DMA_CONFIG_ER; + dma_desc[i].next = 0; + } + } +} + +static int +a10_mmc_prepare_dma(struct a10_mmc_softc *sc) +{ + bus_dmasync_op_t sync_op; + int error; + struct mmc_command *cmd; + uint32_t val; + + cmd = sc->a10_req->cmd; + if (cmd->data->len > A10_MMC_DMA_MAX_SIZE * A10_MMC_DMA_SEGS) + return (EFBIG); + error = bus_dmamap_load(sc->a10_dma_buf_tag, sc->a10_dma_buf_map, + cmd->data->data, cmd->data->len, a10_dma_cb, sc, BUS_DMA_NOWAIT); + if (error) + return (error); + if (sc->a10_dma_map_err) + return (sc->a10_dma_map_err); + + sc->a10_dma_inuse = 1; + if (cmd->data->flags & MMC_DATA_WRITE) + sync_op = BUS_DMASYNC_PREWRITE; + else + sync_op = BUS_DMASYNC_PREREAD; + bus_dmamap_sync(sc->a10_dma_buf_tag, sc->a10_dma_buf_map, sync_op); + bus_dmamap_sync(sc->a10_dma_tag, sc->a10_dma_map, BUS_DMASYNC_PREWRITE); + + val = A10_MMC_READ_4(sc, A10_MMC_IMASK); + val &= ~(A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ); + A10_MMC_WRITE_4(sc, A10_MMC_IMASK, val); + val = A10_MMC_READ_4(sc, A10_MMC_GCTRL); + val &= ~A10_MMC_ACCESS_BY_AHB; + val |= A10_MMC_DMA_ENABLE; + A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, val); + val |= A10_MMC_DMA_RESET; + A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, val); + A10_MMC_WRITE_4(sc, A10_MMC_DMAC, A10_MMC_IDMAC_SOFT_RST); + A10_MMC_WRITE_4(sc, A10_MMC_DMAC, + A10_MMC_IDMAC_IDMA_ON | A10_MMC_IDMAC_FIX_BURST); + val = A10_MMC_READ_4(sc, A10_MMC_IDIE); + val &= ~(A10_MMC_IDMAC_RECEIVE_INT | A10_MMC_IDMAC_TRANSMIT_INT); + if (cmd->data->flags & MMC_DATA_WRITE) + val |= A10_MMC_IDMAC_TRANSMIT_INT; + else + val |= A10_MMC_IDMAC_RECEIVE_INT; + A10_MMC_WRITE_4(sc, A10_MMC_IDIE, val); + A10_MMC_WRITE_4(sc, A10_MMC_DLBA, sc->a10_dma_desc_phys); + A10_MMC_WRITE_4(sc, A10_MMC_FTRGL, A10_MMC_DMA_FTRGLEVEL); + + return (0); +} + static int a10_mmc_reset(struct a10_mmc_softc *sc) { @@ -222,15 +382,14 @@ a10_mmc_reset(struct a10_mmc_softc *sc) /* Clear pending interrupts. */ A10_MMC_WRITE_4(sc, A10_MMC_RINTR, 0xffffffff); + A10_MMC_WRITE_4(sc, A10_MMC_IDST, 0xffffffff); /* Unmask interrupts. */ A10_MMC_WRITE_4(sc, A10_MMC_IMASK, A10_MMC_CMD_DONE | A10_MMC_INT_ERR_BIT | - A10_MMC_DATA_OVER | A10_MMC_AUTOCMD_DONE | - A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ); + A10_MMC_DATA_OVER | A10_MMC_AUTOCMD_DONE); /* Enable interrupts and AHB access. */ A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, - A10_MMC_READ_4(sc, A10_MMC_GCTRL) | - A10_MMC_INT_ENABLE | A10_MMC_ACCESS_BY_AHB); + A10_MMC_READ_4(sc, A10_MMC_GCTRL) | A10_MMC_INT_ENABLE); return (0); } @@ -247,15 +406,19 @@ a10_mmc_req_done(struct a10_mmc_softc *sc) a10_mmc_reset(sc); a10_mmc_update_clock(sc); } - /* Reset the FIFO. */ - A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, - A10_MMC_READ_4(sc, A10_MMC_GCTRL) | A10_MMC_FIFO_RESET); + if (sc->a10_dma_inuse == 0) { + /* Reset the FIFO. */ + A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, + A10_MMC_READ_4(sc, A10_MMC_GCTRL) | A10_MMC_FIFO_RESET); + } req = sc->a10_req; callout_stop(&sc->a10_timeoutc); sc->a10_req = NULL; sc->a10_intr = 0; sc->a10_resid = 0; + sc->a10_dma_inuse = 0; + sc->a10_dma_map_err = 0; sc->a10_intr_wait = 0; req->done(req); } @@ -295,7 +458,7 @@ a10_mmc_req_ok(struct a10_mmc_softc *sc) a10_mmc_req_done(sc); } -static void +static void a10_mmc_timeout(void *arg) { struct a10_mmc_softc *sc; @@ -335,28 +498,29 @@ a10_mmc_pio_transfer(struct a10_mmc_softc *sc, struct mmc_data *data) static void a10_mmc_intr(void *arg) { + bus_dmasync_op_t sync_op; struct a10_mmc_softc *sc; struct mmc_data *data; - uint32_t imask, rint; + uint32_t idst, imask, rint; sc = (struct a10_mmc_softc *)arg; A10_MMC_LOCK(sc); rint = A10_MMC_READ_4(sc, A10_MMC_RINTR); + idst = A10_MMC_READ_4(sc, A10_MMC_IDST); imask = A10_MMC_READ_4(sc, A10_MMC_IMASK); - if (imask == 0 && rint == 0) { + if (idst == 0 && imask == 0 && rint == 0) { A10_MMC_UNLOCK(sc); return; } #ifdef DEBUG - device_printf(sc->a10_dev, "imask: %#x, rint: %#x\n", imask, rint); + device_printf(sc->a10_dev, "idst: %#x, imask: %#x, rint: %#x\n", + idst, imask, rint); #endif if (sc->a10_req == NULL) { device_printf(sc->a10_dev, "Spurious interrupt - no active request, rint: 0x%08X\n", rint); - A10_MMC_WRITE_4(sc, A10_MMC_RINTR, rint); - A10_MMC_UNLOCK(sc); - return; + goto end; } if (rint & A10_MMC_INT_ERR_BIT) { device_printf(sc->a10_dev, "error rint: 0x%08X\n", rint); @@ -364,20 +528,39 @@ a10_mmc_intr(void *arg) sc->a10_req->cmd->error = MMC_ERR_TIMEOUT; else sc->a10_req->cmd->error = MMC_ERR_FAILED; - A10_MMC_WRITE_4(sc, A10_MMC_RINTR, rint); a10_mmc_req_done(sc); - A10_MMC_UNLOCK(sc); - return; + goto end; + } + if (idst & A10_MMC_IDMAC_ERROR) { + device_printf(sc->a10_dev, "error idst: 0x%08x\n", idst); + sc->a10_req->cmd->error = MMC_ERR_FAILED; + a10_mmc_req_done(sc); + goto end; } sc->a10_intr |= rint; data = sc->a10_req->cmd->data; - if (data != NULL && (rint & (A10_MMC_DATA_OVER | - A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ)) != 0) + if (data != NULL && sc->a10_dma_inuse == 1 && + (idst & A10_MMC_IDMAC_COMPLETE)) { + if (data->flags & MMC_DATA_WRITE) + sync_op = BUS_DMASYNC_POSTWRITE; + else + sync_op = BUS_DMASYNC_POSTREAD; + bus_dmamap_sync(sc->a10_dma_buf_tag, sc->a10_dma_buf_map, + sync_op); + bus_dmamap_sync(sc->a10_dma_tag, sc->a10_dma_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->a10_dma_buf_tag, sc->a10_dma_buf_map); + sc->a10_resid = data->len >> 2; + } else if (data != NULL && sc->a10_dma_inuse == 0 && + (rint & (A10_MMC_DATA_OVER | A10_MMC_RX_DATA_REQ | + A10_MMC_TX_DATA_REQ)) != 0) a10_mmc_pio_transfer(sc, data); if ((sc->a10_intr & sc->a10_intr_wait) == sc->a10_intr_wait) a10_mmc_req_ok(sc); +end: + A10_MMC_WRITE_4(sc, A10_MMC_IDST, idst); A10_MMC_WRITE_4(sc, A10_MMC_RINTR, rint); A10_MMC_UNLOCK(sc); } @@ -388,7 +571,7 @@ a10_mmc_request(device_t bus, device_t child, struct mmc_request *req) int blksz; struct a10_mmc_softc *sc; struct mmc_command *cmd; - uint32_t cmdreg; + uint32_t cmdreg, val; sc = device_get_softc(bus); A10_MMC_LOCK(sc); @@ -424,6 +607,19 @@ a10_mmc_request(device_t bus, device_t child, struct mmc_request *req) blksz = min(cmd->data->len, MMC_SECTOR_SIZE); A10_MMC_WRITE_4(sc, A10_MMC_BLKSZ, blksz); A10_MMC_WRITE_4(sc, A10_MMC_BCNTR, cmd->data->len); + + if (a10_mmc_pio_mode == 0) + a10_mmc_prepare_dma(sc); + /* Enable PIO access if sc->a10_dma_inuse is not set. */ + if (sc->a10_dma_inuse == 0) { + val = A10_MMC_READ_4(sc, A10_MMC_GCTRL); + val &= ~A10_MMC_DMA_ENABLE; + val |= A10_MMC_ACCESS_BY_AHB; + A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, val); + val = A10_MMC_READ_4(sc, A10_MMC_IMASK); + val |= A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ; + A10_MMC_WRITE_4(sc, A10_MMC_IMASK, val); + } } A10_MMC_WRITE_4(sc, A10_MMC_CARG, cmd->arg); @@ -436,7 +632,7 @@ a10_mmc_request(device_t bus, device_t child, struct mmc_request *req) } static int -a10_mmc_read_ivar(device_t bus, device_t child, int which, +a10_mmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) { struct a10_mmc_softc *sc; diff --git a/sys/arm/allwinner/a10_mmc.h b/sys/arm/allwinner/a10_mmc.h index 84c13a5..4b5d943 100644 --- a/sys/arm/allwinner/a10_mmc.h +++ b/sys/arm/allwinner/a10_mmc.h @@ -66,7 +66,6 @@ #define A10_MMC_DMA_ENABLE (1U << 5) #define A10_MMC_DEBOUNCE_ENABLE (1U << 8) #define A10_MMC_DDR_MODE (1U << 10) -#define A10_MMC_ACCESS_BY_DMA (1U << 30) #define A10_MMC_ACCESS_BY_AHB (1U << 31) #define A10_MMC_RESET \ (A10_MMC_SOFT_RESET | A10_MMC_FIFO_RESET | A10_MMC_DMA_RESET) @@ -175,5 +174,25 @@ #define A10_MMC_IDMAC_RD (6U << 13) #define A10_MMC_IDMAC_WR (7U << 13) #define A10_MMC_IDMAC_DESC_CLOSE (8U << 13) +#define A10_MMC_IDMAC_ERROR \ + (A10_MMC_IDMAC_FATAL_BUS_ERR | A10_MMC_IDMAC_CARD_ERR_SUM | \ + A10_MMC_IDMAC_DES_INVALID | A10_MMC_IDMAC_ABNORMAL_INT_SUM) +#define A10_MMC_IDMAC_COMPLETE \ + (A10_MMC_IDMAC_TRANSMIT_INT | A10_MMC_IDMAC_RECEIVE_INT) + +/* The DMA descriptor table. */ +struct a10_mmc_dma_desc { + uint32_t config; +#define A10_MMC_DMA_CONFIG_DIC (1U << 1) +#define A10_MMC_DMA_CONFIG_LD (1U << 2) +#define A10_MMC_DMA_CONFIG_FD (1U << 3) +#define A10_MMC_DMA_CONFIG_CH (1U << 4) +#define A10_MMC_DMA_CONFIG_ER (1U << 5) +#define A10_MMC_DMA_CONFIG_CES (1U << 30) +#define A10_MMC_DMA_CONFIG_OWN (1U << 31) + uint32_t buf_size; + uint32_t buf_addr; + uint32_t next; +}; #endif /* _A10_MMC_H_ */ diff --git a/sys/arm/allwinner/files.allwinner b/sys/arm/allwinner/files.allwinner index 8447939..08e688b 100644 --- a/sys/arm/allwinner/files.allwinner +++ b/sys/arm/allwinner/files.allwinner @@ -5,6 +5,7 @@ arm/arm/bus_space_base.c standard arm/arm/bus_space_asm_generic.S standard arm/arm/bus_space_generic.c standard +arm/allwinner/a10_ahci.c optional ahci arm/allwinner/a10_clk.c standard arm/allwinner/a10_common.c standard arm/allwinner/a10_ehci.c optional ehci diff --git a/sys/arm/allwinner/if_emac.c b/sys/arm/allwinner/if_emac.c index e311049..18aeb8f 100644 --- a/sys/arm/allwinner/if_emac.c +++ b/sys/arm/allwinner/if_emac.c @@ -142,17 +142,12 @@ static int sysctl_hw_emac_proc_limit(SYSCTL_HANDLER_ARGS); static void emac_sys_setup(void) { - int i; + /* Activate EMAC clock. */ a10_clk_emac_activate(); - - /* - * Configure pin mux settings for MII. - * Pins PA0 from PA17. - */ - for (i = 0; i <= 17; i++) - a10_emac_gpio_config(i); - /* Map sram */ + /* Set the pin mux to EMAC (mii). */ + a10_gpio_ethernet_activate(A10_GPIO_FUNC_MII); + /* Map sram. */ a10_map_to_emac(); } diff --git a/sys/arm/arm/genassym.c b/sys/arm/arm/genassym.c index 11bb90f..900fc41 100644 --- a/sys/arm/arm/genassym.c +++ b/sys/arm/arm/genassym.c @@ -134,7 +134,9 @@ ASSYM(ARM_RAS_END, ARM_RAS_END); #ifdef VFP ASSYM(PCB_VFPSTATE, offsetof(struct pcb, pcb_vfpstate)); +#endif +#if __ARM_ARCH >= 6 ASSYM(PC_CURPMAP, offsetof(struct pcpu, pc_curpmap)); #endif diff --git a/sys/arm/conf/CUBIEBOARD b/sys/arm/conf/CUBIEBOARD index c1df03a..2de16f7 100644 --- a/sys/arm/conf/CUBIEBOARD +++ b/sys/arm/conf/CUBIEBOARD @@ -57,7 +57,7 @@ device mmc # mmc/sd bus device mmcsd # mmc/sd flash cards # ATA controllers -#device ahci # AHCI-compatible SATA controllers +device ahci # AHCI-compatible SATA controllers #device ata # Legacy ATA/SATA controllers #options ATA_STATIC_ID # Static device numbering @@ -78,7 +78,7 @@ device gpio device scbus # SCSI bus (required for ATA/SCSI) device da # Direct Access (disks) -device pass +device pass # Passthrough device (direct ATA/SCSI access) # USB support options USB_HOST_ALIGN=64 # Align usb buffers to cache line size. diff --git a/sys/arm/conf/CUBIEBOARD2 b/sys/arm/conf/CUBIEBOARD2 index c8fded0..5330725 100644 --- a/sys/arm/conf/CUBIEBOARD2 +++ b/sys/arm/conf/CUBIEBOARD2 @@ -61,7 +61,7 @@ device mmc # mmc/sd bus device mmcsd # mmc/sd flash cards # ATA controllers -#device ahci # AHCI-compatible SATA controllers +device ahci # AHCI-compatible SATA controllers #device ata # Legacy ATA/SATA controllers #options ATA_STATIC_ID # Static device numbering @@ -82,7 +82,7 @@ device gpio device scbus # SCSI bus (required for ATA/SCSI) device da # Direct Access (disks) -device pass +device pass # Passthrough device (direct ATA/SCSI access) # USB support options USB_HOST_ALIGN=64 # Align usb buffers to cache line size. |