Date: Tue, 10 May 2011 14:35:39 +0200 From: "Tobias Quintern" <Tobias.Quintern@brunel.de> To: <freebsd-arm@freebsd.org> Subject: SD/MMC driver for OpenRD Board Message-ID: <5EABE6DCF1B84BAAA5460DD75F075C82@bcs.brunel.local>
index | next in thread | raw e-mail
[-- Attachment #1 --] Hello, I've integrated Rafal Czubaks SDIO driver (http://people.freebsd.org/~raj/misc/mv_sdio.c) into the FreeBSD HEAD (r221725). The patch has been verified to function with the OpenRD Ultimate board. To ensure data-consistency the options noclusterr and noclusterw have to be given to mount. This is the current status: - Booting from SD works - DMA transfers work fine - PIO transfers not tested This patch only adds SDIO support to the kernel. The current Ethernet-Issues (http://www.freebsd.org/cgi/query-pr.cgi?pr=156814) have to be patched separately. The openrd-cl.dts from this patch is preferable over the patch from Arnaud, because it includes the configuration for several SD specific GPIO pins. I attached 3 different kernel-configs for the sake of completeness. The OPENRD-CL-SDBOOT and OPENRD-CL-SATABOOT work just fine. The OPENRD-CL config tries to boot via BOOTP/NFS, which seems to be broken momentarily. I have an older SVN checkout (r219450) where the board is able to boot from NFS. That kernel is patched quite similar to http://www.freebsd.org/cgi/query-pr.cgi?pr=156814. A bootlog of the failed boot is attached. Regards Tobias [-- Attachment #2 --] ## Starting application at 0x00900000 ... dtbp = 0xc0be3f10 KDB: debugger backends: ddb KDB: current backend: ddb Copyright (c) 1992-2011 The FreeBSD Project. Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994 The Regents of the University of California. All rights reserved. FreeBSD is a registered trademark of The FreeBSD Foundation. FreeBSD 9.0-CURRENT #5 r221725M: Tue May 10 11:51:35 UTC 2011 root@vbcspc15.bcs.brunel.local:/usr/obj/arm.arm/build/arm/head_r221525/sys/O PENRD-CL arm module mvs already present! CPU: Feroceon 88FR131 rev 1 (Marvell core) DC enabled IC enabled WB enabled EABT branch prediction enabled 16KB/32B 4-way Instruction cache 16KB/32B 4-way write-back-locking-C Data cache real memory = 536870912 (512 MB) avail memory = 520105984 (496 MB) SOC: Marvell 88F6281 rev A1, TClock 200MHz simplebus0: <Flattened device tree simple bus> on fdtbus0 ic0: <Marvell Integrated Interrupt Controller> mem 0xf1020200-0xf102023b on simp lebus0 timer0: <Marvell CPU Timer> mem 0xf1020300-0xf102032f irq 1 on simplebus0 Event timer "CPUTimer0" frequency 200000000 Hz quality 1000 Timecounter "CPUTimer1" frequency 200000000 Hz quality 1000 gpio0: <Marvell Integrated GPIO Controller> mem 0xf1010100-0xf101011f irq 35,36, 37,38,39,40,41 on simplebus0 rtc0: <Marvell Integrated RTC> mem 0xf1010300-0xf1010307 on simplebus0 twsi0: <Marvell Integrated I2C Bus Controller> mem 0xf1011000-0xf101101f irq 43 on simplebus0 iicbus0: <Philips I2C bus> on twsi0 iic0: <I2C generic I/O> on iicbus0 mge0: <Marvell Gigabit Ethernet controller> mem 0xf1072000-0xf1073fff irq 12,13, 14,11,46 on simplebus0 mge0: Ethernet address: f0:ad:4e:00:61:96 miibus0: <MII bus> on mge0 e1000phy0: <Marvell 88E1149 Gigabit PHY> PHY 0 on miibus0 e1000phy0: none, 10baseT, 10baseT-FDX, 100baseTX, 100baseTX-FDX, 1000baseT, 100 0baseT-master, 1000baseT-FDX, 1000baseT-FDX-master, auto mge1: <Marvell Gigabit Ethernet controller> mem 0xf1076000-0xf1077fff irq 16,17, 18,15,47 on simplebus0 mge1: Ethernet address: f0:ad:4e:00:61:97 miibus1: <MII bus> on mge1 e1000phy1: <Marvell 88E1149 Gigabit PHY> PHY 1 on miibus1 e1000phy1: none, 10baseT, 10baseT-FDX, 100baseTX, 100baseTX-FDX, 1000baseT, 100 0baseT-master, 1000baseT-FDX, 1000baseT-FDX-master, auto uart0: <16550 or compatible> mem 0xf1012000-0xf101201f irq 33 on simplebus0 uart0: console (1066,n,8,1) ehci0: <Marvell Integrated USB 2.0 controller> mem 0xf1050000-0xf1050fff irq 48, 19 on simplebus0 usbus0: EHCI version 1.0 usbus0: set host controller mode usbus0: <Marvell Integrated USB 2.0 controller> on ehci0 mvs0: <Marvell 88F6281 SATA controller> mem 0xf1080000-0xf1085fff irq 21 on simp lebus0 mvs0: Gen-IIe, 2 3Gbps ports, Port Multiplier supported with FBS mvsch0: <Marvell SATA channel> at channel 0 on mvs0 mvsch1: <Marvell SATA channel> at channel 1 on mvs0 sdio0: <Marvell Integrated SDIO Host Controller> mem 0xf1090000-0xf1090133 irq 2 8 on simplebus0 mmc0: <MMC/SD bus> on sdio0 pcib0: <Marvell Integrated PCI/PCI-E Controller> mem 0xf1040000-0xf1041fff irq 4 4 on fdtbus0 pcib0: PCI IO/Memory space exhausted device_attach: pcib0 attach returned 12 Timecounters tick every 10.000 msec usbus0: 480Mbps High Speed USB v2.0 ugen0.1: <Marvell> at usbus0 uhub0: <Marvell EHCI root HUB, class 9/0, rev 2.00/1.00, addr 1> on usbus0 ada0 at mvsch0 bus 0 scbus0 target 0 lun 0 ada0: <WDC WD600BEVS-00RST0 04.01G04> ATA-7 SATA 1.x device ada0: 150.000MB/s transfers (SATA 1.x, UDMA6, PIO 8192bytes) ada0: Command Queueing enabled ada0: 57231MB (117210240 512 byte sectors: 16H 63S/T 16383C) mmcsd0: 3849MB <SDHC Memory Card> at mmc0 50MHz/4bit bootpc_init: wired to interface 'mge0' Sending DHCP Discover packet from interface mge0 (f0:ad:4e:00:61:96) Received DHCP Offer packet on mge0 from 172.16.102.155 (accepted) (no root path) uhub0: 1 port with 1 removable, self powered mge0: link state changed to UP ugen0.2: <vendor 0x05e3> at usbus0 uhub1: <vendor 0x05e3 USB2.0 Hub, class 9/0, rev 2.00/77.32, addr 2> on usbus0 uhub1: 4 ports with 4 removable, self powered ugen0.3: <vendor 0x05e3> at usbus0 uhub2: <vendor 0x05e3 USB2.0 Hub, class 9/0, rev 2.00/77.32, addr 3> on usbus0 Sending DHCP Request packet from interface mge0 (f0:ad:4e:00:61:96) Received DHCP Ack packet on mge0 from 172.16.102.155 (accepted) (got root path) uhub2: 4 ports with 4 removable, self powered mge0 at 172.16.102.152 server 172.16.102.155 subnet mask 255.255.255.0 router 172.16.102.3 rootfs 172.16.102.78:/bcstoqu/open rd5_rootfs/ hostname open-rd5 Adjusted interface mge0 Trying to mount root from nfs: []... Mounting from nfs: failed with error 2: unknown file system. Loader variables: Manual root filesystem specification: <fstype>:<device> [options] Mount <device> using filesystem <fstype> and with the specified (optional) option list. eg. ufs:/dev/da0s1a zfs:tank cd9660:/dev/acd0 ro (which is equivalent to: mount -t cd9660 -o ro /dev/acd0 /) ? List valid disk boot devices . Yield 1 second (for background tasks) <empty line> Abort manual input mountroot> [-- Attachment #3 --] Ôò¡ ÿÿ 'ÉM v v V~Ò×ðN a– E h @U{¬f˜¬fNÿ o TÉVÿÿÿ † †¥ 'ÉMà F F ðN a– V~Ò× E 8 @ @®¬fN¬f˜ oÿ $%=ÿÿÿ ¡'ÉMDÄ ‚ ‚ V~Ò×ðN a– E t @Un¬f˜¬fNÿ¡ `«ûÿÿÿ †¥ /bcstoqu/openrd5_rootfs/'ÉMÐÄ v v ðN a– V~Ò× E h @ @~¬fN¬f˜¡ÿ T%mÿÿÿ ý €L ù<¤â ós ót óu'ÉM Æ v v V~Ò×ðN a– E h @Uy¬f˜¬fNÿ o TÉVÿÿÿ † †£ 'ÉM”Ê F F ðN a– V~Ò× E 8 @ @®¬fN¬f˜ oÿ $%=ÿÿÿ [-- Attachment #4 --] diff -prN --exclude=.svn head_r221725/sys/arm/conf/OPENRD-CL head_r221525/sys/arm/conf/OPENRD-CL *** head_r221725/sys/arm/conf/OPENRD-CL Thu Jan 1 00:00:00 1970 --- head_r221525/sys/arm/conf/OPENRD-CL Tue May 10 08:25:28 2011 *************** *** 0 **** --- 1,94 ---- + # + # Custom kernel for OpenRD Client/Ultimate devices. + # + # $FreeBSD$ + # + + ident OPENRD-CL + include "../mv/kirkwood/std.db88f6xxx" + + options SOC_MV_KIRKWOOD + makeoptions MODULES_OVERRIDE="" + + makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols + makeoptions WERROR="-Werror" + + options SCHED_4BSD #4BSD scheduler + options INET #InterNETworking + options INET6 #IPv6 communications protocols + options FFS #Berkeley Fast Filesystem + options NFSCLIENT #Network Filesystem Client + options NFSLOCKD #Network Lock Manager + options NFS_ROOT #NFS usable as /, requires NFSCLIENT + options BOOTP + options BOOTP_NFSROOT + options BOOTP_NFSV3 + options BOOTP_WIRED_TO=mge0 + + # Root fs on USB device + #options ROOTDEVNAME=\"ufs:/dev/da0a\" + + 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 MUTEX_NOINLINE + options RWLOCK_NOINLINE + options NO_FFS_SNAPSHOT + options NO_SWAPPING + + # Debugging + options ALT_BREAK_TO_DEBUGGER + options DDB + options KDB + + # Pseudo devices + device loop + device md + device pty + device random + + # PCI Express + device pci + + # Serial ports + device uart + + # Networking + device ether + device mge # Marvell Gigabit Ethernet controller + device mii + device e1000phy + device bpf + + # USB + options USB_DEBUG # enable debug msgs + device usb + device ehci + device umass + + # SATA + device mvs + + # CAM + device scbus + device da + device cd + device pass + + # I2C (TWSI) + device iic + device iicbus + + # Enable Flattened Device Tree support + options FDT + options FDT_DTB_STATIC + makeoptions FDT_DTS_FILE=openrd-cl.dts + + + # MMC/SD + device mv_sdio + device mmc + device mmcsd + + diff -prN --exclude=.svn head_r221725/sys/arm/conf/OPENRD-CL-SATABOOT head_r221525/sys/arm/conf/OPENRD-CL-SATABOOT *** head_r221725/sys/arm/conf/OPENRD-CL-SATABOOT Thu Jan 1 00:00:00 1970 --- head_r221525/sys/arm/conf/OPENRD-CL-SATABOOT Tue May 10 08:25:27 2011 *************** *** 0 **** --- 1,97 ---- + # + # Custom kernel for OpenRD Client/Ultimate devices. + # + # $FreeBSD$ + # + + ident OPENRD-CL + include "../mv/kirkwood/std.db88f6xxx" + + options SOC_MV_KIRKWOOD + makeoptions MODULES_OVERRIDE="" + + makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols + makeoptions WERROR="-Wall" + + options SCHED_4BSD #4BSD scheduler + options INET #InterNETworking + options INET6 #IPv6 communications protocols + #options MFS # Memory Filesystem + options FFS #Berkeley Fast Filesystem + options NFSCLIENT #Network Filesystem Client + options NFSLOCKD #Network Lock Manager + #options NFS_ROOT #NFS usable as /, requires NFSCLIENT + #options BOOTP + #options BOOTP_NFSROOT + #options BOOTP_NFSV3 + #options BOOTP_WIRED_TO=mge0 + + # Root fs on USB device + #options ROOTDEVNAME=\"ufs:/dev/da0a\" + options ROOTDEVNAME=\"ufs:/dev/ada0s2a\" + 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 MUTEX_NOINLINE + options RWLOCK_NOINLINE + options NO_FFS_SNAPSHOT + options NO_SWAPPING + options HZ=2000 + + # Debugging + #options ALT_BREAK_TO_DEBUGGER + #options DDB + #options KDB + + # Pseudo devices + device loop + device md + device pty + device random + + # PCI Express + device pci + + # Serial ports + device uart + + # Networking + device ether + device mge # Marvell Gigabit Ethernet controller + device mii + device e1000phy + device bpf + + # USB + options USB_DEBUG # enable debug msgs + device usb + device ehci + device umass + + # SATA + device mvs + #device ata + #device atadisk # ATA disk drives + #options ATA_STATIC_ID # Static device numbering + options EXT2FS + + # CAM + device scbus + device da + device cd + device pass + + # I2C (TWSI) + device iic + device iicbus + + # Enable Flattened Device Tree support + options FDT + options FDT_DTB_STATIC + makeoptions FDT_DTS_FILE=openrd-cl.dts + + # MMC/SD + device mv_sdio + device mmc + device mmcsd \ No newline at end of file diff -prN --exclude=.svn head_r221725/sys/arm/conf/OPENRD-CL-SDBOOT head_r221525/sys/arm/conf/OPENRD-CL-SDBOOT *** head_r221725/sys/arm/conf/OPENRD-CL-SDBOOT Thu Jan 1 00:00:00 1970 --- head_r221525/sys/arm/conf/OPENRD-CL-SDBOOT Tue May 10 08:25:27 2011 *************** *** 0 **** --- 1,96 ---- + # + # Custom kernel for OpenRD Client/Ultimate devices. + # + # $FreeBSD$ + # + + ident OPENRD-CL + include "../mv/kirkwood/std.db88f6xxx" + + options SOC_MV_KIRKWOOD + makeoptions MODULES_OVERRIDE="" + + makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols + makeoptions WERROR="-Wall" + + options SCHED_4BSD #4BSD scheduler + options INET #InterNETworking + options INET6 #IPv6 communications protocols + #options MFS # Memory Filesystem + options FFS #Berkeley Fast Filesystem + options NFSCLIENT #Network Filesystem Client + options NFSLOCKD #Network Lock Manager + #options NFS_ROOT #NFS usable as /, requires NFSCLIENT + #options BOOTP + #options BOOTP_NFSROOT + #options BOOTP_NFSV3 + #options BOOTP_WIRED_TO=mge0 + + # Root fs on USB device + options ROOTDEVNAME=\"ufs:/dev/mmcsd0a\" + 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 MUTEX_NOINLINE + options RWLOCK_NOINLINE + options NO_FFS_SNAPSHOT + options NO_SWAPPING + options HZ=2000 + + # Debugging + #options ALT_BREAK_TO_DEBUGGER + #options DDB + #options KDB + + # Pseudo devices + device loop + device md + device pty + device random + + # PCI Express + device pci + + # Serial ports + device uart + + # Networking + device ether + device mge # Marvell Gigabit Ethernet controller + device mii + device e1000phy + device bpf + + # USB + options USB_DEBUG # enable debug msgs + device usb + device ehci + device umass + + # SATA + device mvs + #device ata + #device atadisk # ATA disk drives + #options ATA_STATIC_ID # Static device numbering + options EXT2FS + + # CAM + device scbus + device da + device cd + device pass + + # I2C (TWSI) + device iic + device iicbus + + # Enable Flattened Device Tree support + options FDT + options FDT_DTB_STATIC + makeoptions FDT_DTS_FILE=openrd-cl.dts + + # MMC/SD + device mv_sdio + device mmc + device mmcsd diff -prN --exclude=.svn head_r221725/sys/arm/mv/files.mv head_r221525/sys/arm/mv/files.mv *** head_r221725/sys/arm/mv/files.mv Tue May 10 10:11:30 2011 --- head_r221525/sys/arm/mv/files.mv Tue May 10 08:24:17 2011 *************** arm/mv/mv_pci.c optional pci *** 27,32 **** --- 27,33 ---- arm/mv/mv_sata.c optional ata | atamvsata arm/mv/timer.c standard arm/mv/twsi.c optional iicbus + arm/mv/mv_sdio.c optional mv_sdio dev/mge/if_mge.c optional mge dev/mvs/mvs_soc.c optional mvs diff -prN --exclude=.svn head_r221725/sys/arm/mv/gpio.c head_r221525/sys/arm/mv/gpio.c *** head_r221725/sys/arm/mv/gpio.c Tue May 10 10:11:30 2011 --- head_r221525/sys/arm/mv/gpio.c Tue May 10 11:50:01 2011 *************** *** 29,35 **** */ #include <sys/cdefs.h> ! __FBSDID("$FreeBSD: head/sys/arm/mv/gpio.c 219684 2011-03-16 00:42:15Z marcel $"); #include <sys/param.h> #include <sys/systm.h> --- 29,35 ---- */ #include <sys/cdefs.h> ! __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> *************** mv_gpio_attach(device_t dev) *** 145,151 **** mv_gpio_softc = sc; ! /* Get chip id and revision */ soc_id(&dev_id, &rev_id); if (dev_id == MV_DEV_88F5182 || --- 145,151 ---- mv_gpio_softc = sc; ! /* Get chip id and revision */ soc_id(&dev_id, &rev_id); if (dev_id == MV_DEV_88F5182 || *************** static int *** 207,213 **** mv_gpio_intr(void *arg) { uint32_t int_cause, gpio_val; ! uint32_t int_cause_hi, gpio_val_hi = 0; int i; int_cause = mv_gpio_reg_read(GPIO_INT_CAUSE); --- 207,214 ---- mv_gpio_intr(void *arg) { uint32_t int_cause, gpio_val; ! uint32_t int_cause_hi = 0; ! uint32_t gpio_val_hi = 0; int i; int_cause = mv_gpio_reg_read(GPIO_INT_CAUSE); *************** mv_gpio_intr(void *arg) *** 240,245 **** --- 241,287 ---- return (FILTER_HANDLED); } + + static int32_t mv_gpio_get_gpio_irq( const uint32_t u32_gpio ) + { + int32_t s32_ret_val = 0; + + if( 7 >= u32_gpio ) + { + s32_ret_val = MV_INT_GPIO7_0; + } + else if( 15 >= u32_gpio ) + { + s32_ret_val = MV_INT_GPIO15_8; + } + else if( 23 >= u32_gpio ) + { + s32_ret_val = MV_INT_GPIO23_16; + } + else if( 31 >= u32_gpio ) + { + s32_ret_val = MV_INT_GPIO31_24; + } + else if( ( 32 + 7 ) >= u32_gpio ) + { + s32_ret_val = MV_INT_GPIOHI7_0; + } + else if( ( 32 + 15 ) >= u32_gpio ) + { + s32_ret_val = MV_INT_GPIOHI15_8; + } + else if( ( 32 + 23 ) >= u32_gpio ) + { + s32_ret_val = MV_INT_GPIOHI23_16; + } + else + { + s32_ret_val = -1; + } + + return s32_ret_val; + } + /* * GPIO interrupt handling */ *************** mv_gpio_setup_intrhandler(const char *na *** 253,268 **** struct intr_event *event; int error; if (pin < 0 || pin >= mv_gpio_softc->pin_num) return (ENXIO); event = gpio_events[pin]; if (event == NULL) { ! error = intr_event_create(&event, (void *)pin, 0, pin, ! (void (*)(void *))mv_gpio_intr_mask, ! (void (*)(void *))mv_gpio_intr_unmask, ! (void (*)(void *))mv_gpio_int_ack, ! NULL, ! "gpio%d:", pin); if (error != 0) return (error); gpio_events[pin] = event; --- 295,321 ---- struct intr_event *event; int error; + int32_t s32_irq = mv_gpio_get_gpio_irq( pin ); + + if( 0 > s32_irq ) + { + printf("Could not find corresponding IRQ for pin %i\n", + pin ); + return( ENXIO ); + } + if (pin < 0 || pin >= mv_gpio_softc->pin_num) return (ENXIO); event = gpio_events[pin]; if (event == NULL) { ! ! error = intr_event_create(&event, (void *)pin, 0, ! s32_irq , ! (void (*)(void *))mv_gpio_intr_mask, ! (void (*)(void *))mv_gpio_intr_unmask, ! (void (*)(void *))mv_gpio_int_ack, ! NULL, ! "gpio%d:", pin); if (error != 0) return (error); gpio_events[pin] = event; *************** mv_gpio_intr_unmask(int pin) *** 294,302 **** --- 347,359 ---- return; if (gpio_setup[pin] & MV_GPIO_IN_IRQ_EDGE) + { mv_gpio_edge(pin, 1); + } else + { mv_gpio_level(pin, 1); + } } static void *************** mv_gpio_intr_handler(int pin) *** 306,314 **** event = gpio_events[pin]; if (event == NULL || TAILQ_EMPTY(&event->ie_handlers)) return; ! intr_event_handle(event, NULL); } static int --- 363,377 ---- event = gpio_events[pin]; if (event == NULL || TAILQ_EMPTY(&event->ie_handlers)) + { + printf("mv_gpio_intr_handler(): No handler for Pin %i\n", + pin); + mv_gpio_int_ack(pin); return; ! } intr_event_handle(event, NULL); + + mv_gpio_int_ack(pin); } static int diff -prN --exclude=.svn head_r221725/sys/arm/mv/mv_sdio.c head_r221525/sys/arm/mv/mv_sdio.c *** head_r221725/sys/arm/mv/mv_sdio.c Thu Jan 1 00:00:00 1970 --- head_r221525/sys/arm/mv/mv_sdio.c Tue May 10 08:24:17 2011 *************** *** 0 **** --- 1,1806 ---- + /*- + * Copyright (c) 2009 Semihalf, Rafal Czubak + * 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. + */ + + /* + * Driver for Marvell Integrated SDIO Host Controller. + * Works stable in DMA mode. PIO mode has problems with large data transfers + * (timeouts). + */ + + #include <sys/cdefs.h> + + #include <sys/param.h> + #include <sys/bus.h> + #include <sys/kernel.h> + #include <sys/lock.h> + #include <sys/module.h> + #include <sys/mutex.h> + #include <sys/rman.h> + #include <sys/sysctl.h> + #include <sys/systm.h> + #include <sys/taskqueue.h> + + #include <arm/mv/mvreg.h> + #include <arm/mv/mvvar.h> + + #include <machine/bus.h> + #include <machine/intr.h> + + #include <dev/mmc/bridge.h> + #include <dev/mmc/mmcreg.h> + #include <dev/mmc/mmcvar.h> + #include <dev/mmc/mmcbrvar.h> + + #include <dev/ofw/ofw_bus.h> + #include <dev/ofw/ofw_bus_subr.h> + + #include "mmcbr_if.h" + + #include "mv_sdio.h" + + /* Minimum DMA segment size. */ + #define MV_SDIO_DMA_SEGMENT_SIZE 4096 + + /* Transferred block size. */ + #define MV_SDIO_BLOCK_SIZE 512 + + /* Maximum number of blocks the controller can handle. */ + #define MV_SDIO_BLOCKS_MAX 65535 + + /* Halfword bit masks used for command response extraction. */ + #define MV_SDIO_RSP48_BM2 0x0002 /* Lower 2 bits. */ + #define MV_SDIO_RSP48_BM6 0x003f /* Lower 6 bits. */ + #define MV_SDIO_RSP48_BM16 0xffff /* 16 bits */ + + /* SDIO aggregated command interrupts */ + #define MV_SDIO_IRQS_CMD (MV_SDIO_IRQ_CMD | MV_SDIO_IRQ_UNEXPECTED_RSP) + #define MV_SDIO_EIRQS_CMD (MV_SDIO_EIRQ_CMD_TMO | MV_SDIO_EIRQ_CMD_CRC7 | \ + MV_SDIO_EIRQ_CMD_ENDBIT | MV_SDIO_EIRQ_CMD_INDEX | \ + MV_SDIO_EIRQ_CMD_STARTBIT | MV_SDIO_EIRQ_RSP_TBIT) + + /* SDIO aggregated data interrupts */ + #define MV_SDIO_IRQS_DATA (MV_SDIO_IRQ_XFER | MV_SDIO_IRQ_TX_EMPTY | \ + MV_SDIO_IRQ_RX_FULL | MV_SDIO_IRQ_DMA | MV_SDIO_IRQ_AUTOCMD12) + #define MV_SDIO_EIRQS_DATA (MV_SDIO_EIRQ_DATA_TMO | \ + MV_SDIO_EIRQ_DATA_CRC16 | MV_SDIO_EIRQ_DATA_ENDBIT | \ + MV_SDIO_EIRQ_AUTOCMD12 | MV_SDIO_EIRQ_XFER_SIZE | \ + MV_SDIO_EIRQ_CRC_ENDBIT | MV_SDIO_EIRQ_CRC_STARTBIT | \ + MV_SDIO_EIRQ_CRC_STAT) + + /* + * Timing configuration. + */ + + /* SDIO controller base clock frequency. */ + #define MV_SDIO_F_BASE 100000000 /* 200 MHz */ + + /* Maximum SD clock frequency. */ + #define MV_SDIO_F_MAX (MV_SDIO_F_BASE / 2) /* 50 MHz */ + + /* Maximum timeout value. */ + #define MV_SDIO_TMO_MAX 0xf + + /* Reset delay in microseconds. */ + #define MV_SDIO_RESET_DELAY 10000 /* 10 ms */ + + /* Empty FIFO polling delay. */ + #define MV_SDIO_FIFO_EMPTY_DELAY 1000 /* 1 ms */ + + /* Delays between operations on multiple blocks. */ + #define MV_SDIO_RD_DELAY 50 /*50*/ /* Read access time. */ + #define MV_SDIO_WR_DELAY 10 /*10*/ /* Write access time. */ + + /* Maximum clock divider value. */ + #define MV_SDIO_CLK_DIV_MAX 0x7ff + + /* + * Platform-dependent pins + */ + /* Card-Detect Pin */ + #define MV_SDIO_CARD_DETECT_PIN 29 + + /* Pin that connects the SD-Card-Port Pins to the processor */ + #define MV_SDIO_OPENRD_MUX_PIN 34 + + struct mv_sdio_softc { + device_t sc_dev; + device_t sc_child; + + bus_space_handle_t sc_bsh; + bus_space_tag_t sc_bst; + + int sc_use_dma; + bus_dma_tag_t sc_dmatag; + bus_dmamap_t sc_dmamap; + uint8_t *sc_dmamem; + bus_addr_t sc_physaddr; + int sc_mapped; + size_t sc_dma_size; + + struct resource *sc_mem_res; + int sc_mem_rid; + + struct resource *sc_irq_res; + int sc_irq_rid; + void *sc_ihl; + + struct resource *sc_cd_irq_res; + int sc_cd_irq_rid; + void *sc_cd_ihl; + + uint32_t sc_irq_mask; + uint32_t sc_eirq_mask; + + struct task sc_card_task; + struct callout sc_card_callout; + + struct mtx sc_mtx; + + int sc_bus_busy; + int sc_card_present; + struct mmc_host sc_host; + struct mmc_request *sc_req; + struct mmc_command *sc_curcmd; + + uint32_t sc_data_offset; + }; + + /* Read/write data from/to registers.*/ + static uint32_t MV_SDIO_RD4(struct mv_sdio_softc *, bus_size_t); + static void MV_SDIO_WR4(struct mv_sdio_softc *, bus_size_t, uint32_t); + + static int mv_sdio_probe(device_t); + static int mv_sdio_attach(device_t); + + static int mv_sdio_read_ivar(device_t, device_t, int, uintptr_t *); + static int mv_sdio_write_ivar(device_t, device_t, int, uintptr_t); + + static int mv_sdio_update_ios(device_t, device_t); + static int mv_sdio_request(device_t, device_t, struct mmc_request *); + static int mv_sdio_get_ro(device_t, device_t); + static int mv_sdio_acquire_host(device_t, device_t); + static int mv_sdio_release_host(device_t, device_t); + + /* Finalizes active MMC request. */ + static void mv_sdio_finalize_request(struct mv_sdio_softc *); + + /* Initializes controller's registers. */ + static void mv_sdio_init(device_t); + + /* Initializes host structure. */ + static void mv_sdio_init_host(struct mv_sdio_softc *); + + /* Used to add and handle sysctls. */ + static void mv_sdio_add_sysctls(struct mv_sdio_softc *); + static int mv_sdio_sysctl_use_dma(SYSCTL_HANDLER_ARGS); + + /* DMA initialization and cleanup functions. */ + static int mv_sdio_dma_init(struct mv_sdio_softc *); + static void mv_sdio_dma_finish(struct mv_sdio_softc *); + + /* DMA map load callback. */ + static void mv_sdio_getaddr(void *, bus_dma_segment_t *, int, int); + + /* Prepare command/data before transaction. */ + static int mv_sdio_start_command(struct mv_sdio_softc *, struct + mmc_command *); + static int mv_sdio_start_data(struct mv_sdio_softc *, struct mmc_data *); + + /* Finish command after transaction. */ + static void mv_sdio_finish_command(struct mv_sdio_softc *); + + /* Response handling. */ + static void mv_sdio_handle_136bit_resp(struct mv_sdio_softc *); + static void mv_sdio_handle_48bit_resp(struct mv_sdio_softc *, + struct mmc_command *); + + /* Interrupt handler and interrupt helper functions. */ + static void mv_sdio_intr(void *); + static void mv_sdio_cmd_intr(struct mv_sdio_softc *, uint32_t, uint32_t); + static void mv_sdio_data_intr(struct mv_sdio_softc *, uint32_t, uint32_t); + static void mv_sdio_cd_intr(void *); + static void mv_sdio_disable_intr(struct mv_sdio_softc *); + + /* Used after card detect interrupt has been handled. */ + static void mv_sdio_card_delay(void *); + static void mv_sdio_card_task(void *, int); + + /* Read/write data from FIFO in PIO mode. */ + static uint32_t mv_sdio_read_fifo(struct mv_sdio_softc *); + static void mv_sdio_write_fifo(struct mv_sdio_softc *, uint32_t); + + /* + * PIO mode handling. + * + * Inspired by sdhci(4) driver routines. + */ + static void mv_sdio_transfer_pio(struct mv_sdio_softc *); + static void mv_sdio_read_block_pio(struct mv_sdio_softc *); + static void mv_sdio_write_block_pio(struct mv_sdio_softc *); + + + static device_method_t mv_sdio_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, mv_sdio_probe), + DEVMETHOD(device_attach, mv_sdio_attach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, mv_sdio_read_ivar), + DEVMETHOD(bus_write_ivar, mv_sdio_write_ivar), + + /* mmcbr_if */ + DEVMETHOD(mmcbr_update_ios, mv_sdio_update_ios), + DEVMETHOD(mmcbr_request, mv_sdio_request), + DEVMETHOD(mmcbr_get_ro, mv_sdio_get_ro), + DEVMETHOD(mmcbr_acquire_host, mv_sdio_acquire_host), + DEVMETHOD(mmcbr_release_host, mv_sdio_release_host), + + {0, 0}, + }; + + static driver_t mv_sdio_driver = { + "sdio", + mv_sdio_methods, + sizeof(struct mv_sdio_softc), + }; + static devclass_t mv_sdio_devclass; + + DRIVER_MODULE( sdio, simplebus, mv_sdio_driver, mv_sdio_devclass, 0, 0); + + + static __inline uint32_t + MV_SDIO_RD4(struct mv_sdio_softc *sc, bus_size_t off) + { + + return (bus_read_4(sc->sc_mem_res, off)); + } + + static __inline void + MV_SDIO_WR4(struct mv_sdio_softc *sc, bus_size_t off, uint32_t val) + { + + bus_write_4(sc->sc_mem_res, off, val); + } + + static int platform_sdio_slot_signal( int signal ) + { + #ifdef MV_SDIO_OPENRD_MUX_PIN + uint32_t gpio_read = 0; + #endif /* MV_SDIO_OPENRD_MUX_PIN */ + + switch( signal ) + { + case MV_SDIO_SIG_CD: + { + #ifndef MV_SDIO_OPENRD_MUX_PIN + return -1; + #else + gpio_read = mv_gpio_in(MV_SDIO_CARD_DETECT_PIN); + + if( 0 == gpio_read ) + { + return 0; + } + else + { + return 1; + } + #endif /* MV_SDIO_OPENRD_MUX_PIN */ + break; + } + case MV_SDIO_SIG_WP: + return 0; + break; + default: + return -1; + break; + } + + return 0; + } + + static int + mv_sdio_probe(device_t dev) + { + uint32_t device, revision; + + /*device_printf(dev,"SDIO probe!\n");*/ + + if (!ofw_bus_is_compatible(dev, "mrvl,sdio")) + return (ENXIO); + + + soc_id(&device, &revision); + + switch (device) { + case MV_DEV_88F6281: + break; + default: + printf("ENXIO!\n"); + return (ENXIO); + } + + device_set_desc(dev, "Marvell Integrated SDIO Host Controller"); + + return (BUS_PROBE_SPECIFIC); + } + + static int + mv_sdio_attach(device_t dev) + { + struct mv_sdio_softc *sc; + int task_initialized = 0; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + /* Allocate memory and interrupt resources. */ + sc->sc_mem_rid = 0; + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_mem_rid, RF_ACTIVE); + + if (sc->sc_mem_res == NULL) { + device_printf(dev, "Could not allocate memory!\n"); + goto fail; + } + + sc->sc_irq_rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->sc_irq_rid, RF_ACTIVE); + + if (sc->sc_irq_res == NULL) { + device_printf(dev, "Could not allocate IRQ!\n"); + goto fail; + } + + sc->sc_bst = rman_get_bustag(sc->sc_mem_res); + sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); + + + /* Initialize host controller's registers. */ + mv_sdio_init(dev); + + /* Try to setup DMA. */ + sc->sc_mapped = 0; /* No DMA buffer is mapped. */ + sc->sc_use_dma = 1; /* DMA mode is preferred to PIO mode. */ + + if (mv_sdio_dma_init(sc) < 0) { + device_printf(dev, "Falling back to PIO mode.\n"); + sc->sc_use_dma = 0; + } + + /* Add sysctls. */ + mv_sdio_add_sysctls(sc); + + #ifdef MV_SDIO_OPENRD_MUX_PIN + + /* Set Pin 34 to 1, to forward SD-Card Pins to processor */ + mv_gpio_out( MV_SDIO_OPENRD_MUX_PIN, /* uint32_t pin*/ + 1, /* uint8_t val,*/ + 1 /*uint8_t enable*/); + + #endif /* MV_SDIO_OPENRD_MUX_PIN */ + + if (platform_sdio_slot_signal(MV_SDIO_SIG_CD) != -1) { + + #ifdef MV_SDIO_OPENRD_MUX_PIN + + /* Register IRQ-handler for pin in GPIO-framework */ + if( 0 != mv_gpio_setup_intrhandler( "Card detect IRQ", /* name */ + NULL, /*driver_filter_t *filt*/ + mv_sdio_cd_intr, /*void (*hand)(void *)*/ + sc, /*void *arg*/ + MV_SDIO_CARD_DETECT_PIN, /*int pin*/ + INTR_TYPE_MISC | INTR_MPSAFE, /*int flags*/ + &sc->sc_cd_ihl /*void **cookiep*/) ) + { + device_printf(dev, "mv_gpio_setup_intrhandler failed!\n"); + goto fail; + } + + /* Activate IRQ for Pin 29 */ + mv_gpio_intr_unmask(MV_SDIO_CARD_DETECT_PIN); + + #endif /* MV_SDIO_OPENRD_MUX_PIN */ + + + /* Check if card is present in the slot. */ + if (platform_sdio_slot_signal(MV_SDIO_SIG_CD) == 1) + sc->sc_card_present = 1; + } + + TASK_INIT(&sc->sc_card_task, 0, mv_sdio_card_task, sc); + callout_init(&sc->sc_card_callout, 1); + task_initialized = 1; + + /* Setup interrupt. */ + if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | + INTR_MPSAFE, NULL, mv_sdio_intr, sc, &sc->sc_ihl) != 0) { + device_printf(dev, "Could not setup interrupt!\n"); + goto fail; + } + + /* Host can be acquired. */ + sc->sc_bus_busy = 0; + + /* + * Attach MMC bus only if the card is in the slot or card detect is + * not supported on the platform. + */ + if ((platform_sdio_slot_signal(MV_SDIO_SIG_CD) == -1) || + sc->sc_card_present) { + sc->sc_child = device_add_child(dev, "mmc", -1); + + if (sc->sc_child == NULL) { + device_printf(dev, "Could not add MMC bus!\n"); + goto fail; + } + + /* Initialize host structure for MMC bus. */ + mv_sdio_init_host(sc); + + device_set_ivars(sc->sc_child, &sc->sc_host); + } + + return (bus_generic_attach(dev)); + + fail: + mv_sdio_dma_finish(sc); + if (task_initialized) { + callout_drain(&sc->sc_card_callout); + taskqueue_drain(taskqueue_swi, &sc->sc_card_task); + } + if (sc->sc_ihl != NULL) + bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_ihl); + if (sc->sc_cd_ihl != NULL) + bus_teardown_intr(dev, sc->sc_cd_irq_res, sc->sc_cd_ihl); + if (sc->sc_irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, + sc->sc_irq_res); + if (sc->sc_cd_irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_cd_irq_rid, + sc->sc_cd_irq_res); + if (sc->sc_mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, + sc->sc_mem_res); + mtx_destroy(&sc->sc_mtx); + return (ENXIO); + } + + static int + mv_sdio_update_ios(device_t brdev, device_t reqdev) + { + struct mv_sdio_softc *sc; + struct mmc_host *host; + struct mmc_ios *ios; + uint32_t xfer, clk_div, host_cr; + + sc = device_get_softc(brdev); + host = device_get_ivars(reqdev); + ios = &host->ios; + + mtx_lock(&sc->sc_mtx); + + if (ios->power_mode == power_off) + /* Re-initialize the controller. */ + mv_sdio_init(brdev); + + xfer = MV_SDIO_RD4(sc, MV_SDIO_XFER); + + if (ios->clock == 0) { + /* Disable clock. */ + xfer |= MV_SDIO_XFER_STOP_CLK; + MV_SDIO_WR4(sc, MV_SDIO_XFER, xfer); + + /* Set maximum clock divider. */ + MV_SDIO_WR4(sc, MV_SDIO_CLK_DIV, MV_SDIO_CLK_DIV_MAX); + } else { + /* + * Calculate and set clock divider. + * Clock rate value is: + * clock = MV_SDIO_F_BASE / (clk_div + 1) + * Thus we calculate the divider value as: + * clk_div = (MV_SDIO_F_BASE / clock) - 1 + */ + clk_div = (MV_SDIO_F_BASE / ios->clock) - 1; + if (clk_div > MV_SDIO_CLK_DIV_MAX) + clk_div = MV_SDIO_CLK_DIV_MAX; + MV_SDIO_WR4(sc, MV_SDIO_CLK_DIV, clk_div); + + /* Enable clock. */ + xfer &= ~MV_SDIO_XFER_STOP_CLK; + MV_SDIO_WR4(sc, MV_SDIO_XFER, xfer); + } + + host_cr = MV_SDIO_RD4(sc, MV_SDIO_HOST_CR); + + /* Set card type. */ + if (host->mode == mode_mmc) + host_cr |= MV_SDIO_HOST_CR_MMC; /* MMC card. */ + else + host_cr &= ~MV_SDIO_HOST_CR_MMC; /* SD card. */ + + /* Set bus width. */ + if (ios->bus_width == bus_width_4) + host_cr |= MV_SDIO_HOST_CR_4BIT; /* 4-bit bus width */ + else + host_cr &= ~MV_SDIO_HOST_CR_4BIT; /* 1-bit bus width */ + + /* Set high/normal speed mode. */ + #if 0 /* Some cards have problems with the highspeed-mode + * Not selecting High-Speed mode enables all cards to work + */ + + if ((ios->timing == bus_timing_hs ) && ( 1 == 0 ) ) + host_cr |= MV_SDIO_HOST_CR_HIGHSPEED; + else + #endif + host_cr &= ~MV_SDIO_HOST_CR_HIGHSPEED; + + MV_SDIO_WR4(sc, MV_SDIO_HOST_CR, host_cr); + + mtx_unlock(&sc->sc_mtx); + + return (0); + } + + static int + mv_sdio_request(device_t brdev, device_t reqdev, struct mmc_request *req) + { + struct mv_sdio_softc *sc; + int rv; + + sc = device_get_softc(brdev); + rv = EBUSY; + + mtx_lock(&sc->sc_mtx); + + if (sc->sc_req != NULL) { + mtx_unlock(&sc->sc_mtx); + return (rv); + } + + sc->sc_req = req; + /* + device_printf(sc->sc_dev, "cmd %d (hw state 0x%04x)\n", + req->cmd->opcode , MV_SDIO_RD4( sc, MV_SDIO_HOST_SR ) ); + */ + rv = mv_sdio_start_command(sc, req->cmd); + + mtx_unlock(&sc->sc_mtx); + + return (rv); + } + + static int + mv_sdio_get_ro(device_t brdev, device_t reqdev) + { + int rv; + + /* Check if card is read only. */ + rv = platform_sdio_slot_signal(MV_SDIO_SIG_WP); + + /* + * Assume that card is not write protected, when platform doesn't + * support WP signal. + */ + if (rv < 0) + rv = 0; + + return (rv); + } + + static int + mv_sdio_acquire_host(device_t brdev, device_t reqdev) + { + struct mv_sdio_softc *sc; + int rv; + + sc = device_get_softc(brdev); + rv = 0; + + mtx_lock(&sc->sc_mtx); + while (sc->sc_bus_busy) + rv = mtx_sleep(sc, &sc->sc_mtx, PZERO, "sdioah", 0); + sc->sc_bus_busy++; + mtx_unlock(&sc->sc_mtx); + + return (rv); + } + + static int + mv_sdio_release_host(device_t brdev, device_t reqdev) + { + struct mv_sdio_softc *sc; + + sc = device_get_softc(brdev); + + mtx_lock(&sc->sc_mtx); + sc->sc_bus_busy--; + wakeup(sc); + mtx_unlock(&sc->sc_mtx); + + return (0); + } + + static void + mv_sdio_finalize_request(struct mv_sdio_softc *sc) + { + struct mmc_request *req; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + req = sc->sc_req; + + if (req) { + /* Finalize active request. */ + /*device_printf(sc->sc_dev, "Finalize request %i\n",req->cmd->opcode);*/ + sc->sc_req = NULL; + sc->sc_curcmd = NULL; + req->done(req); + + + } else + device_printf(sc->sc_dev, "No active request to finalize!\n"); + } + + static void + mv_sdio_init(device_t dev) + { + struct mv_sdio_softc *sc; + uint32_t host_cr; + + sc = device_get_softc(dev); + + /* Disable interrupts. */ + sc->sc_irq_mask = 0; + sc->sc_eirq_mask = 0; + MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask); + MV_SDIO_WR4(sc, MV_SDIO_EIRQ_EN, sc->sc_eirq_mask); + + /* Clear interrupt status registers. */ + MV_SDIO_WR4(sc, MV_SDIO_IRQ_SR, MV_SDIO_IRQ_ALL); + MV_SDIO_WR4(sc, MV_SDIO_EIRQ_SR, MV_SDIO_EIRQ_ALL); + + /* Enable interrupt status registers. */ + MV_SDIO_WR4(sc, MV_SDIO_IRQ_SR_EN, MV_SDIO_IRQ_ALL); + MV_SDIO_WR4(sc, MV_SDIO_EIRQ_SR_EN, MV_SDIO_EIRQ_ALL); + + /* Initialize Host Control Register. */ + host_cr = (MV_SDIO_HOST_CR_PUSHPULL | MV_SDIO_HOST_CR_BE | + MV_SDIO_HOST_CR_TMOVAL(MV_SDIO_TMO_MAX) | MV_SDIO_HOST_CR_TMO); + + MV_SDIO_WR4(sc, MV_SDIO_HOST_CR, host_cr); + + /* Stop clock and reset Transfer Mode Register. */ + MV_SDIO_WR4(sc, MV_SDIO_XFER, MV_SDIO_XFER_STOP_CLK); + + /* Set maximum clock divider value. */ + MV_SDIO_WR4(sc, MV_SDIO_CLK_DIV, MV_SDIO_CLK_DIV_MAX); + + /* Reset status, state machine and FIFOs synchronously. */ + MV_SDIO_WR4(sc, MV_SDIO_SW_RESET, MV_SDIO_SW_RESET_ALL); + DELAY(MV_SDIO_RESET_DELAY); + } + + static void + mv_sdio_init_host(struct mv_sdio_softc *sc) + { + struct mmc_host *host; + + host = &sc->sc_host; + + /* Clear host structure. */ + bzero(host, sizeof(struct mmc_host)); + + /* Calculate minimum and maximum operating frequencies. */ + host->f_min = MV_SDIO_F_BASE / (MV_SDIO_CLK_DIV_MAX + 1); + host->f_max = MV_SDIO_F_MAX; + + /* Set operation conditions (voltage). */ + host->host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340; + + /* Set additional host controller capabilities. */ + host->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_HSPEED; + } + + static void + mv_sdio_add_sysctls(struct mv_sdio_softc *sc) + { + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *children; + struct sysctl_oid *tree; + + ctx = device_get_sysctl_ctx(sc->sc_dev); + children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); + tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO, "params", + CTLFLAG_RD, 0, "Driver parameters"); + children = SYSCTL_CHILDREN(tree); + + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "use_dma", + CTLTYPE_UINT | CTLFLAG_RW, sc, 0, mv_sdio_sysctl_use_dma, + "I", "Use DMA for data transfers (0-1)"); + } + + /* + * This sysctl allows switching between DMA and PIO modes for data transfers: + * + * dev.mv_sdio.<unit>.params.use_dma + * + * Values: + * + * - 1 sets DMA mode + * - 0 sets PIO mode + * + * Driver uses DMA mode by default. + */ + static int + mv_sdio_sysctl_use_dma(SYSCTL_HANDLER_ARGS) + { + struct mv_sdio_softc *sc; + uint32_t use_dma; + int error; + + sc = (struct mv_sdio_softc *)arg1; + + use_dma = sc->sc_use_dma; + + error = sysctl_handle_int(oidp, &use_dma, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + + if (use_dma > 1) + return (EINVAL); + + mtx_lock(&sc->sc_mtx); + + /* Check if requested mode is already being used. */ + if (sc->sc_use_dma == use_dma) { + mtx_unlock(&sc->sc_mtx); + return (EPERM); + } + + if (!(sc->sc_mapped)) { + device_printf(sc->sc_dev, "DMA not initialized!\n"); + mtx_unlock(&sc->sc_mtx); + return (ENOMEM); + } + + /* Set new mode. */ + sc->sc_use_dma = use_dma; + + mtx_unlock(&sc->sc_mtx); + + return (0); + } + + static void + mv_sdio_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) + { + + if (error != 0) + return; + + /* Get first segment's physical address. */ + *(bus_addr_t *)arg = segs->ds_addr; + } + + static int + mv_sdio_dma_init(struct mv_sdio_softc *sc) + { + device_t dev; + bus_size_t dmabuf_size; + + dev = sc->sc_dev; + dmabuf_size = MAXPHYS; + + /* Create DMA tag. */ + if (bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ + MV_SDIO_DMA_SEGMENT_SIZE, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filtfunc, filtfuncarg */ + MAXPHYS, 1, /* maxsize, nsegments */ + MAXPHYS, BUS_DMA_ALLOCNOW, /* maxsegsz, flags */ + NULL, NULL, /* lockfunc, lockfuncarg */ + &sc->sc_dmatag) != 0) { + device_printf(dev, "Could not create DMA tag!\n"); + return (-1); + } + + /* Allocate DMA memory. */ + if (bus_dmamem_alloc(sc->sc_dmatag, (void **)&sc->sc_dmamem, + BUS_DMA_NOWAIT, &sc->sc_dmamap) != 0) { + device_printf(dev, "Could not allocate DMA memory!\n"); + mv_sdio_dma_finish(sc); + return (-1); + } + + /* Find the biggest available DMA buffer size. */ + while (bus_dmamap_load(sc->sc_dmatag, sc->sc_dmamap, + (void *)sc->sc_dmamem, dmabuf_size, mv_sdio_getaddr, + &sc->sc_physaddr, 0) != 0) { + dmabuf_size >>= 1; + if (dmabuf_size < MV_SDIO_BLOCK_SIZE) { + device_printf(dev, "Could not load DMA map!\n"); + mv_sdio_dma_finish(sc); + return (-1); + } + } + + sc->sc_mapped++; + sc->sc_dma_size = dmabuf_size; + + return (0); + } + + static void + mv_sdio_dma_finish(struct mv_sdio_softc *sc) + { + + /* Free DMA resources. */ + if (sc->sc_mapped) { + bus_dmamap_unload(sc->sc_dmatag, sc->sc_dmamap); + sc->sc_mapped--; + } + if (sc->sc_dmamem != NULL) + bus_dmamem_free(sc->sc_dmatag, sc->sc_dmamem, sc->sc_dmamap); + if (sc->sc_dmamap != NULL) + bus_dmamap_destroy(sc->sc_dmatag, sc->sc_dmamap); + if (sc->sc_dmatag != NULL) + bus_dma_tag_destroy(sc->sc_dmatag); + } + + static int + mv_sdio_start_command(struct mv_sdio_softc *sc, struct mmc_command *cmd) + { + struct mmc_request *req; + uint32_t cmdreg; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + req = sc->sc_req; + + sc->sc_curcmd = cmd; + + cmd->error = MMC_ERR_NONE; + + /* Check if card is in the slot. */ + if ((platform_sdio_slot_signal(MV_SDIO_SIG_CD) != -1) && + (sc->sc_card_present == 0)) { + cmd->error = MMC_ERR_FAILED; + mv_sdio_finalize_request(sc); + return (-1); + } + + /* Check if clock is enabled. */ + if (MV_SDIO_RD4(sc, MV_SDIO_XFER) & MV_SDIO_XFER_STOP_CLK) { + cmd->error = MMC_ERR_FAILED; + mv_sdio_finalize_request(sc); + return (-1); + } + + /* Write command argument. */ + MV_SDIO_WR4(sc, MV_SDIO_CMD_ARGL, cmd->arg & 0xffff); + MV_SDIO_WR4(sc, MV_SDIO_CMD_ARGH, cmd->arg >> 16); + + /* Determine response type. */ + if (cmd->flags & MMC_RSP_136) + cmdreg = MV_SDIO_CMD_RSP_136; + else if (cmd->flags & MMC_RSP_BUSY) + cmdreg = MV_SDIO_CMD_RSP_48_BUSY; + else if (cmd->flags & MMC_RSP_PRESENT) + cmdreg = MV_SDIO_CMD_RSP_48; + else { + /* No response. */ + cmdreg = MV_SDIO_CMD_RSP_NONE; + /* Enable host to detect unexpected response. */ + cmdreg |= MV_SDIO_CMD_UNEXPECTED_RSP; + sc->sc_irq_mask |= MV_SDIO_CMD_UNEXPECTED_RSP; + } + + /* Check command checksum if needed. */ + if (cmd->flags & MMC_RSP_CRC) + cmdreg |= MV_SDIO_CMD_CRC7; + /* Check command opcode if needed. */ + if (cmd->flags & MMC_RSP_OPCODE) + cmdreg |= MV_SDIO_CMD_INDEX_CHECK; + + /* Set commannd opcode. */ + cmdreg |= MV_SDIO_CMD_INDEX(cmd->opcode); + + /* Setup interrupts. */ + sc->sc_irq_mask = MV_SDIO_IRQ_CMD; + sc->sc_eirq_mask = MV_SDIO_EIRQ_ALL; + + /* Prepare data transfer. */ + if (cmd->data) { + cmdreg |= (MV_SDIO_CMD_DATA_PRESENT | MV_SDIO_CMD_DATA_CRC16); + if (mv_sdio_start_data(sc, cmd->data) < 0) { + cmd->error = MMC_ERR_FAILED; + printf("mv_sdio_start_data() failed!\n"); + mv_sdio_finalize_request(sc); + return (-1); + } + } + + /* Write command register. */ + MV_SDIO_WR4(sc, MV_SDIO_CMD, cmdreg); + + /* Clear interrupt status. */ + MV_SDIO_WR4(sc, MV_SDIO_IRQ_SR, ~MV_SDIO_IRQ_CARD_EVENT /*MV_SDIO_IRQ_ALL*/); + MV_SDIO_WR4(sc, MV_SDIO_EIRQ_SR, 0xffff /*MV_SDIO_EIRQ_ALL*/); + + /* Update interrupt/error interrupt enable registers. */ + MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask); + MV_SDIO_WR4(sc, MV_SDIO_EIRQ_EN, sc->sc_eirq_mask); + + /* Do not complete request, interrupt handler will do this. */ + return (0); + } + + static void + mv_sdio_finish_command(struct mv_sdio_softc *sc) + { + struct mmc_command *cmd; + struct mmc_data *data; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + cmd = sc->sc_curcmd; + data = cmd->data; + + /* Get response. */ + if (cmd->flags & MMC_RSP_PRESENT) { + if(cmd->flags & MMC_RSP_136) + /* 136-bit response. */ + mv_sdio_handle_136bit_resp(sc); + else + /* 48-bit response. */ + mv_sdio_handle_48bit_resp(sc, NULL); + } + + if (data) { + /* + * Disable command complete interrupt. It has already been + * handled. + */ + sc->sc_irq_mask &= ~MV_SDIO_IRQ_CMD; + + /* Enable XFER interrupt. */ + sc->sc_irq_mask |= MV_SDIO_IRQ_XFER; + + /* Check which data interrupts we need to activate. */ + if (sc->sc_use_dma) + /* DMA transaction. */ + sc->sc_irq_mask |= MV_SDIO_IRQ_DMA; + else if (data->flags & MMC_DATA_READ) + /* Read transaction in PIO mode. */ + sc->sc_irq_mask |= MV_SDIO_IRQ_RX_FULL; + else + /* Write transaction in PIO mode. */ + sc->sc_irq_mask |= MV_SDIO_IRQ_TX_EMPTY; + + /* Check if Auto-CMD12 interrupt will be needed. */ + if (sc->sc_req->stop) + sc->sc_irq_mask |= MV_SDIO_IRQ_AUTOCMD12; + + /* Update interrupt enable register. */ + MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask); + } else { + /* We're done. Disable interrupts and finalize request. */ + mv_sdio_disable_intr(sc); + mv_sdio_finalize_request(sc); + } + } + + static int + mv_sdio_start_data(struct mv_sdio_softc *sc, struct mmc_data *data) + { + struct mmc_command *stop; + uint32_t autocmd12reg, xfer, host_sr; + size_t blk_size, blk_count; + int retries; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + /* + * No transfer can be started when FIFO_EMPTY bit in MV_SDIO_HOST_SR + * is not set. This bit is sometimes not set instantly after XFER + * interrupt has been asserted. + */ + host_sr = MV_SDIO_RD4(sc, MV_SDIO_HOST_SR); + + retries = 10; + while (!(host_sr & MV_SDIO_HOST_SR_FIFO_EMPTY)) { + if (retries == 0) + return (-1); + retries--; + DELAY(MV_SDIO_FIFO_EMPTY_DELAY); + host_sr = MV_SDIO_RD4(sc, MV_SDIO_HOST_SR); + } + + /* Clear data offset. */ + sc->sc_data_offset = 0; + + /* + * Set block size. It can be less than or equal to MV_SDIO_BLOCK_SIZE + * bytes. + */ + blk_size = (data->len < MV_SDIO_BLOCK_SIZE) ? data->len : + MV_SDIO_BLOCK_SIZE; + MV_SDIO_WR4(sc, MV_SDIO_BLK_SIZE, blk_size); + + /* Set block count. */ + blk_count = (data->len + MV_SDIO_BLOCK_SIZE - 1) / MV_SDIO_BLOCK_SIZE; + MV_SDIO_WR4(sc, MV_SDIO_BLK_COUNT, blk_count); + + /* We want to initiate transfer by software. */ + xfer = MV_SDIO_XFER_SW_WR_EN; + + if (sc->sc_use_dma) { + /* Synchronize before DMA transfer. */ + if (data->flags & MMC_DATA_READ) + bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, + BUS_DMASYNC_PREREAD); + else { + memcpy(sc->sc_dmamem, data->data, data->len); + bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, + BUS_DMASYNC_PREWRITE); + } + + /* Write DMA buffer address register. */ + MV_SDIO_WR4(sc, MV_SDIO_DMA_ADDRL, sc->sc_physaddr & 0xffff); + MV_SDIO_WR4(sc, MV_SDIO_DMA_ADDRH, sc->sc_physaddr >> 16); + } else + /* Set PIO transfer mode. */ + xfer |= MV_SDIO_XFER_PIO; + + /* + * Prepare Auto-CMD12. This command is automatically sent to the card + * by the host controller to stop multiple-block data transaction. + */ + if (sc->sc_req->stop) { + stop = sc->sc_req->stop; + + /* Set Auto-CMD12 argument. */ + MV_SDIO_WR4(sc, MV_SDIO_AUTOCMD12_ARGL, stop->arg & 0xffff); + MV_SDIO_WR4(sc, MV_SDIO_AUTOCMD12_ARGH, stop->arg >> 16); + + /* Set Auto-CMD12 opcode. */ + autocmd12reg = MV_SDIO_AUTOCMD12_INDEX(stop->opcode); + + /* Check busy signal if needed. */ + if (stop->flags & MMC_RSP_BUSY) + autocmd12reg |= MV_SDIO_AUTOCMD12_BUSY_CHECK; + /* Check Auto-CMD12 index. */ + if (stop->flags & MMC_RSP_OPCODE) + autocmd12reg |= MV_SDIO_AUTOCMD12_INDEX_CHECK; + + MV_SDIO_WR4(sc, MV_SDIO_AUTOCMD12, autocmd12reg); + + xfer |= MV_SDIO_XFER_AUTOCMD12; + } + + /* Change data direction. */ + if (data->flags & MMC_DATA_READ) + xfer |= MV_SDIO_XFER_TO_HOST; + + /* Write transfer mode register. */ + MV_SDIO_WR4(sc, MV_SDIO_XFER, xfer); + + return (0); + } + + static void + mv_sdio_handle_136bit_resp(struct mv_sdio_softc *sc) + { + struct mmc_command *cmd; + uint32_t resp[8]; + uint32_t base, extra; + int i, j, off; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + cmd = sc->sc_curcmd; + + /* Collect raw response from the controller. */ + for (i = 0; i < 8; i++) + resp[i] = MV_SDIO_RD4(sc, MV_SDIO_RSP(i)); + + /* Response passed to MMC bus is shifted by one byte. */ + extra = 0; + for (i = 0, j = 7; i < 4; i++, j -= 2) { + off = (i ? 0 : 2); + base = resp[j] | (resp[j - 1] << (16 - off)); + cmd->resp[3 - i] = (base << (6 + off)) + extra; + extra = base >> (26 - off); + } + } + + static void + mv_sdio_handle_48bit_resp(struct mv_sdio_softc *sc, struct mmc_command *stop) + { + struct mmc_command *cmd; + uint32_t resp[3], word; + uint8_t *rp; + int i; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + if (stop == NULL) + cmd = sc->sc_curcmd; + else + cmd = stop; + + /* Collect raw response from the controller. */ + for (i = 0; i < 3; i++) { + if (stop == NULL) + resp[i] = MV_SDIO_RD4(sc, MV_SDIO_RSP(i)); + else + resp[i] = MV_SDIO_RD4(sc, MV_SDIO_AUTOCMD12_RSP(i)); + } + + /* Clear MMC bus response buffer. */ + bzero(&cmd->resp[0], 4 * sizeof(uint32_t)); + + /* + * Fill MMC bus response buffer. + */ + + rp = (uint8_t *)&cmd->resp[0]; + + /* Response bits [45:14] */ + word = (resp[1] & MV_SDIO_RSP48_BM16) | + ((resp[0] & MV_SDIO_RSP48_BM16) << 16); + + /* Response bits [15:14] and [13:8] */ + *rp++ = (resp[2] & MV_SDIO_RSP48_BM6) | + ((word & MV_SDIO_RSP48_BM2) << 6); + + /* Response bits [15:14] are already included. */ + word >>= 2; + + /* Response bits [45:16] */ + memcpy(rp, &word, sizeof(uint32_t)); + } + + static void + mv_sdio_intr(void *arg) + { + struct mv_sdio_softc *sc; + uint32_t irq_stat, eirq_stat; + + sc = (struct mv_sdio_softc *)arg; + #if 0 + device_printf(sc->sc_dev,"intr 0x%04x intr_en 0x%04x hw_state 0x%04x\n", + MV_SDIO_RD4( sc, MV_SDIO_IRQ_SR ) , + MV_SDIO_RD4( sc, MV_SDIO_IRQ_EN ), + MV_SDIO_RD4( sc, MV_SDIO_HOST_SR )); + #endif + + + mtx_lock(&sc->sc_mtx); + + + + irq_stat = MV_SDIO_RD4(sc, MV_SDIO_IRQ_SR) & sc->sc_irq_mask; + eirq_stat = MV_SDIO_RD4(sc, MV_SDIO_EIRQ_SR) & sc->sc_eirq_mask; + + /* + * In case of error interrupt, interrupt cause will be identified by + * checking bits in error interrupt status register. + */ + irq_stat &= ~MV_SDIO_IRQ_ERR; + + /* Handle command interrupts. */ + if ((irq_stat & MV_SDIO_IRQS_CMD) || + (eirq_stat & MV_SDIO_EIRQS_CMD)) { + MV_SDIO_WR4(sc, MV_SDIO_IRQ_SR, irq_stat); + MV_SDIO_WR4(sc, MV_SDIO_EIRQ_SR, eirq_stat); + mv_sdio_cmd_intr(sc, irq_stat, eirq_stat); + irq_stat &= ~MV_SDIO_IRQS_CMD; + eirq_stat &= ~MV_SDIO_EIRQS_CMD; + } + + /* Handle data interrupts. */ + if ((irq_stat & MV_SDIO_IRQS_DATA) || + (eirq_stat & MV_SDIO_EIRQS_DATA)) { + MV_SDIO_WR4(sc, MV_SDIO_IRQ_SR, irq_stat); + MV_SDIO_WR4(sc, MV_SDIO_EIRQ_SR, eirq_stat); + mv_sdio_data_intr(sc, irq_stat, eirq_stat); + irq_stat &= ~MV_SDIO_IRQS_DATA; + eirq_stat &= ~MV_SDIO_EIRQS_DATA; + } + + /* Handle unexpected interrupts. */ + if (irq_stat) { + device_printf(sc->sc_dev, "Unexpected interrupt(s)! " + "IRQ SR = 0x%08x\n", irq_stat); + /* Clear interrupt status. */ + MV_SDIO_WR4(sc, MV_SDIO_IRQ_SR, irq_stat); + } + if (eirq_stat) { + device_printf(sc->sc_dev, "Unexpected error interrupt(s)! " + "EIRQ SR = 0x%08x\n", eirq_stat); + /* Clear error interrupt status. */ + MV_SDIO_WR4(sc, MV_SDIO_EIRQ_SR, eirq_stat); + } + + mtx_unlock(&sc->sc_mtx); + } + + static void + mv_sdio_cmd_intr(struct mv_sdio_softc *sc, uint32_t irq, uint32_t eirq) + { + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + if (!sc->sc_curcmd) { + device_printf(sc->sc_dev, "Got command interrupt, but there " + "is no active command!\n"); + return; + } + + /* Handle unexpected response error. */ + if (irq & MV_SDIO_IRQ_UNEXPECTED_RSP) { + sc->sc_curcmd->error = MMC_ERR_FAILED; + device_printf(sc->sc_dev, "Unexpected response!\n"); + } + + /* Handle errors. */ + if (eirq & MV_SDIO_EIRQ_CMD_TMO) { + sc->sc_curcmd->error = MMC_ERR_TIMEOUT; + device_printf(sc->sc_dev, "Error - command %d timeout!\n", + sc->sc_curcmd->opcode); + } else if (eirq & MV_SDIO_EIRQ_CMD_CRC7) { + sc->sc_curcmd->error = MMC_ERR_BADCRC; + device_printf(sc->sc_dev, "Error - bad command %d " + "checksum!\n", sc->sc_curcmd->opcode); + } else if (eirq) { + sc->sc_curcmd->error = MMC_ERR_FAILED; + device_printf(sc->sc_dev, "Command %d error!\n", + sc->sc_curcmd->opcode); + } + + if (sc->sc_curcmd->error != MMC_ERR_NONE) { + /* Error. Disable interrupts and finalize request. */ + mv_sdio_disable_intr(sc); + mv_sdio_finalize_request(sc); + return; + } + + if (irq & MV_SDIO_IRQ_CMD) + mv_sdio_finish_command(sc); + } + + static void + mv_sdio_data_intr(struct mv_sdio_softc *sc, uint32_t irq, uint32_t eirq) + { + struct mmc_command *stop; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + if (!sc->sc_curcmd) { + device_printf(sc->sc_dev, "Got data interrupt, but there is " + "no active command.\n"); + return; + } + if ((!sc->sc_curcmd->data) && ((sc->sc_curcmd->flags & + MMC_RSP_BUSY) == 0)) { + device_printf(sc->sc_dev, "Got data interrupt, but there is " + "no active data transaction.n\n"); + sc->sc_curcmd->error = MMC_ERR_FAILED; + return; + } + + /* Handle errors. */ + if(eirq & MV_SDIO_EIRQ_DATA_TMO) { + sc->sc_curcmd->error = MMC_ERR_TIMEOUT; + device_printf(sc->sc_dev, "Data %s timeout!\n", + (sc->sc_curcmd->data->flags & MMC_DATA_READ) ? "read" : + "write"); + } else if (eirq & (MV_SDIO_EIRQ_DATA_CRC16 | + MV_SDIO_EIRQ_DATA_ENDBIT)) { + sc->sc_curcmd->error = MMC_ERR_BADCRC; + device_printf(sc->sc_dev, "Bad data checksum!\n"); + } else if (eirq) { + sc->sc_curcmd->error = MMC_ERR_FAILED; + device_printf(sc->sc_dev, "Data error!: 0x%04X \n", + eirq); + + if( 0 != ( eirq & MV_SDIO_EIRQ_CRC_STAT ) ) + { + device_printf(sc->sc_dev, "MV_SDIO_EIRQ_CRC_STAT\n"); + } + } + + /* Handle Auto-CMD12 error. */ + if (eirq & MV_SDIO_EIRQ_AUTOCMD12) { + sc->sc_req->stop->error = MMC_ERR_FAILED; + sc->sc_curcmd->error = MMC_ERR_FAILED; + device_printf(sc->sc_dev, "Auto-CMD12 error!\n"); + } + + if (sc->sc_curcmd->error != MMC_ERR_NONE) { + /* Error. Disable interrupts and finalize request. */ + mv_sdio_disable_intr(sc); + mv_sdio_finalize_request(sc); + return; + } + + /* Handle PIO interrupt. */ + if (irq & (MV_SDIO_IRQ_TX_EMPTY | MV_SDIO_IRQ_RX_FULL)) + mv_sdio_transfer_pio(sc); + + /* Handle DMA interrupt. */ + if (irq & (MV_SDIO_IRQ_DMA)) { + /* Synchronize DMA buffer. */ + if (MV_SDIO_RD4(sc, MV_SDIO_XFER) & MV_SDIO_XFER_TO_HOST) { + bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, + BUS_DMASYNC_POSTWRITE); + memcpy(sc->sc_curcmd->data->data, sc->sc_dmamem, + sc->sc_curcmd->data->len); + } else + bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, + BUS_DMASYNC_POSTREAD); + + /* Disable DMA interrupt. */ + sc->sc_irq_mask &= ~MV_SDIO_IRQ_DMA; + MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask); + } + + /* Handle Auto-CMD12 interrupt. */ + if (irq & (MV_SDIO_IRQ_AUTOCMD12)) { + stop = sc->sc_req->stop; + /* Get 48-bit response. */ + mv_sdio_handle_48bit_resp(sc, stop); + + /* Disable Auto-CMD12 interrupt. */ + sc->sc_irq_mask &= ~MV_SDIO_IRQ_AUTOCMD12; + MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask); + } + + /* Transfer finished. Disable interrupts and finalize request. */ + if (irq & (MV_SDIO_IRQ_XFER)) { + mv_sdio_disable_intr(sc); + mv_sdio_finalize_request(sc); + } + } + + static void + mv_sdio_cd_intr(void *arg) + { + struct mv_sdio_softc *sc; + + sc = (struct mv_sdio_softc *)arg; + + mtx_lock(&sc->sc_mtx); + + if (platform_sdio_slot_signal(MV_SDIO_SIG_CD) == 0) { + + device_printf(sc->sc_dev, "Card removed\n"); + + callout_stop(&sc->sc_card_callout); + + sc->sc_card_present = 0; + + taskqueue_enqueue(taskqueue_swi, &sc->sc_card_task); + + + } else { + + device_printf(sc->sc_dev, "Card inserted\n"); + + + if( 1 == sc->sc_card_present ) + { + /* Card has been removed previously + * Remove the child-device, before it is added again + */ + if( NULL != sc->sc_child ) + { + device_printf(sc->sc_dev, "Previous removal has not been detected properly\n"); + if (device_delete_child(sc->sc_dev, sc->sc_child) != 0) { + device_printf(sc->sc_dev, "Could not delete MMC " + "bus!\n"); + } + sc->sc_child = NULL; + } + } + else + { + sc->sc_card_present = 1; + } + callout_reset(&sc->sc_card_callout, hz / 2, + mv_sdio_card_delay, sc); + + + } + + mtx_unlock(&sc->sc_mtx); + } + + static void + mv_sdio_disable_intr(struct mv_sdio_softc *sc) + { + + /* Disable interrupts that were enabled. */ + sc->sc_irq_mask &= ~(sc->sc_irq_mask); + sc->sc_eirq_mask &= ~(sc->sc_eirq_mask); + MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask); + MV_SDIO_WR4(sc, MV_SDIO_EIRQ_EN, sc->sc_eirq_mask); + } + + #if 1 + static void + mv_sdio_card_delay(void *arg) + { + struct mv_sdio_softc *sc; + + sc = (struct mv_sdio_softc *)arg; + + taskqueue_enqueue(taskqueue_swi, &sc->sc_card_task); + } + #endif + + static void + mv_sdio_card_task(void *arg, int pending) + { + struct mv_sdio_softc *sc; + + int device_probe_and_attach_ret_val = 0; + + sc = (struct mv_sdio_softc *)arg; + + mtx_lock(&sc->sc_mtx); + + #if 0 + device_printf(sc->sc_dev, "mv_sdio_card_task\n"); + #endif + + if (sc->sc_card_present) { + if (sc->sc_child) { + mtx_unlock(&sc->sc_mtx); + return; + } + + /* Initialize host controller's registers. */ + mv_sdio_init(sc->sc_dev); + + sc->sc_child = device_add_child(sc->sc_dev, "mmc", -1); + if (sc->sc_child == NULL) { + device_printf(sc->sc_dev, "Could not add MMC bus!\n"); + mtx_unlock(&sc->sc_mtx); + return; + } + + /* Initialize host structure for MMC bus. */ + mv_sdio_init_host(sc); + + device_set_ivars(sc->sc_child, &sc->sc_host); + + mtx_unlock(&sc->sc_mtx); + + // device_probe_and_attach_ret_val = bus_generic_attach( sc->sc_dev ); + + device_probe_and_attach_ret_val = device_probe_and_attach(sc->sc_child); + + //device_probe_and_attach_ret_val = device_probe_child( sc->sc_dev, sc->sc_child ); //device_t dev, device_t child) + + if( 0 != device_probe_and_attach_ret_val ) { + device_printf(sc->sc_dev, "MMC bus failed on probe " + "and attach! %i\n",device_probe_and_attach_ret_val); + device_delete_child(sc->sc_dev, sc->sc_child); + sc->sc_child = NULL; + } + } else { + if (sc->sc_child == NULL) { + mtx_unlock(&sc->sc_mtx); + return; + } + + mtx_unlock(&sc->sc_mtx); + if (device_delete_child(sc->sc_dev, sc->sc_child) != 0) { + device_printf(sc->sc_dev, "Could not delete MMC " + "bus!\n"); + } + sc->sc_child = NULL; + } + } + + static uint32_t + mv_sdio_read_fifo(struct mv_sdio_softc *sc) + { + uint32_t data; + device_printf(sc->sc_dev, "This is not tested, yet MV_SDIO_FIFO not ensured\n "); + + while (!(MV_SDIO_RD4(sc, MV_SDIO_IRQ_SR) & MV_SDIO_IRQ_RX_FULL)); + data = MV_SDIO_RD4(sc, MV_SDIO_FIFO); + while (!(MV_SDIO_RD4(sc, MV_SDIO_IRQ_SR) & MV_SDIO_IRQ_RX_FULL)); + data |= (MV_SDIO_RD4(sc, MV_SDIO_FIFO) << 16); + return data; + } + + static void + mv_sdio_write_fifo(struct mv_sdio_softc *sc, uint32_t val) + { + while (!(MV_SDIO_RD4(sc, MV_SDIO_IRQ_SR) & MV_SDIO_IRQ_TX_EMPTY)); + MV_SDIO_WR4(sc, MV_SDIO_FIFO, val & 0xffff); + while (!(MV_SDIO_RD4(sc, MV_SDIO_IRQ_SR) & MV_SDIO_IRQ_TX_EMPTY)); + MV_SDIO_WR4(sc, MV_SDIO_FIFO, val >> 16); + } + + static void + mv_sdio_transfer_pio(struct mv_sdio_softc *sc) + { + struct mmc_command *cmd; + + device_printf(sc->sc_dev, "mv_sdio_transfer_pio()\n"); + + cmd = sc->sc_curcmd; + + if (cmd->data->flags & MMC_DATA_READ) { + while (MV_SDIO_RD4(sc, MV_SDIO_IRQ_SR) & + MV_SDIO_IRQ_RX_FULL) { + mv_sdio_read_block_pio(sc); + /* + * Assert delay after each block transfer to meet read + * access timing constraint. + */ + DELAY(MV_SDIO_RD_DELAY); + if (sc->sc_data_offset >= cmd->data->len) + break; + } + /* All blocks read in PIO mode. Disable interrupt. */ + sc->sc_irq_mask &= ~MV_SDIO_IRQ_RX_FULL; + MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask); + } else { + while (MV_SDIO_RD4(sc, MV_SDIO_IRQ_SR) & + MV_SDIO_IRQ_TX_EMPTY) { + mv_sdio_write_block_pio(sc); + /* Wait while card is programming the memory. */ + while ((MV_SDIO_RD4(sc, MV_SDIO_HOST_SR) & + MV_SDIO_HOST_SR_CARD_BUSY)); + /* + * Assert delay after each block transfer to meet + * write access timing constraint. + */ + DELAY(MV_SDIO_WR_DELAY); + + if (sc->sc_data_offset >= cmd->data->len) + break; + } + /* All blocks written in PIO mode. Disable interrupt. */ + sc->sc_irq_mask &= ~MV_SDIO_IRQ_TX_EMPTY; + MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask); + } + } + + static void + mv_sdio_read_block_pio(struct mv_sdio_softc *sc) + { + uint32_t data; + char *buffer; + size_t left; + + buffer = sc->sc_curcmd->data->data; + buffer += sc->sc_data_offset; + /* Transfer one block at a time. */ + left = min(MV_SDIO_BLOCK_SIZE, sc->sc_curcmd->data->len - + sc->sc_data_offset); + sc->sc_data_offset += left; + + /* Handle unaligned and aligned buffer cases. */ + if ((intptr_t)buffer & 3) { + while (left > 3) { + data = mv_sdio_read_fifo(sc); + buffer[0] = data; + buffer[1] = (data >> 8); + buffer[2] = (data >> 16); + buffer[3] = (data >> 24); + buffer += 4; + left -= 4; + } + } else { + while (left > 3) { + data = mv_sdio_read_fifo(sc); + *((uint32_t *)buffer) = data; + buffer += 4; + left -= 4; + } + } + /* Handle uneven size case. */ + if (left > 0) { + data = mv_sdio_read_fifo(sc); + while (left > 0) { + *(buffer++) = data; + data >>= 8; + left--; + } + } + } + + static void + mv_sdio_write_block_pio(struct mv_sdio_softc *sc) + { + uint32_t data = 0; + char *buffer; + size_t left; + + buffer = sc->sc_curcmd->data->data; + buffer += sc->sc_data_offset; + /* Transfer one block at a time. */ + left = min(MV_SDIO_BLOCK_SIZE, sc->sc_curcmd->data->len - + sc->sc_data_offset); + sc->sc_data_offset += left; + + /* Handle unaligned and aligned buffer cases. */ + if ((intptr_t)buffer & 3) { + while (left > 3) { + data = buffer[0] + + (buffer[1] << 8) + + (buffer[2] << 16) + + (buffer[3] << 24); + left -= 4; + buffer += 4; + mv_sdio_write_fifo(sc, data); + } + } else { + while (left > 3) { + data = *((uint32_t *)buffer); + left -= 4; + buffer += 4; + mv_sdio_write_fifo(sc, data); + } + } + /* Handle uneven size case. */ + if (left > 0) { + data = 0; + while (left > 0) { + data <<= 8; + data += *(buffer++); + left--; + } + mv_sdio_write_fifo(sc, data); + } + } + + static int + mv_sdio_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) + { + struct mv_sdio_softc *sc; + struct mmc_host *host; + + sc = device_get_softc(dev); + host = device_get_ivars(child); + + switch (index) { + case MMCBR_IVAR_BUS_MODE: + *(int *)result = host->ios.bus_mode; + break; + case MMCBR_IVAR_BUS_WIDTH: + *(int *)result = host->ios.bus_width; + break; + case MMCBR_IVAR_CHIP_SELECT: + *(int *)result = host->ios.chip_select; + break; + case MMCBR_IVAR_CLOCK: + *(int *)result = host->ios.clock; + break; + case MMCBR_IVAR_F_MIN: + *(int *)result = host->f_min; + break; + case MMCBR_IVAR_F_MAX: + *(int *)result = host->f_max; + break; + case MMCBR_IVAR_HOST_OCR: + *(int *)result = host->host_ocr; + break; + case MMCBR_IVAR_MODE: + *(int *)result = host->mode; + break; + case MMCBR_IVAR_OCR: + *(int *)result = host->ocr; + break; + case MMCBR_IVAR_POWER_MODE: + *(int *)result = host->ios.power_mode; + break; + case MMCBR_IVAR_VDD: + *(int *)result = host->ios.vdd; + break; + case MMCBR_IVAR_CAPS: + *(int *)result = host->caps; + break; + case MMCBR_IVAR_TIMING: + *(int *)result = host->ios.timing; + break; + case MMCBR_IVAR_MAX_DATA: + mtx_lock(&sc->sc_mtx); + /* Return maximum number of blocks the driver can handle. */ + if (sc->sc_use_dma) + *(int *)result = (sc->sc_dma_size / + MV_SDIO_BLOCK_SIZE); + else + *(int *)result = MV_SDIO_BLOCKS_MAX; + mtx_unlock(&sc->sc_mtx); + break; + default: + return (EINVAL); + } + + return (0); + } + + static int + mv_sdio_write_ivar(device_t dev, device_t child, int index, uintptr_t value) + { + struct mmc_host *host; + + host = device_get_ivars(child); + + switch (index) { + case MMCBR_IVAR_BUS_MODE: + host->ios.bus_mode = value; + break; + case MMCBR_IVAR_BUS_WIDTH: + host->ios.bus_width = value; + break; + case MMCBR_IVAR_CHIP_SELECT: + host->ios.chip_select = value; + break; + case MMCBR_IVAR_CLOCK: + host->ios.clock = value; + break; + case MMCBR_IVAR_MODE: + host->mode = value; + break; + case MMCBR_IVAR_OCR: + host->ocr = value; + break; + case MMCBR_IVAR_POWER_MODE: + host->ios.power_mode = value; + break; + case MMCBR_IVAR_VDD: + host->ios.vdd = value; + break; + case MMCBR_IVAR_TIMING: + host->ios.timing = value; + break; + case MMCBR_IVAR_CAPS: + case MMCBR_IVAR_HOST_OCR: + case MMCBR_IVAR_F_MIN: + case MMCBR_IVAR_F_MAX: + case MMCBR_IVAR_MAX_DATA: + default: + /* Instance variable not writable. */ + return (EINVAL); + } + + return (0); + } diff -prN --exclude=.svn head_r221725/sys/arm/mv/mv_sdio.h head_r221525/sys/arm/mv/mv_sdio.h *** head_r221725/sys/arm/mv/mv_sdio.h Thu Jan 1 00:00:00 1970 --- head_r221525/sys/arm/mv/mv_sdio.h Tue May 10 08:24:17 2011 *************** *** 0 **** --- 1,172 ---- + /* + * Copyright (C) 2008 Marvell Semiconductors, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + + #ifndef _MVSDMMC_INCLUDE + #define _MVSDMMC_INCLUDE + + + #define MVSDMMC_DMA_SIZE 65536 + + + + /* + * The base MMC clock rate + */ + + #define MVSDMMC_CLOCKRATE_MIN 100000 + #define MVSDMMC_CLOCKRATE_MAX 50000000 + + #define MVSDMMC_BASE_FAST_CLOCK 200000000 + + + /* + * SDIO register + */ + + #define MV_SDIO_DMA_ADDRL 0x000 + #define MV_SDIO_DMA_ADDRH 0x004 + #define MV_SDIO_BLK_SIZE 0x008 + #define MV_SDIO_BLK_COUNT 0x00c + #define MV_SDIO_CMD 0x01c + #define MV_SDIO_CMD_ARGL 0x010 + #define MV_SDIO_CMD_ARGH 0x014 + #define MV_SDIO_XFER 0x018 + #define MV_SDIO_HOST_SR 0x048 + #define MV_SDIO_HOST_CR 0x050 + #define MV_SDIO_SW_RESET 0x05c + #define MV_SDIO_IRQ_SR 0x060 + #define MV_SDIO_EIRQ_SR 0x064 + #define MV_SDIO_IRQ_SR_EN 0x068 + #define MV_SDIO_EIRQ_SR_EN 0x06c + #define MV_SDIO_IRQ_EN 0x070 + #define MV_SDIO_EIRQ_EN 0x074 + #define MV_SDIO_AUTOCMD12_ARGL 0x084 + #define MV_SDIO_AUTOCMD12_ARGH 0x088 + #define MV_SDIO_AUTOCMD12 0x08c + #define MV_SDIO_CLK_DIV 0x128 + #define MV_SDIO_FIFO 0xa2100 /* FIXME!!! */ + + #define MV_SDIO_RSP(i) (0x020 + ((i)<<2)) + #define MV_SDIO_AUTOCMD12_RSP(i) (0x090 + ((i)<<2)) + + /* + * SDIO Status-Register + */ + #define MV_SDIO_HOST_SR_CARD_BUSY (1<<1) + #define MV_SDIO_HOST_SR_FIFO_EMPTY (1<<13) + + + + /* + * SDIO_CMD + */ + #define MV_SDIO_CMD_RSP_NONE (0 << 0) + #define MV_SDIO_CMD_RSP_136 (1 << 0) + #define MV_SDIO_CMD_RSP_48 (2 << 0) + #define MV_SDIO_CMD_RSP_48_BUSY (3 << 0) + #define MV_SDIO_CMD_DATA_CRC16 (1<<2) + #define MV_SDIO_CMD_CRC7 (1<<3) + #define MV_SDIO_CMD_INDEX_CHECK (1<<4) + #define MV_SDIO_CMD_DATA_PRESENT (1<<5) + #define MV_SDIO_CMD_UNEXPECTED_RSP (1<<7) + #define MV_SDIO_CMD_INDEX(x) ( (x) << 8 ) + + + /* + * SDIO_XFER_MODE + */ + #define MV_SDIO_XFER_STOP_CLK (1 << 5) + #define MV_SDIO_XFER_TO_HOST (1 << 4) + #define MV_SDIO_XFER_PIO (1 << 3) + #define MV_SDIO_XFER_AUTOCMD12 (1 << 2) + #define MV_SDIO_XFER_SW_WR_EN (1 << 1) + + /* + * SDIO_HOST_CTRL + */ + #define MV_SDIO_HOST_CR_PUSHPULL (1 << 0) + #define MV_SDIO_HOST_CR_MMC (3 << 1) + #define MV_SDIO_HOST_CR_BE (1 << 3) + #define MV_SDIO_HOST_CR_4BIT (1 << 9) + #define MV_SDIO_HOST_CR_HIGHSPEED (1 << 10) + + #define MV_SDIO_HOST_CR_TMOVAL(x) ((x) << 11) + #define MV_SDIO_HOST_CR_TMO ( 1 << 15 ) + + /* + * NORmal status bits + */ + + + #define MV_SDIO_IRQ_ERR (1<<15) + #define MV_SDIO_IRQ_UNEXPECTED_RSP (1<<14) + #define MV_SDIO_IRQ_AUTOCMD12 (1<<13) + #define MV_SDIO_IRQ_SUSPENSE_ON_IRQ_EN (1<<12) + #define MV_SDIO_IRQ_IMB_FIFO_WORD_AVAIL (1<<11) + #define MV_SDIO_IRQ_IMB_FIFO_WORD_FILLED (1<<10) + #define MV_SDIO_IRQ_READ_WAIT (1<<9) + #define MV_SDIO_IRQ_CARD_EVENT (1<<8) + #define MV_SDIO_IRQ_RX_FULL (1<<5) + #define MV_SDIO_IRQ_TX_EMPTY (1<<4) + #define MV_SDIO_IRQ_DMA (1<<3) + #define MV_SDIO_IRQ_BLOCK_GAP (1<<2) + #define MV_SDIO_IRQ_XFER (1<<1) + #define MV_SDIO_IRQ_CMD (1<<0) + + #define MV_SDIO_IRQ_ALL (MV_SDIO_IRQ_CMD | MV_SDIO_IRQ_XFER | MV_SDIO_IRQ_BLOCK_GAP | MV_SDIO_IRQ_DMA | MV_SDIO_IRQ_RX_FULL | MV_SDIO_IRQ_TX_EMPTY | MV_SDIO_IRQ_CARD_EVENT | MV_SDIO_IRQ_READ_WAIT | MV_SDIO_IRQ_IMB_FIFO_WORD_FILLED | MV_SDIO_IRQ_IMB_FIFO_WORD_AVAIL | MV_SDIO_IRQ_SUSPENSE_ON_IRQ_EN | MV_SDIO_IRQ_AUTOCMD12 | MV_SDIO_IRQ_UNEXPECTED_RSP | MV_SDIO_IRQ_ERR ) + + //#define MV_SDIO_IRQ_SR + + + /* + * ERR status bits + */ + #define MV_SDIO_EIRQ_CRC_STAT (1<<14) + #define MV_SDIO_EIRQ_CRC_STARTBIT (1<<13) + #define MV_SDIO_EIRQ_CRC_ENDBIT (1<<12) + #define MV_SDIO_EIRQ_RSP_TBIT (1<<11) + #define MV_SDIO_EIRQ_XFER_SIZE (1<<10) + #define MV_SDIO_EIRQ_CMD_STARTBIT (1<<9) + #define MV_SDIO_EIRQ_AUTOCMD12 (1<<8) + #define MV_SDIO_EIRQ_DATA_ENDBIT (1<<6) + #define MV_SDIO_EIRQ_DATA_CRC16 (1<<5) + #define MV_SDIO_EIRQ_DATA_TMO (1<<4) + #define MV_SDIO_EIRQ_CMD_INDEX (1<<3) + #define MV_SDIO_EIRQ_CMD_ENDBIT (1<<2) + #define MV_SDIO_EIRQ_CMD_CRC7 (1<<1) + #define MV_SDIO_EIRQ_CMD_TMO (1<<0) + + #define MV_SDIO_EIRQ_ALL (MV_SDIO_EIRQ_CMD_TMO | \ + MV_SDIO_EIRQ_CMD_CRC7 | \ + MV_SDIO_EIRQ_CMD_ENDBIT | \ + MV_SDIO_EIRQ_CMD_INDEX | \ + MV_SDIO_EIRQ_DATA_TMO | \ + MV_SDIO_EIRQ_DATA_CRC16 | \ + MV_SDIO_EIRQ_DATA_ENDBIT | \ + MV_SDIO_EIRQ_AUTOCMD12 | \ + MV_SDIO_EIRQ_CMD_STARTBIT |\ + MV_SDIO_EIRQ_XFER_SIZE |\ + MV_SDIO_EIRQ_RSP_TBIT |\ + MV_SDIO_EIRQ_CRC_ENDBIT |\ + MV_SDIO_EIRQ_CRC_STARTBIT |\ + MV_SDIO_EIRQ_CRC_STAT) + + /* AUTOCMD12 register values */ + #define MV_SDIO_AUTOCMD12_BUSY_CHECK (1<<0) + #define MV_SDIO_AUTOCMD12_INDEX_CHECK (1<<1) + #define MV_SDIO_AUTOCMD12_INDEX(x) (x<<8) + + /* Software reset register */ + #define MV_SDIO_SW_RESET_ALL (1<<8) + + /* */ + #define MV_SDIO_SIG_CD 1 + #define MV_SDIO_SIG_WP 2 + + #endif /* _MVSDMMC_INCLUDE */ diff -prN --exclude=.svn head_r221725/sys/boot/fdt/dts/openrd-cl.dts head_r221525/sys/boot/fdt/dts/openrd-cl.dts *** head_r221725/sys/boot/fdt/dts/openrd-cl.dts Thu Jan 1 00:00:00 1970 --- head_r221525/sys/boot/fdt/dts/openrd-cl.dts Tue May 10 08:24:54 2011 *************** *** 0 **** --- 1,399 ---- + /* + * Copyright (c) 2009-2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Semihalf under sponsorship from + * the FreeBSD Foundation. + * + * 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. + * + * OpenRD-Client/Ultimate Device Tree Source. + * + * $FreeBSD$ + */ + + /dts-v1/; + + / { + model = "mrvl,OpenRD-CL"; + compatible = "OpenRD-CL"; + #address-cells = <1>; + #size-cells = <1>; + + aliases { + ethernet0 = &enet0; + ethernet1 = &enet1; + mpp = &MPP; + pci0 = &pci0; + serial0 = &serial0; + /*serial1 = &serial1;*/ + soc = &SOC; + sram = &SRAM; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "ARM,88FR131"; + reg = <0x0>; + d-cache-line-size = <32>; // 32 bytes + i-cache-line-size = <32>; // 32 bytes + d-cache-size = <0x4000>; // L1, 16K + i-cache-size = <0x4000>; // L1, 16K + timebase-frequency = <0>; + bus-frequency = <0>; + clock-frequency = <0>; + }; + }; + + memory { + device_type = "memory"; + reg = <0x0 0x20000000>; // 512M at 0x0 + }; + + localbus@f1000000 { + #address-cells = <2>; + #size-cells = <1>; + compatible = "mrvl,lbc"; + + /* This reflects CPU decode windows setup. */ + ranges = <0x0 0x0f 0xf9300000 0x00100000 + 0x1 0x1e 0xfa000000 0x00100000 + 0x2 0x1d 0xfa100000 0x02000000 + 0x3 0x1b 0xfc100000 0x00000400>; + + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x00100000>; + bank-width = <2>; + device-width = <1>; + }; + + led@1,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "led"; + reg = <0x1 0x0 0x00100000>; + }; + + nor@2,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x2 0x0 0x02000000>; + bank-width = <2>; + device-width = <1>; + }; + + nand@3,0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0x3 0x0 0x00100000>; + bank-width = <2>; + device-width = <1>; + }; + }; + + SOC: soc88f6281@f1000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + ranges = <0x0 0xf1000000 0x00100000>; + bus-frequency = <0>; + + PIC: pic@20200 { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + reg = <0x20200 0x3c>; + compatible = "mrvl,pic"; + }; + + timer@20300 { + compatible = "mrvl,timer"; + reg = <0x20300 0x30>; + interrupts = <1>; + interrupt-parent = <&PIC>; + mrvl,has-wdt; + }; + + MPP: mpp@10000 { + #pin-cells = <2>; + compatible = "mrvl,mpp"; + reg = <0x10000 0x34>; + pin-count = <50>; + pin-map = < + 0 1 /* MPP[0]: NF_IO[2] */ + 1 1 /* MPP[1]: NF_IO[3] */ + 2 1 /* MPP[2]: NF_IO[4] */ + 3 1 /* MPP[3]: NF_IO[5] */ + 4 1 /* MPP[4]: NF_IO[6] */ + 5 1 /* MPP[5]: NF_IO[7] */ + 6 1 /* MPP[6]: SYSRST_OUTn */ + 7 0 /* MPP[7]: GPO[7] */ + 8 1 /* MPP[8]: TW_SDA */ + 9 1 /* MPP[9]: TW_SCK */ + 10 3 /* MPP[10]: UA0_TXD */ + 11 3 /* MPP[11]: UA0_RXD */ + 12 1 /* MPP[12]: SD_CLK */ + 13 1 /* MPP[13]: SD_CMD */ + 14 1 /* MPP[14]: SD_D[0] */ + 15 1 /* MPP[15]: SD_D[1] */ + 16 1 /* MPP[16]: SD_D[2] */ + 17 1 /* MPP[17]: SD_D[3] */ + 18 1 /* MPP[18]: NF_IO[0] */ + 19 1 /* MPP[19]: NF_IO[1] */ + 20 3 /* MPP[20]: GE1[0] */ + 21 3 /* MPP[21]: GE1[1] */ + 22 3 /* MPP[22]: GE1[2] */ + 23 3 /* MPP[23]: GE1[3] */ + 24 3 /* MPP[24]: GE1[4] */ + 25 3 /* MPP[25]: GE1[5] */ + 26 3 /* MPP[26]: GE1[6] */ + 27 3 /* MPP[27]: GE1[7] */ + 28 0 /* MPP[28]: GPIO[28] */ + 29 0 /* MPP[29]: GPIO[29] */ + 30 3 /* MPP[30]: GE1[10] */ + 31 3 /* MPP[31]: GE1[11] */ + 32 3 /* MPP[32]: GE1[12] */ + 33 3 /* MPP[33]: GE1[13] */ + 34 0 /* MPP[34]: GPIO[34] */ + 35 2 /* MPP[35]: TDM_CH0_TX_QL */ + 36 2 /* MPP[36]: TDM_SPI_CS1 */ + 37 2 /* MPP[37]: TDM_CH2_TX_QL */ + 38 2 /* MPP[38]: TDM_CH2_RX_QL */ + 39 4 /* MPP[39]: AU_I2SBCLK */ + 40 4 /* MPP[40]: AU_I2SDO */ + 41 4 /* MPP[41]: AU_I2SLRCLK */ + 42 4 /* MPP[42]: AU_I2SMCLK */ + 43 4 /* MPP[43]: AU_I2SDI */ + 44 4 /* MPP[44]: AU_EXTCLK */ + 45 2 /* MPP[45]: TDM_PCLK */ + 46 2 /* MPP[46]: TDM_FS */ + 47 2 /* MPP[47]: TDM_DRX */ + 48 2 /* MPP[48]: TDM_DTX */ + 49 2>; /* MPP[49]: TDM_CH0_TX_QL */ + }; + + GPIO: gpio@10100 { + #gpio-cells = <3>; + compatible = "mrvl,gpio"; + reg = <0x10100 0x20>; + gpio-controller = <1>; + pin-count = <50>; + interrupts = <35 36 37 38 39 40 41>; + interrupt-parent = <&PIC>; + + + + + }; + + rtc@10300 { + compatible = "mrvl,rtc"; + reg = <0x10300 0x08>; + }; + + twsi@11000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "mrvl,twsi"; + reg = <0x11000 0x20>; + interrupts = <43>; + interrupt-parent = <&PIC>; + }; + + mdio0: mdio@72000 { + device_type = "mdio"; + compatible = "mrvl,mdio"; + reg = <72000 20>; + #address-cells = <1>; + #size-cells = <0>; + + phy0: ethernet-phy@0 { + reg = <0>; + device_type = "ethernet-phy"; + }; + + + }; + + mdio1: mdio@76000 { + device_type = "mdio"; + compatible = "mrvl,mdio"; + reg = <76000 20>; + #address-cells = <1>; + #size-cells = <0>; + + phy1: ethernet-phy@0 { + reg = <1>; + device_type = "ethernet-phy"; + }; + + + }; + + enet0: ethernet@72000 { + #address-cells = <1>; + #size-cells = <1>; + model = "V2"; + compatible = "mrvl,ge"; + reg = <0x72000 0x2000>; + ranges = <0x0 0x72000 0x2000>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <12 13 14 11 46>; + interrupt-parent = <&PIC>; + phy-handle = <&phy0>; + + }; + + enet1: ethernet@76000 { + #address-cells = <1>; + #size-cells = <1>; + model = "V2"; + compatible = "mrvl,ge"; + reg = <0x76000 0x2000>; + ranges = <0x0 0x76000 0x2000>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <16 17 18 15 47>; + interrupt-parent = <&PIC>; + phy-handle = <&phy1>; + /* + mdio@1 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "mrvl,mdio"; + }; + */ + }; + + serial0: serial@12000 { + compatible = "ns16550"; + reg = <0x12000 0x20>; + reg-shift = <2>; + clock-frequency = <0>; + interrupts = <33>; + interrupt-parent = <&PIC>; + }; + /* + serial1: serial@12100 { + compatible = "ns16550"; + reg = <0x12100 0x20>; + reg-shift = <2>; + clock-frequency = <0>; + interrupts = <34>; + interrupt-parent = <&PIC>; + }; + */ + + + crypto@30000 { + compatible = "mrvl,cesa"; + reg = <0x30000 0x10000>; + interrupts = <22>; + interrupt-parent = <&PIC>; + }; + + usb@50000 { + compatible = "mrvl,usb-ehci", "usb-ehci"; + reg = <0x50000 0x1000>; + interrupts = <48 19>; + interrupt-parent = <&PIC>; + }; + + xor@60000 { + compatible = "mrvl,xor"; + reg = <0x60000 0x1000>; + interrupts = <5 6 7 8>; + interrupt-parent = <&PIC>; + }; + + sata@80000 { + compatible = "mrvl,sata"; + reg = <0x80000 0x6000>; + interrupts = <21>; + interrupt-parent = <&PIC>; + }; + sdio@90000 { + compatible = "mrvl,sdio"; + reg = <0x90000 0x134>; + interrupts = <28>; + interrupt-parent = <&PIC>; + + gpios = <&GPIO 29 1 0x00030000 /* GPIO[29]: IN_POL_LOW, IRQ (edge) */ + &GPIO 34 2 0x00000000>; /* GPIO[34]: OUT */ + + }; + }; + + SRAM: sram@fd000000 { + compatible = "mrvl,cesa-sram"; + reg = <0xfd000000 0x00100000>; + }; + + chosen { + stdin = "serial0"; + stdout = "serial0"; + }; + + + pci0: pcie@f1040000 { + compatible = "mrvl,pcie"; + device_type = "pci"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = <0xf1040000 0x2000>; + bus-range = <0 255>; + ranges = <0x02000000 0x0 0xf4000000 0xf4000000 0x0 0x04000000 + 0x01000000 0x0 0x00000000 0xf1100000 0x0 0x00100000>; + clock-frequency = <33333333>; + interrupt-parent = <&PIC>; + interrupts = <44>; + interrupt-map-mask = <0xf800 0x0 0x0 0x7>; + interrupt-map = < + /* IDSEL 0x1 */ + 0x0800 0x0 0x0 0x1 &PIC 0x9 + 0x0800 0x0 0x0 0x2 &PIC 0x9 + 0x0800 0x0 0x0 0x3 &PIC 0x9 + 0x0800 0x0 0x0 0x4 &PIC 0x9 + >; + pcie@0 { + reg = <0x0 0x0 0x0 0x0 0x0>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + ranges = <0x02000000 0x0 0xf4000000 + 0x02000000 0x0 0xf4000000 + 0x0 0x04040000 + + 0x01000000 0x0 0x0 + 0x01000000 0x0 0x0 + 0x0 0x00100000>; + }; + }; + }; diff -prN --exclude=.svn head_r221725/sys/dev/mge/if_mge.c head_r221525/sys/dev/mge/if_mge.c *** head_r221725/sys/dev/mge/if_mge.c Tue May 10 10:19:23 2011 --- head_r221525/sys/dev/mge/if_mge.c Fri May 6 09:35:05 2011 *************** *** 34,40 **** #endif #include <sys/cdefs.h> ! __FBSDID("$FreeBSD: head/sys/dev/mge/if_mge.c 213893 2010-10-15 14:52:11Z marius $"); #include <sys/param.h> #include <sys/systm.h> --- 34,40 ---- #endif #include <sys/cdefs.h> ! __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> diff -prN --exclude=.svn head_r221725/sys/dev/mmc/mmc.c head_r221525/sys/dev/mmc/mmc.c *** head_r221725/sys/dev/mmc/mmc.c Tue May 10 10:17:04 2011 --- head_r221525/sys/dev/mmc/mmc.c Fri May 6 09:04:27 2011 *************** static devclass_t mmc_devclass; *** 1539,1542 **** --- 1539,1543 ---- DRIVER_MODULE(mmc, at91_mci, mmc_driver, mmc_devclass, NULL, NULL); + DRIVER_MODULE(mmc, sdio, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, sdhci, mmc_driver, mmc_devclass, NULL, NULL);home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?5EABE6DCF1B84BAAA5460DD75F075C82>
