Index: head/sys/arm/conf/RK3188 =================================================================== --- head/sys/arm/conf/RK3188 (revision 277412) +++ head/sys/arm/conf/RK3188 (revision 277413) @@ -1,135 +1,136 @@ # # Kernel configuration for Rockchip RK3188 systems. # # For more information on this file, please read the config(5) manual page, # and/or the handbook section on Kernel Configuration Files: # # http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the ../../conf/NOTES and NOTES files. # If you are in doubt as to the purpose or necessity of a line, check first # in NOTES. # # $FreeBSD$ ident RK3188 include "../rockchip/std.rk30xx" options HZ=100 options SCHED_ULE # ULE scheduler options PREEMPTION # Enable kernel thread preemption options INET # InterNETworking options INET6 # IPv6 communications protocols options SCTP # Stream Control Transmission Protocol options FFS # Berkeley Fast Filesystem options SOFTUPDATES # Enable FFS soft updates support options UFS_ACL # Support for access control lists options UFS_DIRHASH # Improve performance on big directories options UFS_GJOURNAL # Enable gjournal-based UFS journaling options QUOTA # Enable disk quotas for UFS options NFSCL # Network Filesystem Client options NFSLOCKD # Network Lock Manager options NFS_ROOT # NFS usable as /, requires NFSCL options MSDOSFS # MSDOS Filesystem options CD9660 # ISO 9660 Filesystem options PROCFS # Process filesystem (requires PSEUDOFS) options PSEUDOFS # Pseudo-filesystem framework options TMPFS # Efficient memory filesystem options GEOM_PART_GPT # GUID Partition Tables options GEOM_PART_BSD # BSD partition scheme options GEOM_PART_MBR # MBR partition scheme options COMPAT_43 # Compatible with BSD 4.3 [KEEP THIS!] options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI options KTRACE # ktrace(1) support options SYSVSHM # SYSV-style shared memory options SYSVMSG # SYSV-style message queues options SYSVSEM # SYSV-style semaphores options _KPOSIX_PRIORITY_SCHEDULING # POSIX P1003_1B real-time extensions options KBD_INSTALL_CDEV # install a CDEV entry in /dev options FREEBSD_BOOT_LOADER # Process metadata passed from loader(8) options VFP # Enable floating point hardware support options SMP # Enable multiple cores # Debugging for use in -current makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols options BREAK_TO_DEBUGGER #options VERBOSE_SYSINIT # Enable verbose sysinit messages options KDB # Enable kernel debugger support # For minimum debugger support (stable branch) use: #options KDB_TRACE # Print a stack trace for a panic # For full debugger support use this instead: options DDB # Enable the kernel debugger #options INVARIANTS # Enable calls of extra sanity checking #options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS options WITNESS # Enable checks to detect deadlocks and cycles options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed options DIAGNOSTIC # Boot device is 2nd slice on USB options ROOTDEVNAME=\"ufs:/dev/da0s2\" # MMC/SD/SDIO Card slot support -#device mmc # mmc/sd bus -#device mmcsd # mmc/sd flash cards +device mmc # mmc/sd bus +device mmcsd # mmc/sd flash cards +device dwmmc # Console and misc device uart device uart_ns8250 device pty device snp device md device random # Entropy device # I2C support #device iicbus #device iic # GPIO device gpio device scbus # SCSI bus (required for ATA/SCSI) device da # Direct Access (disks) device pass # USB support options USB_HOST_ALIGN=32 # Align usb buffers to cache line size. device usb options USB_DEBUG #options USB_REQ_DEBUG #options USB_VERBOSE device dwcotg # DWC OTG controller device umass # Ethernet device loop device ether device mii device bpf # Wireless NIC cards options IEEE80211_DEBUG options IEEE80211_AMPDU_AGE options IEEE80211_SUPPORT_MESH options IEEE80211_SUPPORT_TDMA device wlan # 802.11 support device wlan_wep # 802.11 WEP support device wlan_ccmp # 802.11 CCMP support device wlan_tkip # 802.11 TKIP support device urtwn device urtwnfw device firmware # Used by the above # USB Ethernet support, requires miibus device miibus device udav # Flattened Device Tree options FDT # Configure using FDT/DTB data Index: head/sys/arm/rockchip/files.rk30xx =================================================================== --- head/sys/arm/rockchip/files.rk30xx (revision 277412) +++ head/sys/arm/rockchip/files.rk30xx (revision 277413) @@ -1,21 +1,23 @@ # $FreeBSD$ kern/kern_clocksource.c standard arm/arm/bus_space_asm_generic.S standard arm/arm/bus_space_generic.c standard arm/arm/cpufunc_asm_armv5.S standard arm/arm/cpufunc_asm_arm10.S standard arm/arm/cpufunc_asm_arm11.S standard arm/arm/cpufunc_asm_armv7.S standard arm/arm/gic.c standard arm/arm/mpcore_timer.c standard arm/arm/bus_space-v6.c standard arm/rockchip/rk30xx_common.c standard arm/rockchip/rk30xx_machdep.c standard arm/rockchip/rk30xx_pmu.c standard arm/rockchip/rk30xx_grf.c standard arm/rockchip/rk30xx_wdog.c standard arm/rockchip/rk30xx_gpio.c optional gpio arm/rockchip/rk30xx_mp.c optional smp + +dev/mmc/host/dwmmc.c optional dwmmc Index: head/sys/boot/fdt/dts/arm/rk3188-radxa-lite.dts =================================================================== --- head/sys/boot/fdt/dts/arm/rk3188-radxa-lite.dts (revision 277412) +++ head/sys/boot/fdt/dts/arm/rk3188-radxa-lite.dts (revision 277413) @@ -1,59 +1,63 @@ /*- * Copyright (c) 2014 Ganbold Tsagaankhuu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /dts-v1/; /include/ "rk3188.dtsi" / { model = "Radxa RadxaRock Lite"; memory { device_type = "memory"; reg = < 0x60000000 0x40000000 >; /* 1GB RAM */ }; aliases { soc = &SOC; }; SOC: rk3188 { uart2: serial@20064000 { status = "okay"; }; + mmc@10214000 { + status = "okay"; + }; + }; chosen { bootargs = "-v"; stdin = &uart2; stdout = &uart2; }; }; Index: head/sys/boot/fdt/dts/arm/rk3188-radxa.dts =================================================================== --- head/sys/boot/fdt/dts/arm/rk3188-radxa.dts (revision 277412) +++ head/sys/boot/fdt/dts/arm/rk3188-radxa.dts (revision 277413) @@ -1,59 +1,63 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /dts-v1/; /include/ "rk3188.dtsi" / { model = "Radxa RadxaRock"; memory { device_type = "memory"; reg = < 0x60000000 0x80000000 >; /* 2GB RAM */ }; aliases { soc = &SOC; }; SOC: rk3188 { uart2: serial@20064000 { status = "okay"; }; + mmc@10214000 { + status = "okay"; + }; + }; chosen { bootargs = "-v"; stdin = &uart2; stdout = &uart2; }; }; Index: head/sys/boot/fdt/dts/arm/rk3188.dtsi =================================================================== --- head/sys/boot/fdt/dts/arm/rk3188.dtsi (revision 277412) +++ head/sys/boot/fdt/dts/arm/rk3188.dtsi (revision 277413) @@ -1,254 +1,258 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ / { compatible = "rockchip,rk3188"; #address-cells = <1>; #size-cells = <1>; interrupt-parent = <&GIC>; aliases { soc = &SOC; }; SOC: rk3188 { #address-cells = <1>; #size-cells = <1>; compatible = "simple-bus"; ranges; bus-frequency = <0>; GIC: interrupt-controller@1013d000 { compatible = "arm,gic"; reg = <0x1013d000 0x1000>, /* Distributor Registers */ <0x1013c100 0x0100>; /* CPU Interface Registers */ interrupt-controller; #interrupt-cells = <1>; }; pmu@20004000 { compatible = "rockchip,rk30xx-pmu"; #address-cells = <1>; #size-cells = <1>; reg = <0x20004000 0x100>; }; grf@20008000 { compatible = "rockchip,rk30xx-grf"; #address-cells = <1>; #size-cells = <1>; reg = < 0x20008000 0x2000 >; }; mp_tmr@1013c600 { compatible = "arm,mpcore-timers"; #address-cells = <1>; #size-cells = <0>; clock-frequency = < 148500000 >; reg = <0x1013c200 0x100>, /* Global Timer Regs */ <0x1013c600 0x20>; /* Private Timer Regs */ interrupts = < 27 29 >; interrupt-parent = <&GIC>; }; timer@20038000 { compatible = "rockchip,rk30xx-timer"; reg = <0x20038000 0x20>; interrupts = <76>; clock-frequency = <24000000>; status = "disabled"; }; timer@20038020 { compatible = "rockchip,rk30xx-timer"; reg = <0x20038020 0x20>; interrupts = <77>; clock-frequency = <24000000>; status = "disabled"; }; timer@20038060 { compatible = "rockchip,rk30xx-timer"; reg = <0x20038060 0x20>; interrupts = <91>; clock-frequency = <24000000>; status = "disabled"; }; timer@20038080 { compatible = "rockchip,rk30xx-timer"; reg = <0x20038080 0x20>; interrupts = <92>; clock-frequency = <24000000>; status = "disabled"; }; timer@200380a0 { compatible = "rockchip,rk30xx-timer"; reg = <0x200380a0 0x20>; interrupts = <96>; clock-frequency = <24000000>; status = "disabled"; }; watchdog@2004c000 { compatible = "rockchip,rk30xx-wdt"; reg = <0x2004c000 0x100>; clock-frequency = < 66000000 >; }; gpio0: gpio@2000a000 { compatible = "rockchip,rk30xx-gpio"; gpio-controller; #gpio-cells = <2>; reg = <0x2000a000 0x100>; interrupts = <86>; interrupt-parent = <&GIC>; }; gpio1: gpio@2003c000 { compatible = "rockchip,rk30xx-gpio"; gpio-controller; #gpio-cells = <2>; reg = <0x2003c000 0x100>; interrupts = <87>; interrupt-parent = <&GIC>; }; gpio2: gpio@2003e000 { compatible = "rockchip,rk30xx-gpio"; gpio-controller; #gpio-cells = <2>; reg = <0x2003e000 0x100>; interrupts = <88>; interrupt-parent = <&GIC>; }; gpio3: gpio@20080000 { compatible = "rockchip,rk30xx-gpio"; gpio-controller; #gpio-cells = <2>; reg = <0x20080000 0x100>; interrupts = <89>; interrupt-parent = <&GIC>; }; usb0: usb@10180000 { compatible = "synopsys,designware-hs-otg2"; reg = <0x10180000 0x40000>; interrupts = <48>; interrupt-parent = <&GIC>; #address-cells = <1>; #size-cells = <0>; }; usb1: usb@101c0000 { compatible = "synopsys,designware-hs-otg2"; reg = <0x101c0000 0x40000>; interrupts = < 49 >; interrupt-parent = <&GIC>; #address-cells = <1>; #size-cells = <0>; gpios = <&gpio0 3 2 2>; }; uart0: serial@10124000 { compatible = "ns16550"; reg = <0x10124000 0x400>; reg-shift = <2>; interrupts = <66>; interrupt-parent = <&GIC>; current-speed = <115200>; clock-frequency = < 24000000 >; busy-detect = <1>; broken-txfifo = <1>; status = "disabled"; }; uart1: serial@10126000 { compatible = "ns16550"; reg = <0x10126000 0x400>; reg-shift = <2>; interrupts = <67>; interrupt-parent = <&GIC>; current-speed = <115200>; clock-frequency = < 24000000 >; busy-detect = <1>; broken-txfifo = <1>; status = "disabled"; }; uart2: serial@20064000 { compatible = "ns16550"; reg = <0x20064000 0x400>; reg-shift = <2>; interrupts = <68>; interrupt-parent = <&GIC>; current-speed = <115200>; clock-frequency = < 24000000 >; busy-detect = <1>; broken-txfifo = <1>; status = "disabled"; }; uart3: serial@20068000 { compatible = "ns16550"; reg = <0x20068000 0x400>; reg-shift = <2>; interrupts = <69>; interrupt-parent = <&GIC>; current-speed = <115200>; clock-frequency = < 24000000 >; busy-detect = <1>; broken-txfifo = <1>; status = "disabled"; }; mmc@10214000 { - compatible = "rockchip,rk30xx-mmc"; + compatible = "rockchip,rk2928-dw-mshc"; reg = <0x10214000 0x1000>; interrupts = <55>; #address-cells = <1>; #size-cells = <0>; - clock-frequency = <24000000>; /* TODO: verify freq */ + bus-frequency = <48000000>; /* TODO: verify freq */ + fifo-depth = <0x40>; + num-slots = <1>; status = "disabled"; }; mmc@10218000 { - compatible = "rockchip,rk30xx-mmc"; + compatible = "rockchip,rk2928-dw-mshc"; reg = <0x10218000 0x1000>; interrupts = <56>; #address-cells = <1>; #size-cells = <0>; - clock-frequency = <24000000>; /* TODO: verify freq */ + bus-frequency = <48000000>; /* TODO: verify freq */ + fifo-depth = <0x40>; + num-slots = <1>; status = "disabled"; }; }; }; Index: head/sys/dev/mmc/host/dwmmc.c =================================================================== --- head/sys/dev/mmc/host/dwmmc.c (revision 277412) +++ head/sys/dev/mmc/host/dwmmc.c (revision 277413) @@ -1,1101 +1,1212 @@ /*- * Copyright (c) 2014 Ruslan Bukin * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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. */ /* * Synopsys DesignWare Mobile Storage Host Controller * Chapter 14, Altera Cyclone V Device Handbook (CV-5V2 2014.07.22) */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmcbr_if.h" #define dprintf(x, arg...) #define READ4(_sc, _reg) \ bus_read_4((_sc)->res[0], _reg) #define WRITE4(_sc, _reg, _val) \ bus_write_4((_sc)->res[0], _reg, _val) #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) #define DWMMC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define DWMMC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define DWMMC_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ "dwmmc", MTX_DEF) #define DWMMC_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define DWMMC_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); #define DWMMC_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); #define PENDING_CMD 0x01 #define PENDING_STOP 0x02 #define CARD_INIT_DONE 0x04 #define DWMMC_DATA_ERR_FLAGS (SDMMC_INTMASK_DRT | SDMMC_INTMASK_DCRC \ |SDMMC_INTMASK_HTO | SDMMC_INTMASK_SBE \ |SDMMC_INTMASK_EBE) #define DWMMC_CMD_ERR_FLAGS (SDMMC_INTMASK_RTO | SDMMC_INTMASK_RCRC \ |SDMMC_INTMASK_RE) #define DWMMC_ERR_FLAGS (DWMMC_DATA_ERR_FLAGS | DWMMC_CMD_ERR_FLAGS \ |SDMMC_INTMASK_HLE) #define DES0_DIC (1 << 1) #define DES0_LD (1 << 2) #define DES0_FS (1 << 3) #define DES0_CH (1 << 4) #define DES0_ER (1 << 5) #define DES0_CES (1 << 30) #define DES0_OWN (1 << 31) #define DES1_BS1_MASK 0xfff #define DES1_BS1_SHIFT 0 struct idmac_desc { uint32_t des0; /* control */ uint32_t des1; /* bufsize */ uint32_t des2; /* buf1 phys addr */ uint32_t des3; /* buf2 phys addr or next descr */ }; #define DESC_COUNT 256 #define DESC_SIZE (sizeof(struct idmac_desc) * DESC_COUNT) #define DEF_MSIZE 0x2 /* Burst size of multiple transaction */ struct dwmmc_softc { struct resource *res[2]; bus_space_tag_t bst; bus_space_handle_t bsh; device_t dev; void *intr_cookie; struct mmc_host host; struct mtx sc_mtx; struct mmc_request *req; struct mmc_command *curcmd; uint32_t flags; uint32_t hwtype; uint32_t use_auto_stop; + uint32_t use_pio; + uint32_t pwren_inverted; bus_dma_tag_t desc_tag; bus_dmamap_t desc_map; struct idmac_desc *desc_ring; bus_addr_t desc_ring_paddr; bus_dma_tag_t buf_tag; bus_dmamap_t buf_map; uint32_t bus_busy; uint32_t dto_rcvd; uint32_t acd_rcvd; uint32_t cmd_done; uint32_t bus_hz; uint32_t fifo_depth; uint32_t num_slots; uint32_t sdr_timing; uint32_t ddr_timing; }; static void dwmmc_next_operation(struct dwmmc_softc *); static int dwmmc_setup_bus(struct dwmmc_softc *, int); static int dma_done(struct dwmmc_softc *, struct mmc_command *); static int dma_stop(struct dwmmc_softc *); +static void pio_read(struct dwmmc_softc *, struct mmc_command *); +static void pio_write(struct dwmmc_softc *, struct mmc_command *); static struct resource_spec dwmmc_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; enum { HWTYPE_NONE, HWTYPE_ALTERA, HWTYPE_EXYNOS, + HWTYPE_ROCKCHIP, }; #define HWTYPE_MASK (0x0000ffff) #define HWFLAG_MASK (0xffff << 16) static struct ofw_compat_data compat_data[] = { {"altr,socfpga-dw-mshc", HWTYPE_ALTERA}, {"samsung,exynos5420-dw-mshc", HWTYPE_EXYNOS}, + {"rockchip,rk2928-dw-mshc", HWTYPE_ROCKCHIP}, {NULL, HWTYPE_NONE}, }; static void dwmmc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; *(bus_addr_t *)arg = segs[0].ds_addr; } static void dwmmc_ring_setup(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct dwmmc_softc *sc; int idx; if (error != 0) return; sc = arg; dprintf("nsegs %d seg0len %lu\n", nsegs, segs[0].ds_len); for (idx = 0; idx < nsegs; idx++) { sc->desc_ring[idx].des0 = (DES0_OWN | DES0_DIC | DES0_CH); sc->desc_ring[idx].des1 = segs[idx].ds_len; sc->desc_ring[idx].des2 = segs[idx].ds_addr; if (idx == 0) sc->desc_ring[idx].des0 |= DES0_FS; if (idx == (nsegs - 1)) { sc->desc_ring[idx].des0 &= ~(DES0_DIC | DES0_CH); sc->desc_ring[idx].des0 |= DES0_LD; } } } static int dwmmc_ctrl_reset(struct dwmmc_softc *sc, int reset_bits) { int reg; int i; reg = READ4(sc, SDMMC_CTRL); reg |= (reset_bits); WRITE4(sc, SDMMC_CTRL, reg); /* Wait reset done */ for (i = 0; i < 100; i++) { if (!(READ4(sc, SDMMC_CTRL) & reset_bits)) return (0); DELAY(10); }; device_printf(sc->dev, "Reset failed\n"); return (1); } static int dma_setup(struct dwmmc_softc *sc) { int error; int nidx; int idx; /* * Set up TX descriptor ring, descriptors, and dma maps. */ error = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* Parent tag. */ 4096, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ DESC_SIZE, 1, /* maxsize, nsegments */ DESC_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->desc_tag); if (error != 0) { device_printf(sc->dev, "could not create ring DMA tag.\n"); return (1); } error = bus_dmamem_alloc(sc->desc_tag, (void**)&sc->desc_ring, BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->desc_map); if (error != 0) { device_printf(sc->dev, "could not allocate descriptor ring.\n"); return (1); } error = bus_dmamap_load(sc->desc_tag, sc->desc_map, sc->desc_ring, DESC_SIZE, dwmmc_get1paddr, &sc->desc_ring_paddr, 0); if (error != 0) { device_printf(sc->dev, "could not load descriptor ring map.\n"); return (1); } for (idx = 0; idx < DESC_COUNT; idx++) { sc->desc_ring[idx].des0 = DES0_CH; sc->desc_ring[idx].des1 = 0; nidx = (idx + 1) % DESC_COUNT; sc->desc_ring[idx].des3 = sc->desc_ring_paddr + \ (nidx * sizeof(struct idmac_desc)); } error = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* Parent tag. */ 4096, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ DESC_COUNT*MMC_SECTOR_SIZE, /* maxsize */ DESC_COUNT, /* nsegments */ MMC_SECTOR_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->buf_tag); if (error != 0) { device_printf(sc->dev, "could not create ring DMA tag.\n"); return (1); } error = bus_dmamap_create(sc->buf_tag, 0, &sc->buf_map); if (error != 0) { device_printf(sc->dev, "could not create TX buffer DMA map.\n"); return (1); } return (0); } static void dwmmc_cmd_done(struct dwmmc_softc *sc) { struct mmc_command *cmd; cmd = sc->curcmd; if (cmd == NULL) return; if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_136) { cmd->resp[3] = READ4(sc, SDMMC_RESP0); cmd->resp[2] = READ4(sc, SDMMC_RESP1); cmd->resp[1] = READ4(sc, SDMMC_RESP2); cmd->resp[0] = READ4(sc, SDMMC_RESP3); } else { cmd->resp[3] = 0; cmd->resp[2] = 0; cmd->resp[1] = 0; cmd->resp[0] = READ4(sc, SDMMC_RESP0); } } } static void dwmmc_tasklet(struct dwmmc_softc *sc) { struct mmc_command *cmd; cmd = sc->curcmd; if (cmd == NULL) return; if (!sc->cmd_done) return; if (cmd->error != MMC_ERR_NONE || !cmd->data) { dwmmc_next_operation(sc); } else if (cmd->data && sc->dto_rcvd) { if ((cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK || cmd->opcode == MMC_READ_MULTIPLE_BLOCK) && sc->use_auto_stop) { if (sc->acd_rcvd) dwmmc_next_operation(sc); } else { dwmmc_next_operation(sc); } } } static void dwmmc_intr(void *arg) { struct mmc_command *cmd; struct dwmmc_softc *sc; uint32_t reg; sc = arg; DWMMC_LOCK(sc); cmd = sc->curcmd; /* First handle SDMMC controller interrupts */ reg = READ4(sc, SDMMC_MINTSTS); if (reg) { dprintf("%s 0x%08x\n", __func__, reg); if (reg & DWMMC_CMD_ERR_FLAGS) { WRITE4(sc, SDMMC_RINTSTS, DWMMC_CMD_ERR_FLAGS); dprintf("cmd err 0x%08x cmd 0x%08x\n", reg, cmd->opcode); cmd->error = MMC_ERR_TIMEOUT; } if (reg & DWMMC_DATA_ERR_FLAGS) { WRITE4(sc, SDMMC_RINTSTS, DWMMC_DATA_ERR_FLAGS); dprintf("data err 0x%08x cmd 0x%08x\n", reg, cmd->opcode); cmd->error = MMC_ERR_FAILED; - dma_done(sc, cmd); - dma_stop(sc); + if (!sc->use_pio) { + dma_done(sc, cmd); + dma_stop(sc); + } } if (reg & SDMMC_INTMASK_CMD_DONE) { dwmmc_cmd_done(sc); sc->cmd_done = 1; WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_CMD_DONE); } if (reg & SDMMC_INTMASK_ACD) { sc->acd_rcvd = 1; WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_ACD); } if (reg & SDMMC_INTMASK_DTO) { sc->dto_rcvd = 1; WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_DTO); } if (reg & SDMMC_INTMASK_CD) { /* XXX: Handle card detect */ WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_CD); } } - /* Now handle DMA interrupts */ - reg = READ4(sc, SDMMC_IDSTS); - if (reg) { - dprintf("dma intr 0x%08x\n", reg); - if (reg & (SDMMC_IDINTEN_TI | SDMMC_IDINTEN_RI)) { - WRITE4(sc, SDMMC_IDSTS, (SDMMC_IDINTEN_TI | - SDMMC_IDINTEN_RI)); - WRITE4(sc, SDMMC_IDSTS, SDMMC_IDINTEN_NI); - dma_done(sc, cmd); + if (sc->use_pio) { + if (reg & (SDMMC_INTMASK_RXDR|SDMMC_INTMASK_DTO)) { + pio_read(sc, cmd); } + if (reg & (SDMMC_INTMASK_TXDR|SDMMC_INTMASK_DTO)) { + pio_write(sc, cmd); + } + } else { + /* Now handle DMA interrupts */ + reg = READ4(sc, SDMMC_IDSTS); + if (reg) { + dprintf("dma intr 0x%08x\n", reg); + if (reg & (SDMMC_IDINTEN_TI | SDMMC_IDINTEN_RI)) { + WRITE4(sc, SDMMC_IDSTS, (SDMMC_IDINTEN_TI | + SDMMC_IDINTEN_RI)); + WRITE4(sc, SDMMC_IDSTS, SDMMC_IDINTEN_NI); + dma_done(sc, cmd); + } + } } dwmmc_tasklet(sc); DWMMC_UNLOCK(sc); } static int parse_fdt(struct dwmmc_softc *sc) { pcell_t dts_value[3]; phandle_t node; int len; if ((node = ofw_bus_get_node(sc->dev)) == -1) return (ENXIO); /* fifo-depth */ if ((len = OF_getproplen(node, "fifo-depth")) <= 0) return (ENXIO); OF_getencprop(node, "fifo-depth", dts_value, len); sc->fifo_depth = dts_value[0]; /* num-slots */ if ((len = OF_getproplen(node, "num-slots")) <= 0) return (ENXIO); OF_getencprop(node, "num-slots", dts_value, len); sc->num_slots = dts_value[0]; /* * We need some platform-specific code to know * what the clock is supplied for our device. * For now rely on the value specified in FDT. */ if ((len = OF_getproplen(node, "bus-frequency")) <= 0) return (ENXIO); OF_getencprop(node, "bus-frequency", dts_value, len); sc->bus_hz = dts_value[0]; /* * Platform-specific stuff * XXX: Move to separate file */ if ((sc->hwtype & HWTYPE_MASK) != HWTYPE_EXYNOS) return (0); if ((len = OF_getproplen(node, "samsung,dw-mshc-ciu-div")) <= 0) return (ENXIO); OF_getencprop(node, "samsung,dw-mshc-ciu-div", dts_value, len); sc->sdr_timing = (dts_value[0] << SDMMC_CLKSEL_DIVIDER_SHIFT); sc->ddr_timing = (dts_value[0] << SDMMC_CLKSEL_DIVIDER_SHIFT); if ((len = OF_getproplen(node, "samsung,dw-mshc-sdr-timing")) <= 0) return (ENXIO); OF_getencprop(node, "samsung,dw-mshc-sdr-timing", dts_value, len); sc->sdr_timing |= ((dts_value[0] << SDMMC_CLKSEL_SAMPLE_SHIFT) | (dts_value[1] << SDMMC_CLKSEL_DRIVE_SHIFT)); if ((len = OF_getproplen(node, "samsung,dw-mshc-ddr-timing")) <= 0) return (ENXIO); OF_getencprop(node, "samsung,dw-mshc-ddr-timing", dts_value, len); sc->ddr_timing |= ((dts_value[0] << SDMMC_CLKSEL_SAMPLE_SHIFT) | (dts_value[1] << SDMMC_CLKSEL_DRIVE_SHIFT)); return (0); } static int dwmmc_probe(device_t dev) { uintptr_t hwtype; if (!ofw_bus_status_okay(dev)) return (ENXIO); hwtype = ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (hwtype == HWTYPE_NONE) return (ENXIO); device_set_desc(dev, "Synopsys DesignWare Mobile " "Storage Host Controller"); return (BUS_PROBE_DEFAULT); } static int dwmmc_attach(device_t dev) { struct dwmmc_softc *sc; device_t child; int error; int slot; sc = device_get_softc(dev); sc->dev = dev; sc->hwtype = ofw_bus_search_compatible(dev, compat_data)->ocd_data; /* Why not to use Auto Stop? It save a hundred of irq per second */ sc->use_auto_stop = 1; error = parse_fdt(sc); if (error != 0) { device_printf(dev, "Can't get FDT property.\n"); return (ENXIO); } DWMMC_LOCK_INIT(sc); if (bus_alloc_resources(dev, dwmmc_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); /* Setup interrupt handler. */ error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, NULL, dwmmc_intr, sc, &sc->intr_cookie); if (error != 0) { device_printf(dev, "could not setup interrupt handler.\n"); return (ENXIO); } device_printf(dev, "Hardware version ID is %04x\n", READ4(sc, SDMMC_VERID) & 0xffff); - WRITE4(sc, EMMCP_MPSBEGIN0, 0); - WRITE4(sc, EMMCP_SEND0, 0); - WRITE4(sc, EMMCP_CTRL0, (MPSCTRL_SECURE_READ_BIT | - MPSCTRL_SECURE_WRITE_BIT | - MPSCTRL_NON_SECURE_READ_BIT | - MPSCTRL_NON_SECURE_WRITE_BIT | - MPSCTRL_VALID)); + sc->use_pio = 0; + sc->pwren_inverted = 0; + if ((sc->hwtype & HWTYPE_MASK) == HWTYPE_ROCKCHIP) { + sc->use_pio = 1; + sc->pwren_inverted = 1; + } else { + WRITE4(sc, EMMCP_MPSBEGIN0, 0); + WRITE4(sc, EMMCP_SEND0, 0); + WRITE4(sc, EMMCP_CTRL0, (MPSCTRL_SECURE_READ_BIT | + MPSCTRL_SECURE_WRITE_BIT | + MPSCTRL_NON_SECURE_READ_BIT | + MPSCTRL_NON_SECURE_WRITE_BIT | + MPSCTRL_VALID)); + } + /* XXX: we support operation for slot index 0 only */ slot = 0; - WRITE4(sc, SDMMC_PWREN, (1 << slot)); + if (sc->pwren_inverted) { + WRITE4(sc, SDMMC_PWREN, (0 << slot)); + } else { + WRITE4(sc, SDMMC_PWREN, (1 << slot)); + } /* Reset all */ if (dwmmc_ctrl_reset(sc, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET))) return (ENXIO); dwmmc_setup_bus(sc, sc->host.f_min); - if (dma_setup(sc)) - return (ENXIO); + if (!sc->use_pio) { + if (dma_setup(sc)) + return (ENXIO); - /* Install desc base */ - WRITE4(sc, SDMMC_DBADDR, sc->desc_ring_paddr); + /* Install desc base */ + WRITE4(sc, SDMMC_DBADDR, sc->desc_ring_paddr); - /* Enable DMA interrupts */ - WRITE4(sc, SDMMC_IDSTS, SDMMC_IDINTEN_MASK); - WRITE4(sc, SDMMC_IDINTEN, (SDMMC_IDINTEN_NI | - SDMMC_IDINTEN_RI | - SDMMC_IDINTEN_TI)); + /* Enable DMA interrupts */ + WRITE4(sc, SDMMC_IDSTS, SDMMC_IDINTEN_MASK); + WRITE4(sc, SDMMC_IDINTEN, (SDMMC_IDINTEN_NI | + SDMMC_IDINTEN_RI | + SDMMC_IDINTEN_TI)); + } /* Clear and disable interrups for a while */ WRITE4(sc, SDMMC_RINTSTS, 0xffffffff); WRITE4(sc, SDMMC_INTMASK, 0); /* Maximum timeout */ WRITE4(sc, SDMMC_TMOUT, 0xffffffff); /* Enable interrupts */ WRITE4(sc, SDMMC_RINTSTS, 0xffffffff); WRITE4(sc, SDMMC_INTMASK, (SDMMC_INTMASK_CMD_DONE | SDMMC_INTMASK_DTO | SDMMC_INTMASK_ACD | SDMMC_INTMASK_TXDR | SDMMC_INTMASK_RXDR | DWMMC_ERR_FLAGS | SDMMC_INTMASK_CD)); WRITE4(sc, SDMMC_CTRL, SDMMC_CTRL_INT_ENABLE); sc->host.f_min = 400000; sc->host.f_max = 200000000; sc->host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340; sc->host.caps = MMC_CAP_4_BIT_DATA; child = device_add_child(dev, "mmc", 0); return (bus_generic_attach(dev)); } static int dwmmc_setup_bus(struct dwmmc_softc *sc, int freq) { int tout; int div; if (freq == 0) { WRITE4(sc, SDMMC_CLKENA, 0); WRITE4(sc, SDMMC_CMD, (SDMMC_CMD_WAIT_PRVDATA | SDMMC_CMD_UPD_CLK_ONLY | SDMMC_CMD_START)); tout = 1000; do { if (tout-- < 0) { device_printf(sc->dev, "Failed update clk\n"); return (1); } } while (READ4(sc, SDMMC_CMD) & SDMMC_CMD_START); return (0); } WRITE4(sc, SDMMC_CLKENA, 0); WRITE4(sc, SDMMC_CLKSRC, 0); div = (sc->bus_hz != freq) ? DIV_ROUND_UP(sc->bus_hz, 2 * freq) : 0; WRITE4(sc, SDMMC_CLKDIV, div); WRITE4(sc, SDMMC_CMD, (SDMMC_CMD_WAIT_PRVDATA | SDMMC_CMD_UPD_CLK_ONLY | SDMMC_CMD_START)); tout = 1000; do { if (tout-- < 0) { device_printf(sc->dev, "Failed to update clk"); return (1); } } while (READ4(sc, SDMMC_CMD) & SDMMC_CMD_START); WRITE4(sc, SDMMC_CLKENA, (SDMMC_CLKENA_CCLK_EN | SDMMC_CLKENA_LP)); WRITE4(sc, SDMMC_CMD, SDMMC_CMD_WAIT_PRVDATA | SDMMC_CMD_UPD_CLK_ONLY | SDMMC_CMD_START); tout = 1000; do { if (tout-- < 0) { device_printf(sc->dev, "Failed to enable clk\n"); return (1); } } while (READ4(sc, SDMMC_CMD) & SDMMC_CMD_START); return (0); } static int dwmmc_update_ios(device_t brdev, device_t reqdev) { struct dwmmc_softc *sc; struct mmc_ios *ios; sc = device_get_softc(brdev); ios = &sc->host.ios; dprintf("Setting up clk %u bus_width %d\n", ios->clock, ios->bus_width); dwmmc_setup_bus(sc, ios->clock); if (ios->bus_width == bus_width_8) WRITE4(sc, SDMMC_CTYPE, SDMMC_CTYPE_8BIT); else if (ios->bus_width == bus_width_4) WRITE4(sc, SDMMC_CTYPE, SDMMC_CTYPE_4BIT); else WRITE4(sc, SDMMC_CTYPE, 0); if ((sc->hwtype & HWTYPE_MASK) == HWTYPE_EXYNOS) { /* XXX: take care about DDR or SDR use here */ WRITE4(sc, SDMMC_CLKSEL, sc->sdr_timing); } /* * XXX: take care about DDR bit * * reg = READ4(sc, SDMMC_UHS_REG); * reg |= (SDMMC_UHS_REG_DDR); * WRITE4(sc, SDMMC_UHS_REG, reg); */ return (0); } static int dma_done(struct dwmmc_softc *sc, struct mmc_command *cmd) { struct mmc_data *data; data = cmd->data; if (data->flags & MMC_DATA_WRITE) bus_dmamap_sync(sc->buf_tag, sc->buf_map, BUS_DMASYNC_POSTWRITE); else bus_dmamap_sync(sc->buf_tag, sc->buf_map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->buf_tag, sc->buf_map); return (0); } static int dma_stop(struct dwmmc_softc *sc) { int reg; reg = READ4(sc, SDMMC_CTRL); reg &= ~(SDMMC_CTRL_USE_IDMAC); reg |= (SDMMC_CTRL_DMA_RESET); WRITE4(sc, SDMMC_CTRL, reg); reg = READ4(sc, SDMMC_BMOD); reg &= ~(SDMMC_BMOD_DE | SDMMC_BMOD_FB); reg |= (SDMMC_BMOD_SWR); WRITE4(sc, SDMMC_BMOD, reg); return (0); } static int dma_prepare(struct dwmmc_softc *sc, struct mmc_command *cmd) { struct mmc_data *data; int len; int err; int reg; data = cmd->data; len = data->len; reg = READ4(sc, SDMMC_INTMASK); reg &= ~(SDMMC_INTMASK_TXDR | SDMMC_INTMASK_RXDR); WRITE4(sc, SDMMC_INTMASK, reg); err = bus_dmamap_load(sc->buf_tag, sc->buf_map, data->data, data->len, dwmmc_ring_setup, sc, BUS_DMA_NOWAIT); if (err != 0) panic("dmamap_load failed\n"); if (data->flags & MMC_DATA_WRITE) bus_dmamap_sync(sc->buf_tag, sc->buf_map, BUS_DMASYNC_PREWRITE); else bus_dmamap_sync(sc->buf_tag, sc->buf_map, BUS_DMASYNC_PREREAD); reg = (DEF_MSIZE << SDMMC_FIFOTH_MSIZE_S); reg |= ((sc->fifo_depth / 2) - 1) << SDMMC_FIFOTH_RXWMARK_S; reg |= (sc->fifo_depth / 2) << SDMMC_FIFOTH_TXWMARK_S; WRITE4(sc, SDMMC_FIFOTH, reg); wmb(); reg = READ4(sc, SDMMC_CTRL); reg |= (SDMMC_CTRL_USE_IDMAC | SDMMC_CTRL_DMA_ENABLE); WRITE4(sc, SDMMC_CTRL, reg); wmb(); reg = READ4(sc, SDMMC_BMOD); reg |= (SDMMC_BMOD_DE | SDMMC_BMOD_FB); WRITE4(sc, SDMMC_BMOD, reg); /* Start */ WRITE4(sc, SDMMC_PLDMND, 1); return (0); } +static int +pio_prepare(struct dwmmc_softc *sc, struct mmc_command *cmd) +{ + struct mmc_data *data; + int reg; + + data = cmd->data; + data->xfer_len = 0; + + reg = (DEF_MSIZE << SDMMC_FIFOTH_MSIZE_S); + reg |= ((sc->fifo_depth / 2) - 1) << SDMMC_FIFOTH_RXWMARK_S; + reg |= (sc->fifo_depth / 2) << SDMMC_FIFOTH_TXWMARK_S; + + WRITE4(sc, SDMMC_FIFOTH, reg); + wmb(); + + return (0); +} + static void +pio_read(struct dwmmc_softc *sc, struct mmc_command *cmd) +{ + struct mmc_data *data; + uint32_t *p, status; + + if (cmd == NULL || cmd->data == NULL) + return; + + data = cmd->data; + if ((data->flags & MMC_DATA_READ) == 0) + return; + + KASSERT((data->xfer_len & 3) == 0, ("xfer_len not aligned")); + p = (uint32_t *)data->data + (data->xfer_len >> 2); + + while (data->xfer_len < data->len) { + status = READ4(sc, SDMMC_STATUS); + if (status & SDMMC_STATUS_FIFO_EMPTY) + break; + *p++ = READ4(sc, SDMMC_DATA); + data->xfer_len += 4; + } + + WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_RXDR); +} + +static void +pio_write(struct dwmmc_softc *sc, struct mmc_command *cmd) +{ + struct mmc_data *data; + uint32_t *p, status; + + if (cmd == NULL || cmd->data == NULL) + return; + + data = cmd->data; + if ((data->flags & MMC_DATA_WRITE) == 0) + return; + + KASSERT((data->xfer_len & 3) == 0, ("xfer_len not aligned")); + p = (uint32_t *)data->data + (data->xfer_len >> 2); + + while (data->xfer_len < data->len) { + status = READ4(sc, SDMMC_STATUS); + if (status & SDMMC_STATUS_FIFO_FULL) + break; + WRITE4(sc, SDMMC_DATA, *p++); + data->xfer_len += 4; + } + + WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_TXDR); +} + +static void dwmmc_start_cmd(struct dwmmc_softc *sc, struct mmc_command *cmd) { struct mmc_data *data; uint32_t blksz; uint32_t cmdr; sc->curcmd = cmd; data = cmd->data; + if ((sc->hwtype & HWTYPE_MASK) == HWTYPE_ROCKCHIP) + dwmmc_setup_bus(sc, sc->host.ios.clock); + /* XXX Upper layers don't always set this */ cmd->mrq = sc->req; /* Begin setting up command register. */ cmdr = cmd->opcode; dprintf("cmd->opcode 0x%08x\n", cmd->opcode); if (cmd->opcode == MMC_STOP_TRANSMISSION || cmd->opcode == MMC_GO_IDLE_STATE || cmd->opcode == MMC_GO_INACTIVE_STATE) cmdr |= SDMMC_CMD_STOP_ABORT; else if (cmd->opcode != MMC_SEND_STATUS && data) cmdr |= SDMMC_CMD_WAIT_PRVDATA; /* Set up response handling. */ if (MMC_RSP(cmd->flags) != MMC_RSP_NONE) { cmdr |= SDMMC_CMD_RESP_EXP; if (cmd->flags & MMC_RSP_136) cmdr |= SDMMC_CMD_RESP_LONG; } if (cmd->flags & MMC_RSP_CRC) cmdr |= SDMMC_CMD_RESP_CRC; /* * XXX: Not all platforms want this. */ cmdr |= SDMMC_CMD_USE_HOLD_REG; if ((sc->flags & CARD_INIT_DONE) == 0) { sc->flags |= (CARD_INIT_DONE); cmdr |= SDMMC_CMD_SEND_INIT; } if (data) { if ((cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK || cmd->opcode == MMC_READ_MULTIPLE_BLOCK) && sc->use_auto_stop) cmdr |= SDMMC_CMD_SEND_ASTOP; cmdr |= SDMMC_CMD_DATA_EXP; if (data->flags & MMC_DATA_STREAM) cmdr |= SDMMC_CMD_MODE_STREAM; if (data->flags & MMC_DATA_WRITE) cmdr |= SDMMC_CMD_DATA_WRITE; WRITE4(sc, SDMMC_TMOUT, 0xffffffff); WRITE4(sc, SDMMC_BYTCNT, data->len); blksz = (data->len < MMC_SECTOR_SIZE) ? \ data->len : MMC_SECTOR_SIZE; WRITE4(sc, SDMMC_BLKSIZ, blksz); - dma_prepare(sc, cmd); + if (sc->use_pio) { + pio_prepare(sc, cmd); + } else { + dma_prepare(sc, cmd); + } wmb(); } dprintf("cmdr 0x%08x\n", cmdr); WRITE4(sc, SDMMC_CMDARG, cmd->arg); wmb(); WRITE4(sc, SDMMC_CMD, cmdr | SDMMC_CMD_START); }; static void dwmmc_next_operation(struct dwmmc_softc *sc) { struct mmc_request *req; req = sc->req; if (req == NULL) return; sc->acd_rcvd = 0; sc->dto_rcvd = 0; sc->cmd_done = 0; /* * XXX: Wait until card is still busy. * We do need this to prevent data timeouts, * mostly caused by multi-block write command * followed by single-read. */ while(READ4(sc, SDMMC_STATUS) & (SDMMC_STATUS_DATA_BUSY)) continue; if (sc->flags & PENDING_CMD) { sc->flags &= ~PENDING_CMD; dwmmc_start_cmd(sc, req->cmd); return; } else if (sc->flags & PENDING_STOP && !sc->use_auto_stop) { sc->flags &= ~PENDING_STOP; dwmmc_start_cmd(sc, req->stop); return; } sc->req = NULL; sc->curcmd = NULL; req->done(req); } static int dwmmc_request(device_t brdev, device_t reqdev, struct mmc_request *req) { struct dwmmc_softc *sc; sc = device_get_softc(brdev); dprintf("%s\n", __func__); DWMMC_LOCK(sc); if (sc->req != NULL) { DWMMC_UNLOCK(sc); return (EBUSY); } sc->req = req; sc->flags |= PENDING_CMD; if (sc->req->stop) sc->flags |= PENDING_STOP; dwmmc_next_operation(sc); DWMMC_UNLOCK(sc); return (0); } static int dwmmc_get_ro(device_t brdev, device_t reqdev) { dprintf("%s\n", __func__); return (0); } static int dwmmc_acquire_host(device_t brdev, device_t reqdev) { struct dwmmc_softc *sc; sc = device_get_softc(brdev); DWMMC_LOCK(sc); while (sc->bus_busy) msleep(sc, &sc->sc_mtx, PZERO, "dwmmcah", hz / 5); sc->bus_busy++; DWMMC_UNLOCK(sc); return (0); } static int dwmmc_release_host(device_t brdev, device_t reqdev) { struct dwmmc_softc *sc; sc = device_get_softc(brdev); DWMMC_LOCK(sc); sc->bus_busy--; wakeup(sc); DWMMC_UNLOCK(sc); return (0); } static int dwmmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) { struct dwmmc_softc *sc; sc = device_get_softc(bus); switch (which) { default: return (EINVAL); case MMCBR_IVAR_BUS_MODE: *(int *)result = sc->host.ios.bus_mode; break; case MMCBR_IVAR_BUS_WIDTH: *(int *)result = sc->host.ios.bus_width; break; case MMCBR_IVAR_CHIP_SELECT: *(int *)result = sc->host.ios.chip_select; break; case MMCBR_IVAR_CLOCK: *(int *)result = sc->host.ios.clock; break; case MMCBR_IVAR_F_MIN: *(int *)result = sc->host.f_min; break; case MMCBR_IVAR_F_MAX: *(int *)result = sc->host.f_max; break; case MMCBR_IVAR_HOST_OCR: *(int *)result = sc->host.host_ocr; break; case MMCBR_IVAR_MODE: *(int *)result = sc->host.mode; break; case MMCBR_IVAR_OCR: *(int *)result = sc->host.ocr; break; case MMCBR_IVAR_POWER_MODE: *(int *)result = sc->host.ios.power_mode; break; case MMCBR_IVAR_VDD: *(int *)result = sc->host.ios.vdd; break; case MMCBR_IVAR_CAPS: sc->host.caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; *(int *)result = sc->host.caps; break; case MMCBR_IVAR_MAX_DATA: *(int *)result = DESC_COUNT; } return (0); } static int dwmmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value) { struct dwmmc_softc *sc; sc = device_get_softc(bus); switch (which) { default: return (EINVAL); case MMCBR_IVAR_BUS_MODE: sc->host.ios.bus_mode = value; break; case MMCBR_IVAR_BUS_WIDTH: sc->host.ios.bus_width = value; break; case MMCBR_IVAR_CHIP_SELECT: sc->host.ios.chip_select = value; break; case MMCBR_IVAR_CLOCK: sc->host.ios.clock = value; break; case MMCBR_IVAR_MODE: sc->host.mode = value; break; case MMCBR_IVAR_OCR: sc->host.ocr = value; break; case MMCBR_IVAR_POWER_MODE: sc->host.ios.power_mode = value; break; case MMCBR_IVAR_VDD: sc->host.ios.vdd = value; break; /* These are read-only */ case MMCBR_IVAR_CAPS: case MMCBR_IVAR_HOST_OCR: case MMCBR_IVAR_F_MIN: case MMCBR_IVAR_F_MAX: case MMCBR_IVAR_MAX_DATA: return (EINVAL); } return (0); } static device_method_t dwmmc_methods[] = { DEVMETHOD(device_probe, dwmmc_probe), DEVMETHOD(device_attach, dwmmc_attach), /* Bus interface */ DEVMETHOD(bus_read_ivar, dwmmc_read_ivar), DEVMETHOD(bus_write_ivar, dwmmc_write_ivar), /* mmcbr_if */ DEVMETHOD(mmcbr_update_ios, dwmmc_update_ios), DEVMETHOD(mmcbr_request, dwmmc_request), DEVMETHOD(mmcbr_get_ro, dwmmc_get_ro), DEVMETHOD(mmcbr_acquire_host, dwmmc_acquire_host), DEVMETHOD(mmcbr_release_host, dwmmc_release_host), DEVMETHOD_END }; static driver_t dwmmc_driver = { "dwmmc", dwmmc_methods, sizeof(struct dwmmc_softc), }; static devclass_t dwmmc_devclass; DRIVER_MODULE(dwmmc, simplebus, dwmmc_driver, dwmmc_devclass, 0, 0); Index: head/sys/dev/mmc/host/dwmmc.h =================================================================== --- head/sys/dev/mmc/host/dwmmc.h (revision 277412) +++ head/sys/dev/mmc/host/dwmmc.h (revision 277413) @@ -1,150 +1,152 @@ /*- * Copyright (c) 2014 Ruslan Bukin * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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$ */ #define SDMMC_CTRL 0x0 /* Control Register */ #define SDMMC_CTRL_USE_IDMAC (1 << 25) /* Use Internal DMAC */ #define SDMMC_CTRL_DMA_ENABLE (1 << 5) /* */ #define SDMMC_CTRL_INT_ENABLE (1 << 4) /* Enable interrupts */ #define SDMMC_CTRL_DMA_RESET (1 << 2) /* Reset DMA */ #define SDMMC_CTRL_FIFO_RESET (1 << 1) /* Reset FIFO */ #define SDMMC_CTRL_RESET (1 << 0) /* Reset SD/MMC controller */ #define SDMMC_PWREN 0x4 /* Power Enable Register */ #define SDMMC_PWREN_PE (1 << 0) /* Power On */ #define SDMMC_CLKDIV 0x8 /* Clock Divider Register */ #define SDMMC_CLKSRC 0xC /* SD Clock Source Register */ #define SDMMC_CLKENA 0x10 /* Clock Enable Register */ #define SDMMC_CLKENA_LP (1 << 16) /* Low-power mode */ #define SDMMC_CLKENA_CCLK_EN (1 << 0) /* SD/MMC Enable */ #define SDMMC_TMOUT 0x14 /* Timeout Register */ #define SDMMC_CTYPE 0x18 /* Card Type Register */ #define SDMMC_CTYPE_8BIT (1 << 16) #define SDMMC_CTYPE_4BIT (1 << 0) #define SDMMC_BLKSIZ 0x1C /* Block Size Register */ #define SDMMC_BYTCNT 0x20 /* Byte Count Register */ #define SDMMC_INTMASK 0x24 /* Interrupt Mask Register */ #define SDMMC_INTMASK_SDIO (1 << 16) /* SDIO Interrupt Enable */ #define SDMMC_INTMASK_EBE (1 << 15) /* End-bit error */ #define SDMMC_INTMASK_ACD (1 << 14) /* Auto command done */ #define SDMMC_INTMASK_SBE (1 << 13) /* Start-bit error */ #define SDMMC_INTMASK_HLE (1 << 12) /* Hardware locked write err */ #define SDMMC_INTMASK_FRUN (1 << 11) /* FIFO underrun/overrun err */ #define SDMMC_INTMASK_HTO (1 << 10) /* Data starvation by host timeout */ #define SDMMC_INTMASK_DRT (1 << 9) /* Data read timeout */ #define SDMMC_INTMASK_RTO (1 << 8) /* Response timeout */ #define SDMMC_INTMASK_DCRC (1 << 7) /* Data CRC error */ #define SDMMC_INTMASK_RCRC (1 << 6) /* Response CRC error */ #define SDMMC_INTMASK_RXDR (1 << 5) /* Receive FIFO data request */ #define SDMMC_INTMASK_TXDR (1 << 4) /* Transmit FIFO data request */ #define SDMMC_INTMASK_DTO (1 << 3) /* Data transfer over */ #define SDMMC_INTMASK_CMD_DONE (1 << 2) /* Command done */ #define SDMMC_INTMASK_RE (1 << 1) /* Response error */ #define SDMMC_INTMASK_CD (1 << 0) /* Card Detected */ #define SDMMC_CMDARG 0x28 /* Command Argument Register */ #define SDMMC_CMD 0x2C /* Command Register */ #define SDMMC_CMD_START (1 << 31) #define SDMMC_CMD_USE_HOLD_REG (1 << 29) #define SDMMC_CMD_UPD_CLK_ONLY (1 << 21) /* Update clk only */ #define SDMMC_CMD_SEND_INIT (1 << 15) /* Send initialization */ #define SDMMC_CMD_STOP_ABORT (1 << 14) /* stop current data transfer */ #define SDMMC_CMD_WAIT_PRVDATA (1 << 13) /* Wait for prev data transfer completion */ #define SDMMC_CMD_SEND_ASTOP (1 << 12) /* Send stop command at end of data tx/rx */ #define SDMMC_CMD_MODE_STREAM (1 << 11) /* Stream data transfer */ #define SDMMC_CMD_DATA_WRITE (1 << 10) /* Write to card */ #define SDMMC_CMD_DATA_EXP (1 << 9) /* Data transfer expected */ #define SDMMC_CMD_RESP_CRC (1 << 8) /* Check Response CRC */ #define SDMMC_CMD_RESP_LONG (1 << 7) /* Long response expected */ #define SDMMC_CMD_RESP_EXP (1 << 6) /* Response expected */ #define SDMMC_RESP0 0x30 /* Response Register 0 */ #define SDMMC_RESP1 0x34 /* Response Register 1 */ #define SDMMC_RESP2 0x38 /* Response Register 2 */ #define SDMMC_RESP3 0x3C /* Response Register 3 */ #define SDMMC_MINTSTS 0x40 /* Masked Interrupt Status Register */ #define SDMMC_RINTSTS 0x44 /* Raw Interrupt Status Register */ #define SDMMC_STATUS 0x48 /* Status Register */ #define SDMMC_STATUS_DATA_BUSY (1 << 9) /* card_data[0] */ +#define SDMMC_STATUS_FIFO_FULL (1 << 3) /* FIFO full */ +#define SDMMC_STATUS_FIFO_EMPTY (1 << 2) /* FIFO empty */ #define SDMMC_FIFOTH 0x4C /* FIFO Threshold Watermark Register */ #define SDMMC_FIFOTH_MSIZE_S 28 /* Burst size of multiple transaction */ #define SDMMC_FIFOTH_RXWMARK_S 16 /* FIFO threshold watermark level */ #define SDMMC_FIFOTH_TXWMARK_S 0 /* FIFO threshold watermark level */ #define SDMMC_CDETECT 0x50 /* Card Detect Register */ #define SDMMC_WRTPRT 0x54 /* Write Protect Register */ #define SDMMC_TCBCNT 0x5C /* Transferred CIU Card Byte Count */ #define SDMMC_TBBCNT 0x60 /* Transferred Host to BIU-FIFO Byte Count */ #define SDMMC_DEBNCE 0x64 /* Debounce Count Register */ #define SDMMC_USRID 0x68 /* User ID Register */ #define SDMMC_VERID 0x6C /* Version ID Register */ #define SDMMC_HCON 0x70 /* Hardware Configuration Register */ #define SDMMC_UHS_REG 0x74 /* UHS-1 Register */ #define SDMMC_UHS_REG_DDR (1 << 16) /* DDR mode */ #define SDMMC_RST_N 0x78 /* Hardware Reset Register */ #define SDMMC_BMOD 0x80 /* Bus Mode Register */ #define SDMMC_BMOD_DE (1 << 7) /* IDMAC Enable */ #define SDMMC_BMOD_FB (1 << 1) /* AHB Master Fixed Burst */ #define SDMMC_BMOD_SWR (1 << 0) /* Reset DMA */ #define SDMMC_PLDMND 0x84 /* Poll Demand Register */ #define SDMMC_DBADDR 0x88 /* Descriptor List Base Address */ #define SDMMC_IDSTS 0x8C /* Internal DMAC Status Register */ #define SDMMC_IDINTEN 0x90 /* Internal DMAC Interrupt Enable */ #define SDMMC_IDINTEN_AI (1 << 9) /* Abnormal Interrupt Summary */ #define SDMMC_IDINTEN_NI (1 << 8) /* Normal Interrupt Summary */ #define SDMMC_IDINTEN_CES (1 << 5) /* Card Error Summary */ #define SDMMC_IDINTEN_DU (1 << 4) /* Descriptor Unavailable */ #define SDMMC_IDINTEN_FBE (1 << 2) /* Fatal Bus Error */ #define SDMMC_IDINTEN_RI (1 << 1) /* Receive Interrupt */ #define SDMMC_IDINTEN_TI (1 << 0) /* Transmit Interrupt */ #define SDMMC_IDINTEN_MASK (SDMMC_IDINTEN_AI | SDMMC_IDINTEN_NI | SDMMC_IDINTEN_CES | \ SDMMC_IDINTEN_DU | SDMMC_IDINTEN_FBE | SDMMC_IDINTEN_RI | \ SDMMC_IDINTEN_TI) #define SDMMC_DSCADDR 0x94 /* Current Host Descriptor Address */ #define SDMMC_BUFADDR 0x98 /* Current Buffer Descriptor Address */ #define SDMMC_CARDTHRCTL 0x100 /* Card Threshold Control Register */ #define SDMMC_BACK_END_POWER_R 0x104 /* Back End Power Register */ #define SDMMC_DATA 0x200 /* Data FIFO Access */ /* eMMC */ #define EMMCP_MPSBEGIN0 0x1200 /* */ #define EMMCP_SEND0 0x1204 /* */ #define EMMCP_CTRL0 0x120C /* */ #define MPSCTRL_SECURE_READ_BIT (1 << 7) #define MPSCTRL_SECURE_WRITE_BIT (1 << 6) #define MPSCTRL_NON_SECURE_READ_BIT (1 << 5) #define MPSCTRL_NON_SECURE_WRITE_BIT (1 << 4) #define MPSCTRL_USE_FUSE_KEY (1 << 3) #define MPSCTRL_ECB_MODE (1 << 2) #define MPSCTRL_ENCRYPTION (1 << 1) #define MPSCTRL_VALID (1 << 0) /* Platform-specific defines */ #define SDMMC_CLKSEL 0x9C #define SDMMC_CLKSEL_SAMPLE_SHIFT 0 #define SDMMC_CLKSEL_DRIVE_SHIFT 16 #define SDMMC_CLKSEL_DIVIDER_SHIFT 24