From saso.vrbinc at meis.si Tue Sep 1 06:21:46 2009 From: saso.vrbinc at meis.si (saso.vrbinc at meis.si) Date: Tue, 1 Sep 2009 13:21:46 +0200 (CEST) Subject: [Avila] CF speed slow Message-ID: <2b61de9284e0cdff6d052f9e83decf3c.squirrel@server11.hitrost.net> Greetings! I'm not sure if this is the right place for asking questions regarding the Cambria platform. However, recently we bought 7 Cambria GW2358-4 platform with preinstalled dd-wrt firmware. We attended to use CF but unfortunately dd-wrt had no support. So we switched to openWRT where CF works in "worm" speed. The average file transfer speed to CF was avg. 400kb/s and every attempt to increase it have failed. Is there any HW issue on MB? What is the bus frequency anyway? Is there any know issue with the driver? Any information would be much appreciated! best regards! From tim_harvey at yahoo.com Tue Sep 8 14:59:00 2009 From: tim_harvey at yahoo.com (Tim Harvey) Date: Tue, 8 Sep 2009 12:59:00 -0700 (PDT) Subject: [Avila] GPS access via gpsd In-Reply-To: <56afad540908310905q78d192bdpc7c2f83db29e6e94@mail.gmail.com> References: <188640.77238.qm@web82106.mail.mud.yahoo.com> <515045.33487.qm@web82103.mail.mud.yahoo.com> <56afad540908310905q78d192bdpc7c2f83db29e6e94@mail.gmail.com> Message-ID: <527714.48710.qm@web82105.mail.mud.yahoo.com> Nathan, I believe I'm seeing the same thing (also with trunk). I've found that restarting gpsd solves the issue (thus sending the commands again) but don't know the root cause. Without the extra messages for the trimble you won't get most of the desired sentences from the GPS. Tim ________________________________ From: Nathan Wharton To: Tim Harvey Cc: avila at lists.gateworks.com Sent: Monday, August 31, 2009 9:05:34 AM Subject: Re: [Avila] GPS access via gpsd I am using a 2350 with the GPS module on openwrt trunk with those patches present. About half the time after a cold boot the added messages for trimble don't show up in the stream, only the 2 default messages. Even though the data coming from the GPS looks good, it seems that commands to the GPS must be getting garbled. I have to issue a command several times before the GPS will respond to it. On Fri, Feb 27, 2009 at 12:50 PM, Tim Harvey wrote: >ah... I see the kernel version for ixp4xx was bumped to 2.6.28 just last week and the cambria-uart patch is a part of that kernel not the kernel ver in the 8.09 release. So hopefully this will resolve my issues. > >>I'll report my findings tomorrow. > >>Thanks, > >>Tim > > > > >>----- Original Message ---- >>From: Imre Kaloz >>To: Tim Harvey ; avila at lists.gateworks.com >>Sent: Friday, February 27, 2009 8:57:14 AM >>Subject: Re: [Avila] GPS access via gpsd > >>Hello Tim, > >>On 2009.02.27. 17:41:28 Tim Harvey wrote: > >>> Has anyone had issues with the GPS module using gpsd? I'm currently using a GW2350 with on-board GPS module and it would appear that perhaps the Trimble module is using something other than 8N1 serial configuration as this is what gpsd uses (hard-coded) and looking at gpsd logs some of the messages from the gps have some duplicate characters indicative of serial misconfiguration. I'm told that Trimble often uses 7E1 but I haven't been able to confirm this. Before I go on a possible goose chase and start patching gpsd I was wondering if anyone else had seen some issues like this or had more info? I'm using gpsd 2.37 and OpenWRT 8.09 (also tried gpsd 2.38 with the same results). >> >> >>> Basically, the state of my GW2350+GPS is such that many of the messages from the GPS are tossed by gpsd and it takes many minutes to get a fix and often the fix is lost or I get funny data. >>> >>> I've used the 'GPS module' on a GW2348-4 in the past and don't recall these issues but perhaps I just didn't notice them back then. It was also a different version of gpsd, and a completely different toolchain/build. Are there any differences in the part used with the GPS module on the avila and the one on the Cambria? > >>Please try trunk instead of 8.09, and let me know if everything works there. > > >>Regards, > >>Imre > > >>_______________________________________________ >>Avila mailing list >Avila at lists.gateworks.com >http://lists.gateworks.com/cgi-bin/mailman/listinfo/avila > -------------- next part -------------- An HTML attachment was scrubbed... URL: http://lists.gateworks.com/pipermail/avila/attachments/20090908/aae1d397/attachment.htm From clang at gateworks.com Thu Sep 10 03:26:33 2009 From: clang at gateworks.com (clang at gateworks.com) Date: Thu, 10 Sep 2009 08:26:33 +0000 Subject: [Avila] Gateworks SVN Commit r89 - in trunk/marvell_owrt_8.09: . bin target/linux/marvell target/linux/marvell/patches-2.6.22 Message-ID: Author: clang Date: 2009-09-10 08:26:25 +0000 (Thu, 10 Sep 2009) New Revision: 89 Added: trunk/marvell_owrt_8.09/bin/ trunk/marvell_owrt_8.09/bin/openwrt-marvell-squashfs.img trunk/marvell_owrt_8.09/bin/openwrt-marvell-uImage trunk/marvell_owrt_8.09/target/linux/marvell/patches-2.6.22/ trunk/marvell_owrt_8.09/target/linux/marvell/patches-2.6.22/001-gw2367-support.patch Modified: trunk/marvell_owrt_8.09/target/linux/marvell/config-default Log: Add Support for the GW2367, along with the DX107 Switch. The switch supports vlan's and can be configured using stadard vconfig commands and a couple of entries in /proc/net/mv_switch. Add binaries for ease of customers using standard binaries. Added: trunk/marvell_owrt_8.09/bin/openwrt-marvell-squashfs.img =================================================================== (Binary files differ) Property changes on: trunk/marvell_owrt_8.09/bin/openwrt-marvell-squashfs.img ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/marvell_owrt_8.09/bin/openwrt-marvell-uImage =================================================================== (Binary files differ) Property changes on: trunk/marvell_owrt_8.09/bin/openwrt-marvell-uImage ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Modified: trunk/marvell_owrt_8.09/target/linux/marvell/config-default =================================================================== --- trunk/marvell_owrt_8.09/target/linux/marvell/config-default 2009-08-26 18:33:09 UTC (rev 88) +++ trunk/marvell_owrt_8.09/target/linux/marvell/config-default 2009-09-10 08:26:25 UTC (rev 89) @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.22.18-mv -# Fri Aug 14 19:22:54 2009 +# Wed Sep 9 11:48:35 2009 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -51,9 +51,7 @@ CONFIG_SYSFS_DEPRECATED=y # CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y -CONFIG_INITRAMFS_SOURCE="/usr/src/openwrt/new_marvell/8.09/build_dir/arm/root-marvell /usr/src/openwrt/new_marvell/8.09/target/linux/generic-2.6/image/initramfs-base-files.txt" -CONFIG_INITRAMFS_ROOT_UID=0 -CONFIG_INITRAMFS_ROOT_GID=0 +CONFIG_INITRAMFS_SOURCE="" CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_SYSCTL=y # CONFIG_EMBEDDED is not set @@ -170,7 +168,7 @@ # CONFIG_MV_INCLUDE_NAND is not set # CONFIG_MV_INCLUDE_INTEG_SATA is not set CONFIG_MV_INCLUDE_GIG_ETH=y -# CONFIG_MV_INCLUDE_SPI is not set +CONFIG_MV_INCLUDE_SPI=y CONFIG_MV_GPP_MAX_PINS=32 CONFIG_MV_DCACHE_SIZE=0x8000 CONFIG_MV_ICACHE_SIZE=0x8000 @@ -178,7 +176,8 @@ # # Feroceon SoC MTD support # -# CONFIG_MV_FLASH_CTRL is not set +CONFIG_MV_FLASH_CTRL=y +CONFIG_MV_INCLUDE_SFLASH_MTD=y CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y CONFIG_USE_DSP=y CONFIG_L2_CACHE_ENABLE=y @@ -232,6 +231,7 @@ # # Control and Statistics # +CONFIG_MV_SWITCH_PROC=y # CONFIG_MV_ETH_PROC is not set # CONFIG_MV_ETH_STATS is not set @@ -530,7 +530,7 @@ # CONFIG_TIPC is not set # CONFIG_ATM is not set CONFIG_BRIDGE=y -# CONFIG_VLAN_8021Q is not set +CONFIG_VLAN_8021Q=y # CONFIG_DECNET is not set CONFIG_LLC=y # CONFIG_LLC2 is not set Added: trunk/marvell_owrt_8.09/target/linux/marvell/patches-2.6.22/001-gw2367-support.patch =================================================================== --- trunk/marvell_owrt_8.09/target/linux/marvell/patches-2.6.22/001-gw2367-support.patch (rev 0) +++ trunk/marvell_owrt_8.09/target/linux/marvell/patches-2.6.22/001-gw2367-support.patch 2009-09-10 08:26:25 UTC (rev 89) @@ -0,0 +1,5305 @@ +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/mach-feroceon-mv78xx0/config/mvRules.mk linux-2.6.22.18-mv/arch/arm/mach-feroceon-mv78xx0/config/mvRules.mk +--- linux-2.6.22.18-mv-clean/arch/arm/mach-feroceon-mv78xx0/config/mvRules.mk 2008-09-03 18:34:16.000000000 -0700 ++++ linux-2.6.22.18-mv/arch/arm/mach-feroceon-mv78xx0/config/mvRules.mk 2009-08-26 10:12:50.000000000 -0700 +@@ -76,6 +76,9 @@ + HAL_TS_DIR = $(HAL_DIR)/ts + endif + ++HAL_PRESTERA_DIR = $(HAL_DIR)/prestera ++ ++ + # Environment components + FAM_DIR = $(LSP_DIR)/mv78xx0_family + SOC_DEVICE_DIR = $(FAM_DIR)/device +@@ -171,3 +174,8 @@ + ifeq ($(CONFIG_MV78XX0_Z0),y) + EXTRA_CFLAGS += -DMV78XX0_Z0 -DMV_BRIDGE_SYNC_REORDER + endif ++ ++ifeq ($(CONFIG_MV_PRESTERA_SWITCH),y) ++EXTRA_CFLAGS += -DMV_PRESTERA_SWITCH ++endif ++ +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/mach-feroceon-mv78xx0/core.c linux-2.6.22.18-mv/arch/arm/mach-feroceon-mv78xx0/core.c +--- linux-2.6.22.18-mv-clean/arch/arm/mach-feroceon-mv78xx0/core.c 2009-02-13 13:28:53.000000000 -0800 ++++ linux-2.6.22.18-mv/arch/arm/mach-feroceon-mv78xx0/core.c 2009-08-25 14:38:00.000000000 -0700 +@@ -90,6 +90,11 @@ + #endif + unsigned int support_wait_for_interrupt = 0x1; + ++#ifdef MV_PRESTERA_SWITCH ++extern MV_STATUS __init mvSwitchDecodingInit(void); ++#endif ++ ++ + u32 mvTclk = 166666667; + u32 mvSysclk = 200000000; + u32 mvIsUsbHost = 1; +@@ -258,8 +263,8 @@ + ****************************************************************************/ + + static struct mv64xxx_i2c_pdata mv78x00_i2c_pdata = { +- .freq_m = 5, /* assumes 200 MHz TCLK */ +- .freq_n = 4, ++ .freq_m = 6, /* assumes 200 MHz TCLK */ ++ .freq_n = 5, + .timeout = 1000, /* Default timeout of 1 second */ + }; + +@@ -326,6 +331,11 @@ + + serial_initialize(); + ++#ifdef MV_PRESTERA_SWITCH ++ /* init switch decoding */ ++ mvSwitchDecodingInit(); ++#endif ++ + /* At this point, the CPU windows are configured according to default definitions in mvSysHwConfig.h */ + /* and cpuAddrWinMap table in mvCpuIf.c. Now it's time to change defaults for each platform. */ + mvCpuIfAddrDecShow(whoAmI()); +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/mach-feroceon-mv78xx0/Makefile linux-2.6.22.18-mv/arch/arm/mach-feroceon-mv78xx0/Makefile +--- linux-2.6.22.18-mv-clean/arch/arm/mach-feroceon-mv78xx0/Makefile 2008-09-10 12:05:28.000000000 -0700 ++++ linux-2.6.22.18-mv/arch/arm/mach-feroceon-mv78xx0/Makefile 2009-09-04 09:46:21.000000000 -0700 +@@ -20,7 +20,7 @@ + + OSSERVICES_OBJS = $(OSSERV_DIR)/mvOs.o + +-HAL_OBJS = $(HAL_RTC_DIR)/mvDS133x.o $(HAL_DRAM_SPD_DIR)/mvSpd.o \ ++HAL_OBJS = $(HAL_DRAM_SPD_DIR)/mvSpd.o \ + $(HAL_CNTMR_DIR)/mvCntmr.o $(HAL_TWSI_DIR)/mvTwsi.o \ + $(HAL_UART_DIR)/mvUart.o $(HAL_GPP_DIR)/mvGpp.o \ + $(HAL_DRAM_DIR)/mvDramIf.o \ +@@ -51,8 +51,8 @@ + $(HAL_QD_DIR)/src/msapi/gtPIRL2.o + + LSP_OBJS = $(LSP_DIR)/core.o $(LSP_DIR)/irq.o $(LSP_DIR)/time.o \ +- $(LSP_DIR)/leds.o $(LSP_DIR)/sysmap.o $(LSP_DIR)/rtc.o \ +- $(LSP_DIR)/export.o ++ $(LSP_DIR)/leds.o $(LSP_DIR)/sysmap.o \ ++ $(LSP_DIR)/export.o + + obj-y := feroceon.o + feroceon-objs :=$(LSP_OBJS) $(COMMON_OBJS) $(OSSERVICES_OBJS) $(HAL_OBJS) \ +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/mach-feroceon-mv78xx0/mv78xx0_family/boardEnv/mvBoardEnvLib.c linux-2.6.22.18-mv/arch/arm/mach-feroceon-mv78xx0/mv78xx0_family/boardEnv/mvBoardEnvLib.c +--- linux-2.6.22.18-mv-clean/arch/arm/mach-feroceon-mv78xx0/mv78xx0_family/boardEnv/mvBoardEnvLib.c 2009-02-12 09:15:56.000000000 -0800 ++++ linux-2.6.22.18-mv/arch/arm/mach-feroceon-mv78xx0/mv78xx0_family/boardEnv/mvBoardEnvLib.c 2009-09-04 12:34:01.000000000 -0700 +@@ -526,6 +526,8 @@ + MV_BOARD_MAC_SPEED mvBoardMacSpeedGet(MV_U32 ethPortNum) + { + MV_32 boardId = mvBoardIdGet(); ++ if (ethPortNum == 2) ++ return BOARD_MAC_SPEED_1000M; + if (boardId == RD_78XX0_H3C_ID) + /* RD-H3C 1145 works in RGMII to SGMII mode which disable speed AN, + force speed to 1000*/ +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/mach-feroceon-mv78xx0/mv78xx0_family/boardEnv/mvBoardEnvSpec.h linux-2.6.22.18-mv/arch/arm/mach-feroceon-mv78xx0/mv78xx0_family/boardEnv/mvBoardEnvSpec.h +--- linux-2.6.22.18-mv-clean/arch/arm/mach-feroceon-mv78xx0/mv78xx0_family/boardEnv/mvBoardEnvSpec.h 2009-02-09 21:47:18.000000000 -0800 ++++ linux-2.6.22.18-mv/arch/arm/mach-feroceon-mv78xx0/mv78xx0_family/boardEnv/mvBoardEnvSpec.h 2009-08-14 18:18:51.000000000 -0700 +@@ -155,19 +155,19 @@ + /* MPP[10] = GE_RXD2 MPP[11] = GE_RXD3 */ + /* MPP[12] = GPIO[12] - USB1_VBUS MPP[13] = MV_SYS_RST */ + /* MPP[14] = SATA1_ACT MPP[15] = SATA2_ACT */ +-#define DB_78XX0_MPP8_15 0x00302222 ++#define DB_78XX0_MPP8_15 0x22222222 + + /* MPP[16] = UART2_TXD MPP[17] = UART2_RXD */ + /* MPP[18] = UART0_CTS MPP[19] = UART0_RTS */ + /* MPP[20] = UART1_CTS MPP[21] = UART1_RTS */ + /* MPP[22] = UART3_TXD MPP[23] = UART3_RXD */ +-#define DB_78XX0_MPP16_23 0x00000044 ++#define DB_78XX0_MPP16_23 0x00000000 + + /* MPP[24] = DC MPP[25] = DC */ + /* MPP[26] = DC MPP[26] = DC */ + /* MPP[28] = DC MPP[27] = DC */ + /* MPP[30] = DC MPP[28] = DC */ +-#define DB_78XX0_MPP24_31 0x00000000 ++#define DB_78XX0_MPP24_31 0x51441111 + + /* MPP[0] = GE_TXCLK MPP[1] = GE_TXCTL */ + /* MPP[2] = GE_RXCTL MPP[3] = GE_RXCLK */ +@@ -179,25 +179,25 @@ + /* MPP[10] = GE_RXD2 MPP[11] = GE_RXD3 */ + /* MPP[12] = GPIO[12] - USB1_VBUS MPP[13] = MV_SYS_RST */ + /* MPP[14] = SATA1_ACT MPP[15] = SATA2_ACT */ +-#define DB_78200_MPP8_15 0x00302222 ++#define DB_78200_MPP8_15 0x22222222 + + /* MPP[16] = UART2_TXD MPP[17] = UART2_RXD */ + /* MPP[18] = UART0_CTS MPP[19] = UART0_RTS */ + /* MPP[20] = UART1_CTS MPP[21] = UART1_RTS */ + /* MPP[22] = UART3_TXD MPP[23] = UART3_RXD */ +-#define DB_78200_MPP16_23 0x00000044 ++#define DB_78200_MPP16_23 0x22222222 + + /* MPP[24] = DC MPP[25] = DC */ + /* MPP[26] = DC MPP[26] = DC */ + /* MPP[28] = DC MPP[27] = DC */ + /* MPP[30] = DC MPP[28] = DC */ +-#define DB_78200_MPP24_31 0x11111111 ++#define DB_78200_MPP24_31 0x00000000 + + /* MPP[24] = DC MPP[25] = DC */ + /* MPP[26] = DC MPP[26] = DC */ + /* MPP[28] = DC MPP[27] = DC */ + /* MPP[30] = DC MPP[28] = DC */ +-#define DB_78200_MPP32_39 0x11111111 ++#define DB_78200_MPP32_39 0x51441111 + + #define DB_78XX0_HAS_PTP_BRIDGE 1 + +@@ -326,7 +326,7 @@ + #define MV_BOARD_RTC_I2C_ADDR_TYPE ADDR7_BIT + + /* Ethernet stuff */ +-#define MV78XX0_ETH_MAX_PORTS 2 ++#define MV78XX0_ETH_MAX_PORTS 4 + #define MV78200_ETH_MAX_PORTS 4 + + #define BOARD_ETH_START_PORT_NUM 0 +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/common/mvTypes.h linux-2.6.22.18-mv/arch/arm/plat-feroceon/common/mvTypes.h +--- linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/common/mvTypes.h 2008-07-29 11:30:50.000000000 -0700 ++++ linux-2.6.22.18-mv/arch/arm/plat-feroceon/common/mvTypes.h 2009-08-30 23:30:24.000000000 -0700 +@@ -117,12 +117,18 @@ + #define NULL ((void*)0) + #endif + ++#undef IN ++#define IN ++#undef OUT ++#define OUT ++#undef INOUT ++#define INOUT + + #ifndef MV_ASMLANGUAGE + /* typedefs */ + + typedef char MV_8; +-typedef unsigned char MV_U8; ++typedef unsigned char MV_U8, *MV_U8_PTR; + + typedef int MV_32; + typedef unsigned int MV_U32; +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/Kconfig linux-2.6.22.18-mv/arch/arm/plat-feroceon/Kconfig +--- linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/Kconfig 2008-07-29 12:07:10.000000000 -0700 ++++ linux-2.6.22.18-mv/arch/arm/plat-feroceon/Kconfig 2009-08-26 10:15:49.000000000 -0700 +@@ -134,7 +134,6 @@ + ---help--- + Please don't change this configs unless you know what you are doing. + +- + endmenu + + config MV_GPP_MAX_PINS +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/Kconfig linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/Kconfig +--- linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/Kconfig 2008-09-07 11:51:14.000000000 -0700 ++++ linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/Kconfig 2009-09-09 11:47:58.000000000 -0700 +@@ -119,6 +119,12 @@ + if MV_ETHERNET + menu "Control and Statistics" + ++config MV_SWITCH_PROC ++ bool "Support switch proc FS " ++ default y ++ ---help--- ++ Helper file to configure vlans on Prestera Switch. ++ + config MV_ETH_PROC + bool "Support eth proc FS " + default y +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/Makefile linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/Makefile +--- linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/Makefile 2008-07-29 12:03:44.000000000 -0700 ++++ linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/Makefile 2009-09-09 11:46:44.000000000 -0700 +@@ -13,8 +13,9 @@ + include $(TOPDIR)/arch/arm/mach-feroceon-mv78xx0/config/mvRules.mk + endif + +-obj-$(CONFIG_MV_ETHERNET) += mv_netdev.o mv_ethernet.o ++obj-$(CONFIG_MV_ETHERNET) += mv_netdev.o mv_ethernet.o #mv_prestera_switch.o + obj-$(CONFIG_MV_ETH_PROC) += mv_eth_proc.o ++obj-$(CONFIG_MV_SWITCH_PROC) += mv_switch_proc.o + obj-$(CONFIG_MV_GATEWAY) += mv_gateway.o + obj-$(CONFIG_MV_GTW_IGMP) += mv_gtw_igmp.o + +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_ethernet.c linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_ethernet.c +--- linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_ethernet.c 2008-09-08 10:19:12.000000000 -0700 ++++ linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_ethernet.c 2009-09-09 21:04:23.000000000 -0700 +@@ -1,56 +1,56 @@ +-/******************************************************************************* +-Copyright (C) Marvell International Ltd. and its affiliates +- +-This software file (the "File") is owned and distributed by Marvell +-International Ltd. and/or its affiliates ("Marvell") under the following +-alternative licensing terms. Once you have made an election to distribute the +-File under one of the following license alternatives, please (i) delete this +-introductory statement regarding license alternatives, (ii) delete the two +-license alternatives that you have not elected to use and (iii) preserve the +-Marvell copyright notice above. +- +- +-******************************************************************************** +-Marvell GPL License Option +- +-If you received this File from Marvell, you may opt to use, redistribute and/or +-modify this File in accordance with the terms and conditions of the General +-Public License Version 2, June 1991 (the "GPL License"), a copy of which is +-available along with the File in the license.txt file or by writing to the Free +-Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +-on the worldwide web at http://www.gnu.org/licenses/gpl.txt. +- +-THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +-WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +-DISCLAIMED. The GPL License provides additional details about this warranty +-disclaimer. +-*******************************************************************************/ +- +-#include "mvCommon.h" /* Should be included before mvSysHwConfig */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "mvOs.h" +-#include "dbg-trace.h" +-#include "mvSysHwConfig.h" +-#include "eth/mvEth.h" +-#include "eth-phy/mvEthPhy.h" +-#include "ctrlEnv/sys/mvSysGbe.h" +- +-#include "mv_netdev.h" +-#ifdef CONFIG_MV78200 +-#include "mv78200/mvSemaphore.h" +-#endif +- ++/******************************************************************************* ++Copyright (C) Marvell International Ltd. and its affiliates ++ ++This software file (the "File") is owned and distributed by Marvell ++International Ltd. and/or its affiliates ("Marvell") under the following ++alternative licensing terms. Once you have made an election to distribute the ++File under one of the following license alternatives, please (i) delete this ++introductory statement regarding license alternatives, (ii) delete the two ++license alternatives that you have not elected to use and (iii) preserve the ++Marvell copyright notice above. ++ ++ ++******************************************************************************** ++Marvell GPL License Option ++ ++If you received this File from Marvell, you may opt to use, redistribute and/or ++modify this File in accordance with the terms and conditions of the General ++Public License Version 2, June 1991 (the "GPL License"), a copy of which is ++available along with the File in the license.txt file or by writing to the Free ++Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or ++on the worldwide web at http://www.gnu.org/licenses/gpl.txt. ++ ++THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED ++WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY ++DISCLAIMED. The GPL License provides additional details about this warranty ++disclaimer. ++*******************************************************************************/ ++ ++#include "mvCommon.h" /* Should be included before mvSysHwConfig */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mvOs.h" ++#include "dbg-trace.h" ++#include "mvSysHwConfig.h" ++#include "eth/mvEth.h" ++#include "eth-phy/mvEthPhy.h" ++#include "ctrlEnv/sys/mvSysGbe.h" ++ ++#include "mv_netdev.h" ++#ifdef CONFIG_MV78200 ++#include "mv78200/mvSemaphore.h" ++#endif ++ + int mv_eth_read_mii(unsigned int portNumber, unsigned int MIIReg, unsigned int* value) + { + unsigned long flags; +@@ -58,13 +58,13 @@ + MV_STATUS status; + + spin_lock_irqsave(&mii_lock, flags); +-#ifdef CONFIG_MV78200 +- mvSemaLock(MV_SEMA_SMI); +-#endif ++#ifdef CONFIG_MV78200 ++ mvSemaLock(MV_SEMA_SMI); ++#endif + status = mvEthPhyRegRead(mvBoardPhyAddrGet(portNumber), MIIReg, &tmp); +-#ifdef CONFIG_MV78200 +- mvSemaUnlock(MV_SEMA_SMI); +-#endif ++#ifdef CONFIG_MV78200 ++ mvSemaUnlock(MV_SEMA_SMI); ++#endif + spin_unlock_irqrestore(&mii_lock, flags); + *value = tmp; + if (status == MV_OK) +@@ -82,13 +82,13 @@ + + spin_lock_irqsave(&mii_lock, flags); + tmp = (unsigned short)data; +-#ifdef CONFIG_MV78200 +- mvSemaLock(MV_SEMA_SMI); +-#endif ++#ifdef CONFIG_MV78200 ++ mvSemaLock(MV_SEMA_SMI); ++#endif + status = mvEthPhyRegWrite(mvBoardPhyAddrGet(portNumber), MIIReg, tmp); +-#ifdef CONFIG_MV78200 +- mvSemaUnlock(MV_SEMA_SMI); +-#endif ++#ifdef CONFIG_MV78200 ++ mvSemaUnlock(MV_SEMA_SMI); ++#endif + spin_unlock_irqrestore(&mii_lock, flags); + + if (status == MV_OK) +@@ -127,7 +127,8 @@ + static int mv_eth_start( struct net_device *dev ) + { + mv_eth_priv *priv = MV_ETH_PRIV(dev); +- ++ unsigned long flags; ++ int temp = 45; + ETH_DBG( ETH_DBG_LOAD, ("%s: starting... ", dev->name ) ); + + /* in default link is down */ +@@ -165,8 +166,15 @@ + + ETH_DBG( ETH_DBG_LOAD, ("%s: start ok\n", dev->name) ); + ++ if (priv->port == 2) ++ { ++ eth_check_link_status(dev); ++ } ++ + printk( KERN_NOTICE "%s: started\n", dev->name ); + ++ ++ + return 0; + + error: +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_netdev.c linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_netdev.c +--- linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_netdev.c 2008-09-10 13:25:00.000000000 -0700 ++++ linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_netdev.c 2009-09-09 22:04:19.000000000 -0700 +@@ -1,52 +1,53 @@ +-/******************************************************************************* +-Copyright (C) Marvell International Ltd. and its affiliates +- +-This software file (the "File") is owned and distributed by Marvell +-International Ltd. and/or its affiliates ("Marvell") under the following +-alternative licensing terms. Once you have made an election to distribute the +-File under one of the following license alternatives, please (i) delete this +-introductory statement regarding license alternatives, (ii) delete the two +-license alternatives that you have not elected to use and (iii) preserve the +-Marvell copyright notice above. +- +- +-******************************************************************************** +-Marvell GPL License Option +- +-If you received this File from Marvell, you may opt to use, redistribute and/or +-modify this File in accordance with the terms and conditions of the General +-Public License Version 2, June 1991 (the "GPL License"), a copy of which is +-available along with the File in the license.txt file or by writing to the Free +-Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +-on the worldwide web at http://www.gnu.org/licenses/gpl.txt. +- +-THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +-WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +-DISCLAIMED. The GPL License provides additional details about this warranty +-disclaimer. +-*******************************************************************************/ +- +-#include "mvCommon.h" /* Should be included before mvSysHwConfig */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "mvOs.h" +-#include "dbg-trace.h" +-#include "mvSysHwConfig.h" +-#include "eth/mvEth.h" +-#include "eth-phy/mvEthPhy.h" +-#include "ctrlEnv/sys/mvSysGbe.h" +- +-#include "mv_netdev.h" ++/******************************************************************************* ++Copyright (C) Marvell International Ltd. and its affiliates ++ ++This software file (the "File") is owned and distributed by Marvell ++International Ltd. and/or its affiliates ("Marvell") under the following ++alternative licensing terms. Once you have made an election to distribute the ++File under one of the following license alternatives, please (i) delete this ++introductory statement regarding license alternatives, (ii) delete the two ++license alternatives that you have not elected to use and (iii) preserve the ++Marvell copyright notice above. ++ ++ ++******************************************************************************** ++Marvell GPL License Option ++ ++If you received this File from Marvell, you may opt to use, redistribute and/or ++modify this File in accordance with the terms and conditions of the General ++Public License Version 2, June 1991 (the "GPL License"), a copy of which is ++available along with the File in the license.txt file or by writing to the Free ++Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or ++on the worldwide web at http://www.gnu.org/licenses/gpl.txt. ++ ++THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED ++WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY ++DISCLAIMED. The GPL License provides additional details about this warranty ++disclaimer. ++*******************************************************************************/ ++ ++#include "mvCommon.h" /* Should be included before mvSysHwConfig */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mvOs.h" ++#include "dbg-trace.h" ++#include "mvSysHwConfig.h" ++#include "eth/mvEth.h" ++#include "eth-phy/mvEthPhy.h" ++#include "ctrlEnv/sys/mvSysGbe.h" ++ ++#include "mv_netdev.h" + + static int __init mv_eth_init_module( void ); + static void __exit mv_eth_exit_module( void ); +@@ -55,26 +56,26 @@ + MODULE_DESCRIPTION("Marvell Ethernet Driver - www.marvell.com"); + MODULE_AUTHOR("Dmitri Epshtein "); + MODULE_LICENSE("GPL"); +- +-extern u8 mvMacAddr[CONFIG_MV_ETH_PORTS_NUM][6]; +-extern u16 mvMtu[CONFIG_MV_ETH_PORTS_NUM]; +- +-int mv_eth_rxq_desc[MV_ETH_RX_Q_NUM]; +-int mv_eth_txq_desc[MV_ETH_TX_Q_NUM]; +- +-int mv_eth_rx_desc_total = 0; +-int mv_eth_tx_desc_total = 0; ++ ++extern u8 mvMacAddr[CONFIG_MV_ETH_PORTS_NUM][6]; ++extern u16 mvMtu[CONFIG_MV_ETH_PORTS_NUM]; ++ ++int mv_eth_rxq_desc[MV_ETH_RX_Q_NUM]; ++int mv_eth_txq_desc[MV_ETH_TX_Q_NUM]; ++ ++int mv_eth_rx_desc_total = 0; ++int mv_eth_tx_desc_total = 0; + + int mv_eth_tx_done_quota = 16; + +-mv_eth_priv** mv_eth_ports; ++mv_eth_priv** mv_eth_ports; + int mv_eth_ports_num = 0; + +-struct net_device** mv_net_devs; ++struct net_device** mv_net_devs; + int mv_net_devs_num = 0; +- +-spinlock_t mii_lock = SPIN_LOCK_UNLOCKED; +-spinlock_t nfp_lock = SPIN_LOCK_UNLOCKED; ++ ++spinlock_t mii_lock = SPIN_LOCK_UNLOCKED; ++spinlock_t nfp_lock = SPIN_LOCK_UNLOCKED; + + void eth_print_irq_status(mv_eth_priv *priv); + +@@ -132,72 +133,72 @@ + + static int eth_get_config(mv_eth_priv* priv, MV_U8* mac_addr) + { +- char* mac_str = NULL; +- u8 zero_mac[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; ++ char* mac_str = NULL; ++ u8 zero_mac[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + int mtu; + + switch(priv->port) + { + case 0: +- if (mvMtu[0] != 0) +- mtu = mvMtu[0]; +- else +- mtu = CONFIG_MV_ETH_0_MTU; +- /* use default MAC address from Kconfig only if the MAC address we got is all 0 */ +- if (memcmp(mvMacAddr[0], zero_mac, ETH_ALEN) == 0) +- mac_str = CONFIG_MV_ETH_0_MACADDR; +- else +- memcpy(mac_addr, mvMacAddr[0], ETH_ALEN); ++ if (mvMtu[0] != 0) ++ mtu = mvMtu[0]; ++ else ++ mtu = CONFIG_MV_ETH_0_MTU; ++ /* use default MAC address from Kconfig only if the MAC address we got is all 0 */ ++ if (memcmp(mvMacAddr[0], zero_mac, ETH_ALEN) == 0) ++ mac_str = CONFIG_MV_ETH_0_MACADDR; ++ else ++ memcpy(mac_addr, mvMacAddr[0], ETH_ALEN); + break; + + #if (CONFIG_MV_ETH_PORTS_NUM > 1) + case 1: +- if (mvMtu[1] != 0) +- mtu = mvMtu[1]; +- else +- mtu = CONFIG_MV_ETH_1_MTU; +- /* use default MAC address from Kconfig only if the MAC address we got is all 0 */ +- if (memcmp(mvMacAddr[1], zero_mac, ETH_ALEN) == 0) +- mac_str = CONFIG_MV_ETH_1_MACADDR; +- else +- memcpy(mac_addr, mvMacAddr[1], ETH_ALEN); ++ if (mvMtu[1] != 0) ++ mtu = mvMtu[1]; ++ else ++ mtu = CONFIG_MV_ETH_1_MTU; ++ /* use default MAC address from Kconfig only if the MAC address we got is all 0 */ ++ if (memcmp(mvMacAddr[1], zero_mac, ETH_ALEN) == 0) ++ mac_str = CONFIG_MV_ETH_1_MACADDR; ++ else ++ memcpy(mac_addr, mvMacAddr[1], ETH_ALEN); + break; + #endif /* CONFIG_MV_ETH_PORTS_NUM > 1 */ + + #if (CONFIG_MV_ETH_PORTS_NUM > 2) + case 2: +- if (mvMtu[2] != 0) +- mtu = mvMtu[2]; +- else +- mtu = CONFIG_MV_ETH_2_MTU; +- /* use default MAC address from Kconfig only if the MAC address we got is all 0 */ +- if (memcmp(mvMacAddr[2], zero_mac, ETH_ALEN) == 0) +- mac_str = CONFIG_MV_ETH_2_MACADDR; +- else +- memcpy(mac_addr, mvMacAddr[2], ETH_ALEN); +- break; +-#endif /* CONFIG_MV_ETH_PORTS_NUM > 2 */ +- +-#if (CONFIG_MV_ETH_PORTS_NUM > 3) +- case 3: +- if (mvMtu[3] != 0) +- mtu = mvMtu[3]; +- else +- mtu = CONFIG_MV_ETH_3_MTU; +- /* use default MAC address from Kconfig only if the MAC address we got is all 0 */ +- if (memcmp(mvMacAddr[3], zero_mac, ETH_ALEN) == 0) +- mac_str = CONFIG_MV_ETH_3_MACADDR; +- else +- memcpy(mac_addr, mvMacAddr[3], ETH_ALEN); ++ if (mvMtu[2] != 0) ++ mtu = mvMtu[2]; ++ else ++ mtu = CONFIG_MV_ETH_2_MTU; ++ /* use default MAC address from Kconfig only if the MAC address we got is all 0 */ ++ if (memcmp(mvMacAddr[2], zero_mac, ETH_ALEN) == 0) ++ mac_str = CONFIG_MV_ETH_2_MACADDR; ++ else ++ memcpy(mac_addr, mvMacAddr[2], ETH_ALEN); ++ break; ++#endif /* CONFIG_MV_ETH_PORTS_NUM > 2 */ ++ ++#if (CONFIG_MV_ETH_PORTS_NUM > 3) ++ case 3: ++ if (mvMtu[3] != 0) ++ mtu = mvMtu[3]; ++ else ++ mtu = CONFIG_MV_ETH_3_MTU; ++ /* use default MAC address from Kconfig only if the MAC address we got is all 0 */ ++ if (memcmp(mvMacAddr[3], zero_mac, ETH_ALEN) == 0) ++ mac_str = CONFIG_MV_ETH_3_MACADDR; ++ else ++ memcpy(mac_addr, mvMacAddr[3], ETH_ALEN); + break; +-#endif /* CONFIG_MV_ETH_PORTS_NUM > 3 */ ++#endif /* CONFIG_MV_ETH_PORTS_NUM > 3 */ + + default: + printk("eth_get_config: Unexpected port number %d\n", priv->port); + return -1; + } +- if ((mac_str != NULL) && (mac_addr != NULL)) +- mvMacStrToHex(mac_str, mac_addr); ++ if ((mac_str != NULL) && (mac_addr != NULL)) ++ mvMacStrToHex(mac_str, mac_addr); + + return mtu; + } +@@ -224,230 +225,230 @@ + } + printk("\n"); + } +- ++ + static INLINE struct sk_buff* eth_skb_alloc(mv_eth_priv *priv, MV_PKT_INFO* pPktInfo, +- int mtu) +-{ +- int buf_size; +- struct sk_buff *skb; +- +- /* allocate new skb */ +- /* 32(extra for cache prefetch) + 8 to align on 8B */ +- buf_size = MV_RX_BUF_SIZE(mtu) + CPU_D_CACHE_LINE_SIZE + 8; +- +- skb = dev_alloc_skb( buf_size ); +- if( !skb ) { +- ETH_DBG( ETH_DBG_RX_FILL, ("%s: rx_fill cannot allocate skb\n", dev->name) ); +- priv->eth_stat.skb_alloc_fail++; +- return NULL; +- } +- ETH_STAT(priv->eth_stat.skb_alloc_ok++); +- +- /* align the buffer on 8B */ +- if( (unsigned long)(skb->data) & 0x7 ) { +- skb_reserve( skb, 8 - ((unsigned long)(skb->data) & 0x7) ); +- } +- +- /* Most of PktInfo and BufInfo parameters left unchanged */ +- pPktInfo->osInfo = (MV_ULONG)skb; +- pPktInfo->pFrags->bufSize = MV_RX_BUF_SIZE( mtu); +- pPktInfo->pktSize = pPktInfo->pFrags->bufSize; +- pPktInfo->pFrags->bufPhysAddr = mvOsIoVirtToPhy(NULL, skb->data); +- pPktInfo->pFrags->bufVirtPtr = skb->data; +- +- return skb; +-} +- +-#ifdef CONFIG_MV_SKB_REUSE +-int eth_skb_reuse_enable = MV_ETH_SKB_REUSE_DEFAULT; +-static INLINE mv_eth_priv* eth_skb_reusable(struct sk_buff *skb) +-{ +- int i; +- +- if( (!eth_skb_reuse_enable) ) +- return NULL; +- +- if( (atomic_read(&skb->users) == 1) && +- (!skb_cloned(skb)) && +- (skb->data_len == 0) && +- (skb_shinfo(skb)->nr_frags == 0) ) +- { +- for(i=0; iname) ); ++ priv->eth_stat.skb_alloc_fail++; ++ return NULL; ++ } ++ ETH_STAT(priv->eth_stat.skb_alloc_ok++); ++ ++ /* align the buffer on 8B */ ++ if( (unsigned long)(skb->data) & 0x7 ) { ++ skb_reserve( skb, 8 - ((unsigned long)(skb->data) & 0x7) ); ++ } ++ ++ /* Most of PktInfo and BufInfo parameters left unchanged */ ++ pPktInfo->osInfo = (MV_ULONG)skb; ++ pPktInfo->pFrags->bufSize = MV_RX_BUF_SIZE( mtu); ++ pPktInfo->pktSize = pPktInfo->pFrags->bufSize; ++ pPktInfo->pFrags->bufPhysAddr = mvOsIoVirtToPhy(NULL, skb->data); ++ pPktInfo->pFrags->bufVirtPtr = skb->data; ++ ++ return skb; ++} ++ ++#ifdef CONFIG_MV_SKB_REUSE ++int eth_skb_reuse_enable = MV_ETH_SKB_REUSE_DEFAULT; ++static INLINE mv_eth_priv* eth_skb_reusable(struct sk_buff *skb) ++{ ++ int i; ++ ++ if( (!eth_skb_reuse_enable) ) ++ return NULL; ++ ++ if( (atomic_read(&skb->users) == 1) && ++ (!skb_cloned(skb)) && ++ (skb->data_len == 0) && ++ (skb_shinfo(skb)->nr_frags == 0) ) ++ { ++ for(i=0; inet_dev->mtu) + +- CPU_D_CACHE_LINE_SIZE + 8 + NET_SKB_PAD; +- +- size = SKB_DATA_ALIGN(size) + sizeof(struct sk_buff); +-/* +- printk("port=%d (%d): skb=%p, iff=%d, truesize=%d, size=%d, idx=%d\n", +- priv->port, dev->ifindex, skb, skb->iif, skb->truesize, +- size, mvStackIndex(priv->skbReusePool)); +-*/ +- if( (mvStackIndex(priv->skbReusePool) > 0) && +- (skb->truesize >= size) ) +- { +-/* printk("skb=%p is Reusable\n", skb);*/ +- return priv; +- } +- } +- } +- } +-/* printk("skb=%p is NOT reusable\n", skb); */ +- return NULL; +-} +- +-static INLINE void eth_skb_reset(struct sk_buff *skb) +-{ +- /* Taken from __kfree_skb() */ +- dst_release(skb->dst); +- skb->dst = NULL; +- +-#ifdef CONFIG_XFRM +- secpath_put(skb->sp); +-#endif /* CONFIG_XFRM */ +- +- if (skb->destructor) { +- WARN_ON(in_irq()); +- skb->destructor(skb); +- skb->destructor = NULL; +- } +- +-#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +- if(skb->nfct) +- { +- nf_conntrack_put(skb->nfct); +- skb->nfct = 0; +- } +- if(skb->nfct_reasm) +- { +- nf_conntrack_put_reasm(skb->nfct_reasm); +- skb->nfct_reasm = 0; +- } +-#endif /* CONFIG_NF_CONNTRACK || CONFIG_NF_CONNTRACK_MODULE */ +- +-#ifdef CONFIG_BRIDGE_NETFILTER +- nf_bridge_put(skb->nf_bridge); +- skb->nf_bridge = 0; +-#endif /* CONFIG_BRIDGE_NETFILTER */ +-/* XXX: IS this still necessary? - JHS */ +-#ifdef CONFIG_NET_SCHED +- skb->tc_index = 0; +-#ifdef CONFIG_NET_CLS_ACT +- skb->tc_verd = 0; +-#endif /* CONFIG_NET_CLS_ACT */ +-#endif /* CONFIG_NET_SCHED */ +- +- skb->pkt_type = 0; +- skb->iif = 0; +- skb->sk = 0; +- skb->len = 0; +- +- skb->data = skb->tail = skb->head + NET_SKB_PAD; +- +- /* align the buffer on 8B */ +- if( (unsigned long)(skb->data) & 0x7 ) { +- skb_reserve( skb, 8 - ((unsigned long)(skb->data) & 0x7) ); +- } +-} +- +-static INLINE void eth_skb_alloc_for_reuse(mv_eth_priv *priv, int mtu) +-{ +- int idx; +- MV_PKT_INFO *pPktInfo = NULL; +- struct sk_buff *skb; +- +- idx = mvStackIndex(priv->skbReusePool); +- while(idx > 0) +- { +- ETH_STAT(priv->eth_stat.skb_reuse_alloc++); +- pPktInfo = (MV_PKT_INFO*)mvStackPop(priv->skbReusePool); +- idx--; +- +- skb = eth_skb_alloc(priv, pPktInfo, mtu); +- if(skb == NULL) +- { +- /* return PktInfo to Pool */ +- mvStackPush(priv->skbReusePool, (MV_U32)pPktInfo); +- break; +- } +- mvStackPush(priv->fpRxPool, (MV_U32)pPktInfo); +- } +-} +- +-#endif /* CONFIG_MV_SKB_REUSE */ +- +-#if defined(CONFIG_MV_ETH_NFP) || defined(CONFIG_MV_SKB_REUSE) +-static INLINE MV_PKT_INFO* eth_pkt_info_get(mv_eth_priv *priv) +-{ +- MV_PKT_INFO* pkt_info = NULL; +- +- if(mvStackIndex(priv->fpRxPool) > 0) +- pkt_info = (MV_PKT_INFO*)mvStackPop(priv->fpRxPool); +- +- return pkt_info; +-} +-#endif /* CONFIG_MV_ETH_NFP || CONFIG_MV_SKB_REUSE */ +- +-static INLINE void eth_pkt_info_free(mv_eth_priv *priv, MV_PKT_INFO* pPktInfo) +-{ +-#ifdef CONFIG_MV_ETH_NFP +- if(pPktInfo->srcIdx != (char)-1) +- { ++ if (priv != NULL) ++ { ++ int size = MV_RX_BUF_SIZE( priv->net_dev->mtu) + ++ CPU_D_CACHE_LINE_SIZE + 8 + NET_SKB_PAD; ++ ++ size = SKB_DATA_ALIGN(size) + sizeof(struct sk_buff); ++/* ++ printk("port=%d (%d): skb=%p, iff=%d, truesize=%d, size=%d, idx=%d\n", ++ priv->port, dev->ifindex, skb, skb->iif, skb->truesize, ++ size, mvStackIndex(priv->skbReusePool)); ++*/ ++ if( (mvStackIndex(priv->skbReusePool) > 0) && ++ (skb->truesize >= size) ) ++ { ++/* printk("skb=%p is Reusable\n", skb);*/ ++ return priv; ++ } ++ } ++ } ++ } ++/* printk("skb=%p is NOT reusable\n", skb); */ ++ return NULL; ++} ++ ++static INLINE void eth_skb_reset(struct sk_buff *skb) ++{ ++ /* Taken from __kfree_skb() */ ++ dst_release(skb->dst); ++ skb->dst = NULL; ++ ++#ifdef CONFIG_XFRM ++ secpath_put(skb->sp); ++#endif /* CONFIG_XFRM */ ++ ++ if (skb->destructor) { ++ WARN_ON(in_irq()); ++ skb->destructor(skb); ++ skb->destructor = NULL; ++ } ++ ++#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) ++ if(skb->nfct) ++ { ++ nf_conntrack_put(skb->nfct); ++ skb->nfct = 0; ++ } ++ if(skb->nfct_reasm) ++ { ++ nf_conntrack_put_reasm(skb->nfct_reasm); ++ skb->nfct_reasm = 0; ++ } ++#endif /* CONFIG_NF_CONNTRACK || CONFIG_NF_CONNTRACK_MODULE */ ++ ++#ifdef CONFIG_BRIDGE_NETFILTER ++ nf_bridge_put(skb->nf_bridge); ++ skb->nf_bridge = 0; ++#endif /* CONFIG_BRIDGE_NETFILTER */ ++/* XXX: IS this still necessary? - JHS */ ++#ifdef CONFIG_NET_SCHED ++ skb->tc_index = 0; ++#ifdef CONFIG_NET_CLS_ACT ++ skb->tc_verd = 0; ++#endif /* CONFIG_NET_CLS_ACT */ ++#endif /* CONFIG_NET_SCHED */ ++ ++ skb->pkt_type = 0; ++ skb->iif = 0; ++ skb->sk = 0; ++ skb->len = 0; ++ ++ skb->data = skb->tail = skb->head + NET_SKB_PAD; ++ ++ /* align the buffer on 8B */ ++ if( (unsigned long)(skb->data) & 0x7 ) { ++ skb_reserve( skb, 8 - ((unsigned long)(skb->data) & 0x7) ); ++ } ++} ++ ++static INLINE void eth_skb_alloc_for_reuse(mv_eth_priv *priv, int mtu) ++{ ++ int idx; ++ MV_PKT_INFO *pPktInfo = NULL; ++ struct sk_buff *skb; ++ ++ idx = mvStackIndex(priv->skbReusePool); ++ while(idx > 0) ++ { ++ ETH_STAT(priv->eth_stat.skb_reuse_alloc++); ++ pPktInfo = (MV_PKT_INFO*)mvStackPop(priv->skbReusePool); ++ idx--; ++ ++ skb = eth_skb_alloc(priv, pPktInfo, mtu); ++ if(skb == NULL) ++ { ++ /* return PktInfo to Pool */ ++ mvStackPush(priv->skbReusePool, (MV_U32)pPktInfo); ++ break; ++ } ++ mvStackPush(priv->fpRxPool, (MV_U32)pPktInfo); ++ } ++} ++ ++#endif /* CONFIG_MV_SKB_REUSE */ ++ ++#if defined(CONFIG_MV_ETH_NFP) || defined(CONFIG_MV_SKB_REUSE) ++static INLINE MV_PKT_INFO* eth_pkt_info_get(mv_eth_priv *priv) ++{ ++ MV_PKT_INFO* pkt_info = NULL; ++ ++ if(mvStackIndex(priv->fpRxPool) > 0) ++ pkt_info = (MV_PKT_INFO*)mvStackPop(priv->fpRxPool); ++ ++ return pkt_info; ++} ++#endif /* CONFIG_MV_ETH_NFP || CONFIG_MV_SKB_REUSE */ ++ ++static INLINE void eth_pkt_info_free(mv_eth_priv *priv, MV_PKT_INFO* pPktInfo) ++{ ++#ifdef CONFIG_MV_ETH_NFP ++ if(pPktInfo->srcIdx != (char)-1) ++ { + mv_eth_priv *src_priv = mv_eth_ports[(int)pPktInfo->srcIdx]; +- +- /* Return to the NFP pool */ +-#ifdef CONFIG_MV_GATEWAY +- if(!(priv->isGtw)) ++ ++ /* Return to the NFP pool */ ++#ifdef CONFIG_MV_GATEWAY ++ if(!(priv->isGtw)) + pPktInfo->pFrags->bufPhysAddr -= ETH_MV_HEADER_SIZE; + #else + pPktInfo->pFrags->bufPhysAddr -= ETH_MV_HEADER_SIZE; +-#endif /* CONFIG_MV_GATEWAY */ +- +- mvStackPush(src_priv->fpRxPool, (MV_U32)pPktInfo); +- return; +- } +-#endif /* CONFIG_MV_ETH_NFP */ +- +- if(pPktInfo->osInfo) +- { +- struct sk_buff *skb = (struct sk_buff *)pPktInfo->osInfo; +- +-#ifdef CONFIG_MV_SKB_REUSE +- mv_eth_priv *rx_priv = eth_skb_reusable(skb); +- +- if( rx_priv != NULL) +- { +- MV_PKT_INFO* pkt_info = NULL; +- +- ETH_STAT(rx_priv->eth_stat.skb_reuse_tx++); +- pkt_info = (MV_PKT_INFO*)mvStackPop(rx_priv->skbReusePool); +- eth_skb_reset(skb); +- +- pkt_info->osInfo = (MV_ULONG)skb; +- pkt_info->pktSize = pkt_info->pFrags->bufSize; +- pkt_info->pFrags->bufPhysAddr = mvOsIoVirtToPhy(NULL, skb->data); +- pkt_info->pFrags->bufVirtPtr = skb->data; +- +- mvStackPush(rx_priv->fpRxPool, (MV_U32)pkt_info); +- pPktInfo->osInfo = 0; +- mvStackPush(priv->txPktInfoPool, (MV_U32)pPktInfo); +- return; +- } +-#endif /* CONFIG_MV_SKB_REUSE */ +- +- dev_kfree_skb_any(skb); +- ETH_STAT(priv->eth_stat.skb_free_ok++); +- pPktInfo->osInfo = 0; +- } +- mvStackPush(priv->txPktInfoPool, (MV_U32)pPktInfo); +-} ++#endif /* CONFIG_MV_GATEWAY */ ++ ++ mvStackPush(src_priv->fpRxPool, (MV_U32)pPktInfo); ++ return; ++ } ++#endif /* CONFIG_MV_ETH_NFP */ ++ ++ if(pPktInfo->osInfo) ++ { ++ struct sk_buff *skb = (struct sk_buff *)pPktInfo->osInfo; ++ ++#ifdef CONFIG_MV_SKB_REUSE ++ mv_eth_priv *rx_priv = eth_skb_reusable(skb); ++ ++ if( rx_priv != NULL) ++ { ++ MV_PKT_INFO* pkt_info = NULL; ++ ++ ETH_STAT(rx_priv->eth_stat.skb_reuse_tx++); ++ pkt_info = (MV_PKT_INFO*)mvStackPop(rx_priv->skbReusePool); ++ eth_skb_reset(skb); ++ ++ pkt_info->osInfo = (MV_ULONG)skb; ++ pkt_info->pktSize = pkt_info->pFrags->bufSize; ++ pkt_info->pFrags->bufPhysAddr = mvOsIoVirtToPhy(NULL, skb->data); ++ pkt_info->pFrags->bufVirtPtr = skb->data; ++ ++ mvStackPush(rx_priv->fpRxPool, (MV_U32)pkt_info); ++ pPktInfo->osInfo = 0; ++ mvStackPush(priv->txPktInfoPool, (MV_U32)pPktInfo); ++ return; ++ } ++#endif /* CONFIG_MV_SKB_REUSE */ ++ ++ dev_kfree_skb_any(skb); ++ ETH_STAT(priv->eth_stat.skb_free_ok++); ++ pPktInfo->osInfo = 0; ++ } ++ mvStackPush(priv->txPktInfoPool, (MV_U32)pPktInfo); ++} + /**************************************************************************************************************/ +- +- ++ ++ + #ifdef ETH_MV_TX_EN + void eth_tx_en_config(int port, int value) + { +@@ -473,29 +474,29 @@ + priv->port, priv->tx_en ? "Enabled" : "Disabled", priv->tx_en_deep); + } + +-static INLINE void eth_tx_enable(mv_eth_priv *priv, int queue) +-{ +- int ret; +- +- ret = mvEthPortTxEnable(priv->hal_priv, queue, priv->tx_en_deep); +- if(ret < 0) +- { +- ETH_STAT(priv->eth_stat.tx_en_busy++); +- } +- else +- { +- if(ret > 0) +- { +- ETH_STAT(priv->eth_stat.tx_en_wait++); +- ETH_STAT(priv->eth_stat.tx_en_wait_count += ret); +- } +- else +- { +- ETH_STAT(priv->eth_stat.tx_en_done++); +- } +- } ++static INLINE void eth_tx_enable(mv_eth_priv *priv, int queue) ++{ ++ int ret; ++ ++ ret = mvEthPortTxEnable(priv->hal_priv, queue, priv->tx_en_deep); ++ if(ret < 0) ++ { ++ ETH_STAT(priv->eth_stat.tx_en_busy++); ++ } ++ else ++ { ++ if(ret > 0) ++ { ++ ETH_STAT(priv->eth_stat.tx_en_wait++); ++ ETH_STAT(priv->eth_stat.tx_en_wait_count += ret); ++ } ++ else ++ { ++ ETH_STAT(priv->eth_stat.tx_en_done++); ++ } ++ } + } +-#endif /* ETH_MV_TX_EN */ ++#endif /* ETH_MV_TX_EN */ + /**************************************************************************************************************/ + + +@@ -534,7 +535,7 @@ + } + /**************************************************************************************************************/ + +-static void eth_check_link_status(struct net_device *dev) ++void eth_check_link_status(struct net_device *dev) + { + mv_eth_priv *priv = MV_ETH_PRIV(dev); + MV_U32 port_status = 0; +@@ -546,7 +547,9 @@ + + spin_lock(priv->lock); + +- if(port_status & ETH_LINK_UP_MASK) { ++ printk("priv->port = %i\n", priv->port); ++ ++ if(port_status & ETH_LINK_UP_MASK || priv->port == 2) { + + #ifdef ETH_MV_TX_EN + /* Disable TX Enable WA if one of Giga ports is Half Duplex or 10 Mbps */ +@@ -578,24 +581,24 @@ + } + /**************************************************************************************************************/ + +-static INLINE int eth_rx_policy(mv_eth_priv *priv) +-{ +- u32 rxq_map; +- +- rxq_map = ( ( (priv->picr & ETH_RXQ_MASK) >> ETH_CAUSE_RX_READY_OFFSET) | +- ( (priv->picr & ETH_RXQ_RES_MASK) >> ETH_CAUSE_RX_ERROR_OFFSET) ); +- +- return (fls(rxq_map) - 1); +-} ++static INLINE int eth_rx_policy(mv_eth_priv *priv) ++{ ++ u32 rxq_map; ++ ++ rxq_map = ( ( (priv->picr & ETH_RXQ_MASK) >> ETH_CAUSE_RX_READY_OFFSET) | ++ ( (priv->picr & ETH_RXQ_RES_MASK) >> ETH_CAUSE_RX_ERROR_OFFSET) ); ++ ++ return (fls(rxq_map) - 1); ++} + static INLINE int eth_tx_policy(mv_eth_priv *priv, struct sk_buff *skb) + { + int queue; + + #if (MV_ETH_TX_Q_NUM > 1) +- if( ip_hdr(skb) ) ++ if( ip_hdr(skb) ) + { + /* get queue from TOS value */ +- char prio; ++ char prio; + + /* Map higher values of PRECEDENCE field to existing TX queues */ + prio = ip_hdr(skb)->tos >> 5; +@@ -605,8 +608,8 @@ + queue = prio - (8 - MV_ETH_TX_Q_NUM); + } + else +-#endif /* (MV_ETH_TX_Q_NUM > 1) */ +- { ++#endif /* (MV_ETH_TX_Q_NUM > 1) */ ++ { + /* no multiqueue. all packets go to one default queue. */ + queue = ETH_DEF_TXQ; + } +@@ -616,56 +619,56 @@ + /**************************************************************************************************************/ + + +- +-/*********************************************************** +- * eth_tx_done -- * +- * release transmitted packets. interrupt context. * +- ***********************************************************/ +-static INLINE u32 eth_tx_done(mv_eth_priv *priv, int queue) +-{ +- MV_PKT_INFO* pPktInfo; +- u32 count = 0; +- +- ETH_DBG( ETH_DBG_TX_DONE, ("eth%s: tx-done ", priv->port) ); +- priv->eth_stat.tx_done_events++; +- +- /* release the transmitted packets */ +- while( 1 ) +- { +- /* get a packet */ +- pPktInfo = mvEthPortTxDone(priv->hal_priv, queue); +- if(pPktInfo) +- { +- /* handle tx error */ +- if( pPktInfo->status & (ETH_ERROR_SUMMARY_MASK) ) { +- ETH_DBG( ETH_DBG_TX_DONE, ("eth%d: bad tx-done status\n", priv->port) ); +- priv->net_dev->stats.tx_errors++; +- } +- +- count++; +- ETH_STAT( priv->eth_stat.tx_done_hal_ok[queue]++); +- +- eth_pkt_info_free(priv, pPktInfo); +- continue; +- } +- else +- { +- /* no more work */ +- break; +- } +- } +-#if (MV_ETH_TX_Q_NUM == 1) +- /* if transmission was previously stopped, now it can be restarted. */ +- if( netif_queue_stopped( priv->net_dev ) && (priv->net_dev->flags & IFF_UP) && (count > 0) ) { +- ETH_DBG( ETH_DBG_TX_DONE, ("%s: restart transmit\n", priv->net_dev->name) ); +- ETH_STAT( priv->eth_stat.tx_done_netif_wake++); +- netif_wake_queue( priv->net_dev ); +- } +-#endif /* MV_ETH_TX_Q_NUM == 1 */ +- +- ETH_STAT( priv->eth_stat.tx_done_dist[count]++); +- ETH_DBG( ETH_DBG_TX_DONE, ("eth%d: tx-done %d\n", priv->port, count) ); +- return count; ++ ++/*********************************************************** ++ * eth_tx_done -- * ++ * release transmitted packets. interrupt context. * ++ ***********************************************************/ ++static INLINE u32 eth_tx_done(mv_eth_priv *priv, int queue) ++{ ++ MV_PKT_INFO* pPktInfo; ++ u32 count = 0; ++ ++ ETH_DBG( ETH_DBG_TX_DONE, ("eth%s: tx-done ", priv->port) ); ++ priv->eth_stat.tx_done_events++; ++ ++ /* release the transmitted packets */ ++ while( 1 ) ++ { ++ /* get a packet */ ++ pPktInfo = mvEthPortTxDone(priv->hal_priv, queue); ++ if(pPktInfo) ++ { ++ /* handle tx error */ ++ if( pPktInfo->status & (ETH_ERROR_SUMMARY_MASK) ) { ++ ETH_DBG( ETH_DBG_TX_DONE, ("eth%d: bad tx-done status\n", priv->port) ); ++ priv->net_dev->stats.tx_errors++; ++ } ++ ++ count++; ++ ETH_STAT( priv->eth_stat.tx_done_hal_ok[queue]++); ++ ++ eth_pkt_info_free(priv, pPktInfo); ++ continue; ++ } ++ else ++ { ++ /* no more work */ ++ break; ++ } ++ } ++#if (MV_ETH_TX_Q_NUM == 1) ++ /* if transmission was previously stopped, now it can be restarted. */ ++ if( netif_queue_stopped( priv->net_dev ) && (priv->net_dev->flags & IFF_UP) && (count > 0) ) { ++ ETH_DBG( ETH_DBG_TX_DONE, ("%s: restart transmit\n", priv->net_dev->name) ); ++ ETH_STAT( priv->eth_stat.tx_done_netif_wake++); ++ netif_wake_queue( priv->net_dev ); ++ } ++#endif /* MV_ETH_TX_Q_NUM == 1 */ ++ ++ ETH_STAT( priv->eth_stat.tx_done_dist[count]++); ++ ETH_DBG( ETH_DBG_TX_DONE, ("eth%d: tx-done %d\n", priv->port, count) ); ++ return count; + } + /**************************************************************************************************************/ + +@@ -679,11 +682,14 @@ + MV_STATUS status; + struct net_device *out_dev = NULL; + MV_U32 rx_status; ++ unsigned char *buf; ++ unsigned char eth_buf[12]; ++ int vlan_id; + #ifdef CONFIG_MV_ETH_NFP + int out_if_index; + MV_IP_HEADER *pIpHdr; + int nfp = fp_is_enabled(); +-#endif /* CONFIG_MV_ETH_NFP */ ++#endif /* CONFIG_MV_ETH_NFP */ + + ETH_DBG( ETH_DBG_RX, ("%s: rx_poll work_to_do %d\n", dev->name, work_to_do) ); + +@@ -711,19 +717,19 @@ + + work_done++; + ETH_STAT( priv->eth_stat.rx_hal_ok[queue]++); +- /* good rx */ ++ /* good rx */ ++ ++ skb = (struct sk_buff *)( pPktInfo->osInfo ); ++ ETH_DBG( ETH_DBG_RX, ("good rx: skb=%p, skb->data=%p\n", skb, skb->data) ); + +- skb = (struct sk_buff *)( pPktInfo->osInfo ); +- ETH_DBG( ETH_DBG_RX, ("good rx: skb=%p, skb->data=%p\n", skb, skb->data) ); +- + #ifdef CONFIG_MV_GATEWAY + if(priv->isGtw) +- { +- dev = mv_gtw_get_rx_dev(skb, NULL, NULL); ++ { ++ dev = mv_gtw_get_rx_dev(skb, NULL, NULL); + stats = MV_NETDEV_STATS(dev); +- } +-#endif /* CONFIG_MV_GATEWAY */ +- ++ } ++#endif /* CONFIG_MV_GATEWAY */ ++ + stats->rx_packets++; + stats->rx_bytes += pPktInfo->pFrags->dataSize - ETH_MV_HEADER_SIZE; + +@@ -746,14 +752,14 @@ + #if defined(CONFIG_MV_GATEWAY) + if(out_priv->isGtw) + { +- struct mv_vlan_cfg* vlan_cfg = MV_NETDEV_VLAN(out_dev); +- *(unsigned short *)(pPktInfo->pFrags->bufVirtPtr) = vlan_cfg->header; ++ struct mv_vlan_cfg* vlan_cfg = MV_NETDEV_VLAN(out_dev); ++ *(unsigned short *)(pPktInfo->pFrags->bufVirtPtr) = vlan_cfg->header; + } + else + #endif /* CONFIG_MV_GATEWAY */ + { +- pPktInfo->pFrags->bufPhysAddr += ETH_MV_HEADER_SIZE; +- pPktInfo->pFrags->dataSize -= ETH_MV_HEADER_SIZE; ++ pPktInfo->pFrags->bufPhysAddr += ETH_MV_HEADER_SIZE; ++ pPktInfo->pFrags->dataSize -= ETH_MV_HEADER_SIZE; + } + mvOsCacheLineFlushInv(NULL, pPktInfo->pFrags->bufVirtPtr); + mvOsCacheLineFlushInv(NULL, pPktInfo->pFrags->bufVirtPtr +@@ -764,16 +770,16 @@ + status = mvEthPortTx(out_priv->hal_priv, txq, pPktInfo); + if( status != MV_OK ) + { +- out_dev->stats.tx_dropped++; +- spin_unlock(out_priv->lock); +-#if defined(CONFIG_MV_GATEWAY) +- if(!(out_priv->isGtw)) +-#endif /* CONFIG_MV_GATEWAY */ +- pPktInfo->pFrags->bufPhysAddr -= ETH_MV_HEADER_SIZE; +- +- mvEthPortRxDone( priv->hal_priv, queue, pPktInfo ); +- continue; +- } ++ out_dev->stats.tx_dropped++; ++ spin_unlock(out_priv->lock); ++#if defined(CONFIG_MV_GATEWAY) ++ if(!(out_priv->isGtw)) ++#endif /* CONFIG_MV_GATEWAY */ ++ pPktInfo->pFrags->bufPhysAddr -= ETH_MV_HEADER_SIZE; ++ ++ mvEthPortRxDone( priv->hal_priv, queue, pPktInfo ); ++ continue; ++ } + out_priv->tx_count[txq]++; + out_dev->stats.tx_packets++; + out_dev->stats.tx_bytes += pPktInfo->pFrags->dataSize; +@@ -807,6 +813,26 @@ + } + else + { ++ if (priv->port == 2) ++ { ++ memcpy(eth_buf, skb->data, 12); ++#if 0 ++ printk("%x %x %x %x %x %x %x %x\n", ++ *(u8 *)(skb->data + 12), ++ *(u8 *)(skb->data + 13), ++ *(u8 *)(skb->data + 14), ++ *(u8 *)(skb->data + 15), ++ *(u8 *)(skb->data + 16), ++ *(u8 *)(skb->data + 17), ++ *(u8 *)(skb->data + 18), ++ *(u8 *)(skb->data + 19)); ++#endif ++ vlan_id = (((*(u8 *)(skb->data + 14)) & 0xf) << 8) | ++ ((*(u8 *)(skb->data + 15)) & 0xff); ++ skb_pull(skb, 8); ++ memcpy(skb->data, eth_buf, 12); ++ } ++ + skb->dev = dev; + + skb->ip_summed = CHECKSUM_NONE; +@@ -853,7 +879,24 @@ + lro_receive_skb(&priv->lro_mgr, skb, priv); + else + #endif +- status = netif_receive_skb(skb); ++ if (priv->port == 2) ++ { ++#if 0 ++ if (vlan_id == 1) ++ status = netif_receive_skb(skb); ++ else ++ { ++ status = vlan_hwaccel_receive_skb(skb, priv->grp, vlan_id); ++ } ++#else ++ if (priv->vlan_valid[vlan_id]) ++ status = vlan_hwaccel_receive_skb(skb, priv->grp, vlan_id); ++ else ++ status = netif_receive_skb(skb); ++#endif ++ } ++ else ++ status = netif_receive_skb(skb); + ETH_STAT( if(status) (priv->eth_stat.rx_netif_drop++) ); + + #ifdef CONFIG_MV_SKB_REUSE +@@ -1028,7 +1071,7 @@ + + return IRQ_HANDLED; + } +- ++ + + static int eth_poll( struct net_device *dev, int *budget ) + { +@@ -1038,11 +1081,11 @@ + priv->eth_stat.poll_events++; + ETH_DBG(ETH_DBG_POLL, ("%s: Start eth_poll\n", dev->name)); + +- if( priv->picer & (1 << ETH_CAUSE_LINK_STATE_CHANGE_BIT) ) +- { ++ if( priv->picer & (1 << ETH_CAUSE_LINK_STATE_CHANGE_BIT)) ++ { + eth_check_link_status(dev); +- priv->picer &= ~ETH_LINK_MASK; +- } ++ priv->picer &= ~ETH_LINK_MASK; ++ } + + #ifdef ETH_TX_DONE_ISR + { +@@ -1051,10 +1094,10 @@ + spin_lock(priv->lock); + for(txq=0; txqtx_en) + eth_tx_enable(priv, txq); +-#endif /* ETH_MV_TX_EN */ ++#endif /* ETH_MV_TX_EN */ + priv->tx_count[txq] -= eth_tx_done(priv, txq); + } + spin_unlock(priv->lock); +@@ -1068,16 +1111,16 @@ + { + int rxq; + +- rxq = eth_rx_policy(priv); ++ rxq = eth_rx_policy(priv); + if(rxq == -1) + break; + + rx_done += eth_rx( dev, rx_todo - rx_done, rxq); +- if(rx_done < rx_todo) +- priv->picr &= ~(ETH_CAUSE_RX_READY_MASK(rxq) | +- ETH_CAUSE_RX_ERROR_MASK(rxq)); +- else +- break; ++ if(rx_done < rx_todo) ++ priv->picr &= ~(ETH_CAUSE_RX_READY_MASK(rxq) | ++ ETH_CAUSE_RX_ERROR_MASK(rxq)); ++ else ++ break; + } + #else + rx_done = eth_rx( dev, rx_todo, ETH_DEF_RXQ); +@@ -1110,7 +1153,7 @@ + #endif /* ETH_MV_TX_EN */ + + for(txq=0; txqtx_count[txq] > mv_eth_tx_done_quota) + { + tx_done_count = eth_tx_done(tx_priv, txq); +@@ -1151,24 +1194,24 @@ + } + return 1; + } +- +- +-/* Show network driver configuration */ +-void mv_netdev_config_show(void) +-{ +- int i; +- +- printk( " o %s\n", ETH_DESCR_CONFIG_STR ); +- +-#if defined(ETH_DESCR_IN_SRAM) +- printk( " o %s\n", INTEG_SRAM_CONFIG_STR ); +-#endif +- +- printk( " o %s\n", ETH_SDRAM_CONFIG_STR ); +- +-#if defined(ETH_INCLUDE_TSO) && !defined(TX_CSUM_OFFLOAD) +-#error "If TSO enabled - TX checksum offload must be enabled too" +-#endif ++ ++ ++/* Show network driver configuration */ ++void mv_netdev_config_show(void) ++{ ++ int i; ++ ++ printk( " o %s\n", ETH_DESCR_CONFIG_STR ); ++ ++#if defined(ETH_DESCR_IN_SRAM) ++ printk( " o %s\n", INTEG_SRAM_CONFIG_STR ); ++#endif ++ ++ printk( " o %s\n", ETH_SDRAM_CONFIG_STR ); ++ ++#if defined(ETH_INCLUDE_TSO) && !defined(TX_CSUM_OFFLOAD) ++#error "If TSO enabled - TX checksum offload must be enabled too" ++#endif + + #if (MV_ETH_RX_Q_NUM > 1) + printk( " o Multi RX Queue support - %d RX queues\n", MV_ETH_RX_Q_NUM); +@@ -1181,94 +1224,99 @@ + #else + printk( " o Single TX Queue support - ETH_DEF_TXQ=%d\n", ETH_DEF_TXQ); + #endif /* MV_ETH_TX_Q_NUM > 1 */ +- +-#if defined(ETH_INCLUDE_TSO) +- printk(" o TCP segmentation offload enabled\n"); +-#endif /* ETH_INCLUDE_TSO */ +- +-#if defined(ETH_INCLUDE_UFO) +- printk(" o UDP fragmentation offload enabled\n"); +-#endif /* ETH_INCLUDE_UFO */ +- +-#if defined(RX_CSUM_OFFLOAD) +- printk(" o Receive checksum offload enabled\n"); +-#endif +-#if defined(TX_CSUM_OFFLOAD) +- printk(" o Transmit checksum offload enabled\n"); +-#endif +- +-#ifdef CONFIG_MV_ETH_NFP +- printk(" o Network Fast Processing (Routing) supported\n"); +- +-#ifdef CONFIG_MV_ETH_NFP_NAT_SUPPORT +- printk(" o Network Fast Processing (NAT) supported\n"); +-#endif /* CONFIG_MV_ETH_NFP_NAT_SUPPORT */ +- +-#endif /* CONFIG_MV_ETH_NFP */ +- +-#ifdef ETH_STATISTICS +- printk(" o Driver statistics enabled\n"); +-#endif +- +-#ifdef ETH_DEBUG +- printk(" o Driver debug messages enabled\n"); +-#endif +- +-#ifdef CONFIG_MV_ETH_PROC +- printk(" o Proc tool API enabled\n"); +-#endif +- +-#ifdef ETH_INCLUDE_LRO +- printk(" o LRO TCP support enabled\n"); +-#endif +- +-#ifdef ETH_INCLUDE_LRO_UDP +- printk(" o LRO UDP support enabled\n"); +-#endif ++ ++#if defined(ETH_INCLUDE_TSO) ++ printk(" o TCP segmentation offload enabled\n"); ++#endif /* ETH_INCLUDE_TSO */ ++ ++#if defined(ETH_INCLUDE_UFO) ++ printk(" o UDP fragmentation offload enabled\n"); ++#endif /* ETH_INCLUDE_UFO */ ++ ++#if defined(RX_CSUM_OFFLOAD) ++ printk(" o Receive checksum offload enabled\n"); ++#endif ++#if defined(TX_CSUM_OFFLOAD) ++ printk(" o Transmit checksum offload enabled\n"); ++#endif ++ ++#ifdef CONFIG_MV_ETH_NFP ++ printk(" o Network Fast Processing (Routing) supported\n"); ++ ++#ifdef CONFIG_MV_ETH_NFP_NAT_SUPPORT ++ printk(" o Network Fast Processing (NAT) supported\n"); ++#endif /* CONFIG_MV_ETH_NFP_NAT_SUPPORT */ ++ ++#endif /* CONFIG_MV_ETH_NFP */ ++ ++#ifdef ETH_STATISTICS ++ printk(" o Driver statistics enabled\n"); ++#endif ++ ++#ifdef ETH_DEBUG ++ printk(" o Driver debug messages enabled\n"); ++#endif ++ ++#ifdef CONFIG_MV_ETH_PROC ++ printk(" o Proc tool API enabled\n"); ++#endif ++ ++#ifdef ETH_INCLUDE_LRO ++ printk(" o LRO TCP support enabled\n"); ++#endif ++ ++#ifdef ETH_INCLUDE_LRO_UDP ++ printk(" o LRO UDP support enabled\n"); ++#endif + + #if defined(CONFIG_MV_GATEWAY) +- printk(" o Gateway support enabled\n"); +- +- printk(" o Using Marvell Header Mode\n"); +- +-#ifdef CONFIG_MV_GTW_QOS_VOIP +- printk(" o VoIP QoS support (ToS %s)\n", CONFIG_MV_GTW_QOS_VOIP_TOS); +-#endif +- +-#ifdef CONFIG_MV_GTW_QOS_ROUTING +- printk(" o Routing QoS support (ToS %s)\n", CONFIG_MV_GTW_QOS_ROUTING_TOS); +-#endif +- +-#ifdef CONFIG_MV_GTW_IGMP +- printk(" o L2 IGMP support\n"); ++ printk(" o Gateway support enabled\n"); ++ ++ printk(" o Using Marvell Header Mode\n"); ++ ++#ifdef CONFIG_MV_GTW_QOS_VOIP ++ printk(" o VoIP QoS support (ToS %s)\n", CONFIG_MV_GTW_QOS_VOIP_TOS); ++#endif ++ ++#ifdef CONFIG_MV_GTW_QOS_ROUTING ++ printk(" o Routing QoS support (ToS %s)\n", CONFIG_MV_GTW_QOS_ROUTING_TOS); ++#endif ++ ++#ifdef CONFIG_MV_GTW_IGMP ++ printk(" o L2 IGMP support\n"); + #endif /* CONFIG_MV_GTW_IGMP */ +- ++ + #endif /* CONFIG_MV_GATEWAY */ + + #if MV_ETH_RX_Q_NUM > MV_ETH_MAX_RXQ + #error "MV_ETH_RX_Q_NUM is large than MV_ETH_MAX_RXQ" + #endif +- +- printk(" o Rx descripors:"); +- for(i=0; i MV_ETH_MAX_TXQ + #error "MV_ETH_TX_Q_NUM is large than MV_ETH_MAX_TXQ" + #endif + +- printk(" o Tx descripors:"); +- for(i=0; ifeatures = NETIF_F_SG; ++ mv_eth_priv *priv = MV_ETH_PRIV(dev); ++ ++ dev->features = NETIF_F_SG; ++ ++ if (priv->port == 2) ++ dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER; + + #ifdef TX_CSUM_OFFLOAD + if(dev->mtu <= ETH_CSUM_MAX_BYTE_COUNT) +@@ -1392,7 +1440,7 @@ + return -1; + } + +- ++ + /*********************************************************** + * mv_eth_change_mtu_internals -- * + * stop port activity. release skb from rings. set new * +@@ -1403,7 +1451,7 @@ + { + mv_eth_priv *priv = MV_ETH_PRIV(dev); + +- if(mtu < 1498 /* 1518 - 20 */) { ++ if(mtu < 1496 /* 1518 - 20 */) { + printk( "%s: Illegal MTU value %d, ", dev->name, mtu); + mtu = 1500; + printk(" rounding MTU to: %d \n",mtu); +@@ -1433,241 +1481,242 @@ + return 0; + } + +- +-/*********************************************************** +- * mv_netdev_timer_callback -- * +- * N msec periodic callback for cleanup. * +- ***********************************************************/ +-static void mv_netdev_timer_callback( unsigned long data ) +-{ +- struct net_device *dev = (struct net_device *)data; +- mv_eth_priv *priv = MV_ETH_PRIV(dev); +- int tx_done_count, txq; +-#ifdef CONFIG_MV_ETH_NFP +- int i = 0; +- MV_PKT_INFO* pPktInfo; +-#endif /* CONFIG_MV_ETH_NFP */ +- +- ETH_DBG( ETH_DBG_TX, ("%s: timer_callback", dev->name) ); +- priv->eth_stat.timer_events++; +- +- spin_lock(priv->lock); +- +-#if defined(CONFIG_MV_ETH_NFP) || defined(CONFIG_MV_SKB_REUSE) +- /* Refill pktInfo */ +- if (priv->refill_needed_flag) +- { +- priv->refill_needed_flag = 0; +- for(i=0; ihal_priv, i) < mv_eth_rxq_desc[i]) +- { +- pPktInfo = eth_pkt_info_get(priv); +- if (pPktInfo != NULL) { +- mvEthPortRxDone(priv->hal_priv, i, pPktInfo); +- ETH_STAT( priv->eth_stat.rx_fill_ok[i]++); +- } ++ ++/*********************************************************** ++ * mv_netdev_timer_callback -- * ++ * N msec periodic callback for cleanup. * ++ ***********************************************************/ ++static void mv_netdev_timer_callback( unsigned long data ) ++{ ++ struct net_device *dev = (struct net_device *)data; ++ mv_eth_priv *priv = MV_ETH_PRIV(dev); ++ int tx_done_count, txq; ++#ifdef CONFIG_MV_ETH_NFP ++ int i = 0; ++ MV_PKT_INFO* pPktInfo; ++#endif /* CONFIG_MV_ETH_NFP */ ++ ++ //ETH_DBG( ETH_DBG_TX, ("%s: timer_callback", dev->name) ); ++ priv->eth_stat.timer_events++; ++ ++ spin_lock(priv->lock); ++ ++#if defined(CONFIG_MV_ETH_NFP) || defined(CONFIG_MV_SKB_REUSE) ++ /* Refill pktInfo */ ++ if (priv->refill_needed_flag) ++ { ++ priv->refill_needed_flag = 0; ++ for(i=0; ihal_priv, i) < mv_eth_rxq_desc[i]) ++ { ++ pPktInfo = eth_pkt_info_get(priv); ++ if (pPktInfo != NULL) { ++ mvEthPortRxDone(priv->hal_priv, i, pPktInfo); ++ ETH_STAT( priv->eth_stat.rx_fill_ok[i]++); ++ } + else { + priv->eth_stat.rx_pool_empty++; +- priv->refill_needed_flag = 1; +- break; +- } +- } +- } +- } +-#endif /* CONFIG_MV_ETH_NFP || CONFIG_MV_SKB_REUSE */ ++ priv->refill_needed_flag = 1; ++ break; ++ } ++ } ++ } ++ } ++#endif /* CONFIG_MV_ETH_NFP || CONFIG_MV_SKB_REUSE */ + + mvEthPortTxRestart(priv->hal_priv); +- +- /* Call TX done */ +- for(txq=0; txqtx_count[txq] > 0) +- { +-#ifdef ETH_TX_DONE_ISR +-#else +- tx_done_count = eth_tx_done(priv, txq); +- priv->tx_count[txq] -= tx_done_count; +-#endif /* ETH_TX_DONE_ISR */ +- } +- } +- +-#ifdef CONFIG_MV_SKB_REUSE +- eth_skb_alloc_for_reuse(priv, dev->mtu); +-#endif /* CONFIG_MV_SKB_REUSE */ +- +- spin_unlock(priv->lock); +- +- if(priv->timer_flag) +- { +- priv->timer.expires = jiffies + ((HZ*CONFIG_MV_ETH_TIMER_PERIOD)/1000); /*ms*/ +- add_timer( &priv->timer ); +- } +-} +- +-/* Initialize Ethernet port on chip */ +-int mv_eth_hal_init(mv_eth_priv *priv, int mtu, u8* mac) +-{ +- MV_ETH_PORT_INIT hal_init_struct; +- +- /* init the hal */ +- hal_init_struct.maxRxPktSize = MV_RX_BUF_SIZE(mtu); +- hal_init_struct.rxDefQ = ETH_DEF_RXQ; +- memcpy(hal_init_struct.rxDescrNum, mv_eth_rxq_desc, sizeof(int)*MV_ETH_RX_Q_NUM); +- memcpy(hal_init_struct.txDescrNum, mv_eth_txq_desc, sizeof(int)*MV_ETH_TX_Q_NUM); +- hal_init_struct.osHandle = NULL; +- +- priv->hal_priv = mvEthPortInit( priv->port, &hal_init_struct ); +- if( !priv->hal_priv ) { +- printk( KERN_ERR "eth port=%d: load failed\n", priv->port); +- return -ENODEV; +- } +- +-#ifdef CONFIG_ETH_FLOW_CONTROL +- /* enable flow Control in MAC level */ +- mvEthFlowCtrlSet(priv->hal_priv, MV_ETH_FC_ENABLE); +-#endif +- +- if(mac) +- { +- /* set new addr in hw */ +- if( mvEthMacAddrSet( priv->hal_priv, mac, ETH_DEF_RXQ) != MV_OK ) +- { +- printk("mv_eth_hal_init: ethSetMacAddr failed for port=%d\n", priv->port); +- return -ENODEV; +- } +- } +- else +- { +- mvEthRxFilterModeSet(priv->hal_priv, MV_TRUE); +- } +- +- if( mvEthMaxRxSizeSet( priv->hal_priv, MV_RX_BUF_SIZE(mtu)) ) { +- printk( "mv_eth_hal_init: ethPortSetMaxBufSize failed for port=%d\n", priv->port); +- return -ENODEV; +- } +- return 0; +-} +- +-/* Initialize HAL level of Ethernet driver */ +-int mv_eth_priv_init(mv_eth_priv *priv, int port) +-{ +- int txq, i; +- MV_PKT_INFO *pkt_info; +- +- memset(priv, 0, sizeof(mv_eth_priv) ); +- +-#if defined(ETH_INCLUDE_TSO) || defined(ETH_INCLUDE_UFO) || defined(CONFIG_MV_GATEWAY) +- for(txq=0; txqtx_extra_bufs[txq] = mvOsMalloc(mv_eth_txq_desc[txq]*sizeof(char*)); +- if(priv->tx_extra_bufs[txq] == NULL) +- { +- printk("eth%d TSO/UFO: txq=%d - Can't alloc %d bytes for tx_extra_bufs array\n", +- port, txq, mv_eth_txq_desc[txq]*sizeof(char*)); +- return -ENOMEM; +- } +- for(i=0; itx_extra_bufs[txq][i] = mvOsMalloc(TX_EXTRA_BUF_SIZE); +- if(priv->tx_extra_bufs[txq][i] == NULL) +- { +- printk("eth%d TSO/UFO: txq=%d - Can't alloc %d extra TX buffer (%d bytes)\n", +- port, txq, i, TX_EXTRA_BUF_SIZE); +- return -ENOMEM; +- } +- } +- priv->tx_extra_buf_idx[txq] = 0; +- } +-#endif /* ETH_INCLUDE_TSO || ETH_INCLUDE_UFO || CONFIG_MV_GATEWAY */ +- +- priv->txPktInfoPool = mvStackCreate(mv_eth_tx_desc_total); +- if(priv->txPktInfoPool == NULL) +- { +- printk("eth%d: Can't create txPktInfoPool for %d elements\n", +- port, mv_eth_tx_desc_total); +- return -ENOMEM; +- } +- +- for(i=0; isrcIdx = (char)-1; +- +- pkt_info->pFrags = mvOsMalloc(sizeof(MV_BUF_INFO)*(MAX_SKB_FRAGS+3)); +- if(pkt_info->pFrags == NULL) +- { +- printk("eth%d: Can't alloc %d bytes for %d MV_BUF_INFO array\n", +- port, (int)(sizeof(MV_BUF_INFO)*(MAX_SKB_FRAGS+3)), i); +- return -ENOMEM; +- } +- memset(pkt_info->pFrags, 0, sizeof(MV_BUF_INFO)*(MAX_SKB_FRAGS+3)); +- mvStackPush(priv->txPktInfoPool, (MV_U32)pkt_info); +- } +- +- memset(priv->tx_count, 0, sizeof(priv->tx_count)); +- +- /* init mv_eth_priv */ +- priv->port = port; +- +- memset( &priv->timer, 0, sizeof(struct timer_list) ); +- priv->timer.function = mv_netdev_timer_callback; +- init_timer(&priv->timer); +- priv->timer_flag = 0; +- +-#ifdef ETH_INCLUDE_LRO +- priv->lro_mgr.max_aggr = ETH_LRO_MAX_AGGR; +- priv->lro_mgr.max_desc = ETH_LRO_MAX_DESCRIPTORS; +- priv->lro_mgr.lro_arr = priv->lro_desc; +- priv->lro_mgr.get_skb_header = eth_get_skb_hdr; +- priv->lro_mgr.features = LRO_F_NAPI | LRO_F_EXTRACT_VLAN_ID; +- priv->lro_mgr.dev = dev; +- priv->lro_mgr.ip_summed = CHECKSUM_UNNECESSARY; +- priv->lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY; +-#endif +- +-#if defined(CONFIG_MV_ETH_NFP) || defined(CONFIG_MV_SKB_REUSE) +- priv->fpRxPool = mvStackCreate(mv_eth_rx_desc_total*2 + mv_eth_tx_desc_total); +- if(priv->fpRxPool == NULL) +- { +- mvOsPrintf("eth_priv_init_%d: Can't create fpRxPool for %d elements\n", +- port, (mv_eth_rx_desc_total*2 + mv_eth_tx_desc_total)); +- return -ENOMEM; +- } +-#endif /* (CONFIG_MV_ETH_NFP) || (CONFIG_MV_SKB_REUSE) */ +- +-#ifdef CONFIG_MV_ETH_NFP +- priv->refill_needed_flag = 0; +- memset(&priv->fpStats, 0, sizeof(priv->fpStats) ); +- priv->lock = &nfp_lock; +-#else +- priv->lock = kmalloc(sizeof(spinlock_t), GFP_ATOMIC); +- spin_lock_init( priv->lock ); +-#endif /* CONFIG_MV_ETH_NFP */ +- +-#ifdef CONFIG_MV_SKB_REUSE +- priv->skbReusePool = mvStackCreate(mv_eth_rx_desc_total*2); +- if(priv->skbReusePool == NULL) +- { +- printk("eth_priv_init_%d: Can't create skbReusePool for %d elements\n", +- port, mv_eth_rx_desc_total*2); +- return -ENOMEM; +- } +-#endif /* CONFIG_MV_SKB_REUSE */ ++ ++ /* Call TX done */ ++ for(txq=0; txqtx_count[txq] > 0) ++ { ++#ifdef ETH_TX_DONE_ISR ++#else ++ tx_done_count = eth_tx_done(priv, txq); ++ priv->tx_count[txq] -= tx_done_count; ++#endif /* ETH_TX_DONE_ISR */ ++ } ++ } ++ ++#ifdef CONFIG_MV_SKB_REUSE ++ eth_skb_alloc_for_reuse(priv, dev->mtu); ++#endif /* CONFIG_MV_SKB_REUSE */ ++ ++ spin_unlock(priv->lock); ++ ++ if(priv->timer_flag) ++ { ++ priv->timer.expires = jiffies + ((HZ*CONFIG_MV_ETH_TIMER_PERIOD)/1000); /*ms*/ ++ add_timer( &priv->timer ); ++ } ++} ++ ++/* Initialize Ethernet port on chip */ ++int mv_eth_hal_init(mv_eth_priv *priv, int mtu, u8* mac) ++{ ++ MV_ETH_PORT_INIT hal_init_struct; ++ ++ /* init the hal */ ++ hal_init_struct.maxRxPktSize = MV_RX_BUF_SIZE(mtu); ++ hal_init_struct.rxDefQ = ETH_DEF_RXQ; ++ memcpy(hal_init_struct.rxDescrNum, mv_eth_rxq_desc, sizeof(int)*MV_ETH_RX_Q_NUM); ++ memcpy(hal_init_struct.txDescrNum, mv_eth_txq_desc, sizeof(int)*MV_ETH_TX_Q_NUM); ++ hal_init_struct.osHandle = NULL; ++ ++ priv->hal_priv = mvEthPortInit( priv->port, &hal_init_struct ); ++ if( !priv->hal_priv ) { ++ printk( KERN_ERR "eth port=%d: load failed\n", priv->port); ++ return -ENODEV; ++ } ++ ++#ifdef CONFIG_ETH_FLOW_CONTROL ++ /* enable flow Control in MAC level */ ++ mvEthFlowCtrlSet(priv->hal_priv, MV_ETH_FC_ENABLE); ++#endif ++ ++ if(mac) ++ { ++ /* set new addr in hw */ ++ if( mvEthMacAddrSet( priv->hal_priv, mac, ETH_DEF_RXQ) != MV_OK ) ++ { ++ printk("mv_eth_hal_init: ethSetMacAddr failed for port=%d\n", priv->port); ++ return -ENODEV; ++ } ++ } ++ else ++ { ++ mvEthRxFilterModeSet(priv->hal_priv, MV_TRUE); ++ } ++ ++ if( mvEthMaxRxSizeSet( priv->hal_priv, MV_RX_BUF_SIZE(mtu)) ) { ++ printk( "mv_eth_hal_init: ethPortSetMaxBufSize failed for port=%d\n", priv->port); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++/* Initialize HAL level of Ethernet driver */ ++int mv_eth_priv_init(mv_eth_priv *priv, int port) ++{ ++ int txq, i; ++ MV_PKT_INFO *pkt_info; ++ ++ memset(priv, 0, sizeof(mv_eth_priv) ); ++ ++#if defined(ETH_INCLUDE_TSO) || defined(ETH_INCLUDE_UFO) || defined(CONFIG_MV_GATEWAY) ++ for(txq=0; txqtx_extra_bufs[txq] = mvOsMalloc(mv_eth_txq_desc[txq]*sizeof(char*)); ++ if(priv->tx_extra_bufs[txq] == NULL) ++ { ++ printk("eth%d TSO/UFO: txq=%d - Can't alloc %d bytes for tx_extra_bufs array\n", ++ port, txq, mv_eth_txq_desc[txq]*sizeof(char*)); ++ return -ENOMEM; ++ } ++ for(i=0; itx_extra_bufs[txq][i] = mvOsMalloc(TX_EXTRA_BUF_SIZE); ++ if(priv->tx_extra_bufs[txq][i] == NULL) ++ { ++ printk("eth%d TSO/UFO: txq=%d - Can't alloc %d extra TX buffer (%d bytes)\n", ++ port, txq, i, TX_EXTRA_BUF_SIZE); ++ return -ENOMEM; ++ } ++ } ++ priv->tx_extra_buf_idx[txq] = 0; ++ } ++#endif /* ETH_INCLUDE_TSO || ETH_INCLUDE_UFO || CONFIG_MV_GATEWAY */ ++ ++ priv->txPktInfoPool = mvStackCreate(mv_eth_tx_desc_total); ++ if(priv->txPktInfoPool == NULL) ++ { ++ printk("eth%d: Can't create txPktInfoPool for %d elements\n", ++ port, mv_eth_tx_desc_total); ++ return -ENOMEM; ++ } ++ ++ for(i=0; isrcIdx = (char)-1; ++ ++ pkt_info->pFrags = mvOsMalloc(sizeof(MV_BUF_INFO)*(MAX_SKB_FRAGS+3)); ++ if(pkt_info->pFrags == NULL) ++ { ++ printk("eth%d: Can't alloc %d bytes for %d MV_BUF_INFO array\n", ++ port, (int)(sizeof(MV_BUF_INFO)*(MAX_SKB_FRAGS+3)), i); ++ return -ENOMEM; ++ } ++ memset(pkt_info->pFrags, 0, sizeof(MV_BUF_INFO)*(MAX_SKB_FRAGS+3)); ++ mvStackPush(priv->txPktInfoPool, (MV_U32)pkt_info); ++ } ++ ++ memset(priv->tx_count, 0, sizeof(priv->tx_count)); ++ ++ /* init mv_eth_priv */ ++ priv->port = port; ++ ++ memset( &priv->timer, 0, sizeof(struct timer_list) ); ++ priv->timer.function = mv_netdev_timer_callback; ++ init_timer(&priv->timer); ++ priv->timer_flag = 0; ++ ++#ifdef ETH_INCLUDE_LRO ++ priv->lro_mgr.max_aggr = ETH_LRO_MAX_AGGR; ++ priv->lro_mgr.max_desc = ETH_LRO_MAX_DESCRIPTORS; ++ priv->lro_mgr.lro_arr = priv->lro_desc; ++ priv->lro_mgr.get_skb_header = eth_get_skb_hdr; ++ priv->lro_mgr.features = LRO_F_NAPI | LRO_F_EXTRACT_VLAN_ID; ++ priv->lro_mgr.dev = dev; ++ priv->lro_mgr.ip_summed = CHECKSUM_UNNECESSARY; ++ priv->lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY; ++#endif ++ ++#if defined(CONFIG_MV_ETH_NFP) || defined(CONFIG_MV_SKB_REUSE) ++ priv->fpRxPool = mvStackCreate(mv_eth_rx_desc_total*2 + mv_eth_tx_desc_total); ++ if(priv->fpRxPool == NULL) ++ { ++ mvOsPrintf("eth_priv_init_%d: Can't create fpRxPool for %d elements\n", ++ port, (mv_eth_rx_desc_total*2 + mv_eth_tx_desc_total)); ++ return -ENOMEM; ++ } ++#endif /* (CONFIG_MV_ETH_NFP) || (CONFIG_MV_SKB_REUSE) */ ++ ++#ifdef CONFIG_MV_ETH_NFP ++ priv->refill_needed_flag = 0; ++ memset(&priv->fpStats, 0, sizeof(priv->fpStats) ); ++ priv->lock = &nfp_lock; ++#else ++ priv->lock = kmalloc(sizeof(spinlock_t), GFP_ATOMIC); ++ spin_lock_init( priv->lock ); ++#endif /* CONFIG_MV_ETH_NFP */ ++ ++#ifdef CONFIG_MV_SKB_REUSE ++ priv->skbReusePool = mvStackCreate(mv_eth_rx_desc_total*2); ++ if(priv->skbReusePool == NULL) ++ { ++ printk("eth_priv_init_%d: Can't create skbReusePool for %d elements\n", ++ port, mv_eth_rx_desc_total*2); ++ return -ENOMEM; ++ } ++#endif /* CONFIG_MV_SKB_REUSE */ + + #ifdef ETH_MV_TX_EN + priv->tx_en = priv->tx_en_bk = MV_ETH_TX_EN_DEFAULT; + priv->tx_en_deep = 1; + #endif /* ETH_MV_TX_EN */ +- +- return 0; ++ ++ return 0; + } + + /* Release all allocated memory */ +@@ -1715,7 +1764,7 @@ + mvOsFree(priv->tx_extra_bufs[txq][i]); + priv->tx_extra_bufs[txq][i] = NULL; + } +- } ++ } + mvOsFree(priv->tx_extra_bufs[txq]); + priv->tx_extra_bufs[txq] = NULL; + } +@@ -1723,8 +1772,8 @@ + } + #endif /* ETH_INCLUDE_TSO || ETH_INCLUDE_UFO || CONFIG_MV_GATEWAY */ + +- if(priv->txPktInfoPool) +- { ++ if(priv->txPktInfoPool) ++ { + while(mvStackIsEmpty(priv->txPktInfoPool) == MV_FALSE) + { + pkt_info = (MV_PKT_INFO*)mvStackPop(priv->txPktInfoPool); +@@ -1914,7 +1963,7 @@ + (ETH_TX_IP_NO_FRAG | ETH_TX_L4_TCP_TYPE | + ETH_TX_GENERATE_L4_CHKSUM_MASK | ETH_TX_GENERATE_IP_CHKSUM_MASK | + ((ip_hdr(skb)->ihl) << ETH_TX_IP_HEADER_LEN_OFFSET) ); +- ++ + status = mvEthPortSgTx( priv->hal_priv, txq, pPktInfo); + if( status == MV_OK ) { + priv->tx_count[txq]++; +@@ -2104,7 +2153,7 @@ + pPktInfo->pktSize += pkt_sz; + pPktInfo->numFrags = pkt_nm; + pPktInfo->status = ETH_TX_GENERATE_IP_CHKSUM_MASK | ((iph->ihl) << ETH_TX_IP_HEADER_LEN_OFFSET); +- ++ + status = mvEthPortSgTx(priv->hal_priv, txq, pPktInfo); + ETH_DBG(ETH_DBG_GSO, ("UFO: Tx (ok=%d) %d bytes in %d bufs left=%d\n", + status, pPktInfo->pktSize, pPktInfo->numFrags, left)); +@@ -2193,7 +2242,29 @@ + #endif /* ETH_INCLUDE_LRO_UDP */ + return -1; + } +-#endif /* ETH_INCLUDE_LRO */ ++#endif /* ETH_INCLUDE_LRO */ ++ ++static void ++vlan_add_vid(struct net_device *dev, unsigned short vid) ++{ ++ mv_eth_priv *priv = MV_ETH_PRIV(dev); ++ priv->vlan_valid[vid] = 1; ++} ++ ++static void ++vlan_kill_vid(struct net_device *dev, unsigned short vid) ++{ ++ mv_eth_priv *priv = MV_ETH_PRIV(dev); ++ priv->vlan_valid[vid] = 0; ++} ++ ++static void ++vlan_register(struct net_device *dev, struct vlan_group *grp) ++{ ++ mv_eth_priv *priv = MV_ETH_PRIV(dev); ++ priv->grp = grp; ++} ++ + + + /*********************************************************** +@@ -2209,9 +2280,10 @@ + MV_PKT_INFO *pPktInfo; + int ret = 0, i, queue, tx_done_count; + int tx_in_interrupt = in_interrupt(); +-#ifdef CONFIG_MV_GATEWAY +- unsigned int switch_info; +-#endif /* CONFIG_MV_GATEWAY */ ++ u16 vid; ++#ifdef CONFIG_MV_GATEWAY ++ unsigned int switch_info; ++#endif /* CONFIG_MV_GATEWAY */ + + if( netif_queue_stopped( dev ) ) { + printk( KERN_ERR "%s: transmitting while stopped\n", dev->name ); +@@ -2228,6 +2300,61 @@ + return NETDEV_TX_LOCKED; + } + ++ if (priv->port == 2) // We are on a switched port, we need to modify SKB ++ { ++#if 0 ++ for (i = 0; i < skb->len; i++) ++ { ++ if (!(i % 16)) ++ printk("\n"); ++ printk("0x%2x ", *(skb->data + i)); ++ } ++ printk("\n"); ++#endif ++ if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < 8)) ++ { ++ if (pskb_expand_head(skb, 8, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC)) ++ printk("we had a problem\n"); ++ if (skb->len < 62) ++ skb->len = 62; ++ } ++ ++ skb_push(skb, 8); ++ ++ memmove(skb->data, skb->data + 8, 12); // Move the ETH Header to the Front of SKB ++ ++ if (__vlan_hwaccel_get_tag(skb, &vid)) ++ { ++ *(u8 *)(skb->data + 12) = 0x5f; ++ *(u8 *)(skb->data + 13) = 0xfe; ++ *(u8 *)(skb->data + 14) = 0x10; ++ *(u8 *)(skb->data + 15) = 0x01; ++ *(u8 *)(skb->data + 16) = 0x69; ++ *(u8 *)(skb->data + 17) = 0xff; ++ *(u8 *)(skb->data + 18) = 0xf8; ++ *(u8 *)(skb->data + 19) = 0x00; ++ } ++ else ++ { ++ *(u8 *)(skb->data + 12) = 0x7f; ++ *(u8 *)(skb->data + 13) = 0xfe; ++ *(u8 *)(skb->data + 14) = (0x10 | ((vid >> 8) & 0xf)); ++ *(u8 *)(skb->data + 15) = vid & 0xff; ++ *(u8 *)(skb->data + 16) = 0x69; ++ *(u8 *)(skb->data + 17) = 0xff; ++ *(u8 *)(skb->data + 18) = 0xf8; ++ *(u8 *)(skb->data + 19) = 0x00; ++ } ++#if 0 ++ for (i = 0; i < skb->len; i++) ++ { ++ if (!(i % 16)) ++ printk("\n"); ++ printk("0x%2x ", *(skb->data + i)); ++ } ++ printk("\n"); ++#endif ++ } + ETH_DBG( ETH_DBG_TX, ("%s: tx len=%d headlen=%d frags=%d, ip_summed=%d gso_type=%d\n", + dev->name, skb->len, skb_headlen(skb), skb_shinfo(skb)->nr_frags, skb->ip_summed,skb_shinfo(skb)->gso_type)); + priv->eth_stat.tx_events++; +@@ -2313,10 +2440,10 @@ + #endif + + #ifdef CONFIG_MV_GATEWAY +- if(priv->isGtw) +- mv_gtw_update_tx_skb(&switch_info, dev, pPktInfo); ++ if(priv->isGtw) ++ mv_gtw_update_tx_skb(&switch_info, dev, pPktInfo); + #endif /* CONFIG_MV_GATEWAY */ +- ++ + /* now send the packet */ + status = mvEthPortSgTx( priv->hal_priv, queue, pPktInfo); + /* check status */ +@@ -2355,10 +2482,10 @@ + #else + if( priv->tx_count[queue] >= mv_eth_tx_done_quota) + { +-#ifdef ETH_MV_TX_EN ++#ifdef ETH_MV_TX_EN + if(priv->tx_en) + eth_tx_enable(priv, queue); +-#endif /* ETH_MV_TX_EN */ ++#endif /* ETH_MV_TX_EN */ + + tx_done_count = eth_tx_done(priv, queue); + priv->tx_count[queue] -= tx_done_count; +@@ -2395,71 +2522,74 @@ + priv->eth_stat.tx_timeout++; + printk( KERN_INFO "%s: tx timeout\n", dev->name ); + } +- ++ + /*********************************************************** + * mv_netdev_init -- Allocate and initialize net_device * + * structure * + ***********************************************************/ +-struct net_device* mv_netdev_init(mv_eth_priv *priv, int mtu, u8* mac) +-{ +- struct net_device *dev; +- mv_net_priv *net_priv; +- +- dev = alloc_etherdev(sizeof(mv_net_priv)); +- if( !dev ) { +- return NULL; +- } +- +- net_priv = (mv_net_priv *)dev->priv; +- if( !net_priv ) { +- return NULL; +- } +- memset( net_priv , 0, sizeof(mv_net_priv) ); +- net_priv->giga_priv = priv; +- +- dev->irq = ETH_PORT_IRQ_NUM(priv->port); +- dev->mtu = mtu; +- memcpy(dev->dev_addr, mac, 6); +- dev->weight = (ETH_NUM_OF_RX_DESCR / 2); +- dev->tx_queue_len = ETH_NUM_OF_TX_DESCR; +- dev->watchdog_timeo = 5*HZ; +- +- dev->hard_start_xmit = eth_tx; +- dev->tx_timeout = eth_tx_timeout; +- dev->poll = eth_poll; ++struct net_device* mv_netdev_init(mv_eth_priv *priv, int mtu, u8* mac) ++{ ++ struct net_device *dev; ++ mv_net_priv *net_priv; ++ ++ dev = alloc_etherdev(sizeof(mv_net_priv)); ++ if( !dev ) { ++ return NULL; ++ } ++ ++ net_priv = (mv_net_priv *)dev->priv; ++ if( !net_priv ) { ++ return NULL; ++ } ++ memset( net_priv , 0, sizeof(mv_net_priv) ); ++ net_priv->giga_priv = priv; ++ ++ dev->irq = ETH_PORT_IRQ_NUM(priv->port); ++ dev->mtu = mtu; ++ memcpy(dev->dev_addr, mac, 6); ++ dev->weight = (ETH_NUM_OF_RX_DESCR / 2); ++ dev->tx_queue_len = ETH_NUM_OF_TX_DESCR; ++ dev->watchdog_timeo = 5*HZ; ++ ++ dev->hard_start_xmit = eth_tx; ++ dev->vlan_rx_register = vlan_register; ++ dev->vlan_rx_add_vid = vlan_add_vid; ++ dev->vlan_rx_kill_vid = vlan_kill_vid; ++ dev->tx_timeout = eth_tx_timeout; ++ dev->poll = eth_poll; + + dev->open = mv_eth_open; + dev->stop = mv_eth_stop; + dev->set_mac_address = mv_eth_set_mac_addr; + dev->set_multicast_list = mv_eth_set_multicast_list; + dev->change_mtu = &mv_eth_change_mtu; +- ++ + #ifdef CONFIG_MV_GATEWAY + if(priv->isGtw) + { +- /* For Gateway driver replace some of callback functions */ +- dev->open = mv_gtw_start; ++ /* For Gateway driver replace some of callback functions */ ++ dev->open = mv_gtw_start; + dev->stop = mv_gtw_stop; + dev->set_mac_address = mv_gtw_set_mac_addr; + dev->set_multicast_list = mv_gtw_set_multicast_list; + dev->change_mtu = &mv_gtw_change_mtu; + } + #endif /* CONFIG_MV_GATEWAY */ +- ++ + priv->timer.data = (unsigned long)dev; + + mv_netdev_set_features(dev); +- +- if(register_netdev(dev)) { ++ ++ if(register_netdev(dev)) { + printk(KERN_ERR "failed to register %s\n",dev->name); +- free_netdev(dev); +- return NULL; +- } +- else { +- printk("%s ",dev->name); +- } +- return dev; +-} ++ free_netdev(dev); ++ return NULL; ++ } ++ else { ++ printk("%s ",dev->name); ++ } ++ return dev; ++} + + + /*********************************************************************************** +@@ -2491,17 +2621,17 @@ + } + printk("ethPort_%d Status: priv=%p\n\n", port, priv); + +- printk("tx_total=%d, rx_total=%d, tx_done_quota=%d\n\n", +- mv_eth_tx_desc_total, mv_eth_rx_desc_total, mv_eth_tx_done_quota); +- +-#ifdef ETH_MV_TX_EN +- printk("TxEnable WA - %s. deep=%d\n\n", +- priv->tx_en ? "Enabled" : "Disabled", priv->tx_en_deep); +-#endif /* ETH_MV_TX_EN */ +- ++ printk("tx_total=%d, rx_total=%d, tx_done_quota=%d\n\n", ++ mv_eth_tx_desc_total, mv_eth_rx_desc_total, mv_eth_tx_done_quota); ++ ++#ifdef ETH_MV_TX_EN ++ printk("TxEnable WA - %s. deep=%d\n\n", ++ priv->tx_en ? "Enabled" : "Disabled", priv->tx_en_deep); ++#endif /* ETH_MV_TX_EN */ ++ + #ifdef CONFIG_MV_SKB_REUSE + printk("SKB Reuse - %s. pktInfo pool status:\n", +- eth_skb_reuse_enable ? "Enabled" : "Disabled"); ++ eth_skb_reuse_enable ? "Enabled" : "Disabled"); + mvStackStatus(priv->skbReusePool, 0); + printk("\n"); + #endif /* CONFIG_MV_SKB_REUSE */ +@@ -2772,26 +2902,26 @@ + u8 mac_addr[6]; + int mtu; + +- +-#ifdef CONFIG_MV78200 +- /*if no ports assigned to this CPU, return*/ +- mv_eth_ports_num = mvCtrlEthMaxPortGet(); +-#ifdef CONFIG_MV_ETH_PORTS_NUM +- mv_eth_ports_num = min(mv_eth_ports_num, CONFIG_MV_ETH_PORTS_NUM); +-#endif ++ ++#ifdef CONFIG_MV78200 ++ /*if no ports assigned to this CPU, return*/ ++ mv_eth_ports_num = mvCtrlEthMaxPortGet(); ++#ifdef CONFIG_MV_ETH_PORTS_NUM ++ mv_eth_ports_num = min(mv_eth_ports_num, CONFIG_MV_ETH_PORTS_NUM); ++#endif + for(port=0; port < mv_eth_ports_num; port++) + { +- if (MV_TRUE == mvSocUnitIsMappedToThisCpu(GIGA0+port)) +- { +- break; +- } +- } +- if (port == mv_eth_ports_num) +- { ++ if (MV_TRUE == mvSocUnitIsMappedToThisCpu(GIGA0+port)) ++ { ++ break; ++ } ++ } ++ if (port == mv_eth_ports_num) ++ { + printk("No Giga ports mapped to this CPU\n"); +- return 1; +- } +-#endif ++ return 1; ++ } ++#endif + + printk( "Load Marvell Ethernet Driver\n"); + +@@ -2820,12 +2950,12 @@ + mv_netdev_config_show(); + + mv_eth_ports_num = mvCtrlEthMaxPortGet(); +-#ifdef CONFIG_MV_ETH_PORTS_NUM +- if (CONFIG_MV_ETH_PORTS_NUM < mv_eth_ports_num) +- { +- mv_eth_ports_num = CONFIG_MV_ETH_PORTS_NUM; +- } +-#endif ++#ifdef CONFIG_MV_ETH_PORTS_NUM ++ if (CONFIG_MV_ETH_PORTS_NUM < mv_eth_ports_num) ++ { ++ mv_eth_ports_num = CONFIG_MV_ETH_PORTS_NUM; ++ } ++#endif + mv_eth_ports = mvOsMalloc(mv_eth_ports_num*sizeof(mv_eth_priv*)); + if(mv_eth_ports == NULL) + { +@@ -2844,24 +2974,24 @@ + } + memset(mv_net_devs, 0, ((mv_eth_ports_num + GTW_MAX_NUM_OF_IFS) * sizeof(struct net_device*))); + +- printk(" o Loading network interface: \n"); ++ printk(" o Loading network interface: \n"); + for(port=0; portvlan_valid, 0, 4096); // set all vlan ids to invalid + #if defined(CONFIG_MV_GATEWAY) + priv->isGtw = mvBoardIsSwitchConnected(port); + priv->isGtw = 0; +@@ -2888,7 +3019,7 @@ + mv_eth_priv_cleanup(priv); + return 1; + } +- if( mv_eth_hal_init(priv, gtw_config.mtu, NULL) ) ++ if( mv_eth_hal_init(priv, gtw_config.mtu, NULL) ) + { + printk("eth_init_module: can't init eth_hal driver\n"); + mv_eth_priv_cleanup(priv); +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_netdev.h linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_netdev.h +--- linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_netdev.h 2008-09-07 11:53:12.000000000 -0700 ++++ linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_netdev.h 2009-09-09 21:12:46.000000000 -0700 +@@ -1,149 +1,150 @@ +-/******************************************************************************* +-Copyright (C) Marvell International Ltd. and its affiliates +- +-This software file (the "File") is owned and distributed by Marvell +-International Ltd. and/or its affiliates ("Marvell") under the following +-alternative licensing terms. Once you have made an election to distribute the +-File under one of the following license alternatives, please (i) delete this +-introductory statement regarding license alternatives, (ii) delete the two +-license alternatives that you have not elected to use and (iii) preserve the +-Marvell copyright notice above. +- +- +-******************************************************************************** +-Marvell GPL License Option +- +-If you received this File from Marvell, you may opt to use, redistribute and/or +-modify this File in accordance with the terms and conditions of the General +-Public License Version 2, June 1991 (the "GPL License"), a copy of which is +-available along with the File in the license.txt file or by writing to the Free +-Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +-on the worldwide web at http://www.gnu.org/licenses/gpl.txt. +- +-THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +-WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +-DISCLAIMED. The GPL License provides additional details about this warranty +-disclaimer. +-*******************************************************************************/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "mvOs.h" ++/******************************************************************************* ++Copyright (C) Marvell International Ltd. and its affiliates ++ ++This software file (the "File") is owned and distributed by Marvell ++International Ltd. and/or its affiliates ("Marvell") under the following ++alternative licensing terms. Once you have made an election to distribute the ++File under one of the following license alternatives, please (i) delete this ++introductory statement regarding license alternatives, (ii) delete the two ++license alternatives that you have not elected to use and (iii) preserve the ++Marvell copyright notice above. ++ ++ ++******************************************************************************** ++Marvell GPL License Option ++ ++If you received this File from Marvell, you may opt to use, redistribute and/or ++modify this File in accordance with the terms and conditions of the General ++Public License Version 2, June 1991 (the "GPL License"), a copy of which is ++available along with the File in the license.txt file or by writing to the Free ++Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or ++on the worldwide web at http://www.gnu.org/licenses/gpl.txt. ++ ++THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED ++WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY ++DISCLAIMED. The GPL License provides additional details about this warranty ++disclaimer. ++*******************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mvOs.h" + #include "mvDebug.h" +-#include "mvStack.h" +-#include "mvSysHwConfig.h" +-#include "eth/mvEth.h" +-#include "eth-phy/mvEthPhy.h" +-#include "ctrlEnv/sys/mvSysGbe.h" +-#include "dbg-trace.h" +- +-#if defined(CONFIG_MV_ETH_TSO) +-# define ETH_INCLUDE_TSO +-#endif /* CONFIG_MV_ETH_TSO */ +- +-#if defined(CONFIG_INET_LRO) +- /* LRO support */ +-# include +-# define ETH_INCLUDE_LRO +-# define ETH_LRO_MAX_AGGR 8 +-# define ETH_LRO_MAX_DESCRIPTORS 4 +- +-#if defined(CONFIG_MV_ETH_LRO_UDP) +-# define ETH_INCLUDE_LRO_UDP +-#endif /* CONFIG_MV_ETH_LRO_UDP */ +- +-#endif /* CONFIG_INET_LRO */ +- +-#if defined(CONFIG_MV_ETH_UFO) +- /* UFO support */ +-# include +-# define ETH_INCLUDE_UFO +-#endif +- +-#ifdef CONFIG_MV_ETH_NFP +-# include "eth/nfp/mvNfp.h" +-# include "../nfp_mgr/mv_nfp_mgr.h" +-#endif /* CONFIG_MV_ETH_NFP */ +- +-/****************************************************** +- * driver debug control -- * +- ******************************************************/ +-/* debug main on/off switch (more in debug control below ) */ +-#define ETH_DEBUG +-#undef ETH_DEBUG +- +-#define ETH_DBG_OFF 0x0000 +-#define ETH_DBG_RX 0x0001 +-#define ETH_DBG_TX 0x0002 +-#define ETH_DBG_RX_FILL 0x0004 +-#define ETH_DBG_TX_DONE 0x0008 +-#define ETH_DBG_LOAD 0x0010 +-#define ETH_DBG_IOCTL 0x0020 +-#define ETH_DBG_INT 0x0040 +-#define ETH_DBG_STATS 0x0080 +-#define ETH_DBG_POLL 0x0100 +-#define ETH_DBG_GSO 0x0200 +-#define ETH_DBG_ALL 0xffff +- +-#ifdef ETH_DEBUG +-extern u32 eth_dbg; +-# define ETH_DBG(FLG, X) if( (eth_dbg & (FLG)) == (FLG) ) printk X +-#else +-# define ETH_DBG(FLG, X) +-#endif +- +- +-/****************************************************** +- * driver statistics control -- * +- ******************************************************/ +-/* statistics main on/off switch (more in statistics control below ) */ +- +-#ifdef ETH_STATISTICS +-# define ETH_STAT(CODE) CODE; +-#else +-# define ETH_STAT(CODE) +-#endif +- +- +-/* rx buffer size */ +-/* 2(HW hdr) + 4(VLAN) + 14(MAC hdr) + 4(CRC) */ +-#define MV_WRAP (2 + 4 + ETH_HLEN + 4) +- +-#define MV_RX_BUF_SIZE(mtu) MV_ALIGN_UP(((mtu) + MV_WRAP), CPU_D_CACHE_LINE_SIZE) +- +-/* Interrupt Cause Masks */ +-#define ETH_TXQ_MASK (((1 << MV_ETH_TX_Q_NUM) - 1) << ETH_CAUSE_TX_BUF_OFFSET) +-#define ETH_LINK_MASK ((1 << ETH_CAUSE_PHY_STATUS_CHANGE_BIT) | (1 << ETH_CAUSE_LINK_STATE_CHANGE_BIT)) +-#define ETH_RXQ_MASK (((1 << MV_ETH_RX_Q_NUM) - 1) << ETH_CAUSE_RX_READY_OFFSET) +-#define ETH_RXQ_RES_MASK (((1 << MV_ETH_RX_Q_NUM) - 1) << ETH_CAUSE_RX_ERROR_OFFSET) +- +-/* Gigabit Ethernet Port Interrupt Mask and Port Interrupt Mask Extend Registers (PIMR and PIMER) */ +-#define ETH_PICR_MASK (BIT1 | ETH_RXQ_MASK | ETH_RXQ_RES_MASK) +-/* phy/link-status-change, tx-done-q0 - q7 */ +-#define ETH_PICER_MASK (ETH_LINK_MASK | ETH_TXQ_MASK) ++#include "mvStack.h" ++#include "mvSysHwConfig.h" ++#include "eth/mvEth.h" ++#include "eth-phy/mvEthPhy.h" ++#include "ctrlEnv/sys/mvSysGbe.h" ++#include "dbg-trace.h" ++ ++#if defined(CONFIG_MV_ETH_TSO) ++# define ETH_INCLUDE_TSO ++#endif /* CONFIG_MV_ETH_TSO */ ++ ++#if defined(CONFIG_INET_LRO) ++ /* LRO support */ ++# include ++# define ETH_INCLUDE_LRO ++# define ETH_LRO_MAX_AGGR 8 ++# define ETH_LRO_MAX_DESCRIPTORS 4 ++ ++#if defined(CONFIG_MV_ETH_LRO_UDP) ++# define ETH_INCLUDE_LRO_UDP ++#endif /* CONFIG_MV_ETH_LRO_UDP */ ++ ++#endif /* CONFIG_INET_LRO */ ++ ++#if defined(CONFIG_MV_ETH_UFO) ++ /* UFO support */ ++# include ++# define ETH_INCLUDE_UFO ++#endif ++ ++#ifdef CONFIG_MV_ETH_NFP ++# include "eth/nfp/mvNfp.h" ++# include "../nfp_mgr/mv_nfp_mgr.h" ++#endif /* CONFIG_MV_ETH_NFP */ ++ ++/****************************************************** ++ * driver debug control -- * ++ ******************************************************/ ++/* debug main on/off switch (more in debug control below ) */ ++#define ETH_DEBUG ++#undef ETH_DEBUG ++ ++#define ETH_DBG_OFF 0x0000 ++#define ETH_DBG_RX 0x0001 ++#define ETH_DBG_TX 0x0002 ++#define ETH_DBG_RX_FILL 0x0004 ++#define ETH_DBG_TX_DONE 0x0008 ++#define ETH_DBG_LOAD 0x0010 ++#define ETH_DBG_IOCTL 0x0020 ++#define ETH_DBG_INT 0x0040 ++#define ETH_DBG_STATS 0x0080 ++#define ETH_DBG_POLL 0x0100 ++#define ETH_DBG_GSO 0x0200 ++#define ETH_DBG_ALL 0xffff ++ ++#ifdef ETH_DEBUG ++//extern u32 eth_dbg; ++static u32 eth_dbg = ETH_DBG_ALL; ++# define ETH_DBG(FLG, X) if( (eth_dbg & (FLG)) == (FLG) ) printk X ++#else ++# define ETH_DBG(FLG, X) ++#endif ++ ++ ++/****************************************************** ++ * driver statistics control -- * ++ ******************************************************/ ++/* statistics main on/off switch (more in statistics control below ) */ ++ ++#ifdef ETH_STATISTICS ++# define ETH_STAT(CODE) CODE; ++#else ++# define ETH_STAT(CODE) ++#endif ++ ++ ++/* rx buffer size */ ++/* 2(HW hdr) + 4(VLAN) + 14(MAC hdr) + 4(CRC) */ ++#define MV_WRAP (2 + 4 + ETH_HLEN + 4) ++ ++#define MV_RX_BUF_SIZE(mtu) MV_ALIGN_UP(((mtu) + MV_WRAP), CPU_D_CACHE_LINE_SIZE) ++ ++/* Interrupt Cause Masks */ ++#define ETH_TXQ_MASK (((1 << MV_ETH_TX_Q_NUM) - 1) << ETH_CAUSE_TX_BUF_OFFSET) ++#define ETH_LINK_MASK ((1 << ETH_CAUSE_PHY_STATUS_CHANGE_BIT) | (1 << ETH_CAUSE_LINK_STATE_CHANGE_BIT)) ++#define ETH_RXQ_MASK (((1 << MV_ETH_RX_Q_NUM) - 1) << ETH_CAUSE_RX_READY_OFFSET) ++#define ETH_RXQ_RES_MASK (((1 << MV_ETH_RX_Q_NUM) - 1) << ETH_CAUSE_RX_ERROR_OFFSET) ++ ++/* Gigabit Ethernet Port Interrupt Mask and Port Interrupt Mask Extend Registers (PIMR and PIMER) */ ++#define ETH_PICR_MASK (BIT1 | ETH_RXQ_MASK | ETH_RXQ_RES_MASK) ++/* phy/link-status-change, tx-done-q0 - q7 */ ++#define ETH_PICER_MASK (ETH_LINK_MASK | ETH_TXQ_MASK) + + + #if defined(CONFIG_MV_GATEWAY) + #define GTW_MAX_NUM_OF_IFS 5 +- +-struct mv_vlan_cfg { +- char name[IFNAMSIZ]; +- unsigned int ports_mask; +- unsigned int ports_link; ++ ++struct mv_vlan_cfg { ++ char name[IFNAMSIZ]; ++ unsigned int ports_mask; ++ unsigned int ports_link; + unsigned short vlan_grp_id; +- unsigned short header; +- unsigned char macaddr[ETH_ALEN]; ++ unsigned short header; ++ unsigned char macaddr[ETH_ALEN]; + }; + + struct mv_gtw_config +@@ -157,102 +158,103 @@ + + #else + #define GTW_MAX_NUM_OF_IFS 0 +-#endif /* CONFIG_MV_GATEWAY */ +- +-typedef struct _eth_statistics +-{ +- /* interrupt stats */ +- u32 irq_total, irq_none, irq_while_polling; +- +- /* erorrs */ +- u32 skb_alloc_fail; +- u32 tx_timeout, tx_netif_stop; +- u32 tx_done_netif_wake; ++#endif /* CONFIG_MV_GATEWAY */ ++ ++typedef struct _eth_statistics ++{ ++ /* interrupt stats */ ++ u32 irq_total, irq_none, irq_while_polling; ++ ++ /* erorrs */ ++ u32 skb_alloc_fail; ++ u32 tx_timeout, tx_netif_stop; ++ u32 tx_done_netif_wake; + #if defined(CONFIG_MV_ETH_NFP) || defined(CONFIG_MV_SKB_REUSE) + u32 rx_pool_empty; + #endif /* CONFIG_MV_ETH_NFP || CONFIG_MV_SKB_REUSE */ +- +- /* Events */ +- u32 poll_events, poll_complete; +- u32 tx_events; +- u32 tx_done_events; +- u32 timer_events; +- u32 link_events; +- +-#ifdef ETH_STATISTICS +- /* rx stats */ +- u32 rx_hal_ok[MV_ETH_RX_Q_NUM]; +- u32 rx_netif_drop; +- u32 rx_dist[ETH_NUM_OF_RX_DESCR+1]; +- u32 rx_csum_hw, rx_csum_hw_frags, rx_csum_sw; +- +- /* skb stats */ +- u32 skb_alloc_ok, skb_free_ok; +- +- /* rx-fill stats */ +- u32 rx_fill_ok[MV_ETH_RX_Q_NUM]; +- +- /* tx stats */ +- u32 tx_hal_ok[MV_ETH_TX_Q_NUM]; +- u32 tx_hal_no_resource[MV_ETH_TX_Q_NUM]; +- u32 tx_csum_hw, tx_csum_sw; +- u32 tso_stats[64], ufo_stats[64]; +- +- /* tx-done stats */ +- u32 tx_done_hal_ok[MV_ETH_TX_Q_NUM]; +- u32 tx_done_dist[ETH_NUM_OF_TX_DESCR*MV_ETH_TX_Q_NUM + 1]; +- +-#ifdef ETH_MV_TX_EN +- u32 tx_en_done, tx_en_wait, tx_en_wait_count, tx_en_busy; +-#endif /* ETH_MV_TX_EN */ +- +-#ifdef CONFIG_MV_SKB_REUSE +- u32 skb_reuse_rx, skb_reuse_tx, skb_reuse_alloc; +-#endif /* CONFIG_MV_SKB_REUSE */ +- +-#endif /* ETH_STATISTICS */ +- +-} eth_statistics; +- +-typedef struct _mv_eth_priv +-{ +- int port; +- void* hal_priv; +- spinlock_t* lock; +- eth_statistics eth_stat; +- u32 picr; +- u32 picer; +- MV_STACK* txPktInfoPool; +- int tx_count[MV_ETH_TX_Q_NUM]; +- struct timer_list timer; +- unsigned int timer_flag; ++ ++ /* Events */ ++ u32 poll_events, poll_complete; ++ u32 tx_events; ++ u32 tx_done_events; ++ u32 timer_events; ++ u32 link_events; ++ ++#ifdef ETH_STATISTICS ++ /* rx stats */ ++ u32 rx_hal_ok[MV_ETH_RX_Q_NUM]; ++ u32 rx_netif_drop; ++ u32 rx_dist[ETH_NUM_OF_RX_DESCR+1]; ++ u32 rx_csum_hw, rx_csum_hw_frags, rx_csum_sw; ++ ++ /* skb stats */ ++ u32 skb_alloc_ok, skb_free_ok; ++ ++ /* rx-fill stats */ ++ u32 rx_fill_ok[MV_ETH_RX_Q_NUM]; ++ ++ /* tx stats */ ++ u32 tx_hal_ok[MV_ETH_TX_Q_NUM]; ++ u32 tx_hal_no_resource[MV_ETH_TX_Q_NUM]; ++ u32 tx_csum_hw, tx_csum_sw; ++ u32 tso_stats[64], ufo_stats[64]; ++ ++ /* tx-done stats */ ++ u32 tx_done_hal_ok[MV_ETH_TX_Q_NUM]; ++ u32 tx_done_dist[ETH_NUM_OF_TX_DESCR*MV_ETH_TX_Q_NUM + 1]; ++ ++#ifdef ETH_MV_TX_EN ++ u32 tx_en_done, tx_en_wait, tx_en_wait_count, tx_en_busy; ++#endif /* ETH_MV_TX_EN */ ++ ++#ifdef CONFIG_MV_SKB_REUSE ++ u32 skb_reuse_rx, skb_reuse_tx, skb_reuse_alloc; ++#endif /* CONFIG_MV_SKB_REUSE */ ++ ++#endif /* ETH_STATISTICS */ ++ ++} eth_statistics; ++ ++typedef struct _mv_eth_priv ++{ ++ int port; ++ void* hal_priv; ++ spinlock_t* lock; ++ eth_statistics eth_stat; ++ u32 picr; ++ u32 picer; ++ MV_STACK* txPktInfoPool; ++ int tx_count[MV_ETH_TX_Q_NUM]; ++ struct timer_list timer; ++ unsigned int timer_flag; + struct net_device *net_dev; /* back reference to the net_device */ +- +-#if defined(ETH_INCLUDE_TSO) || defined(ETH_INCLUDE_UFO) || defined(CONFIG_MV_GATEWAY) +-# define TX_EXTRA_BUF_SIZE 128 +- +- char** tx_extra_bufs[MV_ETH_TX_Q_NUM]; +- int tx_extra_buf_idx[MV_ETH_TX_Q_NUM]; +-#endif /* ETH_INCLUDE_TSO || ETH_INCLUDE_UFO */ +- +-#ifdef ETH_INCLUDE_LRO +- u32 lro_max_aggr; +- struct net_lro_mgr lro_mgr; +- struct net_lro_desc lro_desc[ETH_LRO_MAX_DESCRIPTORS]; +-#endif /* ETH_INCLUDE_LRO */ +- +-#ifdef CONFIG_MV_SKB_REUSE +- MV_STACK* skbReusePool; +-#endif /* CONFIG_MV_SKB_REUSE */ +- +-#ifdef CONFIG_MV_ETH_NFP +- MV_FP_STATS fpStats; +- unsigned int refill_needed_flag; +-#endif /* CONFIG_MV_ETH_NFP */ +- +-#if defined(CONFIG_MV_ETH_NFP) || defined(CONFIG_MV_SKB_REUSE) +- MV_STACK* fpRxPool; +-#endif /* (CONFIG_MV_ETH_NFP) || (CONFIG_MV_SKB_REUSE) */ ++ struct vlan_group *grp; ++ u8 vlan_valid[4096]; ++#if defined(ETH_INCLUDE_TSO) || defined(ETH_INCLUDE_UFO) || defined(CONFIG_MV_GATEWAY) ++# define TX_EXTRA_BUF_SIZE 128 ++ ++ char** tx_extra_bufs[MV_ETH_TX_Q_NUM]; ++ int tx_extra_buf_idx[MV_ETH_TX_Q_NUM]; ++#endif /* ETH_INCLUDE_TSO || ETH_INCLUDE_UFO */ ++ ++#ifdef ETH_INCLUDE_LRO ++ u32 lro_max_aggr; ++ struct net_lro_mgr lro_mgr; ++ struct net_lro_desc lro_desc[ETH_LRO_MAX_DESCRIPTORS]; ++#endif /* ETH_INCLUDE_LRO */ ++ ++#ifdef CONFIG_MV_SKB_REUSE ++ MV_STACK* skbReusePool; ++#endif /* CONFIG_MV_SKB_REUSE */ ++ ++#ifdef CONFIG_MV_ETH_NFP ++ MV_FP_STATS fpStats; ++ unsigned int refill_needed_flag; ++#endif /* CONFIG_MV_ETH_NFP */ ++ ++#if defined(CONFIG_MV_ETH_NFP) || defined(CONFIG_MV_SKB_REUSE) ++ MV_STACK* fpRxPool; ++#endif /* (CONFIG_MV_ETH_NFP) || (CONFIG_MV_SKB_REUSE) */ + + #if defined(CONFIG_MV_GATEWAY) + int isGtw; +@@ -263,34 +265,34 @@ + int tx_en_deep; + MV_BOOL tx_en_bk; + #endif /* ETH_MV_TX_EN */ +- +-} mv_eth_priv; +- +-typedef struct _mv_priv +-{ +- mv_eth_priv *giga_priv; +-#ifdef CONFIG_MV_GATEWAY +- struct mv_vlan_cfg *vlan_cfg; /* reference to entry in net config table */ +-#endif +- +-} mv_net_priv; +- +-#define MV_ETH_PRIV(dev) (((mv_net_priv*)((dev)->priv))->giga_priv) ++ ++} mv_eth_priv; ++ ++typedef struct _mv_priv ++{ ++ mv_eth_priv *giga_priv; ++#ifdef CONFIG_MV_GATEWAY ++ struct mv_vlan_cfg *vlan_cfg; /* reference to entry in net config table */ ++#endif ++ ++} mv_net_priv; ++ ++#define MV_ETH_PRIV(dev) (((mv_net_priv*)((dev)->priv))->giga_priv) + #define MV_NETDEV_STATS(dev) (&((dev)->stats)) +-#define MV_NETDEV_VLAN(dev) (((mv_net_priv*)((dev)->priv))->vlan_cfg) +-#define MV_GTW_VLANID_TO_GROUP(vlanid) ((((vlanid) & 0xf00) >> 8)-1) ++#define MV_NETDEV_VLAN(dev) (((mv_net_priv*)((dev)->priv))->vlan_cfg) ++#define MV_GTW_VLANID_TO_GROUP(vlanid) ((((vlanid) & 0xf00) >> 8)-1) + + extern int mv_eth_rxq_desc[MV_ETH_RX_Q_NUM]; + extern int mv_eth_txq_desc[MV_ETH_TX_Q_NUM]; + extern int mv_eth_rx_desc_total; + extern int mv_eth_tx_desc_total; +- ++ + extern int mv_eth_tx_done_quota; + + extern spinlock_t mii_lock; + extern spinlock_t nfp_lock; +- +-extern struct net_device** mv_net_devs; ++ ++extern struct net_device** mv_net_devs; + extern int mv_net_devs_num; + + extern mv_eth_priv** mv_eth_ports; +@@ -299,7 +301,7 @@ + #ifdef CONFIG_MV_SKB_REUSE + extern int eth_skb_reuse_enable; + #endif /* CONFIG_MV_SKB_REUSE */ +- ++ + static INLINE void mv_eth_save_interrupts(mv_eth_priv *priv) + { + priv->picr |= MV_REG_READ(ETH_INTR_CAUSE_REG(priv->port)) & ETH_PICR_MASK; +@@ -347,11 +349,11 @@ + MV_REG_WRITE( ETH_INTR_CAUSE_EXT_REG(priv->port), 0 ); + } + /**************************************************************************************************************/ +- ++ + /* Function prototypes */ + +-#ifdef CONFIG_MV_GATEWAY +-extern int mv_gtw_start(struct net_device *dev); ++#ifdef CONFIG_MV_GATEWAY ++extern int mv_gtw_start(struct net_device *dev); + extern int mv_gtw_stop(struct net_device *dev); + extern int mv_gtw_change_mtu(struct net_device *dev, int mtu); + extern int mv_gtw_set_mac_addr( struct net_device *dev, void *mac ); +@@ -360,20 +362,21 @@ + extern void mv_gtw_init_complete(mv_eth_priv* priv); + + extern void mv_gtw_update_tx_skb(unsigned int *switch_info, struct net_device *dev, MV_PKT_INFO *pPktInfo); +-extern struct net_device *mv_gtw_get_rx_dev(struct sk_buff *skb, unsigned int *if_num, int *port); ++extern struct net_device *mv_gtw_get_rx_dev(struct sk_buff *skb, unsigned int *if_num, int *port); + #endif /* CONFIG_MV_GATEWAY */ +- +-extern int mv_eth_open(struct net_device *dev); ++ ++extern int mv_eth_open(struct net_device *dev); + extern int mv_eth_stop(struct net_device *dev); + extern int mv_eth_change_mtu(struct net_device *dev, int mtu); +-extern int mv_eth_set_mac_addr( struct net_device *dev, void *mac ); ++extern int mv_eth_set_mac_addr( struct net_device *dev, void *mac ); + extern void mv_eth_set_multicast_list(struct net_device *dev); +- +-void mv_netdev_config_show(void); +-int mv_eth_hal_init(mv_eth_priv *priv, int mtu, u8* mac); ++void eth_check_link_status(struct net_device *dev); ++ ++void mv_netdev_config_show(void); ++int mv_eth_hal_init(mv_eth_priv *priv, int mtu, u8* mac); + int mv_eth_priv_init(mv_eth_priv *priv, int port); +-void mv_eth_priv_cleanup(mv_eth_priv *priv); +-struct net_device* mv_netdev_init(mv_eth_priv *priv, int mtu, u8* mac); ++void mv_eth_priv_cleanup(mv_eth_priv *priv); ++struct net_device* mv_netdev_init(mv_eth_priv *priv, int mtu, u8* mac); + void mv_netdev_status_print(unsigned int idx); + void mv_eth_status_print( unsigned int port ); + void mv_eth_stats_print( unsigned int port ); +@@ -394,4 +397,4 @@ + #ifdef ETH_MV_TX_EN + void eth_tx_en_config(int port, int value); + #endif /* ETH_MV_TX_EN */ +- ++ +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_switch_proc.c linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_switch_proc.c +--- linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_switch_proc.c 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_drivers_lsp/mv_network/mv_ethernet/mv_switch_proc.c 2009-09-09 20:26:24.000000000 -0700 +@@ -0,0 +1,196 @@ ++/******************************************************************************* ++*******************************************************************************/ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include "ctrlEnv/mvCtrlEnvLib.h" ++ ++#include "mv_netdev.h" ++ ++int proc_mv_vlan_table_read(struct seq_file *seq, void *offset); ++int proc_mv_vlan_table_open(struct inode *inode, struct file *file); ++ssize_t proc_mv_vlan_table_write(struct file *instance, const char __user *userbuffer, size_t count, loff_t *data); ++ ++int proc_mv_vlan_port_read(struct seq_file *seq, void *offset); ++int proc_mv_vlan_port_open(struct inode *inode, struct file *file); ++ssize_t proc_mv_vlan_port_write(struct file *instance, const char __user *userbuffer, size_t count, loff_t *data); ++ ++ ++static u16 vlan_bitmap[4096]; ++static u16 vlan_port[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; ++ ++static struct proc_dir_entry *proc_mv_switch_dir = NULL, *proc_mv_vlan_table_file = NULL, *proc_mv_vlan_port_file = NULL; ++ ++static const struct file_operations proc_mv_vlan_table_fops = { ++ .owner = THIS_MODULE, ++ .open = proc_mv_vlan_table_open, ++ .read = seq_read, ++ .write = proc_mv_vlan_table_write, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++int proc_mv_vlan_table_read(struct seq_file *seq, void *offset) ++{ ++ int i; ++ u32 table[3]; ++ seq_printf(seq, "vlan#\tPort mask\tEnabled\n"); ++ seq_printf(seq, "-----\t---------\t-------\n"); ++ for (i = 0; i < 4096; i++) ++ { ++ seq_printf(seq, "%3i \t 0x%3X \t%4i\n", i, (vlan_bitmap[i] >> 2) & 0x3FF, vlan_bitmap[i] & 0x1); ++ } ++ return 0; ++} ++ ++int proc_mv_vlan_table_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, proc_mv_vlan_table_read, NULL); ++} ++ ++ssize_t proc_mv_vlan_table_write(struct file *instance, const char __user *userbuffer, size_t count, loff_t *data) ++{ ++ char *string; ++ char command[24]; ++ int arg1 = 0, arg2 = 0, arg3 = 0; ++ int not_copied = 0; ++ u32 table[3]; ++ ++ table[0] = 0; ++ table[1] = 0; ++ table[2] = 0; ++ ++ string = kmalloc(count, GFP_KERNEL); ++ not_copied = copy_from_user(string, userbuffer, count); ++ string[count - not_copied - 1] = 0; ++ ++ sscanf(string, "%s %i %i %i", command, &arg1, &arg2, &arg3); ++ if (strncmp(command, "vlan", 4) == 0) ++ { ++ if (arg2 & (1 << 0)) ++ table[0] |= 0x1000000; ++ if (arg2 & (1 << 1)) ++ table[0] |= 0x4000000; ++ if (arg2 & (1 << 2)) ++ table[0] |= 0x10000000; ++ if (arg2 & (1 << 3)) ++ table[0] |= 0x40000000; ++ if (arg2 & (1 << 4)) ++ table[1] |= 0x1; ++ if (arg2 & (1 << 5)) ++ table[1] |= 0x4; ++ if (arg2 & (1 << 6)) ++ table[1] |= 0x10; ++ if (arg2 & (1 << 7)) ++ table[1] |= 0x40; ++ if (arg2 & (1 << 8)) ++ table[1] |= 0x100; ++ if (arg2 & (1 << 9)) ++ table[1] |= 0x400; ++ ++ if (arg3 == 0) ++ { ++ vlan_bitmap[arg1] = ((arg2 & 0x3FF) << 2); ++ } ++ else ++ { ++ vlan_bitmap[arg1] = ((arg2 & 0x3FF) << 2) | 0x1; ++ table[0] |= 0x68003; ++ } ++ mv_switch_write_vlan(0x1f, arg1, table); ++ } ++ return count; ++}; ++ ++static const struct file_operations proc_mv_vlan_port_fops = { ++ .owner = THIS_MODULE, ++ .open = proc_mv_vlan_port_open, ++ .read = seq_read, ++ .write = proc_mv_vlan_port_write, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++int proc_mv_vlan_port_read(struct seq_file *seq, void *offset) ++{ ++ int i; ++ u32 table[3]; ++ seq_printf(seq, "Port#\tvlan#\n"); ++ seq_printf(seq, "-----\t-----\n"); ++ for (i = 0; i < 10; i++) ++ { ++ seq_printf(seq, "%3i \t%5X\n", i, vlan_port[i]); ++ } ++ return 0; ++} ++ ++int proc_mv_vlan_port_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, proc_mv_vlan_port_read, NULL); ++} ++ ++ssize_t proc_mv_vlan_port_write(struct file *instance, const char __user *userbuffer, size_t count, loff_t *data) ++{ ++ char *string; ++ char command[24]; ++ int arg1 = 0, arg2 = 0; ++ int not_copied = 0; ++ u32 table[3]; ++ u32 temp_ret; ++ ++ table[0] = 0; ++ table[1] = 0; ++ table[2] = 0; ++ ++ string = kmalloc(count, GFP_KERNEL); ++ not_copied = copy_from_user(string, userbuffer, count); ++ string[count - not_copied - 1] = 0; ++ ++ sscanf(string, "%s %i %i", command, &arg1, &arg2); ++ if (strncmp(command, "port", 4) == 0 && arg1 >= 0 && arg1 < 10) ++ { ++ vlan_port[arg1] = arg2; ++ mv_switch_write(0x1f, 0x0B800320, 0x300000); ++ mv_switch_write(0x1f, 0x0B800324, 0x1000 | (arg2 & 0xFFF)); ++ mv_switch_write(0x1f, 0x0B800328, 0x8003 | (arg1 << 2)); ++ do ++ { ++ temp_ret = mv_switch_read(0x1f, 0x0B800328); ++ }while (temp_ret & 0x1); ++ } ++ return count; ++}; ++ ++int __init start_mv_switch_tool(void) ++{ ++#ifdef __NET_NET_NAMESPACE_H ++ proc_mv_switch_dir = proc_mkdir("mv_switch", init_net.proc_net); ++#else ++ proc_mv_switch_dir = proc_mkdir("mv_switch", proc_net); ++#endif ++ ++ proc_mv_vlan_table_file = create_proc_entry("vlan_table", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, proc_mv_switch_dir); ++ proc_mv_vlan_table_file->proc_fops = &proc_mv_vlan_table_fops; ++ vlan_bitmap[1] = 0xffd; ++ ++ proc_mv_vlan_port_file = create_proc_entry("vlan_port", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, proc_mv_switch_dir); ++ proc_mv_vlan_port_file->proc_fops = &proc_mv_vlan_port_fops; ++ return 0; ++} ++ ++module_init(start_mv_switch_tool); +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_hal/eth-phy/mvEthPhy.c linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_hal/eth-phy/mvEthPhy.c +--- linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_hal/eth-phy/mvEthPhy.c 2008-09-08 10:17:22.000000000 -0700 ++++ linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_hal/eth-phy/mvEthPhy.c 2009-09-09 11:53:00.000000000 -0700 +@@ -208,6 +208,72 @@ + + } + ++MV_U32 mv_switch_read(MV_U16 phyAddr, MV_U32 phyOffset) ++{ ++ MV_U16 temp_ret; ++ MV_U32 ret; ++ ++ mvEthPhyRegWrite(phyAddr, 0x4, (phyOffset >> 16) & 0xffff); ++ mvEthPhyRegWrite(phyAddr, 0x5, (phyOffset) & 0xffff); ++ ++ do ++ { ++ mvEthPhyRegRead(phyAddr, 0x1f, &temp_ret); ++ }while (temp_ret != 0x3); ++ ++ mvEthPhyRegRead(phyAddr, 0x6, &temp_ret); ++ ret = temp_ret << 16; ++ mvEthPhyRegRead(phyAddr, 0x7, &temp_ret); ++ ret |= temp_ret; ++ ++ return ret; ++} ++ ++void mv_switch_write(MV_U16 phyAddr, MV_U32 phyOffset, MV_U32 value) ++{ ++ MV_U16 temp_ret; ++ ++ mvEthPhyRegWrite(phyAddr, 0x0, (phyOffset >> 16) & 0xffff); ++ mvEthPhyRegWrite(phyAddr, 0x1, (phyOffset) & 0xffff); ++ mvEthPhyRegWrite(phyAddr, 0x2, (value >> 16) & 0xffff); ++ mvEthPhyRegWrite(phyAddr, 0x3, (value) & 0xffff); ++ ++ do ++ { ++ mvEthPhyRegRead(phyAddr, 0x1f, &temp_ret); ++ }while (temp_ret != 0x3); ++} ++ ++void mv_switch_read_vlan(MV_U16 switch_addr, MV_U16 table_id, MV_U32 *ret) ++{ ++ MV_U32 temp_ret; ++ ++ mv_switch_write(switch_addr, 0x0A00000C, 0x8000 | (table_id & 0xFFF)); ++ do ++ { ++ temp_ret = mv_switch_read(switch_addr, 0x0A00000C); ++ }while (temp_ret & 0x8000); ++ ++ ret[0] = mv_switch_read(switch_addr, 0x0A000008); ++ ret[1] = mv_switch_read(switch_addr, 0x0A000004); ++ ret[2] = mv_switch_read(switch_addr, 0x0A000000); ++} ++ ++void mv_switch_write_vlan(MV_U16 switch_addr, MV_U16 table_id, MV_U32 *table) ++{ ++ MV_U32 temp_ret; ++ ++ mv_switch_write(switch_addr, 0x0A000000, table[2]); ++ mv_switch_write(switch_addr, 0x0A000004, table[1]); ++ mv_switch_write(switch_addr, 0x0A000008, table[0]); ++ ++ mv_switch_write(switch_addr, 0x0A00000C, 0x9000 | (table_id & 0xFFF)); ++ do ++ { ++ temp_ret = mv_switch_read(switch_addr, 0x0A00000C); ++ }while (temp_ret & 0x8000); ++} ++ + /******************************************************************************* + * mvEthPhyReset - Reset ethernet Phy. + * +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_hal/eth-phy/mvEthPhy.h linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_hal/eth-phy/mvEthPhy.h +--- linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_hal/eth-phy/mvEthPhy.h 2008-06-18 13:47:56.000000000 -0700 ++++ linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_hal/eth-phy/mvEthPhy.h 2009-09-09 11:13:38.000000000 -0700 +@@ -70,12 +70,16 @@ + #include "mvEthPhyRegs.h" + + +-MV_STATUS mvEthPhyRegRead(MV_U32 phyAddr, MV_U32 regOffs, MV_U16 *data); ++MV_STATUS mvEthPhyRegRead(MV_U32 phyAddr, MV_U32 regOffs, MV_U16 *data); + MV_STATUS mvEthPhyRegWrite(MV_U32 phyAddr, MV_U32 regOffs, MV_U16 data); ++MV_U32 mv_switch_read(MV_U16 phyAddr, MV_U32 phyOffset); ++void mv_switch_write(MV_U16 phyAddr, MV_U32 phyOffset, MV_U32 value); ++void mv_switch_read_vlan(MV_U16 switch_addr, MV_U16 table_id, MV_U32 *ret); ++void mv_switch_write_vlan(MV_U16 switch_addr, MV_U16 table_id, MV_U32 *table); + MV_STATUS mvEthPhyReset(MV_U32 phyAddr, int timeout); + MV_STATUS mvEthPhyRestartAN(MV_U32 phyAddr, int timeout); +-MV_BOOL mvEthPhyCheckLink( MV_U32 phyAddr ); +-MV_STATUS mvEthPhyPrintStatus( MV_U32 phyAddr ); ++MV_BOOL mvEthPhyCheckLink( MV_U32 phyAddr ); ++MV_STATUS mvEthPhyPrintStatus( MV_U32 phyAddr ); + + MV_VOID mvEthE1111PhyBasicInit(MV_U32 ethPortNum); + MV_VOID mvEthE1112PhyBasicInit(MV_U32 ethPortNum); +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_hal/eth-phy/mvEthPhyRegs.h linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_hal/eth-phy/mvEthPhyRegs.h +--- linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_hal/eth-phy/mvEthPhyRegs.h 2008-07-20 13:03:04.000000000 -0700 ++++ linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_hal/eth-phy/mvEthPhyRegs.h 2009-08-30 23:36:17.000000000 -0700 +@@ -71,6 +71,11 @@ + #define ETH_PHY_TIMEOUT 10000 + + /* registers offsetes defines */ ++#define ETH_PHY_SMI_ACCEL_REG (MV_ETH_REG_BASE(0) | 0x014) /* Undocumented register */ ++#define ETH_PHY_SMI_ACCEL_8_OFFS 1 /* SMI Speed is core_clk/8 */ ++#define ETH_PHY_SMI_ACCEL_16_OFFS 0 /* SMI Speed is core_clk/16 */ ++ ++/* registers offsetes defines */ + #ifdef MV_88W8660 + #define ETH_PHY_SMI_REG (MV_ETH_REG_BASE(0) + 0x6010) + #else +diff -ruN linux-2.6.22.18-mv-clean/drivers/hwmon/gsp.c linux-2.6.22.18-mv/drivers/hwmon/gsp.c +--- linux-2.6.22.18-mv-clean/drivers/hwmon/gsp.c 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/hwmon/gsp.c 2009-08-12 10:28:08.000000000 -0700 +@@ -0,0 +1,252 @@ ++/* ++ * A hwmon driver for the Gateworks System Peripheral ++ * Copyright (C) 2009 Gateworks Corporation ++ * ++ * Author: Chris Lang ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License, ++ * as published by the Free Software Foundation - version 2. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "lm75.h" ++ ++ ++#define DRV_VERSION "0.1" ++ ++static const unsigned short normal_i2c[] = { 0x29, I2C_CLIENT_END }; ++I2C_CLIENT_INSMOD_1(gsp); ++//enum chips { gsp }; ++ ++/* AD7418 registers */ ++#define GSP_REG_TEMP_IN 0x00 ++#define GSP_REG_VIN 0x02 ++#define GSP_REG_3P3 0x05 ++#define GSP_REG_BAT 0x08 ++#define GSP_REG_5P0 0x0b ++#define GSP_REG_CORE 0x0e ++#define GSP_REG_CPU1 0x11 ++#define GSP_REG_CPU2 0x14 ++#define GSP_REG_DRAM 0x17 ++#define GSP_REG_EXT_BAT 0x1a ++#define GSP_REG_IO1 0x1d ++#define GSP_REG_IO2 0x20 ++#define GSP_REG_PCIE 0x23 ++#define GSP_REG_CURRENT 0x26 ++ ++static const u8 GSP_REGISTERS[] = { ++ GSP_REG_TEMP_IN, ++ GSP_REG_VIN, ++ GSP_REG_3P3, ++ GSP_REG_BAT, ++ GSP_REG_5P0, ++ GSP_REG_CORE, ++ GSP_REG_CPU1, ++ GSP_REG_CPU2, ++ GSP_REG_DRAM, ++ GSP_REG_EXT_BAT, ++ GSP_REG_IO1, ++ GSP_REG_IO2, ++ GSP_REG_PCIE, ++ GSP_REG_CURRENT ++}; ++ ++struct gsp_data { ++ struct i2c_client client; ++ struct device *hwmon_dev; ++ struct attribute_group attrs; ++}; ++ ++ ++static int gsp_attach_adapter(struct i2c_adapter *adapter); ++static int gsp_detect(struct i2c_adapter *adapter, int address, int kind); ++static int gsp_detach_client(struct i2c_client *client); ++ ++#if 0 ++static int gsp_probe(struct i2c_client *client, ++ const struct i2c_device_id *id); ++static int gsp_remove(struct i2c_client *client); ++ ++static const struct i2c_device_id gsp_id[] = { ++ { "gsp", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, gsp_id); ++#endif ++ ++static struct i2c_driver gsp_driver = { ++ .driver = { ++ .name = "gsp", ++ }, ++ .attach_adapter = gsp_attach_adapter, ++ .detach_client = gsp_detach_client, ++}; ++ ++/* All registers are word-sized, except for the configuration registers. ++ * AD7418 uses a high-byte first convention. Do NOT use those functions to ++ * access the configuration registers CONF and CONF2, as they are byte-sized. ++ */ ++static inline int gsp_read(struct i2c_client *client, u8 reg) ++{ ++ unsigned int adc = 0; ++ unsigned short temp = 0; ++ if (reg == GSP_REG_TEMP_IN) ++ { ++ temp |= i2c_smbus_read_byte_data(client, reg); ++ temp |= i2c_smbus_read_byte_data(client, reg + 1) << 8; ++ return (int)temp; ++ } ++ else ++ { ++ adc |= i2c_smbus_read_byte_data(client, reg); ++ adc |= i2c_smbus_read_byte_data(client, reg + 1) << 8; ++ adc |= i2c_smbus_read_byte_data(client, reg + 2) << 16; ++ return adc; ++ } ++} ++ ++static inline int gsp_write(struct i2c_client *client, u8 reg, u16 value) ++{ ++ //return i2c_smbus_write_word_data(client, reg, swab16(value)); ++ return 0; ++} ++ ++static ssize_t show_adc(struct device *dev, struct device_attribute *devattr, ++ char *buf) ++{ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct i2c_client *client = to_i2c_client(dev); ++ return sprintf(buf, "%d\n", gsp_read(client, GSP_REGISTERS[attr->index])); ++} ++ ++static SENSOR_DEVICE_ATTR(temp1, S_IRUGO, show_adc, NULL, 0); ++ ++static SENSOR_DEVICE_ATTR(vin, S_IRUGO, show_adc, NULL, 1); ++static SENSOR_DEVICE_ATTR(3p3, S_IRUGO, show_adc, NULL, 2); ++static SENSOR_DEVICE_ATTR(bat, S_IRUGO, show_adc, NULL, 3); ++static SENSOR_DEVICE_ATTR(5p0, S_IRUGO, show_adc, NULL, 4); ++static SENSOR_DEVICE_ATTR(core, S_IRUGO, show_adc, NULL, 5); ++static SENSOR_DEVICE_ATTR(cpu1, S_IRUGO, show_adc, NULL, 6); ++static SENSOR_DEVICE_ATTR(cpu2, S_IRUGO, show_adc, NULL, 7); ++static SENSOR_DEVICE_ATTR(dram, S_IRUGO, show_adc, NULL, 8); ++static SENSOR_DEVICE_ATTR(ext_bat, S_IRUGO, show_adc, NULL, 9); ++static SENSOR_DEVICE_ATTR(io1, S_IRUGO, show_adc, NULL, 10); ++static SENSOR_DEVICE_ATTR(io2, S_IRUGO, show_adc, NULL, 11); ++static SENSOR_DEVICE_ATTR(pcie, S_IRUGO, show_adc, NULL, 12); ++static SENSOR_DEVICE_ATTR(power, S_IRUGO, show_adc, NULL, 13); ++ ++static int gsp_attach_adapter(struct i2c_adapter *adapter) ++{ ++ if (!(adapter->class & I2C_CLASS_HWMON)) ++ return 0; ++ return i2c_probe(adapter, &addr_data, gsp_detect); ++} ++ ++static struct attribute *gsp_attributes[] = { ++ &sensor_dev_attr_temp1.dev_attr.attr, ++ &sensor_dev_attr_vin.dev_attr.attr, ++ &sensor_dev_attr_3p3.dev_attr.attr, ++ &sensor_dev_attr_bat.dev_attr.attr, ++ &sensor_dev_attr_5p0.dev_attr.attr, ++ &sensor_dev_attr_core.dev_attr.attr, ++ &sensor_dev_attr_cpu1.dev_attr.attr, ++ &sensor_dev_attr_cpu2.dev_attr.attr, ++ &sensor_dev_attr_dram.dev_attr.attr, ++ &sensor_dev_attr_ext_bat.dev_attr.attr, ++ &sensor_dev_attr_io1.dev_attr.attr, ++ &sensor_dev_attr_io2.dev_attr.attr, ++ &sensor_dev_attr_pcie.dev_attr.attr, ++ &sensor_dev_attr_power.dev_attr.attr, ++ NULL ++}; ++ ++ ++static int gsp_detect(struct i2c_adapter *adapter, int address, int kind) ++{ ++ struct i2c_client *client; ++ struct gsp_data *data; ++ int err = 0; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | ++ I2C_FUNC_SMBUS_WORD_DATA)) { ++ goto exit; ++ } ++ ++ if (!(data = kzalloc(sizeof(struct gsp_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ ++ client = &data->client; ++ client->addr = address; ++ client->adapter = adapter; ++ client->driver = &gsp_driver; ++ ++ i2c_set_clientdata(client, data); ++ ++ data->attrs.attrs = gsp_attributes; ++ strlcpy(client->name, "gsp", I2C_NAME_SIZE); ++ ++ if ((err = i2c_attach_client(client))) ++ goto exit_free; ++ ++ dev_info(&client->dev, "%s chip found\n", client->name); ++ ++ /* Register sysfs hooks */ ++ if ((err = sysfs_create_group(&client->dev.kobj, &data->attrs))) ++ goto exit_detach; ++ ++ data->hwmon_dev = hwmon_device_register(&client->dev); ++ if (IS_ERR(data->hwmon_dev)) { ++ err = PTR_ERR(data->hwmon_dev); ++ goto exit_remove; ++ } ++ ++ return 0; ++ ++exit_remove: ++ sysfs_remove_group(&client->dev.kobj, &data->attrs); ++exit_detach: ++ i2c_detach_client(client); ++exit_free: ++ kfree(data); ++exit: ++ return err; ++} ++ ++static int gsp_detach_client(struct i2c_client *client) ++{ ++ struct gsp_data *data = i2c_get_clientdata(client); ++ hwmon_device_unregister(data->hwmon_dev); ++ sysfs_remove_group(&client->dev.kobj, &data->attrs); ++ i2c_detach_client(client); ++ kfree(data); ++ return 0; ++} ++ ++static int __init gsp_init(void) ++{ ++ return i2c_add_driver(&gsp_driver); ++} ++ ++static void __exit gsp_exit(void) ++{ ++ i2c_del_driver(&gsp_driver); ++} ++ ++MODULE_AUTHOR("Chris Lang "); ++MODULE_DESCRIPTION("GSP HWMON driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(DRV_VERSION); ++ ++module_init(gsp_init); ++module_exit(gsp_exit); +diff -ruN linux-2.6.22.18-mv-clean/drivers/hwmon/Kconfig linux-2.6.22.18-mv/drivers/hwmon/Kconfig +--- linux-2.6.22.18-mv-clean/drivers/hwmon/Kconfig 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/hwmon/Kconfig 2009-08-12 10:15:07.000000000 -0700 +@@ -40,6 +40,15 @@ + This driver can also be built as a module. If so, the module + will be called abituguru. + ++config SENSORS_GSP ++ tristate "Gateworks System Peripheral" ++ depends on I2C && EXPERIMENTAL ++ help ++ If you say yes here you get support for the Gateworks System Peripherals. ++ ++ This driver can also be built as a module. If so, the module ++ will be called gsp. ++ + config SENSORS_AD7418 + tristate "Analog Devices AD7416, AD7417 and AD7418" + depends on I2C && EXPERIMENTAL +diff -ruN linux-2.6.22.18-mv-clean/drivers/hwmon/Makefile linux-2.6.22.18-mv/drivers/hwmon/Makefile +--- linux-2.6.22.18-mv-clean/drivers/hwmon/Makefile 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/hwmon/Makefile 2009-08-12 10:15:23.000000000 -0700 +@@ -14,6 +14,7 @@ + obj-$(CONFIG_SENSORS_W83791D) += w83791d.o + + obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o ++obj-$(CONFIG_SENSORS_GSP) += gsp.o + obj-$(CONFIG_SENSORS_AD7418) += ad7418.o + obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o + obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o +diff -ruN linux-2.6.22.18-mv-clean/drivers/rtc/rtc-ds1672.c linux-2.6.22.18-mv/drivers/rtc/rtc-ds1672.c +--- linux-2.6.22.18-mv-clean/drivers/rtc/rtc-ds1672.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/rtc/rtc-ds1672.c 2009-08-12 00:39:31.000000000 -0700 +@@ -16,7 +16,7 @@ + #define DRV_VERSION "0.3" + + /* Addresses to scan: none. This chip cannot be detected. */ +-static unsigned short normal_i2c[] = { I2C_CLIENT_END }; ++static unsigned short normal_i2c[] = { 0x68, I2C_CLIENT_END }; + + /* Insmod parameters */ + I2C_CLIENT_INSMOD; +diff -ruN linux-2.6.22.18-mv-clean/kernel-config linux-2.6.22.18-mv/kernel-config +--- linux-2.6.22.18-mv-clean/kernel-config 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/kernel-config 2009-02-20 21:30:05.000000000 -0800 +@@ -0,0 +1,1395 @@ ++# ++# Automatically generated make config: don't edit ++# Linux kernel version: 2.6.22.18-mv ++# Fri Feb 20 20:59:19 2009 ++# ++CONFIG_ARM=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++# CONFIG_GENERIC_GPIO is not set ++CONFIG_GENERIC_TIME=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_MMU=y ++# CONFIG_NO_IOPORT is not set ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++# CONFIG_ARCH_HAS_ILOG2_U32 is not set ++# CONFIG_ARCH_HAS_ILOG2_U64 is not set ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_ZONE_DMA=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++ ++# ++# Code maturity level options ++# ++CONFIG_EXPERIMENTAL=y ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++ ++# ++# General setup ++# ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++# CONFIG_IPC_NS is not set ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++# CONFIG_UTS_NS is not set ++# CONFIG_AUDIT is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=14 ++CONFIG_SYSFS_DEPRECATED=y ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_CC_OPTIMIZE_FOR_SIZE=y ++CONFIG_SYSCTL=y ++# CONFIG_EMBEDDED is not set ++CONFIG_UID16=y ++CONFIG_SYSCTL_SYSCALL=y ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_EXTRA_PASS is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_ANON_INODES=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLAB=y ++# CONFIG_SLUB is not set ++# CONFIG_SLOB is not set ++CONFIG_RT_MUTEXES=y ++# CONFIG_TINY_SHMEM is not set ++CONFIG_BASE_SMALL=0 ++ ++# ++# Loadable module support ++# ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++CONFIG_KMOD=y ++ ++# ++# Block layer ++# ++CONFIG_BLOCK=y ++CONFIG_LBD=y ++# CONFIG_BLK_DEV_IO_TRACE is not set ++# CONFIG_LSF is not set ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_AS=y ++# CONFIG_IOSCHED_DEADLINE is not set ++# CONFIG_IOSCHED_CFQ is not set ++CONFIG_DEFAULT_AS=y ++# CONFIG_DEFAULT_DEADLINE is not set ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="anticipatory" ++ ++# ++# System Type ++# ++# CONFIG_ARCH_AAEC2000 is not set ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_FEROCEON_ORION is not set ++# CONFIG_ARCH_FEROCEON_KW is not set ++CONFIG_ARCH_FEROCEON_MV78XX0=y ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS7500 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_CO285 is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_H720X is not set ++# CONFIG_ARCH_IMX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP23XX is not set ++# CONFIG_ARCH_IXP2000 is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_L7200 is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_NS9XXX is not set ++# CONFIG_ARCH_PNX4008 is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C2410 is not set ++# CONFIG_ARCH_SHARK is not set ++# CONFIG_ARCH_LH7A40X is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP is not set ++ ++# ++# Feroceon SoC options ++# ++CONFIG_MV78XX0=y ++# CONFIG_MV78XX0_Z0 is not set ++CONFIG_MV78200=y ++# CONFIG_MV78XX0_OVERRIDE_CMDLINE_ETH is not set ++# CONFIG_JTAG_DEBUG is not set ++ ++# ++# Feroceon SoC Included Features ++# ++CONFIG_MV_INCLUDE_PEX=y ++CONFIG_MV_INCLUDE_IDMA=y ++# CONFIG_MV_INCLUDE_USB is not set ++CONFIG_MV_INCLUDE_XOR=y ++CONFIG_MV_INCLUDE_CESA=y ++# CONFIG_MV_INCLUDE_NAND is not set ++# CONFIG_MV_INCLUDE_INTEG_SATA is not set ++CONFIG_MV_INCLUDE_GIG_ETH=y ++# CONFIG_MV_INCLUDE_SPI is not set ++CONFIG_MV_GPP_MAX_PINS=32 ++CONFIG_MV_DCACHE_SIZE=0x8000 ++CONFIG_MV_ICACHE_SIZE=0x8000 ++ ++# ++# Feroceon SoC MTD support ++# ++# CONFIG_MV_FLASH_CTRL is not set ++CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y ++CONFIG_USE_DSP=y ++CONFIG_L2_CACHE_ENABLE=y ++# CONFIG_CPU_L2_DCACHE_WRITETHROUGH is not set ++# CONFIG_FEROCEON_PROC is not set ++# CONFIG_MV_GENERIC_NAS_FS is not set ++CONFIG_UBOOT_STRUCT=y ++# CONFIG_MV_DBG_TRACE is not set ++ ++# ++# Soc DMA accelerations ++# ++CONFIG_MV_USE_XOR_ENGINE=y ++CONFIG_MV_RAID5_XOR_OFFLOAD=y ++CONFIG_MV_XORMEMCOPY=y ++CONFIG_MV_XOR_MEMCOPY_THRESHOLD=128 ++CONFIG_MV_XORMEMZERO=y ++CONFIG_MV_XOR_MEMZERO_THRESHOLD=192 ++# CONFIG_MV_USE_XOR_FOR_COPY_USER_BUFFERS is not set ++CONFIG_USE_TWO_ENGINES=y ++# CONFIG_USE_FOUR_ENGINES is not set ++ ++# ++# SoC Networking support ++# ++CONFIG_MV_ETHERNET=y ++CONFIG_MV_ETH_PORTS_NUM=2 ++ ++# ++# Network Interface Configuration ++# ++CONFIG_MV_ETH_0_MTU=1500 ++CONFIG_MV_ETH_0_MACADDR="64:00:00:00:00:01" ++CONFIG_MV_ETH_1_MTU=1500 ++CONFIG_MV_ETH_1_MACADDR="64:00:00:00:00:02" ++ ++# ++# Rx/Tx Queue Configuration ++# ++CONFIG_MV_ETH_RX_Q_NUM=1 ++CONFIG_MV_ETH_TX_Q_NUM=1 ++ ++# ++# TCP/UDP Offloading ++# ++CONFIG_MV_ETH_TSO=y ++CONFIG_MV_ETH_UFO=y ++ ++# ++# Control and Statistics ++# ++# CONFIG_MV_ETH_PROC is not set ++# CONFIG_MV_ETH_STATS is not set ++ ++# ++# Advanced Features ++# ++CONFIG_MV_ETH_TIMER_PERIOD=10 ++CONFIG_MV_SKB_HEADROOM=96 ++CONFIG_MV_SKB_REUSE=y ++CONFIG_MV_ETH_NFP=y ++CONFIG_MV_ETH_NFP_AGING_TIMER=15 ++CONFIG_MV_ETH_NFP_NAT_SUPPORT=y ++# CONFIG_MV_NFP_STATS is not set ++# CONFIG_MV_GATEWAY is not set ++ ++# ++# cesa options ++# ++CONFIG_MV_CESA=y ++CONFIG_MV_CESA_OCF=y ++CONFIG_MV_CESA_TOOL=y ++CONFIG_ARCH_FEROCEON=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_32=y ++CONFIG_CPU_ARM926T=y ++CONFIG_CPU_32v5=y ++CONFIG_CPU_ABRT_EV5TJ=y ++CONFIG_CPU_CACHE_VIVT=y ++CONFIG_CPU_COPY_V4WB=y ++CONFIG_CPU_TLB_V4WBI=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++CONFIG_ARM_THUMB=y ++# CONFIG_CPU_BIG_ENDIAN is not set ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set ++# CONFIG_CPU_CACHE_ROUND_ROBIN is not set ++# CONFIG_OUTER_CACHE is not set ++ ++# ++# Bus support ++# ++CONFIG_PCI=y ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++ ++# ++# PCCARD (PCMCIA/CardBus) support ++# ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++# CONFIG_TICK_ONESHOT is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ=100 ++# CONFIG_AEABI is not set ++CONFIG_REORDER=y ++# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set ++CONFIG_SELECT_MEMORY_MODEL=y ++CONFIG_FLATMEM_MANUAL=y ++# CONFIG_DISCONTIGMEM_MANUAL is not set ++# CONFIG_SPARSEMEM_MANUAL is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++# CONFIG_SPARSEMEM_STATIC is not set ++CONFIG_SPLIT_PTLOCK_CPUS=4096 ++# CONFIG_RESOURCES_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=1 ++# CONFIG_LEDS is not set ++CONFIG_ALIGNMENT_TRAP=y ++ ++# ++# Boot options ++# ++CONFIG_ZBOOT_ROM_TEXT=0x0 ++CONFIG_ZBOOT_ROM_BSS=0x0 ++CONFIG_CMDLINE="console=ttyS0 root=/dev/nfs rw nfsroot=10.4.50.31:/mnt/armFS mem=32M ip=10.4.50.99:10.4.50.31:::ARM:eth0:none" ++# CONFIG_XIP_KERNEL is not set ++# CONFIG_KEXEC is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++# CONFIG_FPE_NWFPE is not set ++# CONFIG_FPE_FASTFPE is not set ++CONFIG_VFP=y ++# CONFIG_VFP_FASTVFP is not set ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++# CONFIG_BINFMT_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++# CONFIG_ARTHUR is not set ++ ++# ++# Power management options ++# ++# CONFIG_PM is not set ++ ++# ++# Networking ++# ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++CONFIG_PACKET_MMAP=y ++CONFIG_UNIX=y ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_FIB_HASH=y ++# CONFIG_IP_PNP is not set ++CONFIG_NET_IPIP=y ++CONFIG_NET_IPGRE=y ++# CONFIG_NET_IPGRE_BROADCAST is not set ++# CONFIG_IPSEC_NAT_TRAVERSAL is not set ++# CONFIG_IP_MROUTE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=y ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++# CONFIG_INET_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IP_VS is not set ++# CONFIG_IPV6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++# CONFIG_NETWORK_SECMARK is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_BRIDGE_NETFILTER=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK is not set ++CONFIG_NF_CONNTRACK_ENABLED=y ++CONFIG_NF_CONNTRACK=y ++CONFIG_NF_CT_ACCT=y ++CONFIG_NF_CONNTRACK_MARK=y ++# CONFIG_NF_CONNTRACK_EVENTS is not set ++CONFIG_NF_CT_PROTO_GRE=y ++# CONFIG_NF_CT_PROTO_SCTP is not set ++# CONFIG_NF_CONNTRACK_AMANDA is not set ++CONFIG_NF_CONNTRACK_FTP=y ++CONFIG_NF_CONNTRACK_H323=y ++CONFIG_NF_CONNTRACK_IRC=m ++# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set ++CONFIG_NF_CONNTRACK_PPTP=y ++# CONFIG_NF_CONNTRACK_SANE is not set ++# CONFIG_NF_CONNTRACK_SIP is not set ++CONFIG_NF_CONNTRACK_TFTP=y ++# CONFIG_NF_CONNTRACK_RTSP is not set ++CONFIG_NETFILTER_XTABLES=y ++# CONFIG_NETFILTER_XT_TARGET_CHAOS is not set ++CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m ++CONFIG_NETFILTER_XT_TARGET_CONNMARK=m ++# CONFIG_NETFILTER_XT_TARGET_DELUDE is not set ++CONFIG_NETFILTER_XT_TARGET_DSCP=m ++CONFIG_NETFILTER_XT_TARGET_MARK=m ++# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set ++# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set ++# CONFIG_NETFILTER_XT_TARGET_TARPIT is not set ++CONFIG_NETFILTER_XT_TARGET_TCPMSS=m ++# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set ++CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m ++CONFIG_NETFILTER_XT_MATCH_CONNMARK=m ++CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m ++# CONFIG_NETFILTER_XT_MATCH_DCCP is not set ++CONFIG_NETFILTER_XT_MATCH_DSCP=m ++# CONFIG_NETFILTER_XT_MATCH_ESP is not set ++CONFIG_NETFILTER_XT_MATCH_HELPER=m ++CONFIG_NETFILTER_XT_MATCH_LENGTH=m ++CONFIG_NETFILTER_XT_MATCH_LIMIT=m ++CONFIG_NETFILTER_XT_MATCH_MAC=m ++CONFIG_NETFILTER_XT_MATCH_MARK=m ++# CONFIG_NETFILTER_XT_MATCH_PORTSCAN is not set ++CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m ++# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set ++# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set ++# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set ++# CONFIG_NETFILTER_XT_MATCH_REALM is not set ++# CONFIG_NETFILTER_XT_MATCH_SCTP is not set ++CONFIG_NETFILTER_XT_MATCH_STATE=m ++CONFIG_NETFILTER_XT_MATCH_LAYER7=m ++# CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG is not set ++CONFIG_NETFILTER_XT_MATCH_STATISTIC=m ++CONFIG_NETFILTER_XT_MATCH_STRING=m ++CONFIG_NETFILTER_XT_MATCH_TCPMSS=m ++# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set ++ ++# ++# IP: Netfilter Configuration ++# ++CONFIG_NF_CONNTRACK_IPV4=y ++CONFIG_NF_CONNTRACK_PROC_COMPAT=y ++CONFIG_IP_NF_QUEUE=m ++CONFIG_IP_NF_IPTABLES=y ++# CONFIG_IP_NF_MATCH_IPRANGE is not set ++CONFIG_IP_NF_MATCH_IPP2P=m ++CONFIG_IP_NF_MATCH_TOS=m ++CONFIG_IP_NF_MATCH_TIME=m ++CONFIG_IP_NF_MATCH_RECENT=m ++CONFIG_IP_NF_MATCH_ECN=m ++# CONFIG_IP_NF_MATCH_AH is not set ++CONFIG_IP_NF_MATCH_TTL=m ++# CONFIG_IP_NF_MATCH_OWNER is not set ++# CONFIG_IP_NF_MATCH_ADDRTYPE is not set ++CONFIG_IP_NF_FILTER=y ++CONFIG_IP_NF_TARGET_REJECT=m ++CONFIG_IP_NF_TARGET_LOG=m ++# CONFIG_IP_NF_TARGET_ULOG is not set ++CONFIG_NF_NAT=y ++CONFIG_NF_NAT_NEEDED=y ++CONFIG_IP_NF_TARGET_MASQUERADE=y ++CONFIG_IP_NF_TARGET_REDIRECT=y ++# CONFIG_IP_NF_TARGET_NETMAP is not set ++# CONFIG_IP_NF_TARGET_SAME is not set ++# CONFIG_NF_NAT_SNMP_BASIC is not set ++CONFIG_NF_NAT_PROTO_GRE=y ++CONFIG_NF_NAT_FTP=y ++CONFIG_NF_NAT_IRC=m ++# CONFIG_NF_NAT_RTSP is not set ++CONFIG_NF_NAT_TFTP=y ++# CONFIG_NF_NAT_AMANDA is not set ++CONFIG_NF_NAT_PPTP=y ++CONFIG_NF_NAT_H323=y ++# CONFIG_NF_NAT_SIP is not set ++CONFIG_IP_NF_MANGLE=y ++CONFIG_IP_NF_TARGET_IMQ=m ++CONFIG_IP_NF_TARGET_TOS=m ++CONFIG_IP_NF_TARGET_ECN=m ++CONFIG_IP_NF_TARGET_TTL=m ++# CONFIG_IP_NF_TARGET_CLUSTERIP is not set ++# CONFIG_IP_NF_RAW is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++# CONFIG_IP_NF_SET is not set ++# CONFIG_IP_NF_TARGET_ROUTE is not set ++ ++# ++# Bridge: Netfilter Configuration ++# ++CONFIG_BRIDGE_NF_EBTABLES=y ++CONFIG_BRIDGE_EBT_BROUTE=y ++CONFIG_BRIDGE_EBT_T_FILTER=y ++CONFIG_BRIDGE_EBT_T_NAT=y ++CONFIG_BRIDGE_EBT_802_3=y ++CONFIG_BRIDGE_EBT_AMONG=y ++CONFIG_BRIDGE_EBT_ARP=y ++CONFIG_BRIDGE_EBT_IP=y ++CONFIG_BRIDGE_EBT_LIMIT=y ++CONFIG_BRIDGE_EBT_MARK=y ++CONFIG_BRIDGE_EBT_PKTTYPE=y ++CONFIG_BRIDGE_EBT_STP=y ++CONFIG_BRIDGE_EBT_VLAN=y ++CONFIG_BRIDGE_EBT_ARPREPLY=y ++CONFIG_BRIDGE_EBT_DNAT=y ++CONFIG_BRIDGE_EBT_MARK_T=y ++CONFIG_BRIDGE_EBT_REDIRECT=y ++CONFIG_BRIDGE_EBT_SNAT=y ++CONFIG_BRIDGE_EBT_LOG=y ++# CONFIG_BRIDGE_EBT_ULOG is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++CONFIG_BRIDGE=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++CONFIG_LLC=y ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_ECONET is not set ++# CONFIG_WAN_ROUTER is not set ++ ++# ++# QoS and/or fair queueing ++# ++CONFIG_NET_SCHED=y ++CONFIG_NET_SCH_FIFO=y ++ ++# ++# Queueing/Scheduling ++# ++CONFIG_NET_SCH_CBQ=m ++CONFIG_NET_SCH_HTB=m ++CONFIG_NET_SCH_HFSC=m ++CONFIG_NET_SCH_PRIO=m ++CONFIG_NET_SCH_RED=m ++CONFIG_NET_SCH_SFQ=m ++CONFIG_NET_SCH_ESFQ=m ++CONFIG_NET_SCH_TEQL=m ++CONFIG_NET_SCH_TBF=m ++CONFIG_NET_SCH_GRED=m ++CONFIG_NET_SCH_DSMARK=m ++CONFIG_NET_SCH_NETEM=m ++CONFIG_NET_SCH_INGRESS=m ++ ++# ++# Classification ++# ++CONFIG_NET_CLS=y ++CONFIG_NET_CLS_BASIC=m ++CONFIG_NET_CLS_TCINDEX=m ++CONFIG_NET_CLS_ROUTE4=m ++CONFIG_NET_CLS_ROUTE=y ++CONFIG_NET_CLS_FW=m ++CONFIG_NET_CLS_U32=m ++CONFIG_CLS_U32_PERF=y ++CONFIG_CLS_U32_MARK=y ++CONFIG_NET_CLS_RSVP=m ++CONFIG_NET_CLS_RSVP6=m ++CONFIG_NET_EMATCH=y ++CONFIG_NET_EMATCH_STACK=32 ++CONFIG_NET_EMATCH_CMP=m ++CONFIG_NET_EMATCH_NBYTE=m ++CONFIG_NET_EMATCH_U32=m ++CONFIG_NET_EMATCH_META=m ++CONFIG_NET_EMATCH_TEXT=m ++CONFIG_NET_CLS_ACT=y ++CONFIG_NET_ACT_POLICE=m ++CONFIG_NET_ACT_GACT=m ++CONFIG_GACT_PROB=y ++CONFIG_NET_ACT_MIRRED=m ++CONFIG_NET_ACT_IPT=m ++CONFIG_NET_ACT_PEDIT=m ++# CONFIG_NET_ACT_SIMP is not set ++CONFIG_NET_CLS_IND=y ++CONFIG_NET_ESTIMATOR=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++ ++# ++# Wireless ++# ++CONFIG_CFG80211=y ++CONFIG_WIRELESS_EXT=y ++# CONFIG_MAC80211 is not set ++# CONFIG_IEEE80211 is not set ++# CONFIG_RFKILL is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++CONFIG_FW_LOADER=y ++# CONFIG_SYS_HYPERVISOR is not set ++ ++# ++# Connector - unified userspace <-> kernelspace linker ++# ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_DEBUG is not set ++CONFIG_MTD_CONCAT=y ++CONFIG_MTD_PARTITIONS=y ++CONFIG_MTD_ROOTFS_ROOT_DEV=y ++CONFIG_MTD_ROOTFS_SPLIT=y ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++CONFIG_MTD_CFI=y ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_GEN_PROBE=y ++# CONFIG_MTD_CFI_ADV_OPTIONS is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++CONFIG_MTD_CFI_INTELEXT=y ++# CONFIG_MTD_CFI_AMDSTD is not set ++# CONFIG_MTD_CFI_STAA is not set ++CONFIG_MTD_CFI_UTIL=y ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PHYSMAP is not set ++# CONFIG_MTD_ARM_INTEGRATOR is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_PMC551 is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOC2000 is not set ++# CONFIG_MTD_DOC2001 is not set ++# CONFIG_MTD_DOC2001PLUS is not set ++# CONFIG_MTD_NAND is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# UBI - Unsorted block images ++# ++# CONFIG_MTD_UBI is not set ++ ++# ++# Parallel port support ++# ++# CONFIG_PARPORT is not set ++ ++# ++# Plug and Play support ++# ++# CONFIG_PNPACPI is not set ++ ++# ++# Block devices ++# ++# CONFIG_BLK_CPQ_DA is not set ++# CONFIG_BLK_CPQ_CISS_DA is not set ++# CONFIG_BLK_DEV_DAC960 is not set ++# CONFIG_BLK_DEV_UMEM is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_SX8 is not set ++# CONFIG_BLK_DEV_RAM is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++ ++# ++# SCSI device support ++# ++# CONFIG_RAID_ATTRS is not set ++# CONFIG_SCSI is not set ++# CONFIG_SCSI_DMA is not set ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_ATA is not set ++ ++# ++# Multi-device support (RAID and LVM) ++# ++# CONFIG_MD is not set ++ ++# ++# Fusion MPT device support ++# ++# CONFIG_FUSION is not set ++ ++# ++# IEEE 1394 (FireWire) support ++# ++# CONFIG_FIREWIRE is not set ++# CONFIG_IEEE1394 is not set ++ ++# ++# I2O device support ++# ++# CONFIG_I2O is not set ++ ++# ++# Network device support ++# ++CONFIG_NETDEVICES=y ++CONFIG_IFB=m ++# CONFIG_DUMMY is not set ++# CONFIG_BONDING is not set ++# CONFIG_EQUALIZER is not set ++CONFIG_IMQ=m ++# CONFIG_IMQ_BEHAVIOR_AA is not set ++# CONFIG_IMQ_BEHAVIOR_AB is not set ++CONFIG_IMQ_BEHAVIOR_BA=y ++# CONFIG_IMQ_BEHAVIOR_BB is not set ++CONFIG_IMQ_NUM_DEVS=2 ++# CONFIG_TUN is not set ++# CONFIG_ARCNET is not set ++ ++# ++# Ethernet (10 or 100Mbit) ++# ++# CONFIG_NET_ETHERNET is not set ++# CONFIG_NETDEV_1000 is not set ++# CONFIG_NETDEV_10000 is not set ++# CONFIG_TR is not set ++ ++# ++# Wireless LAN ++# ++# CONFIG_WLAN_PRE80211 is not set ++# CONFIG_WLAN_80211 is not set ++# CONFIG_WAN is not set ++# CONFIG_FDDI is not set ++# CONFIG_HIPPI is not set ++CONFIG_PPP=m ++CONFIG_PPP_MULTILINK=y ++CONFIG_PPP_FILTER=y ++CONFIG_PPP_ASYNC=m ++# CONFIG_PPP_SYNC_TTY is not set ++CONFIG_PPP_DEFLATE=m ++CONFIG_PPP_BSDCOMP=m ++# CONFIG_PPP_MPPE is not set ++CONFIG_PPPOE=m ++# CONFIG_SLIP is not set ++CONFIG_SLHC=m ++# CONFIG_SHAPER is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++ ++# ++# ISDN subsystem ++# ++# CONFIG_ISDN is not set ++ ++# ++# Telephony Support ++# ++# CONFIG_PHONE is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++# CONFIG_INPUT_TSDEV is not set ++# CONFIG_INPUT_EVDEV is not set ++# CONFIG_INPUT_EVBUG is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++# CONFIG_SERIO is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_VT_CONSOLE=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_PCI=y ++CONFIG_SERIAL_8250_NR_UARTS=4 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=4 ++# CONFIG_SERIAL_8250_EXTENDED is not set ++ ++# ++# Non-8250 serial port support ++# ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_JSM is not set ++CONFIG_UNIX98_PTYS=y ++CONFIG_LEGACY_PTYS=y ++CONFIG_LEGACY_PTY_COUNT=16 ++ ++# ++# IPMI ++# ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_WATCHDOG is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_NVRAM is not set ++# CONFIG_R3964 is not set ++# CONFIG_APPLICOM is not set ++# CONFIG_DRM is not set ++# CONFIG_RAW_DRIVER is not set ++ ++# ++# TPM devices ++# ++# CONFIG_TCG_TPM is not set ++CONFIG_DEVPORT=y ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_CHARDEV=y ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++# CONFIG_I2C_ALI1535 is not set ++# CONFIG_I2C_ALI1563 is not set ++# CONFIG_I2C_ALI15X3 is not set ++# CONFIG_I2C_AMD756 is not set ++# CONFIG_I2C_AMD8111 is not set ++# CONFIG_I2C_I801 is not set ++# CONFIG_I2C_I810 is not set ++# CONFIG_I2C_PIIX4 is not set ++# CONFIG_I2C_NFORCE2 is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_PROSAVAGE is not set ++# CONFIG_I2C_SAVAGE4 is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_SIS5595 is not set ++# CONFIG_I2C_SIS630 is not set ++# CONFIG_I2C_SIS96X is not set ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_VIA is not set ++# CONFIG_I2C_VIAPRO is not set ++# CONFIG_I2C_VOODOO3 is not set ++CONFIG_I2C_MV64XXX=y ++ ++# ++# Miscellaneous I2C Chip support ++# ++# CONFIG_SENSORS_DS1337 is not set ++# CONFIG_SENSORS_DS1374 is not set ++CONFIG_SENSORS_EEPROM=y ++# CONFIG_SENSORS_PCF8574 is not set ++# CONFIG_SENSORS_PCA9539 is not set ++# CONFIG_SENSORS_PCF8591 is not set ++# CONFIG_SENSORS_MAX6875 is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++# CONFIG_I2C_DEBUG_CHIP is not set ++ ++# ++# SPI support ++# ++# CONFIG_SPI is not set ++# CONFIG_SPI_MASTER is not set ++ ++# ++# Dallas's 1-wire bus ++# ++# CONFIG_W1 is not set ++CONFIG_HWMON=y ++# CONFIG_HWMON_VID is not set ++# CONFIG_SENSORS_ABITUGURU is not set ++CONFIG_SENSORS_AD7418=y ++# CONFIG_SENSORS_ADM1021 is not set ++# CONFIG_SENSORS_ADM1025 is not set ++# CONFIG_SENSORS_ADM1026 is not set ++# CONFIG_SENSORS_ADM1029 is not set ++# CONFIG_SENSORS_ADM1031 is not set ++# CONFIG_SENSORS_ADM9240 is not set ++# CONFIG_SENSORS_ASB100 is not set ++# CONFIG_SENSORS_ATXP1 is not set ++# CONFIG_SENSORS_DS1621 is not set ++# CONFIG_SENSORS_F71805F is not set ++# CONFIG_SENSORS_FSCHER is not set ++# CONFIG_SENSORS_FSCPOS is not set ++# CONFIG_SENSORS_GL518SM is not set ++# CONFIG_SENSORS_GL520SM is not set ++# CONFIG_SENSORS_IT87 is not set ++# CONFIG_SENSORS_LM63 is not set ++# CONFIG_SENSORS_LM75 is not set ++# CONFIG_SENSORS_LM77 is not set ++# CONFIG_SENSORS_LM78 is not set ++# CONFIG_SENSORS_LM80 is not set ++# CONFIG_SENSORS_LM83 is not set ++# CONFIG_SENSORS_LM85 is not set ++# CONFIG_SENSORS_LM87 is not set ++# CONFIG_SENSORS_LM90 is not set ++# CONFIG_SENSORS_LM92 is not set ++# CONFIG_SENSORS_MAX1619 is not set ++# CONFIG_SENSORS_MAX6650 is not set ++# CONFIG_SENSORS_PC87360 is not set ++# CONFIG_SENSORS_PC87427 is not set ++# CONFIG_SENSORS_SIS5595 is not set ++# CONFIG_SENSORS_SMSC47M1 is not set ++# CONFIG_SENSORS_SMSC47M192 is not set ++# CONFIG_SENSORS_SMSC47B397 is not set ++# CONFIG_SENSORS_VIA686A is not set ++# CONFIG_SENSORS_VT1211 is not set ++# CONFIG_SENSORS_VT8231 is not set ++# CONFIG_SENSORS_W83781D is not set ++# CONFIG_SENSORS_W83791D is not set ++# CONFIG_SENSORS_W83792D is not set ++# CONFIG_SENSORS_W83793 is not set ++# CONFIG_SENSORS_W83L785TS is not set ++# CONFIG_SENSORS_W83627HF is not set ++# CONFIG_SENSORS_W83627EHF is not set ++# CONFIG_HWMON_DEBUG_CHIP is not set ++ ++# ++# Misc devices ++# ++# CONFIG_PHANTOM is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_SGI_IOC4 is not set ++# CONFIG_TIFM_CORE is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_SM501 is not set ++ ++# ++# LED devices ++# ++# CONFIG_NEW_LEDS is not set ++ ++# ++# LED drivers ++# ++ ++# ++# LED Triggers ++# ++ ++# ++# Multimedia devices ++# ++# CONFIG_VIDEO_DEV is not set ++# CONFIG_DVB_CORE is not set ++# CONFIG_DAB is not set ++ ++# ++# Graphics support ++# ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++ ++# ++# Display device support ++# ++# CONFIG_DISPLAY_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++# CONFIG_FB is not set ++ ++# ++# Console display driver support ++# ++# CONFIG_VGA_CONSOLE is not set ++CONFIG_DUMMY_CONSOLE=y ++ ++# ++# Sound ++# ++# CONFIG_SOUND is not set ++ ++# ++# HID Devices ++# ++# CONFIG_HID is not set ++ ++# ++# USB support ++# ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB_ARCH_HAS_OHCI=y ++CONFIG_USB_ARCH_HAS_EHCI=y ++# CONFIG_USB is not set ++ ++# ++# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' ++# ++ ++# ++# USB Gadget Support ++# ++# CONFIG_USB_GADGET is not set ++# CONFIG_MMC is not set ++ ++# ++# Real Time Clock ++# ++CONFIG_RTC_LIB=y ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_HCTOSYS=y ++CONFIG_RTC_HCTOSYS_DEVICE="rtc0" ++# CONFIG_RTC_DEBUG is not set ++ ++# ++# RTC interfaces ++# ++CONFIG_RTC_INTF_SYSFS=y ++CONFIG_RTC_INTF_PROC=y ++CONFIG_RTC_INTF_DEV=y ++# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set ++# CONFIG_RTC_DRV_TEST is not set ++ ++# ++# I2C RTC drivers ++# ++# CONFIG_RTC_DRV_DS1307 is not set ++CONFIG_RTC_DRV_DS1672=y ++# CONFIG_RTC_DRV_MAX6900 is not set ++# CONFIG_RTC_DRV_RS5C372 is not set ++# CONFIG_RTC_DRV_ISL1208 is not set ++# CONFIG_RTC_DRV_X1205 is not set ++# CONFIG_RTC_DRV_PCF8563 is not set ++# CONFIG_RTC_DRV_PCF8583 is not set ++ ++# ++# SPI RTC drivers ++# ++ ++# ++# Platform RTC drivers ++# ++# CONFIG_RTC_DRV_CMOS is not set ++# CONFIG_RTC_DRV_DS1553 is not set ++# CONFIG_RTC_DRV_DS1742 is not set ++# CONFIG_RTC_DRV_M48T86 is not set ++# CONFIG_RTC_DRV_V3020 is not set ++ ++# ++# on-CPU RTC drivers ++# ++ ++# ++# File systems ++# ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4DEV_FS is not set ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_FS_POSIX_ACL is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_YAFFS_FS is not set ++# CONFIG_OCFS2_FS is not set ++CONFIG_MINI_FO=y ++# CONFIG_MINIX_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_INOTIFY is not set ++# CONFIG_QUOTA is not set ++CONFIG_DNOTIFY=y ++# CONFIG_AUTOFS_FS is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++# CONFIG_MSDOS_FS is not set ++# CONFIG_VFAT_FS is not set ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_RAMFS=y ++# CONFIG_CONFIGFS_FS is not set ++ ++# ++# Miscellaneous filesystems ++# ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_CRAMFS=y ++CONFIG_SQUASHFS=y ++# CONFIG_SQUASHFS_EMBEDDED is not set ++CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 ++# CONFIG_SQUASHFS_VMALLOC is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++ ++# ++# Network File Systems ++# ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++# CONFIG_NFS_V3_ACL is not set ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_DIRECTIO is not set ++# CONFIG_NFSD is not set ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_BIND34 is not set ++# CONFIG_RPCSEC_GSS_KRB5 is not set ++# CONFIG_RPCSEC_GSS_SPKM3 is not set ++# CONFIG_SMB_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++# CONFIG_9P_FS is not set ++ ++# ++# Partition Types ++# ++# CONFIG_PARTITION_ADVANCED is not set ++CONFIG_MSDOS_PARTITION=y ++ ++# ++# Native Language Support ++# ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++# CONFIG_NLS_CODEPAGE_437 is not set ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++ ++# ++# Distributed Lock Manager ++# ++# CONFIG_DLM is not set ++ ++# ++# Profiling support ++# ++# CONFIG_PROFILING is not set ++ ++# ++# Kernel hacking ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_ENABLE_MUST_CHECK=y ++# CONFIG_MAGIC_SYSRQ is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_KERNEL is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++CONFIG_FRAME_POINTER=y ++# CONFIG_DEBUG_USER is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY is not set ++ ++# ++# Cryptographic options ++# ++CONFIG_CRYPTO=y ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_MANAGER=y ++CONFIG_CRYPTO_HMAC=y ++# CONFIG_CRYPTO_XCBC is not set ++CONFIG_CRYPTO_NULL=y ++CONFIG_CRYPTO_MD4=y ++CONFIG_CRYPTO_MD5=y ++CONFIG_CRYPTO_SHA1=y ++CONFIG_CRYPTO_SHA256=y ++CONFIG_CRYPTO_SHA512=y ++# CONFIG_CRYPTO_WP512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++CONFIG_CRYPTO_GF128MUL=y ++CONFIG_CRYPTO_ECB=y ++CONFIG_CRYPTO_CBC=y ++CONFIG_CRYPTO_PCBC=y ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_FCRYPT is not set ++CONFIG_CRYPTO_BLOWFISH=y ++CONFIG_CRYPTO_TWOFISH=y ++CONFIG_CRYPTO_TWOFISH_COMMON=y ++# CONFIG_CRYPTO_SERPENT is not set ++CONFIG_CRYPTO_AES=y ++CONFIG_CRYPTO_CAST5=y ++CONFIG_CRYPTO_CAST6=y ++CONFIG_CRYPTO_TEA=y ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Hardware crypto devices ++# ++ ++# ++# OCF Configuration ++# ++CONFIG_OCF_OCF=y ++# CONFIG_OCF_RANDOMHARVEST is not set ++CONFIG_OCF_CRYPTODEV=y ++CONFIG_OCF_CRYPTOSOFT=y ++# CONFIG_OCF_OCFNULL is not set ++# CONFIG_OCF_BENCH is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_CRC_CCITT=m ++# CONFIG_CRC16 is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++CONFIG_LIBCRC32C=y ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_TEXTSEARCH=y ++CONFIG_TEXTSEARCH_KMP=m ++CONFIG_TEXTSEARCH_BM=m ++CONFIG_TEXTSEARCH_FSM=m ++CONFIG_PLIST=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y From clang at gateworks.com Thu Sep 10 03:28:04 2009 From: clang at gateworks.com (clang at gateworks.com) Date: Thu, 10 Sep 2009 08:28:04 +0000 Subject: [Avila] Gateworks SVN Commit r90 - in trunk/u-boot-pismo: . board/mv_feroceon/USP board/mv_feroceon/mv_dd board/mv_feroceon/mv_dd/dd_family/boardEnv Message-ID: Author: clang Date: 2009-09-10 08:27:59 +0000 (Thu, 10 Sep 2009) New Revision: 90 Modified: trunk/u-boot-pismo/board/mv_feroceon/USP/mv_egiga.c trunk/u-boot-pismo/board/mv_feroceon/mv_dd/dd_family/boardEnv/mvBoardEnvLib.c trunk/u-boot-pismo/board/mv_feroceon/mv_dd/dd_family/boardEnv/mvBoardEnvSpec.h trunk/u-boot-pismo/board/mv_feroceon/mv_dd/mv_cmd.c trunk/u-boot-pismo/u-boot.bin Log: Update u-boot to support switch functions on the GW2367, and full support for the GW2367. Use switchinit to from the command line to initialize the switch to default values. Modified: trunk/u-boot-pismo/board/mv_feroceon/USP/mv_egiga.c =================================================================== --- trunk/u-boot-pismo/board/mv_feroceon/USP/mv_egiga.c 2009-09-10 08:26:25 UTC (rev 89) +++ trunk/u-boot-pismo/board/mv_feroceon/USP/mv_egiga.c 2009-09-10 08:27:59 UTC (rev 90) @@ -35,7 +35,7 @@ #ifdef CONFIG_MV78200 #include "mv78200/mvSocUnitMap.h" #endif -/* #define MV_DEBUG */ +//#define MV_DEBUG #ifdef MV_DEBUG #define DB(x) x #else @@ -397,7 +397,28 @@ MV_PKT_INFO pktInfo; MV_PKT_INFO *pPktInfo; MV_STATUS status; - + int i; + if (priv->port == 2) + { + memmove((void *)(buf + 20), (void *)(buf + 12), len - 12); + len += 0x8; + *(MV_U8 *)(buf + 12) = 0x5f; + *(MV_U8 *)(buf + 13) = 0xfe; + *(MV_U8 *)(buf + 14) = 0x10; + *(MV_U8 *)(buf + 15) = 0x01; + *(MV_U8 *)(buf + 16) = 0x69; + *(MV_U8 *)(buf + 17) = 0xff; + *(MV_U8 *)(buf + 18) = 0xf8; + *(MV_U8 *)(buf + 19) = 0x00; +#ifdef MV_DEBUG + for ( i = 0; i < len; i++) + { + if (!(i % 16)) + printf("\n"); + printf("0x%2x ", *(uchar *)(buf + i)); + } +#endif + } DB( printf( "mvEgigaTx start\n" ) ); /* if no link exist */ if(!egiga_init) return 0; @@ -472,7 +493,7 @@ egigaPriv* priv = dev->priv; MV_PKT_INFO *pktInfo; MV_STATUS status; - + int i; /* if no link exist */ if(!egiga_init) return 0; @@ -510,6 +531,19 @@ DB( printf( "%s: calling NetRecieve osInfo = 0x%x\n", __FUNCTION__, pktInfo->osInfo) ); DB( printf( "%s: calling NetRecieve pktSize = 0x%x\n", __FUNCTION__, pktInfo->pFrags->dataSize) ); /* good rx - push the packet up (skip on two first empty bytes) */ + if (priv->port == 2) + { +#ifdef MV_DEBUG + for (i = 0; i < (int)pktInfo->pFrags->dataSize; i++) + { + if (!(i % 16)) + printf("\n"); + printf("0x%2x ", *(uchar *)(pktInfo->osInfo + i)); + } + printf("\n"); +#endif + memmove((void *)(pktInfo->osInfo + 14), (void *)(pktInfo->osInfo + 22), (int)pktInfo->pFrags->dataSize - 20); + } NetReceive( ((MV_U8 *)pktInfo->osInfo) + 2, (int)pktInfo->pFrags->dataSize); } Modified: trunk/u-boot-pismo/board/mv_feroceon/mv_dd/dd_family/boardEnv/mvBoardEnvLib.c =================================================================== --- trunk/u-boot-pismo/board/mv_feroceon/mv_dd/dd_family/boardEnv/mvBoardEnvLib.c 2009-09-10 08:26:25 UTC (rev 89) +++ trunk/u-boot-pismo/board/mv_feroceon/mv_dd/dd_family/boardEnv/mvBoardEnvLib.c 2009-09-10 08:27:59 UTC (rev 90) @@ -533,6 +533,8 @@ MV_BOARD_MAC_SPEED mvBoardMacSpeedGet(MV_U32 ethPortNum) { MV_32 boardId = mvBoardIdGet(); + if (ethPortNum == 2) + return BOARD_MAC_SPEED_1000M; if (boardId == RD_78XX0_H3C_ID) /* RD-H3C 1145 works in RGMII to SGMII mode which disable speed AN, force speed to 1000*/ Modified: trunk/u-boot-pismo/board/mv_feroceon/mv_dd/dd_family/boardEnv/mvBoardEnvSpec.h =================================================================== --- trunk/u-boot-pismo/board/mv_feroceon/mv_dd/dd_family/boardEnv/mvBoardEnvSpec.h 2009-09-10 08:26:25 UTC (rev 89) +++ trunk/u-boot-pismo/board/mv_feroceon/mv_dd/dd_family/boardEnv/mvBoardEnvSpec.h 2009-09-10 08:27:59 UTC (rev 90) @@ -333,7 +333,7 @@ #define BOARD_ETH_END_PORT_NUM 3 #define BOARD_ETH_PORT_NUM 4 #define MV_ETH_PORT_SGMII {0, 0, 0, 0}; -#define MV_ETH_PORT_PHY_ADDR {0x0, 0x1, 0x2, 0x3}; +#define MV_ETH_PORT_PHY_ADDR {0x0, 0x1, 0x1f, 0x3}; #define MV_BOARD_MAX_USB_IF (mvBoardGetMaxUsbIf()) Modified: trunk/u-boot-pismo/board/mv_feroceon/mv_dd/mv_cmd.c =================================================================== --- trunk/u-boot-pismo/board/mv_feroceon/mv_dd/mv_cmd.c 2009-09-10 08:26:25 UTC (rev 89) +++ trunk/u-boot-pismo/board/mv_feroceon/mv_dd/mv_cmd.c 2009-09-10 08:27:59 UTC (rev 90) @@ -1147,7 +1147,6 @@ "\tRead the Phy register. \n" ); - int phy_write_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { mvEthPhyRegWrite(simple_strtoul( argv[1], NULL, 16 ), @@ -1164,6 +1163,447 @@ "\tWrite to the Phy register.\n" ); + +MV_U32 mv_switch_read(MV_U16 phyAddr, MV_U32 phyOffset) +{ + MV_U16 temp_ret; + MV_U32 ret; + + mvEthPhyRegWrite(phyAddr, 0x4, (phyOffset >> 16) & 0xffff); + mvEthPhyRegWrite(phyAddr, 0x5, (phyOffset) & 0xffff); + + do + { + mvEthPhyRegRead(phyAddr, 0x1f, &temp_ret); + }while (temp_ret != 0x3); + + mvEthPhyRegRead(phyAddr, 0x6, &temp_ret); + ret = temp_ret << 16; + mvEthPhyRegRead(phyAddr, 0x7, &temp_ret); + ret |= temp_ret; + + return ret; +} + +void mv_switch_write(MV_U16 phyAddr, MV_U32 phyOffset, MV_U32 value) +{ + MV_U16 temp_ret; + + mvEthPhyRegWrite(phyAddr, 0x0, (phyOffset >> 16) & 0xffff); + mvEthPhyRegWrite(phyAddr, 0x1, (phyOffset) & 0xffff); + mvEthPhyRegWrite(phyAddr, 0x2, (value >> 16) & 0xffff); + mvEthPhyRegWrite(phyAddr, 0x3, (value) & 0xffff); + + do + { + mvEthPhyRegRead(phyAddr, 0x1f, &temp_ret); + }while (temp_ret != 0x3); + +} + +MV_U16 mv_switch_phy_read(MV_U16 switchAddr, MV_U16 phyAddr, MV_U16 phyOffset) +{ + MV_U32 temp_ret; + phyAddr = phyAddr & 0x1f; + phyOffset = phyOffset & 0x1f; + do + { + temp_ret = mv_switch_read(switchAddr, 0x04004054); + }while (temp_ret & (1 << 28)); + + mv_switch_write(switchAddr, 0x04004054, (1 << 26) | (phyOffset << 21) | (phyAddr << 16)); + do + { + temp_ret = mv_switch_read(switchAddr, 0x04004054); + }while (!(temp_ret & (1 << 27))); + + return (temp_ret & 0xffff); +} + +void mv_switch_phy_write(MV_U16 switchAddr, MV_U16 phyAddr, MV_U16 phyOffset, MV_U16 value) +{ + MV_U32 temp_ret; + phyAddr = phyAddr & 0x1f; + phyOffset = phyOffset & 0x1f; + do + { + temp_ret = mv_switch_read(switchAddr, 0x04004054); + }while (temp_ret & (1 << 28)); + + mv_switch_write(switchAddr, 0x04004054, (phyOffset << 21) | (phyAddr << 16) | value); +} + +int switch_phy_read_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + MV_U32 phyOffset = simple_strtoul(argv[3], NULL, 16); + MV_U16 switchAddr = simple_strtoul( argv[1], NULL, 16); + MV_U16 phyAddr = simple_strtoul( argv[2], NULL, 16); + + printf ("0x%x\n", mv_switch_phy_read(switchAddr, phyAddr, phyOffset)); + + return 1; +} + +U_BOOT_CMD( + switchphyread, 4, 4, switch_phy_read_cmd, + "switchphyread - Read switch phy register\n", + " Switch_addrss Phy_address Phy_offset. \n" + "\tRead the switch Phy register. \n" +); + + +int switch_read_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + MV_U32 phyOffset = simple_strtoul(argv[2], NULL, 16); + MV_U16 phyAddr = simple_strtoul( argv[1], NULL, 16); + + printf ("0x%x\n", mv_switch_read(phyAddr, phyOffset)); + + return 1; +} + +U_BOOT_CMD( + switchread, 3, 3, switch_read_cmd, + "switchread - Read switch phy register\n", + " Phy_address Phy_offset. \n" + "\tRead the switch Phy register. \n" +); + +int switch_dump_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + MV_U16 phyAddr = simple_strtoul( argv[1], NULL, 16); + MV_U32 phyOffset = simple_strtoul(argv[2], NULL, 16); + MV_U32 value = simple_strtoul(argv[3], NULL, 16); + MV_U32 i; + + for (i = 0; i < value; i+=4) + { + printf("addr: 0x%x => 0x%x\n", phyOffset+i, mv_switch_read(phyAddr, phyOffset+i)); + } + + return 1; +} + +U_BOOT_CMD( + switchdump, 4, 4, switch_dump_cmd, + "switchdump - Dump switch registers\n", + " Phy_address Phy_offset length. \n" + "\tDump the switch registers. \n" +); + +int switch_phy_dump_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + MV_U16 switchAddr = simple_strtoul( argv[1], NULL, 16); + MV_U16 phyAddr = simple_strtoul( argv[2], NULL, 16); + MV_U32 phyOffset = simple_strtoul(argv[3], NULL, 16); + MV_U16 page = simple_strtoul(argv[4], NULL, 16); + MV_U32 value = simple_strtoul(argv[5], NULL, 16); + + MV_U32 i; + + mv_switch_phy_write(switchAddr, phyAddr, 0x16, page); + + for (i = 0; i < value; i+=1) + { + printf("addr: 0x%x => 0x%x\n", phyOffset+i, mv_switch_phy_read(switchAddr, phyAddr, phyOffset+i)); + } + + return 1; +} + +U_BOOT_CMD( + switchphydump, 6, 6, switch_phy_dump_cmd, + "switchphydump - Dump switch registers\n", + " Switch_address Phy_address Phy_offset page length. \n" + "\tDump the switch registers. \n" +); + +int switch_phy_write_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + MV_U32 phyOffset = simple_strtoul(argv[3], NULL, 16); + MV_U32 value = simple_strtoul(argv[4], NULL, 16); + MV_U16 switchAddr = simple_strtoul( argv[1], NULL, 16); + MV_U16 phyAddr = simple_strtoul( argv[2], NULL, 16); + + mv_switch_phy_write(switchAddr, phyAddr, phyOffset, value); + + return 1; +} + +U_BOOT_CMD( + switchphywrite, 5, 5, switch_phy_write_cmd, + "switchphywrite - Read switch phy register\n", + " Switch_address Phy_address Phy_offset value. \n" + "\tRead the switch Phy register. \n" +); + +int switch_write_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + MV_U32 phyOffset = simple_strtoul(argv[2], NULL, 16); + MV_U32 value = simple_strtoul(argv[3], NULL, 16); + MV_U16 phyAddr = simple_strtoul( argv[1], NULL, 16); + + mv_switch_write(phyAddr, phyOffset, value); + + return 1; +} + +U_BOOT_CMD( + switchwrite, 4, 4, switch_write_cmd, + "switchwrite - Read switch phy register\n", + " Phy_address Phy_offset value. \n" + "\tRead the switch Phy register. \n" +); + +int switch_init_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + MV_8 *enet_addr; + MV_8 enetaddr[6]; + enet_addr = getenv("eth2addr"); + mvMacStrToHex(enet_addr, enetaddr); + + mv_switch_write(0x1f, 0x3000000, 0x57870896); // BM MaxBufLimit = 504 buffers -->Buffer Management Global Buffers Limits Configuration Register + mv_switch_write(0x1f, 0x1800000, 0x1a21a03); // TXQ TotalDescLimit = 418 Descriptors --> Transmit Queue Control Register + mv_switch_write(0x1f, 0x1800084, 0x15e); // TXQ TotalBuffersLimit = 350 buffers --> TXQ Total Buffer Limit Configuration Register + mv_switch_write(0x1f, 0x780000c, 0x5042); //GigEFIFORdthreshold = 10 rows --> Buffer Memory MPPM Configuration Register + + +// -------------Auto negotiation enable---------------- +mv_switch_write(0x1f, 0xa80000c, 0xb0e8); +mv_switch_write(0x1f, 0xa80010c, 0xb0e8); +mv_switch_write(0x1f, 0xa80020c, 0xb0e8); +mv_switch_write(0x1f, 0xa80030c, 0xb0e8); +mv_switch_write(0x1f, 0xa80040c, 0xb0e8); +mv_switch_write(0x1f, 0xa80050c, 0xb0e8); +mv_switch_write(0x1f, 0xa80060c, 0xb0e8); +mv_switch_write(0x1f, 0xa80070c, 0xb0e8); +mv_switch_write(0x1f, 0xa80080c, 0xb0e8); +mv_switch_write(0x1f, 0xa80090c, 0xb0e8); + +// 1145 PHY OutOfBand AN Mode Select + +//------------------------ PHY SMI ----------------------------- + +// PHY 0x4 +mv_switch_write(0x1f, 0x4004054, 0x02c40001); // Select SERDES register Identity (move to page 1); Reg. 22.7:0='0x01' +mv_switch_write(0x1f, 0x4004054, 0x00040140); // Disable Auto-Neg +mv_switch_write(0x1f, 0x4004054, 0x00048140); // Apply PHY soft reset +mv_switch_write(0x1f, 0x4004054, 0x02c40000); // MDI reg. access (Return to page 0) + +// PHY 0x5 +mv_switch_write(0x1f, 0x4004054, 0x02c50001); // Select SERDES register Identity (move to page 1); Reg. 22.7:0='0x01' +mv_switch_write(0x1f, 0x4004054, 0x00050140); // Disable Auto-Neg +mv_switch_write(0x1f, 0x4004054, 0x00058140); // Apply PHY soft reset +mv_switch_write(0x1f, 0x4004054, 0x02c50000); // MDI reg. access (Return to page 0) + +// PHY 0x6 +mv_switch_write(0x1f, 0x4004054, 0x02c60001); // Select SERDES register Identity (move to page 1); Reg. 22.7:0='0x01' +mv_switch_write(0x1f, 0x4004054, 0x00060140); // Disable Auto-Neg +mv_switch_write(0x1f, 0x4004054, 0x00068140); // Apply PHY soft reset +mv_switch_write(0x1f, 0x4004054, 0x02c60000); // MDI reg. access (Return to page 0) + +// PHY 0x7 +mv_switch_write(0x1f, 0x4004054, 0x02c70001); // Select SERDES register Identity (move to page 1); Reg. 22.7:0='0x01' +mv_switch_write(0x1f, 0x4004054, 0x00070140); // Disable Auto-Neg +mv_switch_write(0x1f, 0x4004054, 0x00078140); // Apply PHY soft reset +mv_switch_write(0x1f, 0x4004054, 0x02c70000); // MDI reg. access (Return to page 0) + +// PHY 0x8 +mv_switch_write(0x1f, 0x4004054, 0x02c80001); // Select SERDES register Identity (move to page 1); Reg. 22.7:0='0x01' +mv_switch_write(0x1f, 0x4004054, 0x00080140); // Disable Auto-Neg +mv_switch_write(0x1f, 0x4004054, 0x00088140); // Apply PHY soft reset +mv_switch_write(0x1f, 0x4004054, 0x02c80000); // MDI reg. access (Return to page 0) + +// PHY 0x9 +mv_switch_write(0x1f, 0x4004054, 0x02c90001); // Select SERDES register Identity (move to page 1); Reg. 22.7:0='0x01' +mv_switch_write(0x1f, 0x4004054, 0x00090140); // Disable Auto-Neg +mv_switch_write(0x1f, 0x4004054, 0x00098140); // Apply PHY soft reset +mv_switch_write(0x1f, 0x4004054, 0x02c90000); // MDI reg. access (Return to page 0) + +// PHY 0xa +mv_switch_write(0x1f, 0x4004054, 0x02ca0001); // Select SERDES register Identity (move to page 1); Reg. 22.7:0='0x01' +mv_switch_write(0x1f, 0x4004054, 0x000a0140); // Disable Auto-Neg +mv_switch_write(0x1f, 0x4004054, 0x000a8140); // Apply PHY soft reset +mv_switch_write(0x1f, 0x4004054, 0x02ca0000); // MDI reg. access (Return to page 0) + +// PHY 0xb +mv_switch_write(0x1f, 0x4004054, 0x02cb0001); // Select SERDES register Identity (move to page 1); Reg. 22.7:0='0x01' +mv_switch_write(0x1f, 0x4004054, 0x000b0140); // Disable Auto-Neg +mv_switch_write(0x1f, 0x4004054, 0x000b8140); // Apply PHY soft reset +mv_switch_write(0x1f, 0x4004054, 0x02cb0000); // MDI reg. access (Return to page 0) + + +// 1112 PHY configuration + +mv_switch_write(0x1f, 0x4804034, 0x0000000c); // Enable Auto meadia select for ports 8,9 that connected to 1112 + +// PHY 0xc 88E1112 +mv_switch_write(0x1f, 0x4004054, 0x02cc0002); // Select SERDES register Identity (move to page 2); Reg. 22.7:0='0x02' +mv_switch_write(0x1f, 0x4004054, 0x000c0140); // Disable Auto-Neg +mv_switch_write(0x1f, 0x4004054, 0x000c8140); // PHY soft reset +mv_switch_write(0x1f, 0x4004054, 0x02cc0000); // MDI reg. access (Return to page 0) +mv_switch_write(0x1f, 0x4004054, 0x02cc0001); // Select SERDES register Identity (move to page 1); Reg. 22.7:0='0x01' +mv_switch_write(0x1f, 0x4004054, 0x020c0200); // 1112 Reg. 16 (page 1) set to 0x200, SIGDET polarity +mv_switch_write(0x1f, 0x4004054, 0x02cc0000); // MDI reg. access (Return to page 0) +//mv_switch_write(0x1f, 0xA80080C, 0x0000F0E8); // 1112 mode for port 8 + + +// PHY 0xd 88E1112 +mv_switch_write(0x1f, 0x4004054, 0x02cd0002); // Select SERDES register Identity (move to page 2); Reg. 22.7:0='0x02' +mv_switch_write(0x1f, 0x4004054, 0x000d0140); // Disable Auto-Neg +mv_switch_write(0x1f, 0x4004054, 0x000d8140); // PHY soft reset +mv_switch_write(0x1f, 0x4004054, 0x02cd0000); // MDI reg. access (Return to page 0) +mv_switch_write(0x1f, 0x4004054, 0x02cd0001); // Select SERDES register Identity (move to page 1); Reg. 22.7:0='0x01' +mv_switch_write(0x1f, 0x4004054, 0x020d0200); // 1112 Reg. 16 (page 1) set to 0x200, SIGDET polarity +mv_switch_write(0x1f, 0x4004054, 0x02cd0000); // MDI reg. access (Return to page 0) +//mv_switch_write(0x1f, 0xA80090C, 0x0000F0E8); // 1112 mode for port 9 + + +// 1145 D0 ERRATA: +// 1145 PHY - Implement 2 Errata: +// 1. Disable the 125Mhz Clock from the PHY - Reg 16.4='1' +// 2. Disable the Energy Detect mode (Sleep mode) - Reg 16.9:8='00' +// Notice - Reg 16.6:5 has no default. We set it to '11' (Ena auto crossover) +// 3. Apply PHY reset +//------------------------ Phy SMI----------------------------- + +// PHY 0x4 +mv_switch_write(0x1f, 0x4004054, 0x02040070); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x00049140); // Apply PHY reset + +// PHY 0x5 +mv_switch_write(0x1f, 0x4004054, 0x02050070); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x00059140); // Apply PHY reset + +// PHY 0x6 +mv_switch_write(0x1f, 0x4004054, 0x02060070); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x00069140); // Apply PHY reset + +// PHY 0x7 +mv_switch_write(0x1f, 0x4004054, 0x02070070); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x00079140); // Apply PHY reset + +// PHY 0x8 +mv_switch_write(0x1f, 0x4004054, 0x02080070); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x00089140); // Apply PHY reset + +// PHY 0x9 +mv_switch_write(0x1f, 0x4004054, 0x02090070); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x00099140); // Apply PHY reset + +// PHY 0xa +mv_switch_write(0x1f, 0x4004054, 0x020a0070); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x000a9140); // Apply PHY reset + +// PHY 0xb +mv_switch_write(0x1f, 0x4004054, 0x020b0070); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x000b9140); // Apply PHY reset + +// On the GW2367, Doug put don't care for the signal detection. We don't have +// external signal detection thus we need to always enable the signal. +// +//------------------------ Phy SMI----------------------------- +// PHY 0x4 +mv_switch_write(0x1f, 0x4004054, 0x03444002); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x00049140); // Apply PHY reset + +// PHY 0x5 +mv_switch_write(0x1f, 0x4004054, 0x03454002); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x00059140); // Apply PHY reset + +// PHY 0x6 +mv_switch_write(0x1f, 0x4004054, 0x03464002); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x00069140); // Apply PHY reset + +// PHY 0x7 +mv_switch_write(0x1f, 0x4004054, 0x03474002); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x00079140); // Apply PHY reset + +// PHY 0x8 +mv_switch_write(0x1f, 0x4004054, 0x03484002); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x00089140); // Apply PHY reset + +// PHY 0x9 +mv_switch_write(0x1f, 0x4004054, 0x03494002); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x00099140); // Apply PHY reset + +// PHY 0xa +mv_switch_write(0x1f, 0x4004054, 0x034a4002); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x000a9140); // Apply PHY reset + +// PHY 0xb +mv_switch_write(0x1f, 0x4004054, 0x034b4002); // Implement above errata +mv_switch_write(0x1f, 0x4004054, 0x000b9140); // Apply PHY reset + + +//------------------- GE PORTS CONFIGURATION ----------- + +// --- Set each port TPVL = '0x0' (default is 0x4); Reg 0x0A80xxxx[13:11]='0' -------- + +mv_switch_write(0x1f, 0xa800034, 0x00008600); // Port 0 +mv_switch_write(0x1f, 0xa800134, 0x00008600); // Port 1 +mv_switch_write(0x1f, 0xa800234, 0x00008600); // Port 2 +mv_switch_write(0x1f, 0xa800334, 0x00008600); // Port 3 +mv_switch_write(0x1f, 0xa800434, 0x00008600); // Port 4 +mv_switch_write(0x1f, 0xa800534, 0x00008600); // Port 5 +mv_switch_write(0x1f, 0xa800634, 0x00008600); // Port 6 +mv_switch_write(0x1f, 0xa800734, 0x00008600); // Port 7 +mv_switch_write(0x1f, 0xa800834, 0x00008600); // Port 8 +mv_switch_write(0x1f, 0xa800934, 0x00008600); // Port 9 + + + +// Set each port serdes parameters (MEN, PEN, AMP, VCMS): +// Set Amplitude=600mV +// +mv_switch_write(0x1f, 0xa800028, 0x0000601e); // Port 0 - Amplitude=600mV (bits[15:13]='0x3') +mv_switch_write(0x1f, 0xa800128, 0x0000601e); // Port 1 - Amplitude=600mV (bits[15:13]='0x3') +mv_switch_write(0x1f, 0xa800228, 0x0000601e); // Port 2 - Amplitude=600mV (bits[15:13]='0x3') +mv_switch_write(0x1f, 0xa800328, 0x0000601e); // Port 3 - Amplitude=600mV (bits[15:13]='0x3') +mv_switch_write(0x1f, 0xa800428, 0x0000601e); // Port 4 - Amplitude=600mV (bits[15:13]='0x3') +mv_switch_write(0x1f, 0xa800528, 0x0000601e); // Port 5 - Amplitude=600mV (bits[15:13]='0x3') +mv_switch_write(0x1f, 0xa800628, 0x0000601e); // Port 6 - Amplitude=600mV (bits[15:13]='0x3') +mv_switch_write(0x1f, 0xa800728, 0x0000601e); // Port 7 - Amplitude=600mV (bits[15:13]='0x3') +mv_switch_write(0x1f, 0xa800828, 0x0000601e); // Port 8 - Amplitude=600mV (bits[15:13]='0x3') +mv_switch_write(0x1f, 0xa800928, 0x0000601e); // Port 9 - Amplitude=600mV (bits[15:13]='0x3') + +// ------------------ CPU PORT ENABLE ----------------------- +mv_switch_write(0x1f, 0x000000a0, 0x0000000d); // RGMII Mode, Avtivate CPU +mv_switch_write(0x1f, 0x0A803F0C, 0x0000B869); // Force Link Down +mv_switch_write(0x1f, 0x0A803F0C, 0x00009049); // Disable Auto Negotiation, Speed Select +mv_switch_write(0x1f, 0x0A803F08, 0x00000000); // Disable PCS, Unset Port MAC Reset +mv_switch_write(0x1f, 0x0A803F04, 0x00001f86); // Allow the CPU to send packets with bad CRC +mv_switch_write(0x1f, 0x0A803F00, 0x00008c01); // Change MTU to handle vlan tagged frames +mv_switch_write(0x1f, 0x0A803F0C, 0x0000904A); // Force Link Up + +// ------------------ CPU VLAN APR/IPV4/6 PORT ENABLE ------- +mv_switch_write(0x1f, 0x02040000, 0x08002081); // Enable IGMP Snooping, ARP mirror to CPU + +mv_switch_write(0x1f, 0x0a000000, 0x00000000); // IPCONTROLTOCPUENABLE bit in vlan table 1 set +mv_switch_write(0x1f, 0x0a000004, 0x00000555); +mv_switch_write(0x1f, 0x0a000008, 0x55068003); +mv_switch_write(0x1f, 0x0a00000c, 0x00009001); // Update VLAN table + +mv_switch_write(0x1f, 0x06000040, (enetaddr[4] << 24) | (enetaddr[5] << 16) | 0x0002); // Insert my Mac address into FDB +mv_switch_write(0x1f, 0x06000044, (enetaddr[0] << 24) | (enetaddr[1] << 16) | (enetaddr[2] << 8) | (enetaddr[3])); +mv_switch_write(0x1f, 0x06000048, 0x00fc0001); +mv_switch_write(0x1f, 0x0600004c, 0x00040000); +mv_switch_write(0x1f, 0x06000050, 0x00000001); // Update FDB + + +// ------------------ DEVICE ENABLE ------------------------- +mv_switch_write(0x1f, 0x0000058, 0x001e7ff3); // Device Enable (Bit 0 = '1') + + return 1; +} + +U_BOOT_CMD( + switchinit, 1, 1, switch_init_cmd, + "switchinit - Initialize the switch\n", + " Phy_address Phy_offset value. \n" + "\tRead the switch Phy register. \n" +); + #endif /* #if defined(MV_INCLUDE_UNM_ETH) || defined(MV_INCLUDE_GIG_ETH) */ #endif Modified: trunk/u-boot-pismo/u-boot.bin =================================================================== (Binary files differ) From clang at gateworks.com Mon Sep 14 22:26:15 2009 From: clang at gateworks.com (clang at gateworks.com) Date: Tue, 15 Sep 2009 03:26:15 +0000 Subject: [Avila] Gateworks SVN Commit r91 - trunk/marvell_owrt_8.09 Message-ID: Author: clang Date: 2009-09-15 03:26:08 +0000 (Tue, 15 Sep 2009) New Revision: 91 Modified: trunk/marvell_owrt_8.09/feeds.conf.default Log: Update feeds to correct location, https -> svn Modified: trunk/marvell_owrt_8.09/feeds.conf.default =================================================================== --- trunk/marvell_owrt_8.09/feeds.conf.default 2009-09-10 08:27:59 UTC (rev 90) +++ trunk/marvell_owrt_8.09/feeds.conf.default 2009-09-15 03:26:08 UTC (rev 91) @@ -1,3 +1,3 @@ -src-svn packages https://svn.openwrt.org/openwrt/branches/packages_8.09 https://svn.openwrt.org/openwrt/packages +src-svn packages svn://svn.openwrt.org/openwrt/branches/packages_8.09 svn://svn.openwrt.org/openwrt/packages src-svn xwrt http://x-wrt.googlecode.com/svn/tags/kamikaze_8.09/package src-svn luci http://svn.luci.subsignal.org/luci/tags/0.8.6/contrib/package From clang at gateworks.com Tue Sep 15 11:05:14 2009 From: clang at gateworks.com (clang at gateworks.com) Date: Tue, 15 Sep 2009 16:05:14 +0000 Subject: [Avila] Gateworks SVN Commit r92 - in trunk/marvell_owrt_8.09: bin target/linux/marvell target/linux/marvell/patches-2.6.22 Message-ID: Author: clang Date: 2009-09-15 16:05:04 +0000 (Tue, 15 Sep 2009) New Revision: 92 Added: trunk/marvell_owrt_8.09/target/linux/marvell/patches-2.6.22/002-gw2367-usb-mmc-i2c.patch Modified: trunk/marvell_owrt_8.09/bin/openwrt-marvell-squashfs.img trunk/marvell_owrt_8.09/bin/openwrt-marvell-uImage trunk/marvell_owrt_8.09/target/linux/marvell/config-default Log: Update i2c driver in kernel to allow for retries. Update configuration/driver to support USB. Add initial files for MMC/SD over SPI support Modified: trunk/marvell_owrt_8.09/bin/openwrt-marvell-squashfs.img =================================================================== (Binary files differ) Modified: trunk/marvell_owrt_8.09/bin/openwrt-marvell-uImage =================================================================== (Binary files differ) Modified: trunk/marvell_owrt_8.09/target/linux/marvell/config-default =================================================================== --- trunk/marvell_owrt_8.09/target/linux/marvell/config-default 2009-09-15 03:26:08 UTC (rev 91) +++ trunk/marvell_owrt_8.09/target/linux/marvell/config-default 2009-09-15 16:05:04 UTC (rev 92) @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.22.18-mv -# Wed Sep 9 11:48:35 2009 +# Tue Sep 15 08:37:38 2009 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -162,7 +162,7 @@ # CONFIG_MV_INCLUDE_PEX=y CONFIG_MV_INCLUDE_IDMA=y -# CONFIG_MV_INCLUDE_USB is not set +CONFIG_MV_INCLUDE_USB=y CONFIG_MV_INCLUDE_XOR=y CONFIG_MV_INCLUDE_CESA=y # CONFIG_MV_INCLUDE_NAND is not set @@ -253,6 +253,7 @@ CONFIG_MV_CESA=y CONFIG_MV_CESA_OCF=y CONFIG_MV_CESA_TOOL=y +# CONFIG_SCSI_MVSATA is not set CONFIG_ARCH_FEROCEON=y # @@ -418,27 +419,27 @@ # CONFIG_NF_CONNTRACK_RTSP is not set CONFIG_NETFILTER_XTABLES=y # CONFIG_NETFILTER_XT_TARGET_CHAOS is not set -CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m -CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set +# CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set # CONFIG_NETFILTER_XT_TARGET_DELUDE is not set -CONFIG_NETFILTER_XT_TARGET_DSCP=m -CONFIG_NETFILTER_XT_TARGET_MARK=m +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +# CONFIG_NETFILTER_XT_TARGET_MARK is not set # CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set # CONFIG_NETFILTER_XT_TARGET_TARPIT is not set CONFIG_NETFILTER_XT_TARGET_TCPMSS=m # CONFIG_NETFILTER_XT_MATCH_COMMENT is not set -CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m -CONFIG_NETFILTER_XT_MATCH_CONNMARK=m -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set +# CONFIG_NETFILTER_XT_MATCH_CONNMARK is not set +# CONFIG_NETFILTER_XT_MATCH_CONNTRACK is not set # CONFIG_NETFILTER_XT_MATCH_DCCP is not set -CONFIG_NETFILTER_XT_MATCH_DSCP=m +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set # CONFIG_NETFILTER_XT_MATCH_ESP is not set -CONFIG_NETFILTER_XT_MATCH_HELPER=m -CONFIG_NETFILTER_XT_MATCH_LENGTH=m +# CONFIG_NETFILTER_XT_MATCH_HELPER is not set +# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set CONFIG_NETFILTER_XT_MATCH_LIMIT=m CONFIG_NETFILTER_XT_MATCH_MAC=m -CONFIG_NETFILTER_XT_MATCH_MARK=m +# CONFIG_NETFILTER_XT_MATCH_MARK is not set # CONFIG_NETFILTER_XT_MATCH_PORTSCAN is not set CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m # CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set @@ -447,11 +448,10 @@ # CONFIG_NETFILTER_XT_MATCH_REALM is not set # CONFIG_NETFILTER_XT_MATCH_SCTP is not set CONFIG_NETFILTER_XT_MATCH_STATE=m -CONFIG_NETFILTER_XT_MATCH_LAYER7=m -# CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG is not set -CONFIG_NETFILTER_XT_MATCH_STATISTIC=m -CONFIG_NETFILTER_XT_MATCH_STRING=m -CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +# CONFIG_NETFILTER_XT_MATCH_LAYER7 is not set +# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set +# CONFIG_NETFILTER_XT_MATCH_STRING is not set +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set # CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set # @@ -459,16 +459,16 @@ # CONFIG_NF_CONNTRACK_IPV4=y CONFIG_NF_CONNTRACK_PROC_COMPAT=y -CONFIG_IP_NF_QUEUE=m +# CONFIG_IP_NF_QUEUE is not set CONFIG_IP_NF_IPTABLES=y # CONFIG_IP_NF_MATCH_IPRANGE is not set -CONFIG_IP_NF_MATCH_IPP2P=m -CONFIG_IP_NF_MATCH_TOS=m -CONFIG_IP_NF_MATCH_TIME=m -CONFIG_IP_NF_MATCH_RECENT=m -CONFIG_IP_NF_MATCH_ECN=m +# CONFIG_IP_NF_MATCH_IPP2P is not set +# CONFIG_IP_NF_MATCH_TOS is not set +# CONFIG_IP_NF_MATCH_TIME is not set +# CONFIG_IP_NF_MATCH_RECENT is not set +# CONFIG_IP_NF_MATCH_ECN is not set # CONFIG_IP_NF_MATCH_AH is not set -CONFIG_IP_NF_MATCH_TTL=m +# CONFIG_IP_NF_MATCH_TTL is not set # CONFIG_IP_NF_MATCH_OWNER is not set # CONFIG_IP_NF_MATCH_ADDRTYPE is not set CONFIG_IP_NF_FILTER=y @@ -493,9 +493,9 @@ # CONFIG_NF_NAT_SIP is not set CONFIG_IP_NF_MANGLE=y # CONFIG_IP_NF_TARGET_IMQ is not set -CONFIG_IP_NF_TARGET_TOS=m -CONFIG_IP_NF_TARGET_ECN=m -CONFIG_IP_NF_TARGET_TTL=m +# CONFIG_IP_NF_TARGET_TOS is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set # CONFIG_IP_NF_TARGET_CLUSTERIP is not set # CONFIG_IP_NF_RAW is not set # CONFIG_IP_NF_ARPTABLES is not set @@ -551,18 +551,18 @@ # Queueing/Scheduling # CONFIG_NET_SCH_CBQ=m -CONFIG_NET_SCH_HTB=m -CONFIG_NET_SCH_HFSC=m -CONFIG_NET_SCH_PRIO=m -CONFIG_NET_SCH_RED=m -CONFIG_NET_SCH_SFQ=m -CONFIG_NET_SCH_ESFQ=m -CONFIG_NET_SCH_TEQL=m -CONFIG_NET_SCH_TBF=m -CONFIG_NET_SCH_GRED=m -CONFIG_NET_SCH_DSMARK=m +# CONFIG_NET_SCH_HTB is not set +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_ESFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set CONFIG_NET_SCH_NETEM=m -CONFIG_NET_SCH_INGRESS=m +# CONFIG_NET_SCH_INGRESS is not set # # Classification @@ -689,6 +689,8 @@ # Self-contained MTD device drivers # # CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set # CONFIG_MTD_SLRAM is not set # CONFIG_MTD_PHRAM is not set # CONFIG_MTD_MTDRAM is not set @@ -729,6 +731,7 @@ # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set # CONFIG_BLK_DEV_RAM is not set # CONFIG_CDROM_PKTCDVD is not set # CONFIG_ATA_OVER_ETH is not set @@ -737,12 +740,126 @@ # SCSI device support # # CONFIG_RAID_ATTRS is not set -# CONFIG_SCSI is not set -# CONFIG_SCSI_DMA is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set # CONFIG_SCSI_NETLINK is not set -# CONFIG_ATA is not set +CONFIG_SCSI_PROC_FS=y # +# SCSI support type (disk, tape, CD-ROM) +# +# CONFIG_BLK_DEV_SD is not set +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_AIC94XX is not set +# CONFIG_SCSI_ARCMSR is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_MEGARAID_SAS is not set +# CONFIG_SCSI_HPTIOP is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_STEX is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_QLA_FC is not set +# CONFIG_SCSI_QLA_ISCSI is not set +# CONFIG_SCSI_LPFC is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_SRP is not set +CONFIG_ATA=y +# CONFIG_ATA_NONSTANDARD is not set +# CONFIG_SATA_AHCI is not set +# CONFIG_SATA_SVW is not set +# CONFIG_ATA_PIIX is not set +# CONFIG_SATA_MV is not set +# CONFIG_SATA_NV is not set +# CONFIG_PDC_ADMA is not set +# CONFIG_SATA_QSTOR is not set +# CONFIG_SATA_PROMISE is not set +# CONFIG_SATA_SX4 is not set +# CONFIG_SATA_SIL is not set +# CONFIG_SATA_SIL24 is not set +# CONFIG_SATA_SIS is not set +# CONFIG_SATA_ULI is not set +# CONFIG_SATA_VIA is not set +# CONFIG_SATA_VITESSE is not set +# CONFIG_SATA_INIC162X is not set +# CONFIG_PATA_ALI is not set +# CONFIG_PATA_AMD is not set +# CONFIG_PATA_ARTOP is not set +# CONFIG_PATA_ATIIXP is not set +# CONFIG_PATA_CMD640_PCI is not set +# CONFIG_PATA_CMD64X is not set +# CONFIG_PATA_CS5520 is not set +# CONFIG_PATA_CS5530 is not set +# CONFIG_PATA_CYPRESS is not set +# CONFIG_PATA_EFAR is not set +# CONFIG_ATA_GENERIC is not set +# CONFIG_PATA_HPT366 is not set +# CONFIG_PATA_HPT37X is not set +# CONFIG_PATA_HPT3X2N is not set +# CONFIG_PATA_HPT3X3 is not set +# CONFIG_PATA_IT821X is not set +# CONFIG_PATA_IT8213 is not set +# CONFIG_PATA_JMICRON is not set +# CONFIG_PATA_TRIFLEX is not set +# CONFIG_PATA_MARVELL is not set +# CONFIG_PATA_MPIIX is not set +# CONFIG_PATA_OLDPIIX is not set +# CONFIG_PATA_NETCELL is not set +# CONFIG_PATA_NS87410 is not set +# CONFIG_PATA_OPTI is not set +# CONFIG_PATA_OPTIDMA is not set +# CONFIG_PATA_PDC_OLD is not set +# CONFIG_PATA_RADISYS is not set +# CONFIG_PATA_RZ1000 is not set +# CONFIG_PATA_SC1200 is not set +# CONFIG_PATA_SERVERWORKS is not set +# CONFIG_PATA_PDC2027X is not set +# CONFIG_PATA_SIL680 is not set +# CONFIG_PATA_SIS is not set +# CONFIG_PATA_VIA is not set +# CONFIG_PATA_WINBOND is not set + +# # Multi-device support (RAID and LVM) # # CONFIG_MD is not set @@ -751,6 +868,9 @@ # Fusion MPT device support # # CONFIG_FUSION is not set +# CONFIG_FUSION_SPI is not set +# CONFIG_FUSION_FC is not set +# CONFIG_FUSION_SAS is not set # # IEEE 1394 (FireWire) support @@ -788,6 +908,16 @@ # # CONFIG_WLAN_PRE80211 is not set # CONFIG_WLAN_80211 is not set + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET_MII is not set +# CONFIG_USB_USBNET is not set # CONFIG_WAN is not set # CONFIG_FDDI is not set # CONFIG_HIPPI is not set @@ -802,6 +932,7 @@ CONFIG_PPPOE=m # CONFIG_SLIP is not set CONFIG_SLHC=m +# CONFIG_NET_FC is not set # CONFIG_SHAPER is not set # CONFIG_NETCONSOLE is not set # CONFIG_NETPOLL is not set @@ -930,6 +1061,7 @@ # CONFIG_I2C_SIS630 is not set # CONFIG_I2C_SIS96X is not set # CONFIG_I2C_STUB is not set +# CONFIG_I2C_TINY_USB is not set # CONFIG_I2C_VIA is not set # CONFIG_I2C_VIAPRO is not set # CONFIG_I2C_VOODOO3 is not set @@ -953,10 +1085,21 @@ # # SPI support # -# CONFIG_SPI is not set -# CONFIG_SPI_MASTER is not set +CONFIG_SPI=y +CONFIG_SPI_MASTER=y # +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_AT25 is not set +# CONFIG_SPI_SPIDEV is not set + +# # Dallas's 1-wire bus # # CONFIG_W1 is not set @@ -981,6 +1124,7 @@ # CONFIG_SENSORS_GL520SM is not set # CONFIG_SENSORS_IT87 is not set # CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set # CONFIG_SENSORS_LM75 is not set # CONFIG_SENSORS_LM77 is not set # CONFIG_SENSORS_LM78 is not set @@ -1069,21 +1213,159 @@ # # HID Devices # -# CONFIG_HID is not set +CONFIG_HID=y +# CONFIG_HID_DEBUG is not set # +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_USB_HIDINPUT_POWERBOOK is not set +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set + +# # USB support # CONFIG_USB_ARCH_HAS_HCD=y CONFIG_USB_ARCH_HAS_OHCI=y CONFIG_USB_ARCH_HAS_EHCI=y -# CONFIG_USB is not set +CONFIG_USB=y +CONFIG_USB_DEBUG=y # +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_SPLIT_ISO=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +# CONFIG_USB_EHCI_BIG_ENDIAN_MMIO is not set +# CONFIG_USB_ISP116X_HCD is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set +# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +CONFIG_USB_UHCI_HCD=y +# CONFIG_USB_SL811_HCD is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# # NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' # # +# may also be needed; see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_MON is not set + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=y +# CONFIG_USB_SERIAL_CONSOLE is not set +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_AIRPRIME is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_CP2101 is not set +# CONFIG_USB_SERIAL_CYPRESS_M8 is not set +# CONFIG_USB_SERIAL_EMPEG is not set +CONFIG_USB_SERIAL_FTDI_SIO=y +# CONFIG_USB_SERIAL_FUNSOFT is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT_TI is not set +# CONFIG_USB_SERIAL_GARMIN is not set +# CONFIG_USB_SERIAL_IPW is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_KOBIL_SCT is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_MOS7720 is not set +# CONFIG_USB_SERIAL_MOS7840 is not set +# CONFIG_USB_SERIAL_NAVMAN is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_HP4X is not set +# CONFIG_USB_SERIAL_SAFE is not set +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +# CONFIG_USB_SERIAL_TI is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OPTION is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_SERIAL_DEBUG is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGET is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set + +# +# USB DSL modem support +# + +# # USB Gadget Support # # CONFIG_USB_GADGET is not set @@ -1122,6 +1404,8 @@ # # SPI RTC drivers # +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_MAX6902 is not set # # Platform RTC drivers @@ -1139,9 +1423,14 @@ # # File systems # -# CONFIG_EXT2_FS is not set -# CONFIG_EXT3_FS is not set +CONFIG_EXT2_FS=m +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=m +# CONFIG_EXT3_FS_XATTR is not set # CONFIG_EXT4DEV_FS is not set +CONFIG_JBD=m +# CONFIG_JBD_DEBUG is not set # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set # CONFIG_FS_POSIX_ACL is not set @@ -1231,7 +1520,13 @@ # CONFIG_RPCSEC_GSS_KRB5 is not set # CONFIG_RPCSEC_GSS_SPKM3 is not set # CONFIG_SMB_FS is not set -# CONFIG_CIFS is not set +CONFIG_CIFS=m +CONFIG_CIFS_STATS=y +# CONFIG_CIFS_STATS2 is not set +# CONFIG_CIFS_WEAK_PW_HASH is not set +# CONFIG_CIFS_XATTR is not set +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_EXPERIMENTAL is not set # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set # CONFIG_AFS_FS is not set @@ -1380,7 +1675,7 @@ CONFIG_BITREVERSE=y CONFIG_CRC_CCITT=m # CONFIG_CRC16 is not set -# CONFIG_CRC_ITU_T is not set +CONFIG_CRC_ITU_T=y CONFIG_CRC32=y CONFIG_LIBCRC32C=y CONFIG_ZLIB_INFLATE=y Added: trunk/marvell_owrt_8.09/target/linux/marvell/patches-2.6.22/002-gw2367-usb-mmc-i2c.patch =================================================================== --- trunk/marvell_owrt_8.09/target/linux/marvell/patches-2.6.22/002-gw2367-usb-mmc-i2c.patch (rev 0) +++ trunk/marvell_owrt_8.09/target/linux/marvell/patches-2.6.22/002-gw2367-usb-mmc-i2c.patch 2009-09-15 16:05:04 UTC (rev 92) @@ -0,0 +1,12988 @@ +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/mach-feroceon-mv78xx0/core.c linux-2.6.22.18-mv/arch/arm/mach-feroceon-mv78xx0/core.c +--- linux-2.6.22.18-mv-clean/arch/arm/mach-feroceon-mv78xx0/core.c 2009-08-25 14:38:00.000000000 -0700 ++++ linux-2.6.22.18-mv/arch/arm/mach-feroceon-mv78xx0/core.c 2009-09-13 22:07:41.000000000 -0700 +@@ -266,6 +266,7 @@ + .freq_m = 6, /* assumes 200 MHz TCLK */ + .freq_n = 5, + .timeout = 1000, /* Default timeout of 1 second */ ++ .retries = 3, + }; + + static struct resource mv78x00_i2c_resources[] = { +diff -ruN linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_hal/usb/mvUsb.c linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_hal/usb/mvUsb.c +--- linux-2.6.22.18-mv-clean/arch/arm/plat-feroceon/mv_hal/usb/mvUsb.c 2008-08-07 18:29:52.000000000 -0700 ++++ linux-2.6.22.18-mv/arch/arm/plat-feroceon/mv_hal/usb/mvUsb.c 2009-09-14 11:54:10.000000000 -0700 +@@ -209,6 +209,8 @@ + { + MV_U32 regVal; + ++ printk("usbphyinit\n"); ++ + /******* USB PHY PLL Control Register 0x410 *******/ + regVal = MV_REG_READ(MV_USB_PHY_PLL_CTRL_REG(dev)); + +diff -ruN linux-2.6.22.18-mv-clean/drivers/i2c/busses/i2c-mv64xxx.c linux-2.6.22.18-mv/drivers/i2c/busses/i2c-mv64xxx.c +--- linux-2.6.22.18-mv-clean/drivers/i2c/busses/i2c-mv64xxx.c 2008-04-16 20:17:26.000000000 -0700 ++++ linux-2.6.22.18-mv/drivers/i2c/busses/i2c-mv64xxx.c 2009-09-13 22:41:55.000000000 -0700 +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + + #include + +@@ -423,13 +424,24 @@ + mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) + { + struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap); +- int i, rc; ++ int i, r, rc; + +- for (i=0; iretries; r >= 0; r--) ++ { ++ for (i=0; i= 0) ++ break; ++ else ++ udelay(100); ++ } ++ if (rc < 0) ++ return rc; ++ else ++ return num; + } + + static const struct i2c_algorithm mv64xxx_i2c_algo = { +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/card/block.c linux-2.6.22.18-mv/drivers/mmc/card/block.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/card/block.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/card/block.c 2008-01-29 03:26:08.000000000 -0800 +@@ -44,6 +44,9 @@ + * max 8 partitions per card + */ + #define MMC_SHIFT 3 ++#define MMC_NUM_MINORS (256 >> MMC_SHIFT) ++ ++static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))]; + + /* + * There is one mmc_blk_data per slot. +@@ -80,6 +83,8 @@ + mutex_lock(&open_lock); + md->usage--; + if (md->usage == 0) { ++ int devidx = md->disk->first_minor >> MMC_SHIFT; ++ __clear_bit(devidx, dev_use); + put_disk(md->disk); + kfree(md); + } +@@ -151,17 +156,19 @@ + + cmd.opcode = MMC_APP_CMD; + cmd.arg = card->rca << 16; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, 0); +- if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) ++ if (err) ++ return (u32)-1; ++ if (!mmc_host_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD)) + return (u32)-1; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_APP_SEND_NUM_WR_BLKS; + cmd.arg = 0; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + + memset(&data, 0, sizeof(struct mmc_data)); + +@@ -192,7 +199,7 @@ + + mmc_wait_for_req(card->host, &mrq); + +- if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) ++ if (cmd.error || data.error) + return (u32)-1; + + blocks = ntohl(blocks); +@@ -220,17 +227,15 @@ + brq.cmd.arg = req->sector; + if (!mmc_card_blockaddr(card)) + brq.cmd.arg <<= 9; +- brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; ++ brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + brq.data.blksz = 1 << md->block_bits; + brq.stop.opcode = MMC_STOP_TRANSMISSION; + brq.stop.arg = 0; +- brq.stop.flags = MMC_RSP_R1B | MMC_CMD_AC; ++ brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); + if (brq.data.blocks > card->host->max_blk_count) + brq.data.blocks = card->host->max_blk_count; + +- mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ); +- + /* + * If the host doesn't support multiple block writes, force + * block writes to single block. SD cards are excepted from +@@ -243,8 +248,12 @@ + brq.data.blocks = 1; + + if (brq.data.blocks > 1) { +- brq.data.flags |= MMC_DATA_MULTI; +- brq.mrq.stop = &brq.stop; ++ /* SPI multiblock writes terminate using a special ++ * token, not a STOP_TRANSMISSION request. ++ */ ++ if (!mmc_host_is_spi(card->host) ++ || rq_data_dir(req) == READ) ++ brq.mrq.stop = &brq.stop; + readcmd = MMC_READ_MULTIPLE_BLOCK; + writecmd = MMC_WRITE_MULTIPLE_BLOCK; + } else { +@@ -261,8 +270,12 @@ + brq.data.flags |= MMC_DATA_WRITE; + } + ++ mmc_set_data_timeout(&brq.data, card); ++ + brq.data.sg = mq->sg; +- brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg); ++ brq.data.sg_len = mmc_queue_map_sg(mq); ++ ++ mmc_queue_bounce_pre(mq); + + if (brq.data.blocks != + (req->nr_sectors >> (md->block_bits - 9))) { +@@ -279,6 +292,9 @@ + } + + mmc_wait_for_req(card->host, &brq.mrq); ++ ++ mmc_queue_bounce_post(mq); ++ + if (brq.cmd.error) { + printk(KERN_ERR "%s: error %d sending read/write command\n", + req->rq_disk->disk_name, brq.cmd.error); +@@ -297,7 +313,7 @@ + goto cmd_err; + } + +- if (rq_data_dir(req) != READ) { ++ if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) { + do { + int err; + +@@ -310,7 +326,15 @@ + req->rq_disk->disk_name, err); + goto cmd_err; + } +- } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); ++ /*} while (!(cmd.resp[0] & R1_READY_FOR_DATA));*/ ++ /* ++ * Some cards mishandle the status bits, ++ * so make sure to check both the busy ++ * indication and the card state. ++ */ ++ } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || ++ (R1_CURRENT_STATE(cmd.resp[0]) == 7)); ++ + + #if 0 + if (cmd.resp[0] & ~0x00000900) +@@ -389,9 +413,11 @@ + return 0; + } + ++/* + #define MMC_NUM_MINORS (256 >> MMC_SHIFT) + + static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))]; ++*/ + + static inline int mmc_blk_readonly(struct mmc_card *card) + { +@@ -409,13 +435,12 @@ + return ERR_PTR(-ENOSPC); + __set_bit(devidx, dev_use); + +- md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); ++ md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); + if (!md) { + ret = -ENOMEM; + goto out; + } + +- memset(md, 0, sizeof(struct mmc_blk_data)); + + /* + * Set the read-only status based on the supported commands +@@ -506,7 +531,7 @@ + mmc_claim_host(card->host); + cmd.opcode = MMC_SET_BLOCKLEN; + cmd.arg = 1 << md->block_bits; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, 5); + mmc_release_host(card->host); + +@@ -558,7 +583,7 @@ + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (md) { +- int devidx; ++ /*int devidx;*/ + + /* Stop new requests from getting into the queue */ + del_gendisk(md->disk); +@@ -566,8 +591,8 @@ + /* Then flush out any already in there */ + mmc_cleanup_queue(&md->queue); + +- devidx = md->disk->first_minor >> MMC_SHIFT; +- __clear_bit(devidx, dev_use); ++ /*devidx = md->disk->first_minor >> MMC_SHIFT; ++ __clear_bit(devidx, dev_use);*/ + + mmc_blk_put(md); + } +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/card/Kconfig linux-2.6.22.18-mv/drivers/mmc/card/Kconfig +--- linux-2.6.22.18-mv-clean/drivers/mmc/card/Kconfig 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/card/Kconfig 2008-01-29 03:26:08.000000000 -0800 +@@ -14,3 +14,28 @@ + mount the filesystem. Almost everyone wishing MMC support + should say Y or M here. + ++config MMC_BLOCK_BOUNCE ++ bool "Use bounce buffer for simple hosts" ++ depends on MMC_BLOCK ++ default y ++ help ++ SD/MMC is a high latency protocol where it is crucial to ++ send large requests in order to get high performance. Many ++ controllers, however, are restricted to continuous memory ++ (i.e. they can't do scatter-gather), something the kernel ++ rarely can provide. ++ ++ Say Y here to help these restricted hosts by bouncing ++ requests back and forth from a large buffer. You will get ++ a big performance gain at the cost of up to 64 KiB of ++ physical memory. ++ ++ If unsure, say Y here. ++ ++config SDIO_UART ++ tristate "SDIO UART/GPS class support" ++ depends on MMC ++ help ++ SDIO function driver for SDIO cards that implements the UART ++ class, as well as the GPS class which appears like a UART. ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/card/Makefile linux-2.6.22.18-mv/drivers/mmc/card/Makefile +--- linux-2.6.22.18-mv-clean/drivers/mmc/card/Makefile 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/card/Makefile 2008-01-29 03:26:08.000000000 -0800 +@@ -9,3 +9,5 @@ + obj-$(CONFIG_MMC_BLOCK) += mmc_block.o + mmc_block-objs := block.o queue.o + ++obj-$(CONFIG_SDIO_UART) += sdio_uart.o ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/card/queue.c linux-2.6.22.18-mv/drivers/mmc/card/queue.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/card/queue.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/card/queue.c 2008-01-29 03:26:08.000000000 -0800 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/queue.c ++ * linux/drivers/mmc/card/queue.c + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * Copyright 2006-2007 Pierre Ossman +@@ -11,12 +11,16 @@ + */ + #include + #include ++#include + #include ++#include + + #include + #include + #include "queue.h" + ++#define MMC_QUEUE_BOUNCESZ 65536 ++ + #define MMC_QUEUE_SUSPENDED (1 << 0) + + /* +@@ -42,11 +46,7 @@ + struct mmc_queue *mq = d; + struct request_queue *q = mq->queue; + +- /* +- * Set iothread to ensure that we aren't put to sleep by +- * the process freezing. We handle suspension ourselves. +- */ +- current->flags |= PF_MEMALLOC|PF_NOFREEZE; ++ current->flags |= PF_MEMALLOC; + + down(&mq->thread_sem); + do { +@@ -84,7 +84,7 @@ + * on any queue on this host, and attempt to issue it. This may + * not be the queue we were asked to process. + */ +-static void mmc_request(request_queue_t *q) ++static void mmc_request(struct request_queue *q) + { + struct mmc_queue *mq = q->queuedata; + struct request *req; +@@ -127,21 +127,65 @@ + if (!mq->queue) + return -ENOMEM; + +- blk_queue_prep_rq(mq->queue, mmc_prep_request); +- blk_queue_bounce_limit(mq->queue, limit); +- blk_queue_max_sectors(mq->queue, host->max_req_size / 512); +- blk_queue_max_phys_segments(mq->queue, host->max_phys_segs); +- blk_queue_max_hw_segments(mq->queue, host->max_hw_segs); +- blk_queue_max_segment_size(mq->queue, host->max_seg_size); +- + mq->queue->queuedata = mq; + mq->req = NULL; + +- mq->sg = kmalloc(sizeof(struct scatterlist) * host->max_phys_segs, +- GFP_KERNEL); +- if (!mq->sg) { +- ret = -ENOMEM; +- goto cleanup_queue; ++ blk_queue_prep_rq(mq->queue, mmc_prep_request); ++ ++#ifdef CONFIG_MMC_BLOCK_BOUNCE ++ if (host->max_hw_segs == 1) { ++ unsigned int bouncesz; ++ ++ bouncesz = MMC_QUEUE_BOUNCESZ; ++ ++ if (bouncesz > host->max_req_size) ++ bouncesz = host->max_req_size; ++ if (bouncesz > host->max_seg_size) ++ bouncesz = host->max_seg_size; ++ ++ mq->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); ++ if (!mq->bounce_buf) { ++ printk(KERN_WARNING "%s: unable to allocate " ++ "bounce buffer\n", mmc_card_name(card)); ++ } else { ++ blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_HIGH); ++ blk_queue_max_sectors(mq->queue, bouncesz / 512); ++ blk_queue_max_phys_segments(mq->queue, bouncesz / 512); ++ blk_queue_max_hw_segments(mq->queue, bouncesz / 512); ++ blk_queue_max_segment_size(mq->queue, bouncesz); ++ ++ mq->sg = kmalloc(sizeof(struct scatterlist), ++ GFP_KERNEL); ++ if (!mq->sg) { ++ ret = -ENOMEM; ++ goto cleanup_queue; ++ } ++ sg_init_table(mq->sg, 1); ++ ++ mq->bounce_sg = kmalloc(sizeof(struct scatterlist) * ++ bouncesz / 512, GFP_KERNEL); ++ if (!mq->bounce_sg) { ++ ret = -ENOMEM; ++ goto cleanup_queue; ++ } ++ sg_init_table(mq->bounce_sg, bouncesz / 512); ++ } ++ } ++#endif ++ ++ if (!mq->bounce_buf) { ++ blk_queue_bounce_limit(mq->queue, limit); ++ blk_queue_max_sectors(mq->queue, host->max_req_size / 512); ++ blk_queue_max_phys_segments(mq->queue, host->max_phys_segs); ++ blk_queue_max_hw_segments(mq->queue, host->max_hw_segs); ++ blk_queue_max_segment_size(mq->queue, host->max_seg_size); ++ ++ mq->sg = kzalloc(sizeof(struct scatterlist) * ++ host->max_phys_segs, GFP_KERNEL); ++ if (!mq->sg) { ++ ret = -ENOMEM; ++ goto cleanup_queue; ++ } + } + + init_MUTEX(&mq->thread_sem); +@@ -149,22 +193,28 @@ + mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd"); + if (IS_ERR(mq->thread)) { + ret = PTR_ERR(mq->thread); +- goto free_sg; ++ goto free_bounce_sg; + } + + return 0; +- +- free_sg: +- kfree(mq->sg); +- mq->sg = NULL; ++ free_bounce_sg: ++ if (mq->bounce_sg) ++ kfree(mq->bounce_sg); ++ mq->bounce_sg = NULL; + cleanup_queue: ++ if (mq->sg) ++ kfree(mq->sg); ++ mq->sg = NULL; ++ if (mq->bounce_buf) ++ kfree(mq->bounce_buf); ++ mq->bounce_buf = NULL; + blk_cleanup_queue(mq->queue); + return ret; + } + + void mmc_cleanup_queue(struct mmc_queue *mq) + { +- request_queue_t *q = mq->queue; ++ struct request_queue *q = mq->queue; + unsigned long flags; + + /* Mark that we should start throwing out stragglers */ +@@ -178,9 +228,17 @@ + /* Then terminate our worker thread */ + kthread_stop(mq->thread); + ++ if (mq->bounce_sg) ++ kfree(mq->bounce_sg); ++ mq->bounce_sg = NULL; ++ + kfree(mq->sg); + mq->sg = NULL; + ++ if (mq->bounce_buf) ++ kfree(mq->bounce_buf); ++ mq->bounce_buf = NULL; ++ + blk_cleanup_queue(mq->queue); + + mq->card = NULL; +@@ -197,7 +255,7 @@ + */ + void mmc_queue_suspend(struct mmc_queue *mq) + { +- request_queue_t *q = mq->queue; ++ struct request_queue *q = mq->queue; + unsigned long flags; + + if (!(mq->flags & MMC_QUEUE_SUSPENDED)) { +@@ -217,7 +275,7 @@ + */ + void mmc_queue_resume(struct mmc_queue *mq) + { +- request_queue_t *q = mq->queue; ++ struct request_queue *q = mq->queue; + unsigned long flags; + + if (mq->flags & MMC_QUEUE_SUSPENDED) { +@@ -231,3 +289,114 @@ + } + } + ++static void copy_sg(struct scatterlist *dst, unsigned int dst_len, ++ struct scatterlist *src, unsigned int src_len) ++{ ++ unsigned int chunk; ++ char *dst_buf, *src_buf; ++ unsigned int dst_size, src_size; ++ ++ dst_buf = NULL; ++ src_buf = NULL; ++ dst_size = 0; ++ src_size = 0; ++ ++ while (src_len) { ++ BUG_ON(dst_len == 0); ++ ++ if (dst_size == 0) { ++ #if 0 ++ dst_buf = sg_virt(dst); ++ #else ++ dst_buf = sg_virt_addr(dst); ++ #endif ++ dst_size = dst->length; ++ } ++ ++ if (src_size == 0) { ++ #if 0 ++ src_buf = sg_virt(src); ++ #else ++ src_buf = sg_virt_addr(src); ++ #endif ++ src_size = src->length; ++ } ++ ++ chunk = min(dst_size, src_size); ++ ++ memcpy(dst_buf, src_buf, chunk); ++ ++ dst_buf += chunk; ++ src_buf += chunk; ++ dst_size -= chunk; ++ src_size -= chunk; ++ ++ if (dst_size == 0) { ++ dst++; ++ dst_len--; ++ } ++ ++ if (src_size == 0) { ++ src++; ++ src_len--; ++ } ++ } ++} ++ ++unsigned int mmc_queue_map_sg(struct mmc_queue *mq) ++{ ++ unsigned int sg_len; ++ ++ if (!mq->bounce_buf) ++ return blk_rq_map_sg(mq->queue, mq->req, mq->sg); ++ ++ BUG_ON(!mq->bounce_sg); ++ ++ sg_len = blk_rq_map_sg(mq->queue, mq->req, mq->bounce_sg); ++ ++ mq->bounce_sg_len = sg_len; ++ ++ /* ++ * Shortcut in the event we only get a single entry. ++ */ ++ if (sg_len == 1) { ++ memcpy(mq->sg, mq->bounce_sg, sizeof(struct scatterlist)); ++ return 1; ++ } ++ ++ sg_init_one(mq->sg, mq->bounce_buf, 0); ++ ++ while (sg_len) { ++ mq->sg[0].length += mq->bounce_sg[sg_len - 1].length; ++ sg_len--; ++ } ++ ++ return 1; ++} ++ ++void mmc_queue_bounce_pre(struct mmc_queue *mq) ++{ ++ if (!mq->bounce_buf) ++ return; ++ ++ if (mq->bounce_sg_len == 1) ++ return; ++ if (rq_data_dir(mq->req) != WRITE) ++ return; ++ ++ copy_sg(mq->sg, 1, mq->bounce_sg, mq->bounce_sg_len); ++} ++ ++void mmc_queue_bounce_post(struct mmc_queue *mq) ++{ ++ if (!mq->bounce_buf) ++ return; ++ ++ if (mq->bounce_sg_len == 1) ++ return; ++ if (rq_data_dir(mq->req) != READ) ++ return; ++ ++ copy_sg(mq->bounce_sg, mq->bounce_sg_len, mq->sg, 1); ++} ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/card/queue.h linux-2.6.22.18-mv/drivers/mmc/card/queue.h +--- linux-2.6.22.18-mv-clean/drivers/mmc/card/queue.h 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/card/queue.h 2008-01-29 03:26:08.000000000 -0800 +@@ -14,6 +14,9 @@ + void *data; + struct request_queue *queue; + struct scatterlist *sg; ++ char *bounce_buf; ++ struct scatterlist *bounce_sg; ++ unsigned int bounce_sg_len; + }; + + extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *); +@@ -21,4 +24,8 @@ + extern void mmc_queue_suspend(struct mmc_queue *); + extern void mmc_queue_resume(struct mmc_queue *); + ++extern unsigned int mmc_queue_map_sg(struct mmc_queue *); ++extern void mmc_queue_bounce_pre(struct mmc_queue *); ++extern void mmc_queue_bounce_post(struct mmc_queue *); ++ + #endif +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/card/sdio_uart.c linux-2.6.22.18-mv/drivers/mmc/card/sdio_uart.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/card/sdio_uart.c 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/card/sdio_uart.c 2008-01-29 03:26:08.000000000 -0800 +@@ -0,0 +1,1158 @@ ++/* ++ * linux/drivers/mmc/card/sdio_uart.c - SDIO UART/GPS driver ++ * ++ * Based on drivers/serial/8250.c and drivers/serial/serial_core.c ++ * by Russell King. ++ * ++ * Author: Nicolas Pitre ++ * Created: June 15, 2007 ++ * Copyright: MontaVista Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++/* ++ * Note: Although this driver assumes a 16550A-like UART implementation, ++ * it is not possible to leverage the common 8250/16550 driver, nor the ++ * core UART infrastructure, as they assumes direct access to the hardware ++ * registers, often under a spinlock. This is not possible in the SDIO ++ * context as SDIO access functions must be able to sleep. ++ * ++ * Because we need to lock the SDIO host to ensure an exclusive access to ++ * the card, we simply rely on that lock to also prevent and serialize ++ * concurrent access to the same port. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++ ++#define UART_NR 8 /* Number of UARTs this driver can handle */ ++ ++ ++#define UART_XMIT_SIZE PAGE_SIZE ++#define WAKEUP_CHARS 256 ++ ++#define circ_empty(circ) ((circ)->head == (circ)->tail) ++#define circ_clear(circ) ((circ)->head = (circ)->tail = 0) ++ ++#define circ_chars_pending(circ) \ ++ (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE)) ++ ++#define circ_chars_free(circ) \ ++ (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE)) ++ ++ ++struct uart_icount { ++ __u32 cts; ++ __u32 dsr; ++ __u32 rng; ++ __u32 dcd; ++ __u32 rx; ++ __u32 tx; ++ __u32 frame; ++ __u32 overrun; ++ __u32 parity; ++ __u32 brk; ++}; ++ ++struct sdio_uart_port { ++ struct kref kref; ++ struct tty_struct *tty; ++ unsigned int index; ++ unsigned int opened; ++ struct mutex open_lock; ++ struct sdio_func *func; ++ struct mutex func_lock; ++ struct task_struct *in_sdio_uart_irq; ++ unsigned int regs_offset; ++ struct circ_buf xmit; ++ spinlock_t write_lock; ++ struct uart_icount icount; ++ unsigned int uartclk; ++ unsigned int mctrl; ++ unsigned int read_status_mask; ++ unsigned int ignore_status_mask; ++ unsigned char x_char; ++ unsigned char ier; ++ unsigned char lcr; ++}; ++ ++static struct sdio_uart_port *sdio_uart_table[UART_NR]; ++static DEFINE_SPINLOCK(sdio_uart_table_lock); ++ ++static int sdio_uart_add_port(struct sdio_uart_port *port) ++{ ++ int index, ret = -EBUSY; ++ ++ kref_init(&port->kref); ++ mutex_init(&port->open_lock); ++ mutex_init(&port->func_lock); ++ spin_lock_init(&port->write_lock); ++ ++ spin_lock(&sdio_uart_table_lock); ++ for (index = 0; index < UART_NR; index++) { ++ if (!sdio_uart_table[index]) { ++ port->index = index; ++ sdio_uart_table[index] = port; ++ ret = 0; ++ break; ++ } ++ } ++ spin_unlock(&sdio_uart_table_lock); ++ ++ return ret; ++} ++ ++static struct sdio_uart_port *sdio_uart_port_get(unsigned index) ++{ ++ struct sdio_uart_port *port; ++ ++ if (index >= UART_NR) ++ return NULL; ++ ++ spin_lock(&sdio_uart_table_lock); ++ port = sdio_uart_table[index]; ++ if (port) ++ kref_get(&port->kref); ++ spin_unlock(&sdio_uart_table_lock); ++ ++ return port; ++} ++ ++static void sdio_uart_port_destroy(struct kref *kref) ++{ ++ struct sdio_uart_port *port = ++ container_of(kref, struct sdio_uart_port, kref); ++ kfree(port); ++} ++ ++static void sdio_uart_port_put(struct sdio_uart_port *port) ++{ ++ kref_put(&port->kref, sdio_uart_port_destroy); ++} ++ ++static void sdio_uart_port_remove(struct sdio_uart_port *port) ++{ ++ struct sdio_func *func; ++ ++ BUG_ON(sdio_uart_table[port->index] != port); ++ ++ spin_lock(&sdio_uart_table_lock); ++ sdio_uart_table[port->index] = NULL; ++ spin_unlock(&sdio_uart_table_lock); ++ ++ /* ++ * We're killing a port that potentially still is in use by ++ * the tty layer. Be careful to prevent any further access ++ * to the SDIO function and arrange for the tty layer to ++ * give up on that port ASAP. ++ * Beware: the lock ordering is critical. ++ */ ++ mutex_lock(&port->open_lock); ++ mutex_lock(&port->func_lock); ++ func = port->func; ++ sdio_claim_host(func); ++ port->func = NULL; ++ mutex_unlock(&port->func_lock); ++ if (port->opened) ++ tty_hangup(port->tty); ++ mutex_unlock(&port->open_lock); ++ sdio_release_irq(func); ++ sdio_disable_func(func); ++ sdio_release_host(func); ++ ++ sdio_uart_port_put(port); ++} ++ ++static int sdio_uart_claim_func(struct sdio_uart_port *port) ++{ ++ mutex_lock(&port->func_lock); ++ if (unlikely(!port->func)) { ++ mutex_unlock(&port->func_lock); ++ return -ENODEV; ++ } ++ if (likely(port->in_sdio_uart_irq != current)) ++ sdio_claim_host(port->func); ++ mutex_unlock(&port->func_lock); ++ return 0; ++} ++ ++static inline void sdio_uart_release_func(struct sdio_uart_port *port) ++{ ++ if (likely(port->in_sdio_uart_irq != current)) ++ sdio_release_host(port->func); ++} ++ ++static inline unsigned int sdio_in(struct sdio_uart_port *port, int offset) ++{ ++ unsigned char c; ++ c = sdio_readb(port->func, port->regs_offset + offset, NULL); ++ return c; ++} ++ ++static inline void sdio_out(struct sdio_uart_port *port, int offset, int value) ++{ ++ sdio_writeb(port->func, value, port->regs_offset + offset, NULL); ++} ++ ++static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port) ++{ ++ unsigned char status; ++ unsigned int ret; ++ ++ status = sdio_in(port, UART_MSR); ++ ++ ret = 0; ++ if (status & UART_MSR_DCD) ++ ret |= TIOCM_CAR; ++ if (status & UART_MSR_RI) ++ ret |= TIOCM_RNG; ++ if (status & UART_MSR_DSR) ++ ret |= TIOCM_DSR; ++ if (status & UART_MSR_CTS) ++ ret |= TIOCM_CTS; ++ return ret; ++} ++ ++static void sdio_uart_write_mctrl(struct sdio_uart_port *port, unsigned int mctrl) ++{ ++ unsigned char mcr = 0; ++ ++ if (mctrl & TIOCM_RTS) ++ mcr |= UART_MCR_RTS; ++ if (mctrl & TIOCM_DTR) ++ mcr |= UART_MCR_DTR; ++ if (mctrl & TIOCM_OUT1) ++ mcr |= UART_MCR_OUT1; ++ if (mctrl & TIOCM_OUT2) ++ mcr |= UART_MCR_OUT2; ++ if (mctrl & TIOCM_LOOP) ++ mcr |= UART_MCR_LOOP; ++ ++ sdio_out(port, UART_MCR, mcr); ++} ++ ++static inline void sdio_uart_update_mctrl(struct sdio_uart_port *port, ++ unsigned int set, unsigned int clear) ++{ ++ unsigned int old; ++ ++ old = port->mctrl; ++ port->mctrl = (old & ~clear) | set; ++ if (old != port->mctrl) ++ sdio_uart_write_mctrl(port, port->mctrl); ++} ++ ++#define sdio_uart_set_mctrl(port, x) sdio_uart_update_mctrl(port, x, 0) ++#define sdio_uart_clear_mctrl(port, x) sdio_uart_update_mctrl(port, 0, x) ++ ++static void sdio_uart_change_speed(struct sdio_uart_port *port, ++ struct ktermios *termios, ++ struct ktermios *old) ++{ ++ unsigned char cval, fcr = 0; ++ unsigned int baud, quot; ++ ++ switch (termios->c_cflag & CSIZE) { ++ case CS5: ++ cval = UART_LCR_WLEN5; ++ break; ++ case CS6: ++ cval = UART_LCR_WLEN6; ++ break; ++ case CS7: ++ cval = UART_LCR_WLEN7; ++ break; ++ default: ++ case CS8: ++ cval = UART_LCR_WLEN8; ++ break; ++ } ++ ++ if (termios->c_cflag & CSTOPB) ++ cval |= UART_LCR_STOP; ++ if (termios->c_cflag & PARENB) ++ cval |= UART_LCR_PARITY; ++ if (!(termios->c_cflag & PARODD)) ++ cval |= UART_LCR_EPAR; ++ ++ for (;;) { ++ baud = tty_termios_baud_rate(termios); ++ if (baud == 0) ++ baud = 9600; /* Special case: B0 rate. */ ++ if (baud <= port->uartclk) ++ break; ++ /* ++ * Oops, the quotient was zero. Try again with the old ++ * baud rate if possible, otherwise default to 9600. ++ */ ++ termios->c_cflag &= ~CBAUD; ++ if (old) { ++ termios->c_cflag |= old->c_cflag & CBAUD; ++ old = NULL; ++ } else ++ termios->c_cflag |= B9600; ++ } ++ quot = (2 * port->uartclk + baud) / (2 * baud); ++ ++ if (baud < 2400) ++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; ++ else ++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10; ++ ++ port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; ++ if (termios->c_iflag & INPCK) ++ port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; ++ if (termios->c_iflag & (BRKINT | PARMRK)) ++ port->read_status_mask |= UART_LSR_BI; ++ ++ /* ++ * Characters to ignore ++ */ ++ port->ignore_status_mask = 0; ++ if (termios->c_iflag & IGNPAR) ++ port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; ++ if (termios->c_iflag & IGNBRK) { ++ port->ignore_status_mask |= UART_LSR_BI; ++ /* ++ * If we're ignoring parity and break indicators, ++ * ignore overruns too (for real raw support). ++ */ ++ if (termios->c_iflag & IGNPAR) ++ port->ignore_status_mask |= UART_LSR_OE; ++ } ++ ++ /* ++ * ignore all characters if CREAD is not set ++ */ ++ if ((termios->c_cflag & CREAD) == 0) ++ port->ignore_status_mask |= UART_LSR_DR; ++ ++ /* ++ * CTS flow control flag and modem status interrupts ++ */ ++ port->ier &= ~UART_IER_MSI; ++ if ((termios->c_cflag & CRTSCTS) || !(termios->c_cflag & CLOCAL)) ++ port->ier |= UART_IER_MSI; ++ ++ port->lcr = cval; ++ ++ sdio_out(port, UART_IER, port->ier); ++ sdio_out(port, UART_LCR, cval | UART_LCR_DLAB); ++ sdio_out(port, UART_DLL, quot & 0xff); ++ sdio_out(port, UART_DLM, quot >> 8); ++ sdio_out(port, UART_LCR, cval); ++ sdio_out(port, UART_FCR, fcr); ++ ++ sdio_uart_write_mctrl(port, port->mctrl); ++} ++ ++static void sdio_uart_start_tx(struct sdio_uart_port *port) ++{ ++ if (!(port->ier & UART_IER_THRI)) { ++ port->ier |= UART_IER_THRI; ++ sdio_out(port, UART_IER, port->ier); ++ } ++} ++ ++static void sdio_uart_stop_tx(struct sdio_uart_port *port) ++{ ++ if (port->ier & UART_IER_THRI) { ++ port->ier &= ~UART_IER_THRI; ++ sdio_out(port, UART_IER, port->ier); ++ } ++} ++ ++static void sdio_uart_stop_rx(struct sdio_uart_port *port) ++{ ++ port->ier &= ~UART_IER_RLSI; ++ port->read_status_mask &= ~UART_LSR_DR; ++ sdio_out(port, UART_IER, port->ier); ++} ++ ++static void sdio_uart_receive_chars(struct sdio_uart_port *port, int *status) ++{ ++ struct tty_struct *tty = port->tty; ++ unsigned int ch, flag; ++ int max_count = 256; ++ ++ do { ++ ch = sdio_in(port, UART_RX); ++ flag = TTY_NORMAL; ++ port->icount.rx++; ++ ++ if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | ++ UART_LSR_FE | UART_LSR_OE))) { ++ /* ++ * For statistics only ++ */ ++ if (*status & UART_LSR_BI) { ++ *status &= ~(UART_LSR_FE | UART_LSR_PE); ++ port->icount.brk++; ++ } else if (*status & UART_LSR_PE) ++ port->icount.parity++; ++ else if (*status & UART_LSR_FE) ++ port->icount.frame++; ++ if (*status & UART_LSR_OE) ++ port->icount.overrun++; ++ ++ /* ++ * Mask off conditions which should be ignored. ++ */ ++ *status &= port->read_status_mask; ++ if (*status & UART_LSR_BI) { ++ flag = TTY_BREAK; ++ } else if (*status & UART_LSR_PE) ++ flag = TTY_PARITY; ++ else if (*status & UART_LSR_FE) ++ flag = TTY_FRAME; ++ } ++ ++ if ((*status & port->ignore_status_mask & ~UART_LSR_OE) == 0) ++ tty_insert_flip_char(tty, ch, flag); ++ ++ /* ++ * Overrun is special. Since it's reported immediately, ++ * it doesn't affect the current character. ++ */ ++ if (*status & ~port->ignore_status_mask & UART_LSR_OE) ++ tty_insert_flip_char(tty, 0, TTY_OVERRUN); ++ ++ *status = sdio_in(port, UART_LSR); ++ } while ((*status & UART_LSR_DR) && (max_count-- > 0)); ++ tty_flip_buffer_push(tty); ++} ++ ++static void sdio_uart_transmit_chars(struct sdio_uart_port *port) ++{ ++ struct circ_buf *xmit = &port->xmit; ++ int count; ++ ++ if (port->x_char) { ++ sdio_out(port, UART_TX, port->x_char); ++ port->icount.tx++; ++ port->x_char = 0; ++ return; ++ } ++ if (circ_empty(xmit) || port->tty->stopped || port->tty->hw_stopped) { ++ sdio_uart_stop_tx(port); ++ return; ++ } ++ ++ count = 16; ++ do { ++ sdio_out(port, UART_TX, xmit->buf[xmit->tail]); ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ port->icount.tx++; ++ if (circ_empty(xmit)) ++ break; ++ } while (--count > 0); ++ ++ if (circ_chars_pending(xmit) < WAKEUP_CHARS) ++ tty_wakeup(port->tty); ++ ++ if (circ_empty(xmit)) ++ sdio_uart_stop_tx(port); ++} ++ ++static void sdio_uart_check_modem_status(struct sdio_uart_port *port) ++{ ++ int status; ++ ++ status = sdio_in(port, UART_MSR); ++ ++ if ((status & UART_MSR_ANY_DELTA) == 0) ++ return; ++ ++ if (status & UART_MSR_TERI) ++ port->icount.rng++; ++ if (status & UART_MSR_DDSR) ++ port->icount.dsr++; ++ if (status & UART_MSR_DDCD) ++ port->icount.dcd++; ++ if (status & UART_MSR_DCTS) { ++ port->icount.cts++; ++ if (port->tty->termios->c_cflag & CRTSCTS) { ++ int cts = (status & UART_MSR_CTS); ++ if (port->tty->hw_stopped) { ++ if (cts) { ++ port->tty->hw_stopped = 0; ++ sdio_uart_start_tx(port); ++ tty_wakeup(port->tty); ++ } ++ } else { ++ if (!cts) { ++ port->tty->hw_stopped = 1; ++ sdio_uart_stop_tx(port); ++ } ++ } ++ } ++ } ++} ++ ++/* ++ * This handles the interrupt from one port. ++ */ ++static void sdio_uart_irq(struct sdio_func *func) ++{ ++ struct sdio_uart_port *port = sdio_get_drvdata(func); ++ unsigned int iir, lsr; ++ ++ /* ++ * In a few places sdio_uart_irq() is called directly instead of ++ * waiting for the actual interrupt to be raised and the SDIO IRQ ++ * thread scheduled in order to reduce latency. However, some ++ * interaction with the tty core may end up calling us back ++ * (serial echo, flow control, etc.) through those same places ++ * causing undesirable effects. Let's stop the recursion here. ++ */ ++ if (unlikely(port->in_sdio_uart_irq == current)) ++ return; ++ ++ iir = sdio_in(port, UART_IIR); ++ if (iir & UART_IIR_NO_INT) ++ return; ++ ++ port->in_sdio_uart_irq = current; ++ lsr = sdio_in(port, UART_LSR); ++ if (lsr & UART_LSR_DR) ++ sdio_uart_receive_chars(port, &lsr); ++ sdio_uart_check_modem_status(port); ++ if (lsr & UART_LSR_THRE) ++ sdio_uart_transmit_chars(port); ++ port->in_sdio_uart_irq = NULL; ++} ++ ++static int sdio_uart_startup(struct sdio_uart_port *port) ++{ ++ unsigned long page; ++ int ret; ++ ++ /* ++ * Set the TTY IO error marker - we will only clear this ++ * once we have successfully opened the port. ++ */ ++ set_bit(TTY_IO_ERROR, &port->tty->flags); ++ ++ /* Initialise and allocate the transmit buffer. */ ++ page = __get_free_page(GFP_KERNEL); ++ if (!page) ++ return -ENOMEM; ++ port->xmit.buf = (unsigned char *)page; ++ circ_clear(&port->xmit); ++ ++ ret = sdio_uart_claim_func(port); ++ if (ret) ++ goto err1; ++ ret = sdio_enable_func(port->func); ++ if (ret) ++ goto err2; ++ ret = sdio_claim_irq(port->func, sdio_uart_irq); ++ if (ret) ++ goto err3; ++ ++ /* ++ * Clear the FIFO buffers and disable them. ++ * (they will be reenabled in sdio_change_speed()) ++ */ ++ sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO); ++ sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO | ++ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); ++ sdio_out(port, UART_FCR, 0); ++ ++ /* ++ * Clear the interrupt registers. ++ */ ++ (void) sdio_in(port, UART_LSR); ++ (void) sdio_in(port, UART_RX); ++ (void) sdio_in(port, UART_IIR); ++ (void) sdio_in(port, UART_MSR); ++ ++ /* ++ * Now, initialize the UART ++ */ ++ sdio_out(port, UART_LCR, UART_LCR_WLEN8); ++ ++ port->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE; ++ port->mctrl = TIOCM_OUT2; ++ ++ sdio_uart_change_speed(port, port->tty->termios, NULL); ++ ++ if (port->tty->termios->c_cflag & CBAUD) ++ sdio_uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); ++ ++ if (port->tty->termios->c_cflag & CRTSCTS) ++ if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS)) ++ port->tty->hw_stopped = 1; ++ ++ clear_bit(TTY_IO_ERROR, &port->tty->flags); ++ ++ /* Kick the IRQ handler once while we're still holding the host lock */ ++ sdio_uart_irq(port->func); ++ ++ sdio_uart_release_func(port); ++ return 0; ++ ++err3: ++ sdio_disable_func(port->func); ++err2: ++ sdio_uart_release_func(port); ++err1: ++ free_page((unsigned long)port->xmit.buf); ++ return ret; ++} ++ ++static void sdio_uart_shutdown(struct sdio_uart_port *port) ++{ ++ int ret; ++ ++ ret = sdio_uart_claim_func(port); ++ if (ret) ++ goto skip; ++ ++ sdio_uart_stop_rx(port); ++ ++ /* TODO: wait here for TX FIFO to drain */ ++ ++ /* Turn off DTR and RTS early. */ ++ if (port->tty->termios->c_cflag & HUPCL) ++ sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); ++ ++ /* Disable interrupts from this port */ ++ sdio_release_irq(port->func); ++ port->ier = 0; ++ sdio_out(port, UART_IER, 0); ++ ++ sdio_uart_clear_mctrl(port, TIOCM_OUT2); ++ ++ /* Disable break condition and FIFOs. */ ++ port->lcr &= ~UART_LCR_SBC; ++ sdio_out(port, UART_LCR, port->lcr); ++ sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO | ++ UART_FCR_CLEAR_RCVR | ++ UART_FCR_CLEAR_XMIT); ++ sdio_out(port, UART_FCR, 0); ++ ++ sdio_disable_func(port->func); ++ ++ sdio_uart_release_func(port); ++ ++skip: ++ /* Free the transmit buffer page. */ ++ free_page((unsigned long)port->xmit.buf); ++} ++ ++static int sdio_uart_open (struct tty_struct *tty, struct file * filp) ++{ ++ struct sdio_uart_port *port; ++ int ret; ++ ++ port = sdio_uart_port_get(tty->index); ++ if (!port) ++ return -ENODEV; ++ ++ mutex_lock(&port->open_lock); ++ ++ /* ++ * Make sure not to mess up with a dead port ++ * which has not been closed yet. ++ */ ++ if (tty->driver_data && tty->driver_data != port) { ++ mutex_unlock(&port->open_lock); ++ sdio_uart_port_put(port); ++ return -EBUSY; ++ } ++ ++ if (!port->opened) { ++ tty->driver_data = port; ++ port->tty = tty; ++ ret = sdio_uart_startup(port); ++ if (ret) { ++ tty->driver_data = NULL; ++ port->tty = NULL; ++ mutex_unlock(&port->open_lock); ++ sdio_uart_port_put(port); ++ return ret; ++ } ++ } ++ port->opened++; ++ mutex_unlock(&port->open_lock); ++ return 0; ++} ++ ++static void sdio_uart_close(struct tty_struct *tty, struct file * filp) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ ++ if (!port) ++ return; ++ ++ mutex_lock(&port->open_lock); ++ BUG_ON(!port->opened); ++ ++ /* ++ * This is messy. The tty layer calls us even when open() ++ * returned an error. Ignore this close request if tty->count ++ * is larger than port->count. ++ */ ++ if (tty->count > port->opened) { ++ mutex_unlock(&port->open_lock); ++ return; ++ } ++ ++ if (--port->opened == 0) { ++ tty->closing = 1; ++ sdio_uart_shutdown(port); ++ tty_ldisc_flush(tty); ++ port->tty = NULL; ++ tty->driver_data = NULL; ++ tty->closing = 0; ++ } ++ mutex_unlock(&port->open_lock); ++ sdio_uart_port_put(port); ++} ++ ++static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf, ++ int count) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ struct circ_buf *circ = &port->xmit; ++ int c, ret = 0; ++ ++ if (!port->func) ++ return -ENODEV; ++ ++ spin_lock(&port->write_lock); ++ while (1) { ++ c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); ++ if (count < c) ++ c = count; ++ if (c <= 0) ++ break; ++ memcpy(circ->buf + circ->head, buf, c); ++ circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); ++ buf += c; ++ count -= c; ++ ret += c; ++ } ++ spin_unlock(&port->write_lock); ++ ++ if ( !(port->ier & UART_IER_THRI)) { ++ int err = sdio_uart_claim_func(port); ++ if (!err) { ++ sdio_uart_start_tx(port); ++ sdio_uart_irq(port->func); ++ sdio_uart_release_func(port); ++ } else ++ ret = err; ++ } ++ ++ return ret; ++} ++ ++static int sdio_uart_write_room(struct tty_struct *tty) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ return port ? circ_chars_free(&port->xmit) : 0; ++} ++ ++static int sdio_uart_chars_in_buffer(struct tty_struct *tty) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ return port ? circ_chars_pending(&port->xmit) : 0; ++} ++ ++static void sdio_uart_send_xchar(struct tty_struct *tty, char ch) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ ++ port->x_char = ch; ++ if (ch && !(port->ier & UART_IER_THRI)) { ++ if (sdio_uart_claim_func(port) != 0) ++ return; ++ sdio_uart_start_tx(port); ++ sdio_uart_irq(port->func); ++ sdio_uart_release_func(port); ++ } ++} ++ ++static void sdio_uart_throttle(struct tty_struct *tty) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ ++ if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS)) ++ return; ++ ++ if (sdio_uart_claim_func(port) != 0) ++ return; ++ ++ if (I_IXOFF(tty)) { ++ port->x_char = STOP_CHAR(tty); ++ sdio_uart_start_tx(port); ++ } ++ ++ if (tty->termios->c_cflag & CRTSCTS) ++ sdio_uart_clear_mctrl(port, TIOCM_RTS); ++ ++ sdio_uart_irq(port->func); ++ sdio_uart_release_func(port); ++} ++ ++static void sdio_uart_unthrottle(struct tty_struct *tty) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ ++ if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS)) ++ return; ++ ++ if (sdio_uart_claim_func(port) != 0) ++ return; ++ ++ if (I_IXOFF(tty)) { ++ if (port->x_char) { ++ port->x_char = 0; ++ } else { ++ port->x_char = START_CHAR(tty); ++ sdio_uart_start_tx(port); ++ } ++ } ++ ++ if (tty->termios->c_cflag & CRTSCTS) ++ sdio_uart_set_mctrl(port, TIOCM_RTS); ++ ++ sdio_uart_irq(port->func); ++ sdio_uart_release_func(port); ++} ++ ++static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ unsigned int cflag = tty->termios->c_cflag; ++ ++#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) ++ ++ if ((cflag ^ old_termios->c_cflag) == 0 && ++ RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) ++ return; ++ ++ if (sdio_uart_claim_func(port) != 0) ++ return; ++ ++ sdio_uart_change_speed(port, tty->termios, old_termios); ++ ++ /* Handle transition to B0 status */ ++ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) ++ sdio_uart_clear_mctrl(port, TIOCM_RTS | TIOCM_DTR); ++ ++ /* Handle transition away from B0 status */ ++ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { ++ unsigned int mask = TIOCM_DTR; ++ if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) ++ mask |= TIOCM_RTS; ++ sdio_uart_set_mctrl(port, mask); ++ } ++ ++ /* Handle turning off CRTSCTS */ ++ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { ++ tty->hw_stopped = 0; ++ sdio_uart_start_tx(port); ++ } ++ ++ /* Handle turning on CRTSCTS */ ++ if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { ++ if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS)) { ++ tty->hw_stopped = 1; ++ sdio_uart_stop_tx(port); ++ } ++ } ++ ++ sdio_uart_release_func(port); ++} ++ ++static void sdio_uart_break_ctl(struct tty_struct *tty, int break_state) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ ++ if (sdio_uart_claim_func(port) != 0) ++ return; ++ ++ if (break_state == -1) ++ port->lcr |= UART_LCR_SBC; ++ else ++ port->lcr &= ~UART_LCR_SBC; ++ sdio_out(port, UART_LCR, port->lcr); ++ ++ sdio_uart_release_func(port); ++} ++ ++static int sdio_uart_tiocmget(struct tty_struct *tty, struct file *file) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ int result; ++ ++ result = sdio_uart_claim_func(port); ++ if (!result) { ++ result = port->mctrl | sdio_uart_get_mctrl(port); ++ sdio_uart_release_func(port); ++ } ++ ++ return result; ++} ++ ++static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file, ++ unsigned int set, unsigned int clear) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ int result; ++ ++ result =sdio_uart_claim_func(port); ++ if(!result) { ++ sdio_uart_update_mctrl(port, set, clear); ++ sdio_uart_release_func(port); ++ } ++ ++ return result; ++} ++ ++static int sdio_uart_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int i, len = 0; ++ off_t begin = 0; ++ ++ len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n", ++ "", "", ""); ++ for (i = 0; i < UART_NR && len < PAGE_SIZE - 96; i++) { ++ struct sdio_uart_port *port = sdio_uart_port_get(i); ++ if (port) { ++ len += sprintf(page+len, "%d: uart:SDIO", i); ++ if(capable(CAP_SYS_ADMIN)) { ++ len += sprintf(page + len, " tx:%d rx:%d", ++ port->icount.tx, port->icount.rx); ++ if (port->icount.frame) ++ len += sprintf(page + len, " fe:%d", ++ port->icount.frame); ++ if (port->icount.parity) ++ len += sprintf(page + len, " pe:%d", ++ port->icount.parity); ++ if (port->icount.brk) ++ len += sprintf(page + len, " brk:%d", ++ port->icount.brk); ++ if (port->icount.overrun) ++ len += sprintf(page + len, " oe:%d", ++ port->icount.overrun); ++ if (port->icount.cts) ++ len += sprintf(page + len, " cts:%d", ++ port->icount.cts); ++ if (port->icount.dsr) ++ len += sprintf(page + len, " dsr:%d", ++ port->icount.dsr); ++ if (port->icount.rng) ++ len += sprintf(page + len, " rng:%d", ++ port->icount.rng); ++ if (port->icount.dcd) ++ len += sprintf(page + len, " dcd:%d", ++ port->icount.dcd); ++ } ++ strcat(page, "\n"); ++ len++; ++ sdio_uart_port_put(port); ++ } ++ ++ if (len + begin > off + count) ++ goto done; ++ if (len + begin < off) { ++ begin += len; ++ len = 0; ++ } ++ } ++ *eof = 1; ++ ++done: ++ if (off >= len + begin) ++ return 0; ++ *start = page + (off - begin); ++ return (count < begin + len - off) ? count : (begin + len - off); ++} ++ ++static const struct tty_operations sdio_uart_ops = { ++ .open = sdio_uart_open, ++ .close = sdio_uart_close, ++ .write = sdio_uart_write, ++ .write_room = sdio_uart_write_room, ++ .chars_in_buffer = sdio_uart_chars_in_buffer, ++ .send_xchar = sdio_uart_send_xchar, ++ .throttle = sdio_uart_throttle, ++ .unthrottle = sdio_uart_unthrottle, ++ .set_termios = sdio_uart_set_termios, ++ .break_ctl = sdio_uart_break_ctl, ++ .tiocmget = sdio_uart_tiocmget, ++ .tiocmset = sdio_uart_tiocmset, ++ .read_proc = sdio_uart_read_proc, ++}; ++ ++static struct tty_driver *sdio_uart_tty_driver; ++ ++static int sdio_uart_probe(struct sdio_func *func, ++ const struct sdio_device_id *id) ++{ ++ struct sdio_uart_port *port; ++ int ret; ++ ++ port = kzalloc(sizeof(struct sdio_uart_port), GFP_KERNEL); ++ if (!port) ++ return -ENOMEM; ++ ++ if (func->class == SDIO_CLASS_UART) { ++ printk(KERN_WARNING "%s: need info on UART class basic setup\n", ++ sdio_func_id(func)); ++ kfree(port); ++ return -ENOSYS; ++ } else if (func->class == SDIO_CLASS_GPS) { ++ /* ++ * We need tuple 0x91. It contains SUBTPL_SIOREG ++ * and SUBTPL_RCVCAPS. ++ */ ++ struct sdio_func_tuple *tpl; ++ for (tpl = func->tuples; tpl; tpl = tpl->next) { ++ if (tpl->code != 0x91) ++ continue; ++ if (tpl->size < 10) ++ continue; ++ if (tpl->data[1] == 0) /* SUBTPL_SIOREG */ ++ break; ++ } ++ if (!tpl) { ++ printk(KERN_WARNING ++ "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n", ++ sdio_func_id(func)); ++ kfree(port); ++ return -EINVAL; ++ } ++ printk(KERN_DEBUG "%s: Register ID = 0x%02x, Exp ID = 0x%02x\n", ++ sdio_func_id(func), tpl->data[2], tpl->data[3]); ++ port->regs_offset = (tpl->data[4] << 0) | ++ (tpl->data[5] << 8) | ++ (tpl->data[6] << 16); ++ printk(KERN_DEBUG "%s: regs offset = 0x%x\n", ++ sdio_func_id(func), port->regs_offset); ++ port->uartclk = tpl->data[7] * 115200; ++ if (port->uartclk == 0) ++ port->uartclk = 115200; ++ printk(KERN_DEBUG "%s: clk %d baudcode %u 4800-div %u\n", ++ sdio_func_id(func), port->uartclk, ++ tpl->data[7], tpl->data[8] | (tpl->data[9] << 8)); ++ } else { ++ kfree(port); ++ return -EINVAL; ++ } ++ ++ port->func = func; ++ sdio_set_drvdata(func, port); ++ ++ ret = sdio_uart_add_port(port); ++ if (ret) { ++ kfree(port); ++ } else { ++ struct device *dev; ++ dev = tty_register_device(sdio_uart_tty_driver, port->index, &func->dev); ++ if (IS_ERR(dev)) { ++ sdio_uart_port_remove(port); ++ ret = PTR_ERR(dev); ++ } ++ } ++ ++ return ret; ++} ++ ++static void sdio_uart_remove(struct sdio_func *func) ++{ ++ struct sdio_uart_port *port = sdio_get_drvdata(func); ++ ++ tty_unregister_device(sdio_uart_tty_driver, port->index); ++ sdio_uart_port_remove(port); ++} ++ ++static const struct sdio_device_id sdio_uart_ids[] = { ++ { SDIO_DEVICE_CLASS(SDIO_CLASS_UART) }, ++ { SDIO_DEVICE_CLASS(SDIO_CLASS_GPS) }, ++ { /* end: all zeroes */ }, ++}; ++ ++MODULE_DEVICE_TABLE(sdio, sdio_uart_ids); ++ ++static struct sdio_driver sdio_uart_driver = { ++ .probe = sdio_uart_probe, ++ .remove = sdio_uart_remove, ++ .name = "sdio_uart", ++ .id_table = sdio_uart_ids, ++}; ++ ++static int __init sdio_uart_init(void) ++{ ++ int ret; ++ struct tty_driver *tty_drv; ++ ++ sdio_uart_tty_driver = tty_drv = alloc_tty_driver(UART_NR); ++ if (!tty_drv) ++ return -ENOMEM; ++ ++ tty_drv->owner = THIS_MODULE; ++ tty_drv->driver_name = "sdio_uart"; ++ tty_drv->name = "ttySDIO"; ++ tty_drv->major = 0; /* dynamically allocated */ ++ tty_drv->minor_start = 0; ++ tty_drv->type = TTY_DRIVER_TYPE_SERIAL; ++ tty_drv->subtype = SERIAL_TYPE_NORMAL; ++ tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; ++ tty_drv->init_termios = tty_std_termios; ++ tty_drv->init_termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL; ++ tty_drv->init_termios.c_ispeed = 4800; ++ tty_drv->init_termios.c_ospeed = 4800; ++ tty_set_operations(tty_drv, &sdio_uart_ops); ++ ++ ret = tty_register_driver(tty_drv); ++ if (ret) ++ goto err1; ++ ++ ret = sdio_register_driver(&sdio_uart_driver); ++ if (ret) ++ goto err2; ++ ++ return 0; ++ ++err2: ++ tty_unregister_driver(tty_drv); ++err1: ++ put_tty_driver(tty_drv); ++ return ret; ++} ++ ++static void __exit sdio_uart_exit(void) ++{ ++ sdio_unregister_driver(&sdio_uart_driver); ++ tty_unregister_driver(sdio_uart_tty_driver); ++ put_tty_driver(sdio_uart_tty_driver); ++} ++ ++module_init(sdio_uart_init); ++module_exit(sdio_uart_exit); ++ ++MODULE_AUTHOR("Nicolas Pitre"); ++MODULE_LICENSE("GPL"); +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/bus.c linux-2.6.22.18-mv/drivers/mmc/core/bus.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/bus.c 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/bus.c 2008-01-29 03:26:08.000000000 -0800 +@@ -0,0 +1,319 @@ ++/* ++ * linux/drivers/mmc/core/bus.c ++ * ++ * Copyright (C) 2003 Russell King, All Rights Reserved. ++ * Copyright (C) 2007 Pierre Ossman ++ * ++ * 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. ++ * ++ * MMC card bus driver model ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "sysfs.h" ++#include "core.h" ++#include "sdio_cis.h" ++#include "bus.h" ++ ++#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) ++#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) ++ ++static ssize_t mmc_type_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mmc_card *card = dev_to_mmc_card(dev); ++ ++ switch (card->type) { ++ case MMC_TYPE_MMC: ++ return sprintf(buf, "MMC\n"); ++ case MMC_TYPE_SD: ++ return sprintf(buf, "SD\n"); ++ case MMC_TYPE_SDIO: ++ return sprintf(buf, "SDIO\n"); ++ default: ++ return -EFAULT; ++ } ++} ++ ++static struct device_attribute mmc_dev_attrs[] = { ++ MMC_ATTR_RO(type), ++ __ATTR_NULL, ++}; ++ ++/* ++ * This currently matches any MMC driver to any MMC card - drivers ++ * themselves make the decision whether to drive this card in their ++ * probe method. ++ */ ++static int mmc_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ return 1; ++} ++ ++static int ++#if 0 ++mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) ++#else ++mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, ++ int buf_size) ++#endif ++{ ++ ++ struct mmc_card *card = dev_to_mmc_card(dev); ++ const char *type; ++ int retval = 0, i = 0, length = 0; ++ ++#define add_env(fmt,val...) do { \ ++ retval = add_uevent_var(envp, num_envp, &i, \ ++ buf, buf_size, &length, \ ++ fmt, ##val); \ ++ if (retval) \ ++ return retval; \ ++} while (0); ++ ++ switch (card->type) { ++ case MMC_TYPE_MMC: ++ type = "MMC"; ++ break; ++ case MMC_TYPE_SD: ++ type = "SD"; ++ break; ++ case MMC_TYPE_SDIO: ++ type = "SDIO"; ++ break; ++ default: ++ type = NULL; ++ } ++ ++ if (type) { ++ #if 0 ++ retval = add_uevent_var(env, "MMC_TYPE=%s", type); ++ if (retval) ++ return retval; ++ #else ++ add_env("MMC_TYPE=%s", type); ++ #endif ++ } ++ ++ #if 0 ++ retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card)); ++ return retval; ++ #else ++ add_env("MMC_NAME=%s", mmc_card_name(card)); ++ #endif ++#undef add_env ++ ++} ++ ++static int mmc_bus_probe(struct device *dev) ++{ ++ struct mmc_driver *drv = to_mmc_driver(dev->driver); ++ struct mmc_card *card = dev_to_mmc_card(dev); ++ ++ return drv->probe(card); ++} ++ ++static int mmc_bus_remove(struct device *dev) ++{ ++ struct mmc_driver *drv = to_mmc_driver(dev->driver); ++ struct mmc_card *card = dev_to_mmc_card(dev); ++ ++ drv->remove(card); ++ ++ return 0; ++} ++ ++static int mmc_bus_suspend(struct device *dev, pm_message_t state) ++{ ++ struct mmc_driver *drv = to_mmc_driver(dev->driver); ++ struct mmc_card *card = dev_to_mmc_card(dev); ++ int ret = 0; ++ ++ if (dev->driver && drv->suspend) ++ ret = drv->suspend(card, state); ++ return ret; ++} ++ ++static int mmc_bus_resume(struct device *dev) ++{ ++ struct mmc_driver *drv = to_mmc_driver(dev->driver); ++ struct mmc_card *card = dev_to_mmc_card(dev); ++ int ret = 0; ++ ++ if (dev->driver && drv->resume) ++ ret = drv->resume(card); ++ return ret; ++} ++ ++static struct bus_type mmc_bus_type = { ++ .name = "mmc", ++ .dev_attrs = mmc_dev_attrs, ++ .match = mmc_bus_match, ++ .uevent = mmc_bus_uevent, ++ .probe = mmc_bus_probe, ++ .remove = mmc_bus_remove, ++ .suspend = mmc_bus_suspend, ++ .resume = mmc_bus_resume, ++}; ++ ++int mmc_register_bus(void) ++{ ++ return bus_register(&mmc_bus_type); ++} ++ ++void mmc_unregister_bus(void) ++{ ++ bus_unregister(&mmc_bus_type); ++} ++ ++/** ++ * mmc_register_driver - register a media driver ++ * @drv: MMC media driver ++ */ ++int mmc_register_driver(struct mmc_driver *drv) ++{ ++ drv->drv.bus = &mmc_bus_type; ++ return driver_register(&drv->drv); ++} ++ ++EXPORT_SYMBOL(mmc_register_driver); ++ ++/** ++ * mmc_unregister_driver - unregister a media driver ++ * @drv: MMC media driver ++ */ ++void mmc_unregister_driver(struct mmc_driver *drv) ++{ ++ drv->drv.bus = &mmc_bus_type; ++ driver_unregister(&drv->drv); ++} ++ ++EXPORT_SYMBOL(mmc_unregister_driver); ++ ++static void mmc_release_card(struct device *dev) ++{ ++ struct mmc_card *card = dev_to_mmc_card(dev); ++ ++ sdio_free_common_cis(card); ++ ++ if (card->info) ++ kfree(card->info); ++ ++ kfree(card); ++} ++ ++/* ++ * Allocate and initialise a new MMC card structure. ++ */ ++struct mmc_card *mmc_alloc_card(struct mmc_host *host) ++{ ++ struct mmc_card *card; ++ ++ card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL); ++ if (!card) ++ return ERR_PTR(-ENOMEM); ++ ++ card->host = host; ++ ++ device_initialize(&card->dev); ++ ++ card->dev.parent = mmc_classdev(host); ++ card->dev.bus = &mmc_bus_type; ++ card->dev.release = mmc_release_card; ++ ++ return card; ++} ++ ++/* ++ * Register a new MMC card with the driver model. ++ */ ++int mmc_add_card(struct mmc_card *card) ++{ ++ int ret; ++ const char *type; ++ ++ snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), ++ "%s:%04x", mmc_hostname(card->host), card->rca); ++ ++ switch (card->type) { ++ case MMC_TYPE_MMC: ++ type = "MMC"; ++ break; ++ case MMC_TYPE_SD: ++ type = "SD"; ++ if (mmc_card_blockaddr(card)) ++ type = "SDHC"; ++ break; ++ case MMC_TYPE_SDIO: ++ type = "SDIO"; ++ break; ++ default: ++ type = "?"; ++ break; ++ } ++ ++ if (mmc_host_is_spi(card->host)) { ++ printk(KERN_INFO "%s: new %s%s card on SPI\n", ++ mmc_hostname(card->host), ++ mmc_card_highspeed(card) ? "high speed " : "", ++ type); ++ } else { ++ printk(KERN_INFO "%s: new %s%s card at address %04x\n", ++ mmc_hostname(card->host), ++ mmc_card_highspeed(card) ? "high speed " : "", ++ type, card->rca); ++ } ++ ++ card->dev.uevent_suppress = 1; ++ ++ ret = device_add(&card->dev); ++ if (ret) ++ return ret; ++ ++ if (card->host->bus_ops->sysfs_add) { ++ ret = card->host->bus_ops->sysfs_add(card->host, card); ++ if (ret) { ++ device_del(&card->dev); ++ return ret; ++ } ++ } ++ ++ card->dev.uevent_suppress = 0; ++ ++ kobject_uevent(&card->dev.kobj, KOBJ_ADD); ++ ++ mmc_card_set_present(card); ++ ++ return 0; ++} ++ ++/* ++ * Unregister a new MMC card with the driver model, and ++ * (eventually) free it. ++ */ ++void mmc_remove_card(struct mmc_card *card) ++{ ++ if (mmc_card_present(card)) { ++ if (mmc_host_is_spi(card->host)) { ++ printk(KERN_INFO "%s: SPI card removed\n", ++ mmc_hostname(card->host)); ++ } else { ++ printk(KERN_INFO "%s: card %04x removed\n", ++ mmc_hostname(card->host), card->rca); ++ } ++ ++ if (card->host->bus_ops->sysfs_remove) ++ card->host->bus_ops->sysfs_remove(card->host, card); ++ device_del(&card->dev); ++ } ++ ++ put_device(&card->dev); ++} ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/bus.h linux-2.6.22.18-mv/drivers/mmc/core/bus.h +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/bus.h 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/bus.h 2008-01-29 03:26:08.000000000 -0800 +@@ -0,0 +1,22 @@ ++/* ++ * linux/drivers/mmc/core/bus.h ++ * ++ * Copyright (C) 2003 Russell King, All Rights Reserved. ++ * Copyright 2007 Pierre Ossman ++ * ++ * 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 _MMC_CORE_BUS_H ++#define _MMC_CORE_BUS_H ++ ++struct mmc_card *mmc_alloc_card(struct mmc_host *host); ++int mmc_add_card(struct mmc_card *card); ++void mmc_remove_card(struct mmc_card *card); ++ ++int mmc_register_bus(void); ++void mmc_unregister_bus(void); ++ ++#endif ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/core.c linux-2.6.22.18-mv/drivers/mmc/core/core.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/core.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/core.c 2009-03-26 09:33:00.000000000 -0700 +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -27,13 +28,44 @@ + #include + + #include "core.h" +-#include "sysfs.h" ++#include "bus.h" ++#include "host.h" ++#include "sdio_bus.h" + + #include "mmc_ops.h" + #include "sd_ops.h" ++#include "sdio_ops.h" + + extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr); + extern int mmc_attach_sd(struct mmc_host *host, u32 ocr); ++extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr); ++ ++static struct workqueue_struct *workqueue; ++ ++/* ++ * Enabling software CRCs on the data blocks can be a significant (30%) ++ * performance cost, and for other reasons may not always be desired. ++ * So we allow it it to be disabled. ++ */ ++int use_spi_crc = 1; ++module_param(use_spi_crc, bool, 0); ++ ++/* ++ * Internal function. Schedule delayed work in the MMC work queue. ++ */ ++static int mmc_schedule_delayed_work(struct delayed_work *work, ++ unsigned long delay) ++{ ++ return queue_delayed_work(workqueue, work, delay); ++} ++ ++/* ++ * Internal function. Flush all scheduled work from the MMC work queue. ++ */ ++static void mmc_flush_scheduled_work(void) ++{ ++ flush_workqueue(workqueue); ++} + + /** + * mmc_request_done - finish processing an MMC request +@@ -48,32 +80,48 @@ + struct mmc_command *cmd = mrq->cmd; + int err = cmd->error; + +- pr_debug("%s: req done (CMD%u): %d/%d/%d: %08x %08x %08x %08x\n", +- mmc_hostname(host), cmd->opcode, err, +- mrq->data ? mrq->data->error : 0, +- mrq->stop ? mrq->stop->error : 0, +- cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); ++ if (err && cmd->retries && mmc_host_is_spi(host)) { ++ if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND) ++ cmd->retries = 0; ++ } + + if (err && cmd->retries) { ++ pr_debug("%s: req failed (CMD%u): %d, retrying...\n", ++ mmc_hostname(host), cmd->opcode, err); ++ + cmd->retries--; + cmd->error = 0; + host->ops->request(host, mrq); +- } else if (mrq->done) { +- mrq->done(mrq); ++ } else { ++ led_trigger_event(host->led, LED_OFF); ++ ++ pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n", ++ mmc_hostname(host), cmd->opcode, err, ++ cmd->resp[0], cmd->resp[1], ++ cmd->resp[2], cmd->resp[3]); ++ ++ if (mrq->data) { ++ pr_debug("%s: %d bytes transferred: %d\n", ++ mmc_hostname(host), ++ mrq->data->bytes_xfered, mrq->data->error); ++ } ++ ++ if (mrq->stop) { ++ pr_debug("%s: (CMD%u): %d: %08x %08x %08x %08x\n", ++ mmc_hostname(host), mrq->stop->opcode, ++ mrq->stop->error, ++ mrq->stop->resp[0], mrq->stop->resp[1], ++ mrq->stop->resp[2], mrq->stop->resp[3]); ++ } ++ ++ if (mrq->done) ++ mrq->done(mrq); + } + } + + EXPORT_SYMBOL(mmc_request_done); + +-/** +- * mmc_start_request - start a command on a host +- * @host: MMC host to start command on +- * @mrq: MMC request to start +- * +- * Queue a command on the specified host. We expect the +- * caller to be holding the host lock with interrupts disabled. +- */ +-void ++static void + mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) + { + #ifdef CONFIG_MMC_DEBUG +@@ -84,8 +132,25 @@ + mmc_hostname(host), mrq->cmd->opcode, + mrq->cmd->arg, mrq->cmd->flags); + ++ if (mrq->data) { ++ pr_debug("%s: blksz %d blocks %d flags %08x " ++ "tsac %d ms nsac %d\n", ++ mmc_hostname(host), mrq->data->blksz, ++ mrq->data->blocks, mrq->data->flags, ++ mrq->data->timeout_ns / 1000000, ++ mrq->data->timeout_clks); ++ } ++ ++ if (mrq->stop) { ++ pr_debug("%s: CMD%u arg %08x flags %08x\n", ++ mmc_hostname(host), mrq->stop->opcode, ++ mrq->stop->arg, mrq->stop->flags); ++ } ++ + WARN_ON(!host->claimed); + ++ led_trigger_event(host->led, LED_FULL); ++ + mrq->cmd->error = 0; + mrq->cmd->mrq = mrq; + if (mrq->data) { +@@ -113,14 +178,21 @@ + host->ops->request(host, mrq); + } + +-EXPORT_SYMBOL(mmc_start_request); +- + static void mmc_wait_done(struct mmc_request *mrq) + { + complete(mrq->done_data); + } + +-int mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) ++/** ++ * mmc_wait_for_req - start a request and wait for completion ++ * @host: MMC host to start command ++ * @mrq: MMC request to start ++ * ++ * Start a new MMC custom command request for a host, and wait ++ * for the command to complete. Does not attempt to parse the ++ * response. ++ */ ++void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) + { + DECLARE_COMPLETION_ONSTACK(complete); + +@@ -130,8 +202,6 @@ + mmc_start_request(host, mrq); + + wait_for_completion(&complete); +- +- return 0; + } + + EXPORT_SYMBOL(mmc_wait_for_req); +@@ -150,7 +220,7 @@ + { + struct mmc_request mrq; + +- BUG_ON(!host->claimed); ++ WARN_ON(!host->claimed); + + memset(&mrq, 0, sizeof(struct mmc_request)); + +@@ -171,14 +241,24 @@ + * mmc_set_data_timeout - set the timeout for a data command + * @data: data phase for command + * @card: the MMC card associated with the data transfer +- * @write: flag to differentiate reads from writes ++ * ++ * Computes the data timeout parameters according to the ++ * correct algorithm given the card type. + */ +-void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card, +- int write) ++void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) + { + unsigned int mult; + + /* ++ * SDIO cards only define an upper 1 s limit on access. ++ */ ++ if (mmc_card_sdio(card)) { ++ data->timeout_ns = 1000000000; ++ data->timeout_clks = 0; ++ return; ++ } ++ ++ /* + * SD cards use a 100 multiplier rather than 10 + */ + mult = mmc_card_sd(card) ? 100 : 10; +@@ -187,7 +267,7 @@ + * Scale up the multiplier (and therefore the timeout) by + * the r2w factor for writes. + */ +- if (write) ++ if (data->flags & MMC_DATA_WRITE) + mult <<= card->csd.r2w_factor; + + data->timeout_ns = card->csd.tacc_ns * mult; +@@ -203,7 +283,7 @@ + timeout_us += data->timeout_clks * 1000 / + (card->host->ios.clock / 1000); + +- if (write) ++ if (data->flags & MMC_DATA_WRITE) + limit_us = 250000; + else + limit_us = 100000; +@@ -222,36 +302,43 @@ + /** + * __mmc_claim_host - exclusively claim a host + * @host: mmc host to claim +- * @card: mmc card to claim host for +- * +- * Claim a host for a set of operations. If a valid card +- * is passed and this wasn't the last card selected, select +- * the card before returning. ++ * @abort: whether or not the operation should be aborted + * +- * Note: you should use mmc_card_claim_host or mmc_claim_host. ++ * Claim a host for a set of operations. If @abort is non null and ++ * dereference a non-zero value then this will return prematurely with ++ * that non-zero value without acquiring the lock. Returns zero ++ * with the lock held otherwise. + */ +-void mmc_claim_host(struct mmc_host *host) ++int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) + { + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; ++ int stop; ++ ++ might_sleep(); + + add_wait_queue(&host->wq, &wait); + spin_lock_irqsave(&host->lock, flags); + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); +- if (!host->claimed) ++ stop = abort ? atomic_read(abort) : 0; ++ if (stop || !host->claimed) + break; + spin_unlock_irqrestore(&host->lock, flags); + schedule(); + spin_lock_irqsave(&host->lock, flags); + } + set_current_state(TASK_RUNNING); +- host->claimed = 1; ++ if (!stop) ++ host->claimed = 1; ++ else ++ wake_up(&host->wq); + spin_unlock_irqrestore(&host->lock, flags); + remove_wait_queue(&host->wq, &wait); ++ return stop; + } + +-EXPORT_SYMBOL(mmc_claim_host); ++EXPORT_SYMBOL(__mmc_claim_host); + + /** + * mmc_release_host - release a host +@@ -264,7 +351,7 @@ + { + unsigned long flags; + +- BUG_ON(!host->claimed); ++ WARN_ON(!host->claimed); + + spin_lock_irqsave(&host->lock, flags); + host->claimed = 0; +@@ -369,22 +456,6 @@ + } + + /* +- * Allocate a new MMC card +- */ +-struct mmc_card *mmc_alloc_card(struct mmc_host *host) +-{ +- struct mmc_card *card; +- +- card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL); +- if (!card) +- return ERR_PTR(-ENOMEM); +- +- mmc_init_card(card, host); +- +- return card; +-} +- +-/* + * Apply power to the MMC stack. This is a two-stage process. + * First, we enable power to the card without the clock running. + * We then wait a bit for the power to stabilise. Finally, +@@ -400,19 +471,32 @@ + int bit = fls(host->ocr_avail) - 1; + + host->ios.vdd = bit; +- host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; +- host->ios.chip_select = MMC_CS_DONTCARE; ++ if (mmc_host_is_spi(host)) { ++ host->ios.chip_select = MMC_CS_HIGH; ++ host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; ++ } else { ++ host->ios.chip_select = MMC_CS_DONTCARE; ++ host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; ++ } + host->ios.power_mode = MMC_POWER_UP; + host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ios.timing = MMC_TIMING_LEGACY; + mmc_set_ios(host); + +- mmc_delay(1); ++ /* ++ * This delay should be sufficient to allow the power supply ++ * to reach the minimum voltage. ++ */ ++ mmc_delay(2); + + host->ios.clock = host->f_min; + host->ios.power_mode = MMC_POWER_ON; + mmc_set_ios(host); + ++ /* ++ * This delay must be at least 74 clock sizes, or 1 ms, or the ++ * time required to reach a stable voltage. ++ */ + mmc_delay(2); + } + +@@ -420,8 +504,10 @@ + { + host->ios.clock = 0; + host->ios.vdd = 0; +- host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; +- host->ios.chip_select = MMC_CS_DONTCARE; ++ if (!mmc_host_is_spi(host)) { ++ host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; ++ host->ios.chip_select = MMC_CS_DONTCARE; ++ } + host->ios.power_mode = MMC_POWER_OFF; + host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ios.timing = MMC_TIMING_LEGACY; +@@ -429,6 +515,45 @@ + } + + /* ++ * Cleanup when the last reference to the bus operator is dropped. ++ */ ++void __mmc_release_bus(struct mmc_host *host) ++{ ++ BUG_ON(!host); ++ BUG_ON(host->bus_refs); ++ BUG_ON(!host->bus_dead); ++ ++ host->bus_ops = NULL; ++} ++ ++/* ++ * Increase reference count of bus operator ++ */ ++static inline void mmc_bus_get(struct mmc_host *host) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ host->bus_refs++; ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++/* ++ * Decrease reference count of bus operator and free it if ++ * it is the last reference. ++ */ ++static inline void mmc_bus_put(struct mmc_host *host) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ host->bus_refs--; ++ if ((host->bus_refs == 0) && host->bus_ops) ++ __mmc_release_bus(host); ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++/* + * Assign a mmc bus handler to a host. Only one bus handler may control a + * host at any given time. + */ +@@ -439,7 +564,7 @@ + BUG_ON(!host); + BUG_ON(!ops); + +- BUG_ON(!host->claimed); ++ WARN_ON(!host->claimed); + + spin_lock_irqsave(&host->lock, flags); + +@@ -463,8 +588,8 @@ + + BUG_ON(!host); + +- BUG_ON(!host->claimed); +- BUG_ON(!host->bus_ops); ++ WARN_ON(!host->claimed); ++ WARN_ON(!host->bus_ops); + + spin_lock_irqsave(&host->lock, flags); + +@@ -477,32 +602,22 @@ + mmc_bus_put(host); + } + +-/* +- * Cleanup when the last reference to the bus operator is dropped. +- */ +-void __mmc_release_bus(struct mmc_host *host) +-{ +- BUG_ON(!host); +- BUG_ON(host->bus_refs); +- BUG_ON(!host->bus_dead); +- +- host->bus_ops = NULL; +-} +- + /** + * mmc_detect_change - process change of state on a MMC socket + * @host: host which changed state. + * @delay: optional delay to wait before detection (jiffies) + * +- * All we know is that card(s) have been inserted or removed +- * from the socket(s). We don't know which socket or cards. ++ * MMC drivers should call this when they detect a card has been ++ * inserted or removed. The MMC layer will confirm that any ++ * present card is still functional, and initialize any newly ++ * inserted. + */ + void mmc_detect_change(struct mmc_host *host, unsigned long delay) + { + #ifdef CONFIG_MMC_DEBUG + unsigned long flags; + spin_lock_irqsave(&host->lock, flags); +- BUG_ON(host->removed); ++ /*WARN_ON(host->removed);*/ + spin_unlock_irqrestore(&host->lock, flags); + #endif + +@@ -512,7 +627,7 @@ + EXPORT_SYMBOL(mmc_detect_change); + + +-static void mmc_rescan(struct work_struct *work) ++void mmc_rescan(struct work_struct *work) + { + struct mmc_host *host = + container_of(work, struct mmc_host, detect.work); +@@ -535,95 +650,69 @@ + + mmc_send_if_cond(host, host->ocr_avail); + ++ /* ++ * First we search for SDIO... ++ */ ++ host->mode = MMC_MODE_SDIO; ++ ++ mmc_set_ios(host); ++ ++ /* reset sdio hardware */ ++ mmc_force_reset(host); ++ ++ err = mmc_send_io_op_cond(host, 0, &ocr); ++ if (!err) { ++ if (mmc_attach_sdio(host, ocr)) ++ mmc_power_off(host); ++ goto out; ++ } ++ ++ /* ++ * ...then normal SD... ++ */ ++ host->mode = MMC_MODE_SD; ++ mmc_set_ios(host); + err = mmc_send_app_op_cond(host, 0, &ocr); +- if (err == MMC_ERR_NONE) { ++ if (!err) { + if (mmc_attach_sd(host, ocr)) +- mmc_power_off(host); +- } else { +- /* +- * If we fail to detect any SD cards then try +- * searching for MMC cards. +- */ +- err = mmc_send_op_cond(host, 0, &ocr); +- if (err == MMC_ERR_NONE) { +- if (mmc_attach_mmc(host, ocr)) +- mmc_power_off(host); +- } else { +- mmc_power_off(host); +- mmc_release_host(host); +- } ++ mmc_power_off(host); ++ goto out; ++ } ++ ++ /* ++ * ...and finally MMC. ++ */ ++ host->mode = MMC_MODE_MMC; ++ ++ mmc_set_ios(host); ++ err = mmc_send_op_cond(host, 0, &ocr); ++ if (!err) { ++ if (mmc_attach_mmc(host, ocr)) ++ mmc_power_off(host); ++ goto out; + } ++ ++ mmc_release_host(host); ++ mmc_power_off(host); + } else { + if (host->bus_ops->detect && !host->bus_dead) + host->bus_ops->detect(host); + + mmc_bus_put(host); + } +-} +- +- +-/** +- * mmc_alloc_host - initialise the per-host structure. +- * @extra: sizeof private data structure +- * @dev: pointer to host device model structure +- * +- * Initialise the per-host structure. +- */ +-struct mmc_host *mmc_alloc_host(int extra, struct device *dev) +-{ +- struct mmc_host *host; +- +- host = mmc_alloc_host_sysfs(extra, dev); +- if (host) { +- spin_lock_init(&host->lock); +- init_waitqueue_head(&host->wq); +- INIT_DELAYED_WORK(&host->detect, mmc_rescan); +- +- /* +- * By default, hosts do not support SGIO or large requests. +- * They have to set these according to their abilities. +- */ +- host->max_hw_segs = 1; +- host->max_phys_segs = 1; +- host->max_seg_size = PAGE_CACHE_SIZE; +- +- host->max_req_size = PAGE_CACHE_SIZE; +- host->max_blk_size = 512; +- host->max_blk_count = PAGE_CACHE_SIZE / 512; +- } ++out: ++ if (host->caps & MMC_CAP_NEEDS_POLL) ++ mmc_schedule_delayed_work(&host->detect, HZ); + +- return host; + } + +-EXPORT_SYMBOL(mmc_alloc_host); +- +-/** +- * mmc_add_host - initialise host hardware +- * @host: mmc host +- */ +-int mmc_add_host(struct mmc_host *host) ++void mmc_start_host(struct mmc_host *host) + { +- int ret; +- +- ret = mmc_add_host_sysfs(host); +- if (ret == 0) { +- mmc_power_off(host); +- mmc_detect_change(host, 0); +- } +- +- return ret; ++ mmc_power_off(host); ++ mmc_detect_change(host, 0); + } + +-EXPORT_SYMBOL(mmc_add_host); +- +-/** +- * mmc_remove_host - remove host hardware +- * @host: mmc host +- * +- * Unregister and remove all cards associated with this host, +- * and power down the MMC bus. +- */ +-void mmc_remove_host(struct mmc_host *host) ++void mmc_stop_host(struct mmc_host *host) + { + #ifdef CONFIG_MMC_DEBUG + unsigned long flags; +@@ -648,24 +737,8 @@ + BUG_ON(host->card); + + mmc_power_off(host); +- mmc_remove_host_sysfs(host); + } + +-EXPORT_SYMBOL(mmc_remove_host); +- +-/** +- * mmc_free_host - free the host structure +- * @host: mmc host +- * +- * Free the host once all references to it have been dropped. +- */ +-void mmc_free_host(struct mmc_host *host) +-{ +- mmc_free_host_sysfs(host); +-} +- +-EXPORT_SYMBOL(mmc_free_host); +- + #ifdef CONFIG_PM + + /** +@@ -726,4 +799,47 @@ + + #endif + ++static int __init mmc_init(void) ++{ ++ int ret; ++ ++ workqueue = create_singlethread_workqueue("kmmcd"); ++ if (!workqueue) ++ return -ENOMEM; ++ ++ ret = mmc_register_bus(); ++ if (ret) ++ goto destroy_workqueue; ++ ++ ret = mmc_register_host_class(); ++ if (ret) ++ goto unregister_bus; ++ ++ ret = sdio_register_bus(); ++ if (ret) ++ goto unregister_host_class; ++ ++ return 0; ++ ++unregister_host_class: ++ mmc_unregister_host_class(); ++unregister_bus: ++ mmc_unregister_bus(); ++destroy_workqueue: ++ destroy_workqueue(workqueue); ++ ++ return ret; ++} ++ ++static void __exit mmc_exit(void) ++{ ++ sdio_unregister_bus(); ++ mmc_unregister_host_class(); ++ mmc_unregister_bus(); ++ destroy_workqueue(workqueue); ++} ++ ++subsys_initcall(mmc_init); ++module_exit(mmc_exit); ++ + MODULE_LICENSE("GPL"); +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/core.h linux-2.6.22.18-mv/drivers/mmc/core/core.h +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/core.h 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/core.h 2008-01-29 03:26:08.000000000 -0800 +@@ -18,6 +18,8 @@ + struct mmc_bus_ops { + void (*remove)(struct mmc_host *); + void (*detect)(struct mmc_host *); ++ int (*sysfs_add)(struct mmc_host *, struct mmc_card *card); ++ void (*sysfs_remove)(struct mmc_host *, struct mmc_card *card); + void (*suspend)(struct mmc_host *); + void (*resume)(struct mmc_host *); + }; +@@ -25,28 +27,6 @@ + void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); + void mmc_detach_bus(struct mmc_host *host); + +-void __mmc_release_bus(struct mmc_host *host); +- +-static inline void mmc_bus_get(struct mmc_host *host) +-{ +- unsigned long flags; +- +- spin_lock_irqsave(&host->lock, flags); +- host->bus_refs++; +- spin_unlock_irqrestore(&host->lock, flags); +-} +- +-static inline void mmc_bus_put(struct mmc_host *host) +-{ +- unsigned long flags; +- +- spin_lock_irqsave(&host->lock, flags); +- host->bus_refs--; +- if ((host->bus_refs == 0) && host->bus_ops) +- __mmc_release_bus(host); +- spin_unlock_irqrestore(&host->lock, flags); +-} +- + void mmc_set_chip_select(struct mmc_host *host, int mode); + void mmc_set_clock(struct mmc_host *host, unsigned int hz); + void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); +@@ -54,8 +34,6 @@ + u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); + void mmc_set_timing(struct mmc_host *host, unsigned int timing); + +-struct mmc_card *mmc_alloc_card(struct mmc_host *host); +- + static inline void mmc_delay(unsigned int ms) + { + if (ms < 1000 / HZ) { +@@ -66,5 +44,11 @@ + } + } + ++void mmc_rescan(struct work_struct *work); ++void mmc_start_host(struct mmc_host *host); ++void mmc_stop_host(struct mmc_host *host); ++ ++extern int use_spi_crc; ++ + #endif + +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/host.c linux-2.6.22.18-mv/drivers/mmc/core/host.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/host.c 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/host.c 2008-01-29 03:26:08.000000000 -0800 +@@ -0,0 +1,167 @@ ++/* ++ * linux/drivers/mmc/core/host.c ++ * ++ * Copyright (C) 2003 Russell King, All Rights Reserved. ++ * Copyright (C) 2007 Pierre Ossman ++ * ++ * 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. ++ * ++ * MMC host class device management ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "core.h" ++#include "host.h" ++ ++#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) ++ ++static void mmc_host_classdev_release(struct device *dev) ++{ ++ struct mmc_host *host = cls_dev_to_mmc_host(dev); ++ kfree(host); ++} ++ ++static struct class mmc_host_class = { ++ .name = "mmc_host", ++ .dev_release = mmc_host_classdev_release, ++}; ++ ++int mmc_register_host_class(void) ++{ ++ return class_register(&mmc_host_class); ++} ++ ++void mmc_unregister_host_class(void) ++{ ++ class_unregister(&mmc_host_class); ++} ++ ++static DEFINE_IDR(mmc_host_idr); ++static DEFINE_SPINLOCK(mmc_host_lock); ++ ++/** ++ * mmc_alloc_host - initialise the per-host structure. ++ * @extra: sizeof private data structure ++ * @dev: pointer to host device model structure ++ * ++ * Initialise the per-host structure. ++ */ ++struct mmc_host *mmc_alloc_host(int extra, struct device *dev) ++{ ++ struct mmc_host *host; ++ ++ host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); ++ if (!host) ++ return NULL; ++ ++ host->parent = dev; ++ host->class_dev.parent = dev; ++ host->class_dev.class = &mmc_host_class; ++ device_initialize(&host->class_dev); ++ ++ spin_lock_init(&host->lock); ++ init_waitqueue_head(&host->wq); ++ INIT_DELAYED_WORK(&host->detect, mmc_rescan); ++ ++ /* ++ * By default, hosts do not support SGIO or large requests. ++ * They have to set these according to their abilities. ++ */ ++ host->max_hw_segs = 1; ++ host->max_phys_segs = 1; ++ host->max_seg_size = PAGE_CACHE_SIZE; ++ ++ host->max_req_size = PAGE_CACHE_SIZE; ++ host->max_blk_size = 512; ++ host->max_blk_count = PAGE_CACHE_SIZE / 512; ++ ++ return host; ++} ++ ++EXPORT_SYMBOL(mmc_alloc_host); ++ ++/** ++ * mmc_add_host - initialise host hardware ++ * @host: mmc host ++ * ++ * Register the host with the driver model. The host must be ++ * prepared to start servicing requests before this function ++ * completes. ++ */ ++int mmc_add_host(struct mmc_host *host) ++{ ++ int err; ++ ++ WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && ++ !host->ops->enable_sdio_irq); ++ ++ if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) ++ return -ENOMEM; ++ ++ spin_lock(&mmc_host_lock); ++ err = idr_get_new(&mmc_host_idr, host, &host->index); ++ spin_unlock(&mmc_host_lock); ++ if (err) ++ return err; ++ ++ snprintf(host->class_dev.bus_id, BUS_ID_SIZE, ++ "mmc%d", host->index); ++ ++ led_trigger_register_simple(host->class_dev.bus_id, &host->led); ++ ++ err = device_add(&host->class_dev); ++ if (err) ++ return err; ++ ++ mmc_start_host(host); ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL(mmc_add_host); ++ ++/** ++ * mmc_remove_host - remove host hardware ++ * @host: mmc host ++ * ++ * Unregister and remove all cards associated with this host, ++ * and power down the MMC bus. No new requests will be issued ++ * after this function has returned. ++ */ ++void mmc_remove_host(struct mmc_host *host) ++{ ++ mmc_stop_host(host); ++ ++ device_del(&host->class_dev); ++ ++ led_trigger_unregister_simple(host->led); ++ ++ spin_lock(&mmc_host_lock); ++ idr_remove(&mmc_host_idr, host->index); ++ spin_unlock(&mmc_host_lock); ++} ++ ++EXPORT_SYMBOL(mmc_remove_host); ++ ++/** ++ * mmc_free_host - free the host structure ++ * @host: mmc host ++ * ++ * Free the host once all references to it have been dropped. ++ */ ++void mmc_free_host(struct mmc_host *host) ++{ ++ put_device(&host->class_dev); ++} ++ ++EXPORT_SYMBOL(mmc_free_host); ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/host.h linux-2.6.22.18-mv/drivers/mmc/core/host.h +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/host.h 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/host.h 2008-01-29 03:26:08.000000000 -0800 +@@ -0,0 +1,18 @@ ++/* ++ * linux/drivers/mmc/core/host.h ++ * ++ * Copyright (C) 2003 Russell King, All Rights Reserved. ++ * Copyright 2007 Pierre Ossman ++ * ++ * 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 _MMC_CORE_HOST_H ++#define _MMC_CORE_HOST_H ++ ++int mmc_register_host_class(void); ++void mmc_unregister_host_class(void); ++ ++#endif ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/Makefile linux-2.6.22.18-mv/drivers/mmc/core/Makefile +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/Makefile 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/Makefile 2008-01-29 03:26:08.000000000 -0800 +@@ -7,5 +7,8 @@ + endif + + obj-$(CONFIG_MMC) += mmc_core.o +-mmc_core-y := core.o sysfs.o mmc.o mmc_ops.o sd.o sd_ops.o ++mmc_core-y := core.o sysfs.o bus.o host.o \ ++ mmc.o mmc_ops.o sd.o sd_ops.o \ ++ sdio.o sdio_ops.o sdio_bus.o \ ++ sdio_cis.o sdio_io.o sdio_irq.o + +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/mmc.c linux-2.6.22.18-mv/drivers/mmc/core/mmc.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/mmc.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/mmc.c 2008-05-07 00:51:44.000000000 -0700 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/mmc.c ++ * linux/drivers/mmc/core/mmc.c + * + * Copyright (C) 2003-2004 Russell King, All Rights Reserved. + * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. +@@ -18,6 +18,7 @@ + + #include "core.h" + #include "sysfs.h" ++#include "bus.h" + #include "mmc_ops.h" + + static const unsigned int tran_exp[] = { +@@ -99,7 +100,7 @@ + break; + + default: +- printk("%s: card has unknown MMCA version %d\n", ++ printk(KERN_ERR "%s: card has unknown MMCA version %d\n", + mmc_hostname(card->host), card->csd.mmca_vsn); + return -EINVAL; + } +@@ -122,7 +123,7 @@ + */ + csd_struct = UNSTUFF_BITS(resp, 126, 2); + if (csd_struct != 1 && csd_struct != 2) { +- printk("%s: unrecognised CSD structure version %d\n", ++ printk(KERN_ERR "%s: unrecognised CSD structure version %d\n", + mmc_hostname(card->host), csd_struct); + return -EINVAL; + } +@@ -160,13 +161,12 @@ + { + int err; + u8 *ext_csd; ++ unsigned int ext_csd_struct; + + BUG_ON(!card); + +- err = MMC_ERR_FAILED; +- + if (card->csd.mmca_vsn < CSD_SPEC_VER_4) +- return MMC_ERR_NONE; ++ return 0; + + /* + * As the ext_csd is so large and mostly unused, we don't store the +@@ -175,13 +175,19 @@ + ext_csd = kmalloc(512, GFP_KERNEL); + if (!ext_csd) { + printk(KERN_ERR "%s: could not allocate a buffer to " +- "receive the ext_csd. mmc v4 cards will be " +- "treated as v3.\n", mmc_hostname(card->host)); +- return MMC_ERR_FAILED; ++ "receive the ext_csd.\n", mmc_hostname(card->host)); ++ return -ENOMEM; + } + + err = mmc_send_ext_csd(card, ext_csd); +- if (err != MMC_ERR_NONE) { ++ if (err) { ++ /* ++ * We all hosts that cannot perform the command ++ * to fail more gracefully ++ */ ++ if (err != -EINVAL) ++ goto out; ++ + /* + * High capacity cards should have this "magic" size + * stored in their CSD. +@@ -196,18 +202,30 @@ + "EXT_CSD, performance might " + "suffer.\n", + mmc_hostname(card->host)); +- err = MMC_ERR_NONE; ++ err = 0; + } ++ ++ goto out; ++ } ++ ++ ext_csd_struct = ext_csd[EXT_CSD_REV]; ++ if (ext_csd_struct > 2) { ++ printk(KERN_ERR "%s: unrecognised EXT_CSD structure " ++ "version %d\n", mmc_hostname(card->host), ++ ext_csd_struct); ++ err = -EINVAL; + goto out; + } + +- card->ext_csd.sectors = +- ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | +- ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | +- ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | +- ext_csd[EXT_CSD_SEC_CNT + 3] << 24; +- if (card->ext_csd.sectors) +- mmc_card_set_blockaddr(card); ++ if (ext_csd_struct >= 2) { ++ card->ext_csd.sectors = ++ ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | ++ ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | ++ ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | ++ ext_csd[EXT_CSD_SEC_CNT + 3] << 24; ++ if (card->ext_csd.sectors) ++ mmc_card_set_blockaddr(card); ++ } + + switch (ext_csd[EXT_CSD_CARD_TYPE]) { + case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: +@@ -236,7 +254,7 @@ + * In the case of a resume, "curcard" will contain the card + * we're trying to reinitialise. + */ +-static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, ++static int mmc_init_card(struct mmc_host *host, u32 ocr, + struct mmc_card *oldcard) + { + struct mmc_card *card; +@@ -245,7 +263,7 @@ + unsigned int max_dtr; + + BUG_ON(!host); +- BUG_ON(!host->claimed); ++ WARN_ON(!host->claimed); + + /* + * Since we're changing the OCR value, we seem to +@@ -257,19 +275,33 @@ + + /* The extra bit indicates that we support high capacity */ + err = mmc_send_op_cond(host, ocr | (1 << 30), NULL); +- if (err != MMC_ERR_NONE) ++ if (err) + goto err; + + /* ++ * For SPI, enable CRC as appropriate. ++ */ ++ if (mmc_host_is_spi(host)) { ++ err = mmc_spi_set_crc(host, use_spi_crc); ++ if (err) ++ goto err; ++ } ++ ++ /* + * Fetch CID from card. + */ +- err = mmc_all_send_cid(host, cid); +- if (err != MMC_ERR_NONE) ++ if (mmc_host_is_spi(host)) ++ err = mmc_send_cid(host, cid); ++ else ++ err = mmc_all_send_cid(host, cid); ++ if (err) + goto err; + + if (oldcard) { +- if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) ++ if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) { ++ err = -ENOENT; + goto err; ++ } + + card = oldcard; + } else { +@@ -277,8 +309,10 @@ + * Allocate card structure. + */ + card = mmc_alloc_card(host); +- if (IS_ERR(card)) ++ if (IS_ERR(card)) { ++ err = PTR_ERR(card); + goto err; ++ } + + card->type = MMC_TYPE_MMC; + card->rca = 1; +@@ -286,43 +320,47 @@ + } + + /* +- * Set card RCA. ++ * For native busses: set card RCA and quit open drain mode. + */ +- err = mmc_set_relative_addr(card); +- if (err != MMC_ERR_NONE) +- goto free_card; ++ if (!mmc_host_is_spi(host)) { ++ err = mmc_set_relative_addr(card); ++ if (err) ++ goto free_card; + +- mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); ++ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); ++ } + + if (!oldcard) { + /* + * Fetch CSD from card. + */ + err = mmc_send_csd(card, card->raw_csd); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + + err = mmc_decode_csd(card); +- if (err < 0) ++ if (err) + goto free_card; + err = mmc_decode_cid(card); +- if (err < 0) ++ if (err) + goto free_card; + } + + /* + * Select card, as all following commands rely on that. + */ +- err = mmc_select_card(card); +- if (err != MMC_ERR_NONE) +- goto free_card; ++ if (!mmc_host_is_spi(host)) { ++ err = mmc_select_card(card); ++ if (err) ++ goto free_card; ++ } + + if (!oldcard) { + /* +- * Fetch and process extened CSD. ++ * Fetch and process extended CSD. + */ + err = mmc_read_ext_csd(card); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + } + +@@ -331,9 +369,9 @@ + */ + if ((card->ext_csd.hs_max_dtr != 0) && + (host->caps & MMC_CAP_MMC_HIGHSPEED)) { +- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, ++ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, 1); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + + mmc_card_set_highspeed(card); +@@ -362,7 +400,7 @@ + (host->caps & MMC_CAP_4_BIT_DATA)) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + + mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); +@@ -371,14 +409,14 @@ + if (!oldcard) + host->card = card; + +- return MMC_ERR_NONE; ++ return 0; + + free_card: + if (!oldcard) + mmc_remove_card(card); + err: + +- return MMC_ERR_FAILED; ++ return err; + } + + /* +@@ -412,9 +450,8 @@ + + mmc_release_host(host); + +- if (err != MMC_ERR_NONE) { +- mmc_remove_card(host->card); +- host->card = NULL; ++ if (err) { ++ mmc_remove(host); + + mmc_claim_host(host); + mmc_detach_bus(host); +@@ -422,6 +459,53 @@ + } + } + ++MMC_ATTR_FN(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], ++ card->raw_cid[2], card->raw_cid[3]); ++MMC_ATTR_FN(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], ++ card->raw_csd[2], card->raw_csd[3]); ++MMC_ATTR_FN(date, "%02d/%04d\n", card->cid.month, card->cid.year); ++MMC_ATTR_FN(fwrev, "0x%x\n", card->cid.fwrev); ++MMC_ATTR_FN(hwrev, "0x%x\n", card->cid.hwrev); ++MMC_ATTR_FN(manfid, "0x%06x\n", card->cid.manfid); ++MMC_ATTR_FN(name, "%s\n", card->cid.prod_name); ++MMC_ATTR_FN(oemid, "0x%04x\n", card->cid.oemid); ++MMC_ATTR_FN(serial, "0x%08x\n", card->cid.serial); ++ ++static struct device_attribute mmc_dev_attrs[] = { ++ MMC_ATTR_RO(cid), ++ MMC_ATTR_RO(csd), ++ MMC_ATTR_RO(date), ++ MMC_ATTR_RO(fwrev), ++ MMC_ATTR_RO(hwrev), ++ MMC_ATTR_RO(manfid), ++ MMC_ATTR_RO(name), ++ MMC_ATTR_RO(oemid), ++ MMC_ATTR_RO(serial), ++ __ATTR_NULL, ++}; ++ ++/* ++ * Adds sysfs entries as relevant. ++ */ ++static int mmc_sysfs_add(struct mmc_host *host, struct mmc_card *card) ++{ ++ int ret; ++ ++ ret = mmc_add_attrs(card, mmc_dev_attrs); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++/* ++ * Removes the sysfs entries added by mmc_sysfs_add(). ++ */ ++static void mmc_sysfs_remove(struct mmc_host *host, struct mmc_card *card) ++{ ++ mmc_remove_attrs(card, mmc_dev_attrs); ++} ++ + #ifdef CONFIG_MMC_UNSAFE_RESUME + + /* +@@ -433,7 +517,8 @@ + BUG_ON(!host->card); + + mmc_claim_host(host); +- mmc_deselect_cards(host); ++ if (!mmc_host_is_spi(host)) ++ mmc_deselect_cards(host); + host->card->state &= ~MMC_STATE_HIGHSPEED; + mmc_release_host(host); + } +@@ -452,16 +537,17 @@ + BUG_ON(!host->card); + + mmc_claim_host(host); ++ err = mmc_init_card(host, host->ocr, host->card); ++ mmc_release_host(host); + +- err = mmc_sd_init_card(host, host->ocr, host->card); +- if (err != MMC_ERR_NONE) { +- mmc_remove_card(host->card); +- host->card = NULL; ++ if (err) { ++ mmc_remove(host); + ++ mmc_claim_host(host); + mmc_detach_bus(host); ++ mmc_release_host(host); + } + +- mmc_release_host(host); + } + + #else +@@ -474,6 +560,8 @@ + static const struct mmc_bus_ops mmc_ops = { + .remove = mmc_remove, + .detect = mmc_detect, ++ .sysfs_add = mmc_sysfs_add, ++ .sysfs_remove = mmc_sysfs_remove, + .suspend = mmc_suspend, + .resume = mmc_resume, + }; +@@ -486,11 +574,20 @@ + int err; + + BUG_ON(!host); +- BUG_ON(!host->claimed); ++ WARN_ON(!host->claimed); + + mmc_attach_bus(host, &mmc_ops); + + /* ++ * We need to get OCR a different way for SPI. ++ */ ++ if (mmc_host_is_spi(host)) { ++ err = mmc_spi_read_ocr(host, 1, &ocr); ++ if (err) ++ goto err; ++ } ++ ++ /* + * Sanity check the voltages that the card claims to + * support. + */ +@@ -506,32 +603,37 @@ + /* + * Can we support the voltage of the card? + */ +- if (!host->ocr) ++ if (!host->ocr) { ++ err = -EINVAL; + goto err; ++ } + + /* + * Detect and init the card. + */ +- err = mmc_sd_init_card(host, host->ocr, NULL); +- if (err != MMC_ERR_NONE) ++ err = mmc_init_card(host, host->ocr, NULL); ++ if (err) + goto err; + + mmc_release_host(host); + +- err = mmc_register_card(host->card); ++ err = mmc_add_card(host->card); + if (err) +- goto reclaim_host; ++ goto remove_card; + + return 0; + +-reclaim_host: +- mmc_claim_host(host); ++remove_card: + mmc_remove_card(host->card); + host->card = NULL; ++ mmc_claim_host(host); + err: + mmc_detach_bus(host); + mmc_release_host(host); + +- return 0; ++ printk(KERN_ERR "%s: error %d whilst initialising MMC card\n", ++ mmc_hostname(host), err); ++ ++ return err; + } + +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/mmc_ops.c linux-2.6.22.18-mv/drivers/mmc/core/mmc_ops.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/mmc_ops.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/mmc_ops.c 2009-03-26 09:32:48.000000000 -0700 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/mmc_ops.h ++ * linux/drivers/mmc/core/mmc_ops.h + * + * Copyright 2006-2007 Pierre Ossman + * +@@ -20,6 +20,38 @@ + #include "core.h" + #include "mmc_ops.h" + ++int mmc_force_reset (struct mmc_host *host) ++ ++{ ++ struct mmc_command cmd; ++ int err = 0; ++ ++ BUG_ON(!host); ++ ++#ifndef SD_IO_RW_DIRECT ++#define SD_IO_RW_DIRECT 52 ++#endif ++ ++#ifndef SDIO_CCCR_ABORT ++#define SDIO_CCCR_ABORT 0x06 ++#endif ++ ++#ifndef PMR_WRITE ++#define PMR_WRITE 1 ++#endif ++ ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ cmd.opcode = SD_IO_RW_DIRECT; ++ cmd.arg = PMR_WRITE ? 0x80000000 : 0x00000000; ++ cmd.arg |= 0<<28; ++ cmd.arg |= 0x00000000; ++ cmd.arg |= SDIO_CCCR_ABORT << 9; ++ cmd.arg |= (1<<3) ; ++ cmd.flags = MMC_RSP_NONE; ++ err = mmc_wait_for_cmd(host, &cmd, 0); ++ return err; ++} ++ + static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card) + { + int err; +@@ -40,10 +72,10 @@ + } + + err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + +- return MMC_ERR_NONE; ++ return 0; + } + + int mmc_select_card(struct mmc_card *card) +@@ -63,23 +95,36 @@ + int err; + struct mmc_command cmd; + +- mmc_set_chip_select(host, MMC_CS_HIGH); +- +- mmc_delay(1); ++ /* ++ * Non-SPI hosts need to prevent chipselect going active during ++ * GO_IDLE; that would put chips into SPI mode. Remind them of ++ * that in case of hardware that won't pull up DAT3/nCS otherwise. ++ * ++ * SPI hosts ignore ios.chip_select; it's managed according to ++ * rules that must accomodate non-MMC slaves which this layer ++ * won't even know about. ++ */ ++ if (!mmc_host_is_spi(host)) { ++ mmc_set_chip_select(host, MMC_CS_HIGH); ++ mmc_delay(1); ++ } + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_GO_IDLE_STATE; + cmd.arg = 0; +- cmd.flags = MMC_RSP_NONE | MMC_CMD_BC; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC; + + err = mmc_wait_for_cmd(host, &cmd, 0); + + mmc_delay(1); + +- mmc_set_chip_select(host, MMC_CS_DONTCARE); ++ if (!mmc_host_is_spi(host)) { ++ mmc_set_chip_select(host, MMC_CS_DONTCARE); ++ mmc_delay(1); ++ } + +- mmc_delay(1); ++ host->use_spi_crc = 0; + + return err; + } +@@ -94,23 +139,33 @@ + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SEND_OP_COND; +- cmd.arg = ocr; +- cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; ++ cmd.arg = mmc_host_is_spi(host) ? 0 : ocr; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR; + + for (i = 100; i; i--) { + err = mmc_wait_for_cmd(host, &cmd, 0); +- if (err != MMC_ERR_NONE) ++ if (err) + break; + +- if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) ++ /* if we're just probing, do a single pass */ ++ if (ocr == 0) + break; + +- err = MMC_ERR_TIMEOUT; ++ /* otherwise wait until reset completes */ ++ if (mmc_host_is_spi(host)) { ++ if (!(cmd.resp[0] & R1_SPI_IDLE)) ++ break; ++ } else { ++ if (cmd.resp[0] & MMC_CARD_BUSY) ++ break; ++ } ++ ++ err = -ETIMEDOUT; + + mmc_delay(10); + } + +- if (rocr) ++ if (rocr && !mmc_host_is_spi(host)) + *rocr = cmd.resp[0]; + + return err; +@@ -131,12 +186,12 @@ + cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + + memcpy(cid, cmd.resp, sizeof(u32) * 4); + +- return MMC_ERR_NONE; ++ return 0; + } + + int mmc_set_relative_addr(struct mmc_card *card) +@@ -154,46 +209,52 @@ + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + +- return MMC_ERR_NONE; ++ return 0; + } + +-int mmc_send_csd(struct mmc_card *card, u32 *csd) ++static int ++mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode) + { + int err; + struct mmc_command cmd; + +- BUG_ON(!card); +- BUG_ON(!card->host); +- BUG_ON(!csd); ++ BUG_ON(!host); ++ BUG_ON(!cxd); + + memset(&cmd, 0, sizeof(struct mmc_command)); + +- cmd.opcode = MMC_SEND_CSD; +- cmd.arg = card->rca << 16; ++ cmd.opcode = opcode; ++ cmd.arg = arg; + cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; + +- err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); ++ if (err) + return err; + +- memcpy(csd, cmd.resp, sizeof(u32) * 4); ++ memcpy(cxd, cmd.resp, sizeof(u32) * 4); + +- return MMC_ERR_NONE; ++ return 0; + } + +-int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) ++static int ++mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, ++ u32 opcode, void *buf, unsigned len) + { + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg; ++ void *data_buf; + +- BUG_ON(!card); +- BUG_ON(!card->host); +- BUG_ON(!ext_csd); ++ /* dma onto stack is unsafe/nonportable, but callers to this ++ * routine normally provide temporary on-stack buffers ... ++ */ ++ data_buf = kmalloc(len, GFP_KERNEL); ++ if (data_buf == NULL) ++ return -ENOMEM; + + memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&cmd, 0, sizeof(struct mmc_command)); +@@ -202,28 +263,117 @@ + mrq.cmd = &cmd; + mrq.data = &data; + +- cmd.opcode = MMC_SEND_EXT_CSD; ++ cmd.opcode = opcode; + cmd.arg = 0; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + +- data.blksz = 512; ++ /* NOTE HACK: the MMC_RSP_SPI_R1 is always correct here, but we ++ * rely on callers to never use this with "native" calls for reading ++ * CSD or CID. Native versions of those commands use the R2 type, ++ * not R1 plus a data block. ++ */ ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; ++ ++ data.blksz = len; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + +- sg_init_one(&sg, ext_csd, 512); ++ sg_init_one(&sg, data_buf, len); + +- mmc_set_data_timeout(&data, card, 0); ++ if (card) ++ mmc_set_data_timeout(&data, card); + +- mmc_wait_for_req(card->host, &mrq); ++ mmc_wait_for_req(host, &mrq); + +- if (cmd.error != MMC_ERR_NONE) ++ memcpy(buf, data_buf, len); ++ kfree(data_buf); ++ ++ if (cmd.error) + return cmd.error; +- if (data.error != MMC_ERR_NONE) ++ if (data.error) + return data.error; + +- return MMC_ERR_NONE; ++ return 0; ++} ++ ++int mmc_send_csd(struct mmc_card *card, u32 *csd) ++{ ++ int ret, i; ++ ++ if (!mmc_host_is_spi(card->host)) ++ return mmc_send_cxd_native(card->host, card->rca << 16, ++ csd, MMC_SEND_CSD); ++ ++ ret = mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd, 16); ++ if (ret) ++ return ret; ++ ++ for (i = 0;i < 4;i++) ++ csd[i] = be32_to_cpu(csd[i]); ++ ++ return 0; ++} ++ ++int mmc_send_cid(struct mmc_host *host, u32 *cid) ++{ ++ int ret, i; ++ ++ if (!mmc_host_is_spi(host)) { ++ if (!host->card) ++ return -EINVAL; ++ return mmc_send_cxd_native(host, host->card->rca << 16, ++ cid, MMC_SEND_CID); ++ } ++ ++ ret = mmc_send_cxd_data(NULL, host, MMC_SEND_CID, cid, 16); ++ if (ret) ++ return ret; ++ ++ for (i = 0;i < 4;i++) ++ cid[i] = be32_to_cpu(cid[i]); ++ ++ return 0; ++} ++ ++int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) ++{ ++ return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD, ++ ext_csd, 512); ++} ++ ++int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp) ++{ ++ struct mmc_command cmd; ++ int err; ++ ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ ++ cmd.opcode = MMC_SPI_READ_OCR; ++ cmd.arg = highcap ? (1 << 30) : 0; ++ cmd.flags = MMC_RSP_SPI_R3; ++ ++ err = mmc_wait_for_cmd(host, &cmd, 0); ++ ++ *ocrp = cmd.resp[1]; ++ return err; ++} ++ ++int mmc_spi_set_crc(struct mmc_host *host, int use_crc) ++{ ++ struct mmc_command cmd; ++ int err; ++ ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ ++ cmd.opcode = MMC_SPI_CRC_ON_OFF; ++ cmd.flags = MMC_RSP_SPI_R1; ++ cmd.arg = use_crc; ++ ++ err = mmc_wait_for_cmd(host, &cmd, 0); ++ if (!err) ++ host->use_spi_crc = use_crc; ++ return err; + } + + int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) +@@ -241,13 +391,13 @@ + (index << 16) | + (value << 8) | + set; +- cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; ++ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + +- return MMC_ERR_NONE; ++ return 0; + } + + int mmc_send_status(struct mmc_card *card, u32 *status) +@@ -261,16 +411,20 @@ + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SEND_STATUS; +- cmd.arg = card->rca << 16; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; ++ if (!mmc_host_is_spi(card->host)) ++ cmd.arg = card->rca << 16; ++ cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + ++ /* NOTE: callers are required to understand the difference ++ * between "native" and SPI format status words! ++ */ + if (status) + *status = cmd.resp[0]; + +- return MMC_ERR_NONE; ++ return 0; + } + +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/mmc_ops.h linux-2.6.22.18-mv/drivers/mmc/core/mmc_ops.h +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/mmc_ops.h 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/mmc_ops.h 2009-03-26 09:32:48.000000000 -0700 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/mmc_ops.h ++ * linux/drivers/mmc/core/mmc_ops.h + * + * Copyright 2006-2007 Pierre Ossman + * +@@ -12,6 +12,7 @@ + #ifndef _MMC_MMC_OPS_H + #define _MMC_MMC_OPS_H + ++int mmc_force_reset (struct mmc_host *host); + int mmc_select_card(struct mmc_card *card); + int mmc_deselect_cards(struct mmc_host *host); + int mmc_go_idle(struct mmc_host *host); +@@ -22,6 +23,9 @@ + int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); + int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value); + int mmc_send_status(struct mmc_card *card, u32 *status); ++int mmc_send_cid(struct mmc_host *host, u32 *cid); ++int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); ++int mmc_spi_set_crc(struct mmc_host *host, int use_crc); + + #endif + +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/sd.c linux-2.6.22.18-mv/drivers/mmc/core/sd.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/sd.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/sd.c 2008-05-07 00:53:22.000000000 -0700 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/sd.c ++ * linux/drivers/mmc/core/sd.c + * + * Copyright (C) 2003-2004 Russell King, All Rights Reserved. + * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved. +@@ -19,11 +19,10 @@ + + #include "core.h" + #include "sysfs.h" ++#include "bus.h" + #include "mmc_ops.h" + #include "sd_ops.h" + +-#include "core.h" +- + static const unsigned int tran_exp[] = { + 10000, 100000, 1000000, 10000000, + 0, 0, 0, 0 +@@ -150,7 +149,7 @@ + csd->write_partial = 0; + break; + default: +- printk("%s: unrecognised CSD structure version %d\n", ++ printk(KERN_ERR "%s: unrecognised CSD structure version %d\n", + mmc_hostname(card->host), csd_struct); + return -EINVAL; + } +@@ -167,14 +166,12 @@ + unsigned int scr_struct; + u32 resp[4]; + +- BUG_ON(!mmc_card_sd(card)); +- + resp[3] = card->raw_scr[1]; + resp[2] = card->raw_scr[0]; + + scr_struct = UNSTUFF_BITS(resp, 60, 4); + if (scr_struct != 0) { +- printk("%s: unrecognised SCR structure version %d\n", ++ printk(KERN_ERR "%s: unrecognised SCR structure version %d\n", + mmc_hostname(card->host), scr_struct); + return -EINVAL; + } +@@ -194,31 +191,38 @@ + u8 *status; + + if (card->scr.sda_vsn < SCR_SPEC_VER_1) +- return MMC_ERR_NONE; ++ return 0; + + if (!(card->csd.cmdclass & CCC_SWITCH)) { + printk(KERN_WARNING "%s: card lacks mandatory switch " + "function, performance might suffer.\n", + mmc_hostname(card->host)); +- return MMC_ERR_NONE; ++ return 0; + } + +- err = MMC_ERR_FAILED; ++ err = -EIO; + + status = kmalloc(64, GFP_KERNEL); + if (!status) { +- printk("%s: could not allocate a buffer for switch " +- "capabilities.\n", +- mmc_hostname(card->host)); +- return err; ++ printk(KERN_ERR "%s: could not allocate a buffer for " ++ "switch capabilities.\n", mmc_hostname(card->host)); ++ return -ENOMEM; + } + + err = mmc_sd_switch(card, 0, 0, 1, status); +- if (err != MMC_ERR_NONE) { ++ if (err) { ++ /* ++ * We all hosts that cannot perform the command ++ * to fail more gracefully ++ */ ++ if (err != -EINVAL) ++ goto out; ++ + printk(KERN_WARNING "%s: problem reading switch " + "capabilities, performance might suffer.\n", + mmc_hostname(card->host)); +- err = MMC_ERR_NONE; ++ err = 0; ++ + goto out; + } + +@@ -240,42 +244,41 @@ + u8 *status; + + if (card->scr.sda_vsn < SCR_SPEC_VER_1) +- return MMC_ERR_NONE; ++ return 0; + + if (!(card->csd.cmdclass & CCC_SWITCH)) +- return MMC_ERR_NONE; ++ return 0; + + if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED)) +- return MMC_ERR_NONE; ++ return 0; + + if (card->sw_caps.hs_max_dtr == 0) +- return MMC_ERR_NONE; ++ return 0; + +- err = MMC_ERR_FAILED; ++ err = -EIO; + + status = kmalloc(64, GFP_KERNEL); + if (!status) { +- printk("%s: could not allocate a buffer for switch " +- "capabilities.\n", +- mmc_hostname(card->host)); +- return err; ++ printk(KERN_ERR "%s: could not allocate a buffer for " ++ "switch capabilities.\n", mmc_hostname(card->host)); ++ return -ENOMEM; + } + + err = mmc_sd_switch(card, 1, 0, 1, status); +- if (err != MMC_ERR_NONE) ++ if (err) + goto out; +- ++ + if ((status[16] & 0xF) != 1) { + printk(KERN_WARNING "%s: Problem switching card " + "into high-speed mode!\n", + mmc_hostname(card->host)); + } else { + mmc_card_set_highspeed(card); +- mmc_set_timing(card->host, MMC_TIMING_SD_HS); ++ mmc_set_timing(card->host, MMC_TIMING_SD_HS); + } + + out: +- kfree(status); ++ kfree(status); + + return err; + } +@@ -295,7 +298,7 @@ + unsigned int max_dtr; + + BUG_ON(!host); +- BUG_ON(!host->claimed); ++ WARN_ON(!host->claimed); + + /* + * Since we're changing the OCR value, we seem to +@@ -312,23 +315,37 @@ + * block-addressed SDHC cards. + */ + err = mmc_send_if_cond(host, ocr); +- if (err == MMC_ERR_NONE) ++ if (!err) + ocr |= 1 << 30; + + err = mmc_send_app_op_cond(host, ocr, NULL); +- if (err != MMC_ERR_NONE) ++ if (err) + goto err; + + /* ++ * For SPI, enable CRC as appropriate. ++ */ ++ if (mmc_host_is_spi(host)) { ++ err = mmc_spi_set_crc(host, use_spi_crc); ++ if (err) ++ goto err; ++ } ++ ++ /* + * Fetch CID from card. + */ +- err = mmc_all_send_cid(host, cid); +- if (err != MMC_ERR_NONE) ++ if (mmc_host_is_spi(host)) ++ err = mmc_send_cid(host, cid); ++ else ++ err = mmc_all_send_cid(host, cid); ++ if (err) + goto err; + + if (oldcard) { +- if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) ++ if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) { ++ err = -ENOENT; + goto err; ++ } + + card = oldcard; + } else { +@@ -336,32 +353,36 @@ + * Allocate card structure. + */ + card = mmc_alloc_card(host); +- if (IS_ERR(card)) ++ if (IS_ERR(card)) { ++ err = PTR_ERR(card); + goto err; ++ } + + card->type = MMC_TYPE_SD; + memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); + } + + /* +- * Set card RCA. ++ * For native busses: get card RCA and quit open drain mode. + */ +- err = mmc_send_relative_addr(host, &card->rca); +- if (err != MMC_ERR_NONE) +- goto free_card; ++ if (!mmc_host_is_spi(host)) { ++ err = mmc_send_relative_addr(host, &card->rca); ++ if (err) ++ goto free_card; + +- mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); ++ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); ++ } + + if (!oldcard) { + /* + * Fetch CSD from card. + */ + err = mmc_send_csd(card, card->raw_csd); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + + err = mmc_decode_csd(card); +- if (err < 0) ++ if (err) + goto free_card; + + mmc_decode_cid(card); +@@ -370,16 +391,18 @@ + /* + * Select card, as all following commands rely on that. + */ +- err = mmc_select_card(card); +- if (err != MMC_ERR_NONE) +- goto free_card; ++ if (!mmc_host_is_spi(host)) { ++ err = mmc_select_card(card); ++ if (err) ++ goto free_card; ++ } + + if (!oldcard) { + /* + * Fetch SCR from card. + */ + err = mmc_app_send_scr(card, card->raw_scr); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + + err = mmc_decode_scr(card); +@@ -390,15 +413,17 @@ + * Fetch switch information from card. + */ + err = mmc_read_switch(card); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + } + + /* + * Attempt to change to high-speed (if supported) + */ ++ mmc_delay(10); ++ + err = mmc_switch_hs(card); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + + /* +@@ -415,13 +440,14 @@ + + mmc_set_clock(host, max_dtr); + ++ + /* + * Switch to wider bus (if supported). + */ + if ((host->caps & MMC_CAP_4_BIT_DATA) && + (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + + mmc_set_bus_width(host, MMC_BUS_WIDTH_4); +@@ -445,14 +471,14 @@ + if (!oldcard) + host->card = card; + +- return MMC_ERR_NONE; ++ return 0; + + free_card: + if (!oldcard) + mmc_remove_card(card); + err: + +- return MMC_ERR_FAILED; ++ return err; + } + + /* +@@ -486,9 +512,8 @@ + + mmc_release_host(host); + +- if (err != MMC_ERR_NONE) { +- mmc_remove_card(host->card); +- host->card = NULL; ++ if (err) { ++ mmc_sd_remove(host); + + mmc_claim_host(host); + mmc_detach_bus(host); +@@ -496,6 +521,55 @@ + } + } + ++MMC_ATTR_FN(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], ++ card->raw_cid[2], card->raw_cid[3]); ++MMC_ATTR_FN(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], ++ card->raw_csd[2], card->raw_csd[3]); ++MMC_ATTR_FN(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]); ++MMC_ATTR_FN(date, "%02d/%04d\n", card->cid.month, card->cid.year); ++MMC_ATTR_FN(fwrev, "0x%x\n", card->cid.fwrev); ++MMC_ATTR_FN(hwrev, "0x%x\n", card->cid.hwrev); ++MMC_ATTR_FN(manfid, "0x%06x\n", card->cid.manfid); ++MMC_ATTR_FN(name, "%s\n", card->cid.prod_name); ++MMC_ATTR_FN(oemid, "0x%04x\n", card->cid.oemid); ++MMC_ATTR_FN(serial, "0x%08x\n", card->cid.serial); ++ ++static struct device_attribute mmc_sd_dev_attrs[] = { ++ MMC_ATTR_RO(cid), ++ MMC_ATTR_RO(csd), ++ MMC_ATTR_RO(scr), ++ MMC_ATTR_RO(date), ++ MMC_ATTR_RO(fwrev), ++ MMC_ATTR_RO(hwrev), ++ MMC_ATTR_RO(manfid), ++ MMC_ATTR_RO(name), ++ MMC_ATTR_RO(oemid), ++ MMC_ATTR_RO(serial), ++ __ATTR_NULL, ++}; ++ ++/* ++ * Adds sysfs entries as relevant. ++ */ ++static int mmc_sd_sysfs_add(struct mmc_host *host, struct mmc_card *card) ++{ ++ int ret; ++ ++ ret = mmc_add_attrs(card, mmc_sd_dev_attrs); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++/* ++ * Removes the sysfs entries added by mmc_sysfs_add(). ++ */ ++static void mmc_sd_sysfs_remove(struct mmc_host *host, struct mmc_card *card) ++{ ++ mmc_remove_attrs(card, mmc_sd_dev_attrs); ++} ++ + #ifdef CONFIG_MMC_UNSAFE_RESUME + + /* +@@ -507,7 +581,8 @@ + BUG_ON(!host->card); + + mmc_claim_host(host); +- mmc_deselect_cards(host); ++ if (!mmc_host_is_spi(host)) ++ mmc_deselect_cards(host); + host->card->state &= ~MMC_STATE_HIGHSPEED; + mmc_release_host(host); + } +@@ -526,16 +601,17 @@ + BUG_ON(!host->card); + + mmc_claim_host(host); +- + err = mmc_sd_init_card(host, host->ocr, host->card); +- if (err != MMC_ERR_NONE) { +- mmc_remove_card(host->card); +- host->card = NULL; ++ mmc_release_host(host); ++ ++ if (err) { ++ mmc_sd_remove(host); + ++ mmc_claim_host(host); + mmc_detach_bus(host); ++ mmc_release_host(host); + } + +- mmc_release_host(host); + } + + #else +@@ -548,6 +624,8 @@ + static const struct mmc_bus_ops mmc_sd_ops = { + .remove = mmc_sd_remove, + .detect = mmc_sd_detect, ++ .sysfs_add = mmc_sd_sysfs_add, ++ .sysfs_remove = mmc_sd_sysfs_remove, + .suspend = mmc_sd_suspend, + .resume = mmc_sd_resume, + }; +@@ -560,11 +638,22 @@ + int err; + + BUG_ON(!host); +- BUG_ON(!host->claimed); ++ WARN_ON(!host->claimed); + + mmc_attach_bus(host, &mmc_sd_ops); + + /* ++ * We need to get OCR a different way for SPI. ++ */ ++ if (mmc_host_is_spi(host)) { ++ mmc_go_idle(host); ++ ++ err = mmc_spi_read_ocr(host, 0, &ocr); ++ if (err) ++ goto err; ++ } ++ ++ /* + * Sanity check the voltages that the card claims to + * support. + */ +@@ -587,32 +676,37 @@ + /* + * Can we support the voltage(s) of the card(s)? + */ +- if (!host->ocr) ++ if (!host->ocr) { ++ err = -EINVAL; + goto err; ++ } + + /* + * Detect and init the card. + */ + err = mmc_sd_init_card(host, host->ocr, NULL); +- if (err != MMC_ERR_NONE) ++ if (err) + goto err; + + mmc_release_host(host); + +- err = mmc_register_card(host->card); ++ err = mmc_add_card(host->card); + if (err) +- goto reclaim_host; ++ goto remove_card; + + return 0; + +-reclaim_host: +- mmc_claim_host(host); ++remove_card: + mmc_remove_card(host->card); + host->card = NULL; ++ mmc_claim_host(host); + err: + mmc_detach_bus(host); + mmc_release_host(host); + +- return 0; ++ printk(KERN_ERR "%s: error %d whilst initialising SD card\n", ++ mmc_hostname(host), err); ++ ++ return err; + } + +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_bus.c linux-2.6.22.18-mv/drivers/mmc/core/sdio_bus.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_bus.c 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/sdio_bus.c 2008-01-29 03:26:08.000000000 -0800 +@@ -0,0 +1,286 @@ ++/* ++ * linux/drivers/mmc/core/sdio_bus.c ++ * ++ * Copyright 2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ * ++ * SDIO function driver model ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "sdio_cis.h" ++#include "sdio_bus.h" ++ ++#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) ++#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) ++ ++/* show configuration fields */ ++#define sdio_config_attr(field, format_string) \ ++static ssize_t \ ++field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct sdio_func *func; \ ++ \ ++ func = dev_to_sdio_func (dev); \ ++ return sprintf (buf, format_string, func->field); \ ++} ++ ++sdio_config_attr(class, "0x%02x\n"); ++sdio_config_attr(vendor, "0x%04x\n"); ++sdio_config_attr(device, "0x%04x\n"); ++ ++static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct sdio_func *func = dev_to_sdio_func (dev); ++ ++ return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n", ++ func->class, func->vendor, func->device); ++} ++ ++static struct device_attribute sdio_dev_attrs[] = { ++ __ATTR_RO(class), ++ __ATTR_RO(vendor), ++ __ATTR_RO(device), ++ __ATTR_RO(modalias), ++ __ATTR_NULL, ++}; ++ ++static const struct sdio_device_id *sdio_match_one(struct sdio_func *func, ++ const struct sdio_device_id *id) ++{ ++ if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class) ++ return NULL; ++ if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor) ++ return NULL; ++ if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device) ++ return NULL; ++ return id; ++} ++ ++static const struct sdio_device_id *sdio_match_device(struct sdio_func *func, ++ struct sdio_driver *sdrv) ++{ ++ const struct sdio_device_id *ids; ++ ++ ids = sdrv->id_table; ++ ++ if (ids) { ++ while (ids->class || ids->vendor || ids->device) { ++ if (sdio_match_one(func, ids)) ++ return ids; ++ ids++; ++ } ++ } ++ ++ return NULL; ++} ++ ++static int sdio_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ struct sdio_func *func = dev_to_sdio_func(dev); ++ struct sdio_driver *sdrv = to_sdio_driver(drv); ++ ++ if (sdio_match_device(func, sdrv)) ++ return 1; ++ ++ return 0; ++} ++ ++static int ++#if 0 ++sdio_bus_uevent(struct device *dev, struct kobj_uevent_env *env) ++#else ++sdio_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, ++ int buf_size) ++#endif ++{ ++ struct sdio_func *func = dev_to_sdio_func(dev); ++ int retval = 0, i = 0, length = 0; ++ ++#define add_env(fmt,val...) do { \ ++ retval = add_uevent_var(envp, num_envp, &i, \ ++ buf, buf_size, &length, \ ++ fmt, ##val); \ ++ if (retval) \ ++ return retval; \ ++} while (0); ++ ++#if 0 ++ if (add_uevent_var(env, ++ "SDIO_CLASS=%02X", func->class)) ++ return -ENOMEM; ++ ++ if (add_uevent_var(env, ++ "SDIO_ID=%04X:%04X", func->vendor, func->device)) ++ return -ENOMEM; ++ ++ if (add_uevent_var(env, ++ "MODALIAS=sdio:c%02Xv%04Xd%04X", ++ func->class, func->vendor, func->device)) ++ return -ENOMEM; ++#else ++ add_env("SDIO_CLASS=%02X", func->class); ++ add_env("SDIO_ID=%04X:%04X", func->vendor, func->device); ++ add_env("MODALIAS=sdio:c%02Xv%04Xd%04X", ++ func->class, func->vendor, func->device); ++ ++#endif ++ ++#undef add_env ++ return 0; ++} ++ ++static int sdio_bus_probe(struct device *dev) ++{ ++ struct sdio_driver *drv = to_sdio_driver(dev->driver); ++ struct sdio_func *func = dev_to_sdio_func(dev); ++ const struct sdio_device_id *id; ++ int ret; ++ ++ id = sdio_match_device(func, drv); ++ if (!id) ++ return -ENODEV; ++ ++ /* Set the default block size so the driver is sure it's something ++ * sensible. */ ++ sdio_claim_host(func); ++ ret = sdio_set_block_size(func, 0); ++ sdio_release_host(func); ++ if (ret) ++ return ret; ++ ++ return drv->probe(func, id); ++} ++ ++static int sdio_bus_remove(struct device *dev) ++{ ++ struct sdio_driver *drv = to_sdio_driver(dev->driver); ++ struct sdio_func *func = dev_to_sdio_func(dev); ++ ++ drv->remove(func); ++ ++ if (func->irq_handler) { ++ printk(KERN_WARNING "WARNING: driver %s did not remove " ++ "its interrupt handler!\n", drv->name); ++ sdio_claim_host(func); ++ sdio_release_irq(func); ++ sdio_release_host(func); ++ } ++ ++ return 0; ++} ++ ++static struct bus_type sdio_bus_type = { ++ .name = "sdio", ++ .dev_attrs = sdio_dev_attrs, ++ .match = sdio_bus_match, ++ .uevent = sdio_bus_uevent, ++ .probe = sdio_bus_probe, ++ .remove = sdio_bus_remove, ++}; ++ ++int sdio_register_bus(void) ++{ ++ return bus_register(&sdio_bus_type); ++} ++ ++void sdio_unregister_bus(void) ++{ ++ bus_unregister(&sdio_bus_type); ++} ++ ++/** ++ * sdio_register_driver - register a function driver ++ * @drv: SDIO function driver ++ */ ++int sdio_register_driver(struct sdio_driver *drv) ++{ ++ drv->drv.name = drv->name; ++ drv->drv.bus = &sdio_bus_type; ++ return driver_register(&drv->drv); ++} ++EXPORT_SYMBOL_GPL(sdio_register_driver); ++ ++/** ++ * sdio_unregister_driver - unregister a function driver ++ * @drv: SDIO function driver ++ */ ++void sdio_unregister_driver(struct sdio_driver *drv) ++{ ++ drv->drv.bus = &sdio_bus_type; ++ driver_unregister(&drv->drv); ++} ++EXPORT_SYMBOL_GPL(sdio_unregister_driver); ++ ++static void sdio_release_func(struct device *dev) ++{ ++ struct sdio_func *func = dev_to_sdio_func(dev); ++ ++ sdio_free_func_cis(func); ++ ++ if (func->info) ++ kfree(func->info); ++ ++ kfree(func); ++} ++ ++/* ++ * Allocate and initialise a new SDIO function structure. ++ */ ++struct sdio_func *sdio_alloc_func(struct mmc_card *card) ++{ ++ struct sdio_func *func; ++ ++ func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL); ++ if (!func) ++ return ERR_PTR(-ENOMEM); ++ ++ func->card = card; ++ ++ device_initialize(&func->dev); ++ ++ func->dev.parent = &card->dev; ++ func->dev.bus = &sdio_bus_type; ++ func->dev.release = sdio_release_func; ++ ++ return func; ++} ++ ++/* ++ * Register a new SDIO function with the driver model. ++ */ ++int sdio_add_func(struct sdio_func *func) ++{ ++ int ret; ++ ++ snprintf(func->dev.bus_id, sizeof(func->dev.bus_id), ++ "%s:%d", mmc_card_id(func->card), func->num); ++ ++ ret = device_add(&func->dev); ++ if (ret == 0) ++ sdio_func_set_present(func); ++ ++ return ret; ++} ++ ++/* ++ * Unregister a SDIO function with the driver model, and ++ * (eventually) free it. ++ */ ++void sdio_remove_func(struct sdio_func *func) ++{ ++ if (sdio_func_present(func)) ++ device_del(&func->dev); ++ ++ put_device(&func->dev); ++} ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_bus.h linux-2.6.22.18-mv/drivers/mmc/core/sdio_bus.h +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_bus.h 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/sdio_bus.h 2008-01-29 03:26:08.000000000 -0800 +@@ -0,0 +1,22 @@ ++/* ++ * linux/drivers/mmc/core/sdio_bus.h ++ * ++ * Copyright 2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++#ifndef _MMC_CORE_SDIO_BUS_H ++#define _MMC_CORE_SDIO_BUS_H ++ ++struct sdio_func *sdio_alloc_func(struct mmc_card *card); ++int sdio_add_func(struct sdio_func *func); ++void sdio_remove_func(struct sdio_func *func); ++ ++int sdio_register_bus(void); ++void sdio_unregister_bus(void); ++ ++#endif ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio.c linux-2.6.22.18-mv/drivers/mmc/core/sdio.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio.c 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/sdio.c 2008-01-29 03:26:08.000000000 -0800 +@@ -0,0 +1,395 @@ ++/* ++ * linux/drivers/mmc/sdio.c ++ * ++ * Copyright 2006-2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "core.h" ++#include "bus.h" ++#include "sdio_bus.h" ++#include "mmc_ops.h" ++#include "sd_ops.h" ++#include "sdio_ops.h" ++#include "sdio_cis.h" ++ ++static int sdio_read_fbr(struct sdio_func *func) ++{ ++ int ret; ++ unsigned char data; ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, ++ SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF, 0, &data); ++ if (ret) ++ goto out; ++ ++ data &= 0x0f; ++ ++ if (data == 0x0f) { ++ ret = mmc_io_rw_direct(func->card, 0, 0, ++ SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF_EXT, 0, &data); ++ if (ret) ++ goto out; ++ } ++ ++ func->class = data; ++ ++out: ++ return ret; ++} ++ ++static int sdio_init_func(struct mmc_card *card, unsigned int fn) ++{ ++ int ret; ++ struct sdio_func *func; ++ ++ BUG_ON(fn > SDIO_MAX_FUNCS); ++ ++ func = sdio_alloc_func(card); ++ if (IS_ERR(func)) ++ return PTR_ERR(func); ++ ++ func->num = fn; ++ ++ ret = sdio_read_fbr(func); ++ if (ret) ++ goto fail; ++ ++ ret = sdio_read_func_cis(func); ++ if (ret) ++ goto fail; ++ ++ card->sdio_func[fn - 1] = func; ++ ++ return 0; ++ ++fail: ++ /* ++ * It is okay to remove the function here even though we hold ++ * the host lock as we haven't registered the device yet. ++ */ ++ sdio_remove_func(func); ++ return ret; ++} ++ ++static int sdio_read_cccr(struct mmc_card *card) ++{ ++ int ret; ++ int cccr_vsn; ++ unsigned char data; ++ ++ memset(&card->cccr, 0, sizeof(struct sdio_cccr)); ++ ++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CCCR, 0, &data); ++ if (ret) ++ goto out; ++ ++ cccr_vsn = data & 0x0f; ++ ++ if (cccr_vsn > SDIO_CCCR_REV_1_20) { ++ printk(KERN_ERR "%s: unrecognised CCCR structure version %d\n", ++ mmc_hostname(card->host), cccr_vsn); ++ return -EINVAL; ++ } ++ ++ card->cccr.sdio_vsn = (data & 0xf0) >> 4; ++ ++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CAPS, 0, &data); ++ if (ret) ++ goto out; ++ ++ if (data & SDIO_CCCR_CAP_SMB) ++ card->cccr.multi_block = 1; ++ if (data & SDIO_CCCR_CAP_LSC) ++ card->cccr.low_speed = 1; ++ if (data & SDIO_CCCR_CAP_4BLS) ++ card->cccr.wide_bus = 1; ++ ++ if (cccr_vsn >= SDIO_CCCR_REV_1_10) { ++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_POWER, 0, &data); ++ if (ret) ++ goto out; ++ ++ if (data & SDIO_POWER_SMPC) ++ card->cccr.high_power = 1; ++ } ++ ++ if (cccr_vsn >= SDIO_CCCR_REV_1_20) { ++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &data); ++ if (ret) ++ goto out; ++ ++ if (data & SDIO_SPEED_SHS) ++ card->cccr.high_speed = 1; ++ } ++ ++out: ++ return ret; ++} ++ ++static int sdio_enable_wide(struct mmc_card *card) ++{ ++ int ret; ++ u8 ctrl; ++ ++ if (!(card->host->caps & MMC_CAP_4_BIT_DATA)) ++ return 0; ++ ++ if (card->cccr.low_speed && !card->cccr.wide_bus) ++ return 0; ++ ++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl); ++ if (ret) ++ return ret; ++ ++ ctrl |= SDIO_BUS_WIDTH_4BIT; ++ ++ ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL); ++ if (ret) ++ return ret; ++ ++ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); ++ ++ return 0; ++} ++ ++/* ++ * Host is being removed. Free up the current card. ++ */ ++static void mmc_sdio_remove(struct mmc_host *host) ++{ ++ int i; ++ ++ BUG_ON(!host); ++ BUG_ON(!host->card); ++ ++ for (i = 0;i < host->card->sdio_funcs;i++) { ++ if (host->card->sdio_func[i]) { ++ sdio_remove_func(host->card->sdio_func[i]); ++ host->card->sdio_func[i] = NULL; ++ } ++ } ++ ++ mmc_remove_card(host->card); ++ host->card = NULL; ++} ++ ++/* ++ * Card detection callback from host. ++ */ ++static void mmc_sdio_detect(struct mmc_host *host) ++{ ++ int err; ++ ++ BUG_ON(!host); ++ BUG_ON(!host->card); ++ ++ mmc_claim_host(host); ++ ++ /* ++ * Just check if our card has been removed. ++ */ ++ err = mmc_select_card(host->card); ++ ++ mmc_release_host(host); ++ ++ if (err) { ++ mmc_sdio_remove(host); ++ ++ mmc_claim_host(host); ++ mmc_detach_bus(host); ++ mmc_release_host(host); ++ } ++} ++ ++ ++static const struct mmc_bus_ops mmc_sdio_ops = { ++ .remove = mmc_sdio_remove, ++ .detect = mmc_sdio_detect, ++}; ++ ++ ++/* ++ * Starting point for SDIO card init. ++ */ ++int mmc_attach_sdio(struct mmc_host *host, u32 ocr) ++{ ++ int err; ++ int i, funcs; ++ struct mmc_card *card; ++ ++ BUG_ON(!host); ++ WARN_ON(!host->claimed); ++ ++ mmc_attach_bus(host, &mmc_sdio_ops); ++ ++ /* ++ * Sanity check the voltages that the card claims to ++ * support. ++ */ ++ if (ocr & 0x7F) { ++ printk(KERN_WARNING "%s: card claims to support voltages " ++ "below the defined range. These will be ignored.\n", ++ mmc_hostname(host)); ++ ocr &= ~0x7F; ++ } ++ ++ if (ocr & MMC_VDD_165_195) { ++ printk(KERN_WARNING "%s: SDIO card claims to support the " ++ "incompletely defined 'low voltage range'. This " ++ "will be ignored.\n", mmc_hostname(host)); ++ ocr &= ~MMC_VDD_165_195; ++ } ++ ++ host->ocr = mmc_select_voltage(host, ocr); ++ ++ /* ++ * Can we support the voltage(s) of the card(s)? ++ */ ++ if (!host->ocr) { ++ err = -EINVAL; ++ goto err; ++ } ++ ++ /* ++ * Inform the card of the voltage ++ */ ++ err = mmc_send_io_op_cond(host, host->ocr, &ocr); ++ if (err) ++ goto err; ++ ++ /* ++ * For SPI, enable CRC as appropriate. ++ */ ++ if (mmc_host_is_spi(host)) { ++ err = mmc_spi_set_crc(host, use_spi_crc); ++ if (err) ++ goto err; ++ } ++ ++ /* ++ * The number of functions on the card is encoded inside ++ * the ocr. ++ */ ++ funcs = (ocr & 0x70000000) >> 28; ++ ++ /* ++ * Allocate card structure. ++ */ ++ card = mmc_alloc_card(host); ++ if (IS_ERR(card)) { ++ err = PTR_ERR(card); ++ goto err; ++ } ++ ++ card->type = MMC_TYPE_SDIO; ++ card->sdio_funcs = funcs; ++ ++ host->card = card; ++ ++ /* ++ * For native busses: set card RCA and quit open drain mode. ++ */ ++ if (!mmc_host_is_spi(host)) { ++ err = mmc_send_relative_addr(host, &card->rca); ++ if (err) ++ goto remove; ++ ++ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); ++ } ++ ++ /* ++ * Select card, as all following commands rely on that. ++ */ ++ if (!mmc_host_is_spi(host)) { ++ err = mmc_select_card(card); ++ if (err) ++ goto remove; ++ } ++ ++ /* ++ * Read the common registers. ++ */ ++ err = sdio_read_cccr(card); ++ if (err) ++ goto remove; ++ ++ /* ++ * Read the common CIS tuples. ++ */ ++ err = sdio_read_common_cis(card); ++ if (err) ++ goto remove; ++ ++ /* ++ * No support for high-speed yet, so just set ++ * the card's maximum speed. ++ */ ++ mmc_set_clock(host, card->cis.max_dtr); ++ ++ /* ++ * Switch to wider bus (if supported). ++ */ ++ err = sdio_enable_wide(card); ++ if (err) ++ goto remove; ++ ++ /* ++ * Initialize (but don't add) all present functions. ++ */ ++ for (i = 0;i < funcs;i++) { ++ err = sdio_init_func(host->card, i + 1); ++ if (err) ++ goto remove; ++ } ++ ++ mmc_release_host(host); ++ ++ /* ++ * First add the card to the driver model... ++ */ ++ err = mmc_add_card(host->card); ++ if (err) ++ goto remove_added; ++ ++ /* ++ * ...then the SDIO functions. ++ */ ++ for (i = 0;i < funcs;i++) { ++ err = sdio_add_func(host->card->sdio_func[i]); ++ if (err) ++ goto remove_added; ++ } ++ ++ return 0; ++ ++ ++remove_added: ++ /* Remove without lock if the device has been added. */ ++ mmc_sdio_remove(host); ++ mmc_claim_host(host); ++remove: ++ /* And with lock if it hasn't been added. */ ++ if (host->card) ++ mmc_sdio_remove(host); ++err: ++ mmc_detach_bus(host); ++ mmc_release_host(host); ++ ++ printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n", ++ mmc_hostname(host), err); ++ ++ return err; ++} ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_cis.c linux-2.6.22.18-mv/drivers/mmc/core/sdio_cis.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_cis.c 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/sdio_cis.c 2008-01-29 03:26:08.000000000 -0800 +@@ -0,0 +1,347 @@ ++/* ++ * linux/drivers/mmc/core/sdio_cis.c ++ * ++ * Author: Nicolas Pitre ++ * Created: June 11, 2007 ++ * Copyright: MontaVista Software Inc. ++ * ++ * Copyright 2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "sdio_cis.h" ++#include "sdio_ops.h" ++ ++static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func, ++ const unsigned char *buf, unsigned size) ++{ ++ unsigned i, nr_strings; ++ char **buffer, *string; ++ ++ buf += 2; ++ size -= 2; ++ ++ nr_strings = 0; ++ for (i = 0; i < size; i++) { ++ if (buf[i] == 0xff) ++ break; ++ if (buf[i] == 0) ++ nr_strings++; ++ } ++ ++ if (buf[i-1] != '\0') { ++ printk(KERN_WARNING "SDIO: ignoring broken CISTPL_VERS_1\n"); ++ return 0; ++ } ++ ++ size = i; ++ ++ buffer = kzalloc(sizeof(char*) * nr_strings + size, GFP_KERNEL); ++ if (!buffer) ++ return -ENOMEM; ++ ++ string = (char*)(buffer + nr_strings); ++ ++ for (i = 0; i < nr_strings; i++) { ++ buffer[i] = string; ++ strcpy(string, buf); ++ string += strlen(string) + 1; ++ buf += strlen(buf) + 1; ++ } ++ ++ if (func) { ++ func->num_info = nr_strings; ++ func->info = (const char**)buffer; ++ } else { ++ card->num_info = nr_strings; ++ card->info = (const char**)buffer; ++ } ++ ++ return 0; ++} ++ ++static int cistpl_manfid(struct mmc_card *card, struct sdio_func *func, ++ const unsigned char *buf, unsigned size) ++{ ++ unsigned int vendor, device; ++ ++ /* TPLMID_MANF */ ++ vendor = buf[0] | (buf[1] << 8); ++ ++ /* TPLMID_CARD */ ++ device = buf[2] | (buf[3] << 8); ++ ++ if (func) { ++ func->vendor = vendor; ++ func->device = device; ++ } else { ++ card->cis.vendor = vendor; ++ card->cis.device = device; ++ } ++ ++ return 0; ++} ++ ++static const unsigned char speed_val[16] = ++ { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 }; ++static const unsigned int speed_unit[8] = ++ { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; ++ ++static int cistpl_funce_common(struct mmc_card *card, ++ const unsigned char *buf, unsigned size) ++{ ++ if (size < 0x04 || buf[0] != 0) ++ return -EINVAL; ++ ++ /* TPLFE_FN0_BLK_SIZE */ ++ card->cis.blksize = buf[1] | (buf[2] << 8); ++ ++ /* TPLFE_MAX_TRAN_SPEED */ ++ card->cis.max_dtr = speed_val[(buf[3] >> 3) & 15] * ++ speed_unit[buf[3] & 7]; ++ ++ return 0; ++} ++ ++static int cistpl_funce_func(struct sdio_func *func, ++ const unsigned char *buf, unsigned size) ++{ ++ unsigned vsn; ++ unsigned min_size; ++ ++ vsn = func->card->cccr.sdio_vsn; ++ min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42; ++ ++ if (size < min_size || buf[0] != 1) ++ /*return -EINVAL;*/ ++ printk(KERN_ALERT "size=%d min_size=%d\n",size,min_size); ++ ++ /* TPLFE_MAX_BLK_SIZE */ ++ func->max_blksize = buf[12] | (buf[13] << 8); ++ ++ return 0; ++} ++ ++static int cistpl_funce(struct mmc_card *card, struct sdio_func *func, ++ const unsigned char *buf, unsigned size) ++{ ++ int ret; ++ ++ /* ++ * There should be two versions of the CISTPL_FUNCE tuple, ++ * one for the common CIS (function 0) and a version used by ++ * the individual function's CIS (1-7). Yet, the later has a ++ * different length depending on the SDIO spec version. ++ */ ++ if (func) ++ ret = cistpl_funce_func(func, buf, size); ++ else ++ ret = cistpl_funce_common(card, buf, size); ++ ++ if (ret) { ++ printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u " ++ "type %u\n", mmc_hostname(card->host), size, buf[0]); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *, ++ const unsigned char *, unsigned); ++ ++struct cis_tpl { ++ unsigned char code; ++ unsigned char min_size; ++ tpl_parse_t *parse; ++}; ++ ++static const struct cis_tpl cis_tpl_list[] = { ++ { 0x15, 3, cistpl_vers_1 }, ++ { 0x20, 4, cistpl_manfid }, ++ { 0x21, 2, /* cistpl_funcid */ }, ++ { 0x22, 0, cistpl_funce }, ++}; ++ ++static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) ++{ ++ int ret; ++ struct sdio_func_tuple *this, **prev; ++ unsigned i, ptr = 0; ++ ++ /* ++ * Note that this works for the common CIS (function number 0) as ++ * well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS ++ * have the same offset. ++ */ ++ for (i = 0; i < 3; i++) { ++ unsigned char x, fn; ++ ++ if (func) ++ fn = func->num; ++ else ++ fn = 0; ++ ++ ret = mmc_io_rw_direct(card, 0, 0, ++ SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x); ++ if (ret) ++ return ret; ++ ptr |= x << (i * 8); ++ } ++ ++ if (func) ++ prev = &func->tuples; ++ else ++ prev = &card->tuples; ++ ++ BUG_ON(*prev); ++ ++ do { ++ unsigned char tpl_code, tpl_link; ++ ++ ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code); ++ if (ret) ++ break; ++ ++ /* 0xff means we're done */ ++ if (tpl_code == 0xff) ++ break; ++ ++ ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link); ++ if (ret) ++ break; ++ ++ this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL); ++ if (!this) ++ return -ENOMEM; ++ ++ for (i = 0; i < tpl_link; i++) { ++ ret = mmc_io_rw_direct(card, 0, 0, ++ ptr + i, 0, &this->data[i]); ++ if (ret) ++ break; ++ } ++ if (ret) { ++ kfree(this); ++ break; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++) ++ if (cis_tpl_list[i].code == tpl_code) ++ break; ++ if (i >= ARRAY_SIZE(cis_tpl_list)) { ++ /* this tuple is unknown to the core */ ++ this->next = NULL; ++ this->code = tpl_code; ++ this->size = tpl_link; ++ *prev = this; ++ prev = &this->next; ++ printk(KERN_DEBUG ++ "%s: queuing CIS tuple 0x%02x length %u\n", ++ mmc_hostname(card->host), tpl_code, tpl_link); ++ } else { ++ const struct cis_tpl *tpl = cis_tpl_list + i; ++ if (tpl_link < tpl->min_size) { ++ printk(KERN_ERR ++ "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u)\n", ++ mmc_hostname(card->host), ++ tpl_code, tpl_link, tpl->min_size); ++ ret = -EINVAL; ++ } else if (tpl->parse) { ++ ret = tpl->parse(card, func, ++ this->data, tpl_link); ++ } ++ kfree(this); ++ } ++ ++ ptr += tpl_link; ++ } while (!ret); ++ ++ /* ++ * Link in all unknown tuples found in the common CIS so that ++ * drivers don't have to go digging in two places. ++ */ ++ if (func) ++ *prev = card->tuples; ++ ++ return ret; ++} ++ ++int sdio_read_common_cis(struct mmc_card *card) ++{ ++ return sdio_read_cis(card, NULL); ++} ++ ++void sdio_free_common_cis(struct mmc_card *card) ++{ ++ struct sdio_func_tuple *tuple, *victim; ++ ++ tuple = card->tuples; ++ ++ while (tuple) { ++ victim = tuple; ++ tuple = tuple->next; ++ kfree(victim); ++ } ++ ++ card->tuples = NULL; ++} ++ ++int sdio_read_func_cis(struct sdio_func *func) ++{ ++ int ret; ++ ++ ret = sdio_read_cis(func->card, func); ++ if (ret) ++ return ret; ++ ++ /* ++ * Since we've linked to tuples in the card structure, ++ * we must make sure we have a reference to it. ++ */ ++ get_device(&func->card->dev); ++ ++ /* ++ * Vendor/device id is optional for function CIS, so ++ * copy it from the card structure as needed. ++ */ ++ if (func->vendor == 0) { ++ func->vendor = func->card->cis.vendor; ++ func->device = func->card->cis.device; ++ } ++ ++ return 0; ++} ++ ++void sdio_free_func_cis(struct sdio_func *func) ++{ ++ struct sdio_func_tuple *tuple, *victim; ++ ++ tuple = func->tuples; ++ ++ while (tuple && tuple != func->card->tuples) { ++ victim = tuple; ++ tuple = tuple->next; ++ kfree(victim); ++ } ++ ++ func->tuples = NULL; ++ ++ /* ++ * We have now removed the link to the tuples in the ++ * card structure, so remove the reference. ++ */ ++ put_device(&func->card->dev); ++} ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_cis.h linux-2.6.22.18-mv/drivers/mmc/core/sdio_cis.h +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_cis.h 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/sdio_cis.h 2008-01-29 03:26:08.000000000 -0800 +@@ -0,0 +1,23 @@ ++/* ++ * linux/drivers/mmc/core/sdio_cis.h ++ * ++ * Author: Nicolas Pitre ++ * Created: June 11, 2007 ++ * Copyright: MontaVista Software Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef _MMC_SDIO_CIS_H ++#define _MMC_SDIO_CIS_H ++ ++int sdio_read_common_cis(struct mmc_card *card); ++void sdio_free_common_cis(struct mmc_card *card); ++ ++int sdio_read_func_cis(struct sdio_func *func); ++void sdio_free_func_cis(struct sdio_func *func); ++ ++#endif +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_io.c linux-2.6.22.18-mv/drivers/mmc/core/sdio_io.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_io.c 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/sdio_io.c 2008-01-29 03:26:08.000000000 -0800 +@@ -0,0 +1,548 @@ ++/* ++ * linux/drivers/mmc/core/sdio_io.c ++ * ++ * Copyright 2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "sdio_ops.h" ++ ++/** ++ * sdio_claim_host - exclusively claim a bus for a certain SDIO function ++ * @func: SDIO function that will be accessed ++ * ++ * Claim a bus for a set of operations. The SDIO function given ++ * is used to figure out which bus is relevant. ++ */ ++void sdio_claim_host(struct sdio_func *func) ++{ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ mmc_claim_host(func->card->host); ++} ++EXPORT_SYMBOL_GPL(sdio_claim_host); ++ ++/** ++ * sdio_release_host - release a bus for a certain SDIO function ++ * @func: SDIO function that was accessed ++ * ++ * Release a bus, allowing others to claim the bus for their ++ * operations. ++ */ ++void sdio_release_host(struct sdio_func *func) ++{ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ mmc_release_host(func->card->host); ++} ++EXPORT_SYMBOL_GPL(sdio_release_host); ++ ++/** ++ * sdio_enable_func - enables a SDIO function for usage ++ * @func: SDIO function to enable ++ * ++ * Powers up and activates a SDIO function so that register ++ * access is possible. ++ */ ++int sdio_enable_func(struct sdio_func *func) ++{ ++ int ret; ++ unsigned char reg; ++ unsigned long timeout; ++ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func)); ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, ®); ++ if (ret) ++ goto err; ++ ++ reg |= 1 << func->num; ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL); ++ if (ret) ++ goto err; ++ ++ /* ++ * FIXME: This should timeout based on information in the CIS, ++ * but we don't have card to parse that yet. ++ */ ++ timeout = jiffies + HZ; ++ ++ while (1) { ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, ®); ++ if (ret) ++ goto err; ++ if (reg & (1 << func->num)) ++ break; ++ ret = -ETIME; ++ if (time_after(jiffies, timeout)) ++ goto err; ++ } ++ ++ pr_debug("SDIO: Enabled device %s\n", sdio_func_id(func)); ++ ++ return 0; ++ ++err: ++ pr_debug("SDIO: Failed to enable device %s\n", sdio_func_id(func)); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(sdio_enable_func); ++ ++/** ++ * sdio_disable_func - disable a SDIO function ++ * @func: SDIO function to disable ++ * ++ * Powers down and deactivates a SDIO function. Register access ++ * to this function will fail until the function is reenabled. ++ */ ++int sdio_disable_func(struct sdio_func *func) ++{ ++ int ret; ++ unsigned char reg; ++ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ pr_debug("SDIO: Disabling device %s...\n", sdio_func_id(func)); ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, ®); ++ if (ret) ++ goto err; ++ ++ reg &= ~(1 << func->num); ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL); ++ if (ret) ++ goto err; ++ ++ pr_debug("SDIO: Disabled device %s\n", sdio_func_id(func)); ++ ++ return 0; ++ ++err: ++ pr_debug("SDIO: Failed to disable device %s\n", sdio_func_id(func)); ++ return -EIO; ++} ++EXPORT_SYMBOL_GPL(sdio_disable_func); ++ ++/** ++ * sdio_set_block_size - set the block size of an SDIO function ++ * @func: SDIO function to change ++ * @blksz: new block size or 0 to use the default. ++ * ++ * The default block size is the largest supported by both the function ++ * and the host, with a maximum of 512 to ensure that arbitrarily sized ++ * data transfer use the optimal (least) number of commands. ++ * ++ * A driver may call this to override the default block size set by the ++ * core. This can be used to set a block size greater than the maximum ++ * that reported by the card; it is the driver's responsibility to ensure ++ * it uses a value that the card supports. ++ * ++ * Returns 0 on success, -EINVAL if the host does not support the ++ * requested block size, or -EIO (etc.) if one of the resultant FBR block ++ * size register writes failed. ++ * ++ */ ++int sdio_set_block_size(struct sdio_func *func, unsigned blksz) ++{ ++ int ret; ++ ++ if (blksz > func->card->host->max_blk_size) ++ return -EINVAL; ++ ++ if (blksz == 0) { ++ blksz = min(min( ++ func->max_blksize, ++ func->card->host->max_blk_size), ++ 512u); ++ } ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, ++ SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE, ++ blksz & 0xff, NULL); ++ if (ret) ++ return ret; ++ ret = mmc_io_rw_direct(func->card, 1, 0, ++ SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1, ++ (blksz >> 8) & 0xff, NULL); ++ if (ret) ++ return ret; ++ func->cur_blksize = blksz; ++ return 0; ++} ++ ++EXPORT_SYMBOL_GPL(sdio_set_block_size); ++ ++/* Split an arbitrarily sized data transfer into several ++ * IO_RW_EXTENDED commands. */ ++static int sdio_io_rw_ext_helper(struct sdio_func *func, int write, ++ unsigned addr, int incr_addr, u8 *buf, unsigned size) ++{ ++ unsigned remainder = size; ++ unsigned max_blocks; ++ int ret; ++ ++ /* Do the bulk of the transfer using block mode (if supported). */ ++ if (func->card->cccr.multi_block) { ++ /* Blocks per command is limited by host count, host transfer ++ * size (we only use a single sg entry) and the maximum for ++ * IO_RW_EXTENDED of 511 blocks. */ ++ max_blocks = min(min( ++ func->card->host->max_blk_count, ++ func->card->host->max_seg_size / func->cur_blksize), ++ 511u); ++ ++ while (remainder > func->cur_blksize) { ++ unsigned blocks; ++ ++ blocks = remainder / func->cur_blksize; ++ if (blocks > max_blocks) ++ blocks = max_blocks; ++ size = blocks * func->cur_blksize; ++ ++ ret = mmc_io_rw_extended(func->card, write, ++ func->num, addr, incr_addr, buf, ++ blocks, func->cur_blksize); ++ if (ret) ++ return ret; ++ ++ remainder -= size; ++ buf += size; ++ if (incr_addr) ++ addr += size; ++ } ++ } ++ ++ /* Write the remainder using byte mode. */ ++ while (remainder > 0) { ++ size = remainder; ++ if (size > func->cur_blksize) ++ size = func->cur_blksize; ++ if (size > 512) ++ size = 512; /* maximum size for byte mode */ ++ ++ ret = mmc_io_rw_extended(func->card, write, func->num, addr, ++ incr_addr, buf, 1, size); ++ if (ret) ++ return ret; ++ ++ remainder -= size; ++ buf += size; ++ if (incr_addr) ++ addr += size; ++ } ++ return 0; ++} ++ ++/** ++ * sdio_readb - read a single byte from a SDIO function ++ * @func: SDIO function to access ++ * @addr: address to read ++ * @err_ret: optional status value from transfer ++ * ++ * Reads a single byte from the address space of a given SDIO ++ * function. If there is a problem reading the address, 0xff ++ * is returned and @err_ret will contain the error code. ++ */ ++unsigned char sdio_readb(struct sdio_func *func, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ unsigned char val; ++ ++ BUG_ON(!func); ++ ++ if (err_ret) ++ *err_ret = 0; ++ ++ ret = mmc_io_rw_direct(func->card, 0, func->num, addr, 0, &val); ++ if (ret) { ++ if (err_ret) ++ *err_ret = ret; ++ return 0xFF; ++ } ++ ++ return val; ++} ++EXPORT_SYMBOL_GPL(sdio_readb); ++ ++/** ++ * sdio_writeb - write a single byte to a SDIO function ++ * @func: SDIO function to access ++ * @b: byte to write ++ * @addr: address to write to ++ * @err_ret: optional status value from transfer ++ * ++ * Writes a single byte to the address space of a given SDIO ++ * function. @err_ret will contain the status of the actual ++ * transfer. ++ */ ++void sdio_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ BUG_ON(!func); ++ ++ ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL); ++ if (err_ret) ++ *err_ret = ret; ++} ++EXPORT_SYMBOL_GPL(sdio_writeb); ++ ++/** ++ * sdio_memcpy_fromio - read a chunk of memory from a SDIO function ++ * @func: SDIO function to access ++ * @dst: buffer to store the data ++ * @addr: address to begin reading from ++ * @count: number of bytes to read ++ * ++ * Reads from the address space of a given SDIO function. Return ++ * value indicates if the transfer succeeded or not. ++ */ ++int sdio_memcpy_fromio(struct sdio_func *func, void *dst, ++ unsigned int addr, int count) ++{ ++ return sdio_io_rw_ext_helper(func, 0, addr, 1, dst, count); ++} ++EXPORT_SYMBOL_GPL(sdio_memcpy_fromio); ++ ++/** ++ * sdio_memcpy_toio - write a chunk of memory to a SDIO function ++ * @func: SDIO function to access ++ * @addr: address to start writing to ++ * @src: buffer that contains the data to write ++ * @count: number of bytes to write ++ * ++ * Writes to the address space of a given SDIO function. Return ++ * value indicates if the transfer succeeded or not. ++ */ ++int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr, ++ void *src, int count) ++{ ++ return sdio_io_rw_ext_helper(func, 1, addr, 1, src, count); ++} ++EXPORT_SYMBOL_GPL(sdio_memcpy_toio); ++ ++/** ++ * sdio_readsb - read from a FIFO on a SDIO function ++ * @func: SDIO function to access ++ * @dst: buffer to store the data ++ * @addr: address of (single byte) FIFO ++ * @count: number of bytes to read ++ * ++ * Reads from the specified FIFO of a given SDIO function. Return ++ * value indicates if the transfer succeeded or not. ++ */ ++int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr, ++ int count) ++{ ++ return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count); ++} ++ ++EXPORT_SYMBOL_GPL(sdio_readsb); ++ ++/** ++ * sdio_writesb - write to a FIFO of a SDIO function ++ * @func: SDIO function to access ++ * @addr: address of (single byte) FIFO ++ * @src: buffer that contains the data to write ++ * @count: number of bytes to write ++ * ++ * Writes to the specified FIFO of a given SDIO function. Return ++ * value indicates if the transfer succeeded or not. ++ */ ++int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src, ++ int count) ++{ ++ return sdio_io_rw_ext_helper(func, 1, addr, 0, src, count); ++} ++EXPORT_SYMBOL_GPL(sdio_writesb); ++ ++/** ++ * sdio_readw - read a 16 bit integer from a SDIO function ++ * @func: SDIO function to access ++ * @addr: address to read ++ * @err_ret: optional status value from transfer ++ * ++ * Reads a 16 bit integer from the address space of a given SDIO ++ * function. If there is a problem reading the address, 0xffff ++ * is returned and @err_ret will contain the error code. ++ */ ++unsigned short sdio_readw(struct sdio_func *func, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ if (err_ret) ++ *err_ret = 0; ++ ++ ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 2); ++ if (ret) { ++ if (err_ret) ++ *err_ret = ret; ++ return 0xFFFF; ++ } ++ ++ return le16_to_cpu(*(u16*)func->tmpbuf); ++} ++EXPORT_SYMBOL_GPL(sdio_readw); ++ ++/** ++ * sdio_writew - write a 16 bit integer to a SDIO function ++ * @func: SDIO function to access ++ * @b: integer to write ++ * @addr: address to write to ++ * @err_ret: optional status value from transfer ++ * ++ * Writes a 16 bit integer to the address space of a given SDIO ++ * function. @err_ret will contain the status of the actual ++ * transfer. ++ */ ++void sdio_writew(struct sdio_func *func, unsigned short b, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ *(u16*)func->tmpbuf = cpu_to_le16(b); ++ ++ ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2); ++ if (err_ret) ++ *err_ret = ret; ++} ++EXPORT_SYMBOL_GPL(sdio_writew); ++ ++/** ++ * sdio_readl - read a 32 bit integer from a SDIO function ++ * @func: SDIO function to access ++ * @addr: address to read ++ * @err_ret: optional status value from transfer ++ * ++ * Reads a 32 bit integer from the address space of a given SDIO ++ * function. If there is a problem reading the address, ++ * 0xffffffff is returned and @err_ret will contain the error ++ * code. ++ */ ++unsigned long sdio_readl(struct sdio_func *func, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ if (err_ret) ++ *err_ret = 0; ++ ++ ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 4); ++ if (ret) { ++ if (err_ret) ++ *err_ret = ret; ++ return 0xFFFFFFFF; ++ } ++ ++ return le32_to_cpu(*(u32*)func->tmpbuf); ++} ++EXPORT_SYMBOL_GPL(sdio_readl); ++ ++/** ++ * sdio_writel - write a 32 bit integer to a SDIO function ++ * @func: SDIO function to access ++ * @b: integer to write ++ * @addr: address to write to ++ * @err_ret: optional status value from transfer ++ * ++ * Writes a 32 bit integer to the address space of a given SDIO ++ * function. @err_ret will contain the status of the actual ++ * transfer. ++ */ ++void sdio_writel(struct sdio_func *func, unsigned long b, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ *(u32*)func->tmpbuf = cpu_to_le32(b); ++ ++ ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4); ++ if (err_ret) ++ *err_ret = ret; ++} ++EXPORT_SYMBOL_GPL(sdio_writel); ++ ++/** ++ * sdio_f0_readb - read a single byte from SDIO function 0 ++ * @func: an SDIO function of the card ++ * @addr: address to read ++ * @err_ret: optional status value from transfer ++ * ++ * Reads a single byte from the address space of SDIO function 0. ++ * If there is a problem reading the address, 0xff is returned ++ * and @err_ret will contain the error code. ++ */ ++unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ unsigned char val; ++ ++ BUG_ON(!func); ++ ++ if (err_ret) ++ *err_ret = 0; ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, addr, 0, &val); ++ if (ret) { ++ if (err_ret) ++ *err_ret = ret; ++ return 0xFF; ++ } ++ ++ return val; ++} ++EXPORT_SYMBOL_GPL(sdio_f0_readb); ++ ++/** ++ * sdio_f0_writeb - write a single byte to SDIO function 0 ++ * @func: an SDIO function of the card ++ * @b: byte to write ++ * @addr: address to write to ++ * @err_ret: optional status value from transfer ++ * ++ * Writes a single byte to the address space of SDIO function 0. ++ * @err_ret will contain the status of the actual transfer. ++ * ++ * Only writes to the vendor specific CCCR registers (0xF0 - ++ * 0xFF) are permiited; @err_ret will be set to -EINVAL for * ++ * writes outside this range. ++ */ ++void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ BUG_ON(!func); ++ ++ if (addr < 0xF0 || addr > 0xFF) { ++ if (err_ret) ++ *err_ret = -EINVAL; ++ return; ++ } ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, addr, b, NULL); ++ if (err_ret) ++ *err_ret = ret; ++} ++EXPORT_SYMBOL_GPL(sdio_f0_writeb); +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_irq.c linux-2.6.22.18-mv/drivers/mmc/core/sdio_irq.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_irq.c 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/sdio_irq.c 2008-01-29 03:26:10.000000000 -0800 +@@ -0,0 +1,267 @@ ++/* ++ * linux/drivers/mmc/core/sdio_irq.c ++ * ++ * Author: Nicolas Pitre ++ * Created: June 18, 2007 ++ * Copyright: MontaVista Software Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "sdio_ops.h" ++ ++static int process_sdio_pending_irqs(struct mmc_card *card) ++{ ++ int i, ret, count; ++ unsigned char pending; ++ ++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); ++ if (ret) { ++ printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n", ++ mmc_card_id(card), ret); ++ return ret; ++ } ++ ++ count = 0; ++ for (i = 1; i <= 7; i++) { ++ if (pending & (1 << i)) { ++ struct sdio_func *func = card->sdio_func[i - 1]; ++ if (!func) { ++ printk(KERN_WARNING "%s: pending IRQ for " ++ "non-existant function\n", ++ mmc_card_id(card)); ++ ret = -EINVAL; ++ } else if (func->irq_handler) { ++ func->irq_handler(func); ++ count++; ++ } else { ++ printk(KERN_WARNING "%s: pending IRQ with no handler\n", ++ sdio_func_id(func)); ++ ret = -EINVAL; ++ } ++ } ++ } ++ ++ if (count) ++ return count; ++ ++ return ret; ++} ++ ++static int sdio_irq_thread(void *_host) ++{ ++ struct mmc_host *host = _host; ++ struct sched_param param = { .sched_priority = 1 }; ++ unsigned long period, idle_period; ++ int ret; ++ ++ sched_setscheduler(current, SCHED_FIFO, ¶m); ++ ++ /* ++ * We want to allow for SDIO cards to work even on non SDIO ++ * aware hosts. One thing that non SDIO host cannot do is ++ * asynchronous notification of pending SDIO card interrupts ++ * hence we poll for them in that case. ++ */ ++ idle_period = msecs_to_jiffies(10); ++ period = (host->caps & MMC_CAP_SDIO_IRQ) ? ++ MAX_SCHEDULE_TIMEOUT : idle_period; ++ ++ pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n", ++ mmc_hostname(host), period); ++ ++ do { ++ /* ++ * We claim the host here on drivers behalf for a couple ++ * reasons: ++ * ++ * 1) it is already needed to retrieve the CCCR_INTx; ++ * 2) we want the driver(s) to clear the IRQ condition ASAP; ++ * 3) we need to control the abort condition locally. ++ * ++ * Just like traditional hard IRQ handlers, we expect SDIO ++ * IRQ handlers to be quick and to the point, so that the ++ * holding of the host lock does not cover too much work ++ * that doesn't require that lock to be held. ++ */ ++ ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort); ++ if (ret) ++ break; ++ ret = process_sdio_pending_irqs(host->card); ++ mmc_release_host(host); ++ ++ /* ++ * Give other threads a chance to run in the presence of ++ * errors. FIXME: determine if due to card removal and ++ * possibly exit this thread if so. ++ */ ++ if (ret < 0) ++ ssleep(1); ++ ++ /* ++ * Adaptive polling frequency based on the assumption ++ * that an interrupt will be closely followed by more. ++ * This has a substantial benefit for network devices. ++ */ ++ if (!(host->caps & MMC_CAP_SDIO_IRQ)) { ++ if (ret > 0) ++ period /= 2; ++ else { ++ period++; ++ if (period > idle_period) ++ period = idle_period; ++ } ++ } ++ ++ set_task_state(current, TASK_INTERRUPTIBLE); ++ if (host->caps & MMC_CAP_SDIO_IRQ) ++ host->ops->enable_sdio_irq(host, 1); ++ if (!kthread_should_stop()) ++ schedule_timeout(period); ++ set_task_state(current, TASK_RUNNING); ++ } while (!kthread_should_stop()); ++ ++ if (host->caps & MMC_CAP_SDIO_IRQ) ++ host->ops->enable_sdio_irq(host, 0); ++ ++ pr_debug("%s: IRQ thread exiting with code %d\n", ++ mmc_hostname(host), ret); ++ ++ return ret; ++} ++ ++static int sdio_card_irq_get(struct mmc_card *card) ++{ ++ struct mmc_host *host = card->host; ++ ++ WARN_ON(!host->claimed); ++ ++ if (!host->sdio_irqs++) { ++ atomic_set(&host->sdio_irq_thread_abort, 0); ++ host->sdio_irq_thread = ++ kthread_run(sdio_irq_thread, host, "ksdiorqd"); ++ if (IS_ERR(host->sdio_irq_thread)) { ++ int err = PTR_ERR(host->sdio_irq_thread); ++ host->sdio_irqs--; ++ return err; ++ } ++ } ++ ++ return 0; ++} ++ ++static int sdio_card_irq_put(struct mmc_card *card) ++{ ++ struct mmc_host *host = card->host; ++ ++ WARN_ON(!host->claimed); ++ BUG_ON(host->sdio_irqs < 1); ++ ++ if (!--host->sdio_irqs) { ++ atomic_set(&host->sdio_irq_thread_abort, 1); ++ kthread_stop(host->sdio_irq_thread); ++ } ++ ++ return 0; ++} ++ ++/** ++ * sdio_claim_irq - claim the IRQ for a SDIO function ++ * @func: SDIO function ++ * @handler: IRQ handler callback ++ * ++ * Claim and activate the IRQ for the given SDIO function. The provided ++ * handler will be called when that IRQ is asserted. The host is always ++ * claimed already when the handler is called so the handler must not ++ * call sdio_claim_host() nor sdio_release_host(). ++ */ ++int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler) ++{ ++ int ret; ++ unsigned char reg; ++ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func)); ++ ++ if (func->irq_handler) { ++ pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func)); ++ return -EBUSY; ++ } ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®); ++ if (ret) ++ return ret; ++ ++ reg |= 1 << func->num; ++ ++ reg |= 1; /* Master interrupt enable */ ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL); ++ if (ret) ++ return ret; ++ ++ func->irq_handler = handler; ++ ret = sdio_card_irq_get(func->card); ++ if (ret) ++ func->irq_handler = NULL; ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(sdio_claim_irq); ++ ++/** ++ * sdio_release_irq - release the IRQ for a SDIO function ++ * @func: SDIO function ++ * ++ * Disable and release the IRQ for the given SDIO function. ++ */ ++int sdio_release_irq(struct sdio_func *func) ++{ ++ int ret; ++ unsigned char reg; ++ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func)); ++ ++ if (func->irq_handler) { ++ func->irq_handler = NULL; ++ sdio_card_irq_put(func->card); ++ } ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®); ++ if (ret) ++ return ret; ++ ++ reg &= ~(1 << func->num); ++ ++ /* Disable master interrupt with the last function interrupt */ ++ if (!(reg & 0xFE)) ++ reg = 0; ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(sdio_release_irq); ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_ops.c linux-2.6.22.18-mv/drivers/mmc/core/sdio_ops.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_ops.c 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/sdio_ops.c 2008-01-29 03:26:10.000000000 -0800 +@@ -0,0 +1,176 @@ ++/* ++ * linux/drivers/mmc/sdio_ops.c ++ * ++ * Copyright 2006-2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "core.h" ++ ++int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) ++{ ++ struct mmc_command cmd; ++ int i, err = 0; ++ ++ BUG_ON(!host); ++ ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ ++ cmd.opcode = SD_IO_SEND_OP_COND; ++ cmd.arg = ocr; ++ cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR; ++ ++ for (i = 100; i; i--) { ++ err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); ++ if (err) ++ break; ++ ++ /* if we're just probing, do a single pass */ ++ if (ocr == 0) ++ break; ++ ++ /* otherwise wait until reset completes */ ++ if (mmc_host_is_spi(host)) { ++ /* ++ * Both R1_SPI_IDLE and MMC_CARD_BUSY indicate ++ * an initialized card under SPI, but some cards ++ * (Marvell's) only behave when looking at this ++ * one. ++ */ ++ if (cmd.resp[1] & MMC_CARD_BUSY) ++ break; ++ } else { ++ if (cmd.resp[0] & MMC_CARD_BUSY) ++ break; ++ } ++ ++ err = -ETIMEDOUT; ++ ++ mmc_delay(10); ++ } ++ ++ if (rocr) ++ *rocr = cmd.resp[mmc_host_is_spi(host) ? 1 : 0]; ++ ++ return err; ++} ++ ++int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, ++ unsigned addr, u8 in, u8* out) ++{ ++ struct mmc_command cmd; ++ int err; ++ ++ BUG_ON(!card); ++ BUG_ON(fn > 7); ++ ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ ++ cmd.opcode = SD_IO_RW_DIRECT; ++ cmd.arg = write ? 0x80000000 : 0x00000000; ++ cmd.arg |= fn << 28; ++ cmd.arg |= (write && out) ? 0x08000000 : 0x00000000; ++ cmd.arg |= addr << 9; ++ cmd.arg |= in; ++ cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; ++ ++ err = mmc_wait_for_cmd(card->host, &cmd, 0); ++ if (err) ++ return err; ++ ++ if (mmc_host_is_spi(card->host)) { ++ /* host driver already reported errors */ ++ } else { ++ if (cmd.resp[0] & R5_ERROR) ++ return -EIO; ++ if (cmd.resp[0] & R5_FUNCTION_NUMBER) ++ return -EINVAL; ++ if (cmd.resp[0] & R5_OUT_OF_RANGE) ++ return -ERANGE; ++ } ++ ++ if (out) { ++ if (mmc_host_is_spi(card->host)) ++ *out = (cmd.resp[0] >> 8) & 0xFF; ++ else ++ *out = cmd.resp[0] & 0xFF; ++ } ++ ++ return 0; ++} ++ ++int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, ++ unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz) ++{ ++ struct mmc_request mrq; ++ struct mmc_command cmd; ++ struct mmc_data data; ++ struct scatterlist sg; ++ ++ BUG_ON(!card); ++ BUG_ON(fn > 7); ++ BUG_ON(blocks == 1 && blksz > 512); ++ WARN_ON(blocks == 0); ++ WARN_ON(blksz == 0); ++ ++ memset(&mrq, 0, sizeof(struct mmc_request)); ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ memset(&data, 0, sizeof(struct mmc_data)); ++ ++ mrq.cmd = &cmd; ++ mrq.data = &data; ++ ++ cmd.opcode = SD_IO_RW_EXTENDED; ++ cmd.arg = write ? 0x80000000 : 0x00000000; ++ cmd.arg |= fn << 28; ++ cmd.arg |= incr_addr ? 0x04000000 : 0x00000000; ++ cmd.arg |= addr << 9; ++ if (blocks == 1 && blksz <= 512) ++ cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */ ++ else ++ cmd.arg |= 0x08000000 | blocks; /* block mode */ ++ cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; ++ ++ data.blksz = blksz; ++ data.blocks = blocks; ++ data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; ++ data.sg = &sg; ++ data.sg_len = 1; ++ ++ sg_init_one(&sg, buf, blksz * blocks); ++ ++ mmc_set_data_timeout(&data, card); ++ ++ mmc_wait_for_req(card->host, &mrq); ++ ++ if (cmd.error) ++ return cmd.error; ++ if (data.error) ++ return data.error; ++ ++ if (mmc_host_is_spi(card->host)) { ++ /* host driver already reported errors */ ++ } else { ++ if (cmd.resp[0] & R5_ERROR) ++ return -EIO; ++ if (cmd.resp[0] & R5_FUNCTION_NUMBER) ++ return -EINVAL; ++ if (cmd.resp[0] & R5_OUT_OF_RANGE) ++ return -ERANGE; ++ } ++ ++ return 0; ++} ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_ops.h linux-2.6.22.18-mv/drivers/mmc/core/sdio_ops.h +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/sdio_ops.h 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/sdio_ops.h 2008-01-29 03:26:10.000000000 -0800 +@@ -0,0 +1,22 @@ ++/* ++ * linux/drivers/mmc/sdio_ops.c ++ * ++ * Copyright 2006-2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef _MMC_SDIO_OPS_H ++#define _MMC_SDIO_OPS_H ++ ++int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); ++int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, ++ unsigned addr, u8 in, u8* out); ++int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, ++ unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz); ++ ++#endif ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/sd_ops.c linux-2.6.22.18-mv/drivers/mmc/core/sd_ops.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/sd_ops.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/sd_ops.c 2008-01-29 03:26:08.000000000 -0800 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/sd_ops.h ++ * linux/drivers/mmc/core/sd_ops.h + * + * Copyright 2006-2007 Pierre Ossman + * +@@ -21,11 +21,40 @@ + #include "core.h" + #include "sd_ops.h" + ++static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) ++{ ++ int err; ++ struct mmc_command cmd; ++ ++ BUG_ON(!host); ++ BUG_ON(card && (card->host != host)); ++ ++ cmd.opcode = MMC_APP_CMD; ++ ++ if (card) { ++ cmd.arg = card->rca << 16; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; ++ } else { ++ cmd.arg = 0; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_BCR; ++ } ++ ++ err = mmc_wait_for_cmd(host, &cmd, 0); ++ if (err) ++ return err; ++ ++ /* Check that card supported application commands */ ++ if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD)) ++ return -EOPNOTSUPP; ++ ++ return 0; ++} ++ + /** + * mmc_wait_for_app_cmd - start an application command and wait for + completion + * @host: MMC host to start command +- * @rca: RCA to send MMC_APP_CMD to ++ * @card: Card to send MMC_APP_CMD to + * @cmd: MMC command to start + * @retries: maximum number of retries + * +@@ -44,7 +73,7 @@ + BUG_ON(!cmd); + BUG_ON(retries < 0); + +- err = MMC_ERR_INVALID; ++ err = -EIO; + + /* + * We have to resend MMC_APP_CMD for each attempt so +@@ -54,8 +83,14 @@ + memset(&mrq, 0, sizeof(struct mmc_request)); + + err = mmc_app_cmd(host, card); +- if (err != MMC_ERR_NONE) ++ if (err) { ++ /* no point in retrying; no APP commands allowed */ ++ if (mmc_host_is_spi(host)) { ++ if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND) ++ break; ++ } + continue; ++ } + + memset(&mrq, 0, sizeof(struct mmc_request)); + +@@ -68,8 +103,14 @@ + mmc_wait_for_req(host, &mrq); + + err = cmd->error; +- if (cmd->error == MMC_ERR_NONE) ++ if (!cmd->error) + break; ++ ++ /* no point in retrying illegal APP commands */ ++ if (mmc_host_is_spi(host)) { ++ if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND) ++ break; ++ } + } + + return err; +@@ -77,35 +118,6 @@ + + EXPORT_SYMBOL(mmc_wait_for_app_cmd); + +-int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) +-{ +- int err; +- struct mmc_command cmd; +- +- BUG_ON(!host); +- BUG_ON(card && (card->host != host)); +- +- cmd.opcode = MMC_APP_CMD; +- +- if (card) { +- cmd.arg = card->rca << 16; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; +- } else { +- cmd.arg = 0; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR; +- } +- +- err = mmc_wait_for_cmd(host, &cmd, 0); +- if (err != MMC_ERR_NONE) +- return err; +- +- /* Check that card supported application commands */ +- if (!(cmd.resp[0] & R1_APP_CMD)) +- return MMC_ERR_FAILED; +- +- return MMC_ERR_NONE; +-} +- + int mmc_app_set_bus_width(struct mmc_card *card, int width) + { + int err; +@@ -127,14 +139,14 @@ + cmd.arg = SD_BUS_WIDTH_4; + break; + default: +- return MMC_ERR_INVALID; ++ return -EINVAL; + } + + err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + +- return MMC_ERR_NONE; ++ return 0; + } + + int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) +@@ -147,23 +159,36 @@ + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_APP_OP_COND; +- cmd.arg = ocr; +- cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; ++ if (mmc_host_is_spi(host)) ++ cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */ ++ else ++ cmd.arg = ocr; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR; + + for (i = 100; i; i--) { + err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + break; + +- if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) ++ /* if we're just probing, do a single pass */ ++ if (ocr == 0) + break; + +- err = MMC_ERR_TIMEOUT; ++ /* otherwise wait until reset completes */ ++ if (mmc_host_is_spi(host)) { ++ if (!(cmd.resp[0] & R1_SPI_IDLE)) ++ break; ++ } else { ++ if (cmd.resp[0] & MMC_CARD_BUSY) ++ break; ++ } ++ ++ err = -ETIMEDOUT; + + mmc_delay(10); + } + +- if (rocr) ++ if (rocr && !mmc_host_is_spi(host)) + *rocr = cmd.resp[0]; + + return err; +@@ -174,6 +199,7 @@ + struct mmc_command cmd; + int err; + static const u8 test_pattern = 0xAA; ++ u8 result_pattern; + + /* + * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND +@@ -182,16 +208,21 @@ + */ + cmd.opcode = SD_SEND_IF_COND; + cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; +- cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; ++ cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, 0); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + +- if ((cmd.resp[0] & 0xFF) != test_pattern) +- return MMC_ERR_FAILED; ++ if (mmc_host_is_spi(host)) ++ result_pattern = cmd.resp[1] & 0xFF; ++ else ++ result_pattern = cmd.resp[0] & 0xFF; + +- return MMC_ERR_NONE; ++ if (result_pattern != test_pattern) ++ return -EIO; ++ ++ return 0; + } + + int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca) +@@ -209,12 +240,12 @@ + cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + + *rca = cmd.resp[0] >> 16; + +- return MMC_ERR_NONE; ++ return 0; + } + + int mmc_app_send_scr(struct mmc_card *card, u32 *scr) +@@ -229,8 +260,10 @@ + BUG_ON(!card->host); + BUG_ON(!scr); + ++ /* NOTE: caller guarantees scr is heap-allocated */ ++ + err = mmc_app_cmd(card->host, card); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + + memset(&mrq, 0, sizeof(struct mmc_request)); +@@ -242,7 +275,7 @@ + + cmd.opcode = SD_APP_SEND_SCR; + cmd.arg = 0; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = 8; + data.blocks = 1; +@@ -252,19 +285,19 @@ + + sg_init_one(&sg, scr, 8); + +- mmc_set_data_timeout(&data, card, 0); ++ mmc_set_data_timeout(&data, card); + + mmc_wait_for_req(card->host, &mrq); + +- if (cmd.error != MMC_ERR_NONE) ++ if (cmd.error) + return cmd.error; +- if (data.error != MMC_ERR_NONE) ++ if (data.error) + return data.error; + +- scr[0] = ntohl(scr[0]); +- scr[1] = ntohl(scr[1]); ++ scr[0] = be32_to_cpu(scr[0]); ++ scr[1] = be32_to_cpu(scr[1]); + +- return MMC_ERR_NONE; ++ return 0; + } + + int mmc_sd_switch(struct mmc_card *card, int mode, int group, +@@ -278,6 +311,8 @@ + BUG_ON(!card); + BUG_ON(!card->host); + ++ /* NOTE: caller guarantees resp is heap-allocated */ ++ + mode = !!mode; + value &= 0xF; + +@@ -292,7 +327,7 @@ + cmd.arg = mode << 31 | 0x00FFFFFF; + cmd.arg &= ~(0xF << (group * 4)); + cmd.arg |= value << (group * 4); +- cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = 64; + data.blocks = 1; +@@ -302,15 +337,15 @@ + + sg_init_one(&sg, resp, 64); + +- mmc_set_data_timeout(&data, card, 0); ++ mmc_set_data_timeout(&data, card); + + mmc_wait_for_req(card->host, &mrq); + +- if (cmd.error != MMC_ERR_NONE) ++ if (cmd.error) + return cmd.error; +- if (data.error != MMC_ERR_NONE) ++ if (data.error) + return data.error; + +- return MMC_ERR_NONE; ++ return 0; + } + +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/sd_ops.h linux-2.6.22.18-mv/drivers/mmc/core/sd_ops.h +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/sd_ops.h 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/sd_ops.h 2008-01-29 03:26:08.000000000 -0800 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/sd_ops.h ++ * linux/drivers/mmc/core/sd_ops.h + * + * Copyright 2006-2007 Pierre Ossman + * +@@ -12,7 +12,6 @@ + #ifndef _MMC_SD_OPS_H + #define _MMC_SD_OPS_H + +-int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card); + int mmc_app_set_bus_width(struct mmc_card *card, int width); + int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); + int mmc_send_if_cond(struct mmc_host *host, u32 ocr); +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/sysfs.c linux-2.6.22.18-mv/drivers/mmc/core/sysfs.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/sysfs.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/sysfs.c 2008-01-29 03:26:10.000000000 -0800 +@@ -2,6 +2,7 @@ + * linux/drivers/mmc/core/sysfs.c + * + * Copyright (C) 2003 Russell King, All Rights Reserved. ++ * Copyright 2007 Pierre Ossman + * + * 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 +@@ -9,352 +10,34 @@ + * + * MMC sysfs/driver model support. + */ +-#include +-#include + #include +-#include +-#include + + #include +-#include + + #include "sysfs.h" + +-#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) +-#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) +-#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) +- +-#define MMC_ATTR(name, fmt, args...) \ +-static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \ +-{ \ +- struct mmc_card *card = dev_to_mmc_card(dev); \ +- return sprintf(buf, fmt, args); \ +-} +- +-MMC_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], +- card->raw_cid[2], card->raw_cid[3]); +-MMC_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], +- card->raw_csd[2], card->raw_csd[3]); +-MMC_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]); +-MMC_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); +-MMC_ATTR(fwrev, "0x%x\n", card->cid.fwrev); +-MMC_ATTR(hwrev, "0x%x\n", card->cid.hwrev); +-MMC_ATTR(manfid, "0x%06x\n", card->cid.manfid); +-MMC_ATTR(name, "%s\n", card->cid.prod_name); +-MMC_ATTR(oemid, "0x%04x\n", card->cid.oemid); +-MMC_ATTR(serial, "0x%08x\n", card->cid.serial); +- +-#define MMC_ATTR_RO(name) __ATTR(name, S_IRUGO, mmc_##name##_show, NULL) +- +-static struct device_attribute mmc_dev_attrs[] = { +- MMC_ATTR_RO(cid), +- MMC_ATTR_RO(csd), +- MMC_ATTR_RO(date), +- MMC_ATTR_RO(fwrev), +- MMC_ATTR_RO(hwrev), +- MMC_ATTR_RO(manfid), +- MMC_ATTR_RO(name), +- MMC_ATTR_RO(oemid), +- MMC_ATTR_RO(serial), +- __ATTR_NULL +-}; +- +-static struct device_attribute mmc_dev_attr_scr = MMC_ATTR_RO(scr); +- +- +-static void mmc_release_card(struct device *dev) +-{ +- struct mmc_card *card = dev_to_mmc_card(dev); +- +- kfree(card); +-} +- +-/* +- * This currently matches any MMC driver to any MMC card - drivers +- * themselves make the decision whether to drive this card in their +- * probe method. +- */ +-static int mmc_bus_match(struct device *dev, struct device_driver *drv) +-{ +- return 1; +-} +- +-static int +-mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, +- int buf_size) +-{ +- struct mmc_card *card = dev_to_mmc_card(dev); +- char ccc[13]; +- int retval = 0, i = 0, length = 0; +- +-#define add_env(fmt,val) do { \ +- retval = add_uevent_var(envp, num_envp, &i, \ +- buf, buf_size, &length, \ +- fmt, val); \ +- if (retval) \ +- return retval; \ +-} while (0); +- +- for (i = 0; i < 12; i++) +- ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0'; +- ccc[12] = '\0'; +- +- add_env("MMC_CCC=%s", ccc); +- add_env("MMC_MANFID=%06x", card->cid.manfid); +- add_env("MMC_NAME=%s", mmc_card_name(card)); +- add_env("MMC_OEMID=%04x", card->cid.oemid); +-#undef add_env +- envp[i] = NULL; +- +- return 0; +-} +- +-static int mmc_bus_suspend(struct device *dev, pm_message_t state) ++int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs) + { +- struct mmc_driver *drv = to_mmc_driver(dev->driver); +- struct mmc_card *card = dev_to_mmc_card(dev); +- int ret = 0; +- +- if (dev->driver && drv->suspend) +- ret = drv->suspend(card, state); +- return ret; +-} ++ int error = 0; ++ int i; + +-static int mmc_bus_resume(struct device *dev) +-{ +- struct mmc_driver *drv = to_mmc_driver(dev->driver); +- struct mmc_card *card = dev_to_mmc_card(dev); +- int ret = 0; +- +- if (dev->driver && drv->resume) +- ret = drv->resume(card); +- return ret; +-} +- +-static int mmc_bus_probe(struct device *dev) +-{ +- struct mmc_driver *drv = to_mmc_driver(dev->driver); +- struct mmc_card *card = dev_to_mmc_card(dev); +- +- return drv->probe(card); +-} +- +-static int mmc_bus_remove(struct device *dev) +-{ +- struct mmc_driver *drv = to_mmc_driver(dev->driver); +- struct mmc_card *card = dev_to_mmc_card(dev); +- +- drv->remove(card); +- +- return 0; +-} +- +-static struct bus_type mmc_bus_type = { +- .name = "mmc", +- .dev_attrs = mmc_dev_attrs, +- .match = mmc_bus_match, +- .uevent = mmc_bus_uevent, +- .probe = mmc_bus_probe, +- .remove = mmc_bus_remove, +- .suspend = mmc_bus_suspend, +- .resume = mmc_bus_resume, +-}; +- +-/** +- * mmc_register_driver - register a media driver +- * @drv: MMC media driver +- */ +-int mmc_register_driver(struct mmc_driver *drv) +-{ +- drv->drv.bus = &mmc_bus_type; +- return driver_register(&drv->drv); +-} +- +-EXPORT_SYMBOL(mmc_register_driver); +- +-/** +- * mmc_unregister_driver - unregister a media driver +- * @drv: MMC media driver +- */ +-void mmc_unregister_driver(struct mmc_driver *drv) +-{ +- drv->drv.bus = &mmc_bus_type; +- driver_unregister(&drv->drv); +-} +- +-EXPORT_SYMBOL(mmc_unregister_driver); +- +- +-/* +- * Internal function. Initialise a MMC card structure. +- */ +-void mmc_init_card(struct mmc_card *card, struct mmc_host *host) +-{ +- memset(card, 0, sizeof(struct mmc_card)); +- card->host = host; +- device_initialize(&card->dev); +- card->dev.parent = mmc_classdev(host); +- card->dev.bus = &mmc_bus_type; +- card->dev.release = mmc_release_card; +-} +- +-/* +- * Internal function. Register a new MMC card with the driver model. +- */ +-int mmc_register_card(struct mmc_card *card) +-{ +- int ret; +- +- snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), +- "%s:%04x", mmc_hostname(card->host), card->rca); +- +- ret = device_add(&card->dev); +- if (ret == 0) { +- if (mmc_card_sd(card)) { +- ret = device_create_file(&card->dev, &mmc_dev_attr_scr); +- if (ret) +- device_del(&card->dev); ++ for (i = 0; attr_name(attrs[i]); i++) { ++ error = device_create_file(&card->dev, &attrs[i]); ++ if (error) { ++ while (--i >= 0) ++ device_remove_file(&card->dev, &attrs[i]); ++ break; + } + } +- if (ret == 0) +- mmc_card_set_present(card); +- return ret; +-} +- +-/* +- * Internal function. Unregister a new MMC card with the +- * driver model, and (eventually) free it. +- */ +-void mmc_remove_card(struct mmc_card *card) +-{ +- if (mmc_card_present(card)) { +- if (mmc_card_sd(card)) +- device_remove_file(&card->dev, &mmc_dev_attr_scr); +- +- device_del(&card->dev); +- } +- +- put_device(&card->dev); +-} +- +- +-static void mmc_host_classdev_release(struct device *dev) +-{ +- struct mmc_host *host = cls_dev_to_mmc_host(dev); +- kfree(host); +-} +- +-static struct class mmc_host_class = { +- .name = "mmc_host", +- .dev_release = mmc_host_classdev_release, +-}; +- +-static DEFINE_IDR(mmc_host_idr); +-static DEFINE_SPINLOCK(mmc_host_lock); +- +-/* +- * Internal function. Allocate a new MMC host. +- */ +-struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev) +-{ +- struct mmc_host *host; +- +- host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); +- if (host) { +- memset(host, 0, sizeof(struct mmc_host) + extra); +- +- host->parent = dev; +- host->class_dev.parent = dev; +- host->class_dev.class = &mmc_host_class; +- device_initialize(&host->class_dev); +- } +- +- return host; +-} +- +-/* +- * Internal function. Register a new MMC host with the MMC class. +- */ +-int mmc_add_host_sysfs(struct mmc_host *host) +-{ +- int err; +- +- if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) +- return -ENOMEM; +- +- spin_lock(&mmc_host_lock); +- err = idr_get_new(&mmc_host_idr, host, &host->index); +- spin_unlock(&mmc_host_lock); +- if (err) +- return err; +- +- snprintf(host->class_dev.bus_id, BUS_ID_SIZE, +- "mmc%d", host->index); +- +- return device_add(&host->class_dev); +-} +- +-/* +- * Internal function. Unregister a MMC host with the MMC class. +- */ +-void mmc_remove_host_sysfs(struct mmc_host *host) +-{ +- device_del(&host->class_dev); + +- spin_lock(&mmc_host_lock); +- idr_remove(&mmc_host_idr, host->index); +- spin_unlock(&mmc_host_lock); ++ return error; + } + +-/* +- * Internal function. Free a MMC host. +- */ +-void mmc_free_host_sysfs(struct mmc_host *host) ++void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs) + { +- put_device(&host->class_dev); +-} ++ int i; + +-static struct workqueue_struct *workqueue; +- +-/* +- * Internal function. Schedule delayed work in the MMC work queue. +- */ +-int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay) +-{ +- return queue_delayed_work(workqueue, work, delay); +-} +- +-/* +- * Internal function. Flush all scheduled work from the MMC work queue. +- */ +-void mmc_flush_scheduled_work(void) +-{ +- flush_workqueue(workqueue); +-} +- +-static int __init mmc_init(void) +-{ +- int ret; +- +- workqueue = create_singlethread_workqueue("kmmcd"); +- if (!workqueue) +- return -ENOMEM; +- +- ret = bus_register(&mmc_bus_type); +- if (ret == 0) { +- ret = class_register(&mmc_host_class); +- if (ret) +- bus_unregister(&mmc_bus_type); +- } +- return ret; +-} +- +-static void __exit mmc_exit(void) +-{ +- class_unregister(&mmc_host_class); +- bus_unregister(&mmc_bus_type); +- destroy_workqueue(workqueue); ++ for (i = 0; attr_name(attrs[i]); i++) ++ device_remove_file(&card->dev, &attrs[i]); + } + +-module_init(mmc_init); +-module_exit(mmc_exit); +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/core/sysfs.h linux-2.6.22.18-mv/drivers/mmc/core/sysfs.h +--- linux-2.6.22.18-mv-clean/drivers/mmc/core/sysfs.h 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/core/sysfs.h 2008-01-29 03:26:10.000000000 -0800 +@@ -11,17 +11,16 @@ + #ifndef _MMC_CORE_SYSFS_H + #define _MMC_CORE_SYSFS_H + +-void mmc_init_card(struct mmc_card *card, struct mmc_host *host); +-int mmc_register_card(struct mmc_card *card); +-void mmc_remove_card(struct mmc_card *card); ++#define MMC_ATTR_FN(name, fmt, args...) \ ++static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct mmc_card *card = container_of(dev, struct mmc_card, dev);\ ++ return sprintf(buf, fmt, args); \ ++} + +-struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev); +-int mmc_add_host_sysfs(struct mmc_host *host); +-void mmc_remove_host_sysfs(struct mmc_host *host); +-void mmc_free_host_sysfs(struct mmc_host *host); ++#define MMC_ATTR_RO(name) __ATTR(name, S_IRUGO, mmc_##name##_show, NULL) + +-int mmc_schedule_work(struct work_struct *work); +-int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay); +-void mmc_flush_scheduled_work(void); ++int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs); ++void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs); + + #endif +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/at91_mci.c linux-2.6.22.18-mv/drivers/mmc/host/at91_mci.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/at91_mci.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/at91_mci.c 2008-01-29 03:26:10.000000000 -0800 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/at91_mci.c - ATMEL AT91 MCI Driver ++ * linux/drivers/mmc/host/at91_mci.c - ATMEL AT91 MCI Driver + * + * Copyright (C) 2005 Cougar Creek Computing Devices Ltd, All Rights Reserved + * +@@ -78,14 +78,12 @@ + + #define DRIVER_NAME "at91_mci" + +-#undef SUPPORT_4WIRE +- + #define FL_SENT_COMMAND (1 << 0) + #define FL_SENT_STOP (1 << 1) + + #define AT91_MCI_ERRORS (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE \ + | AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE \ +- | AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE) ++ | AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE) + + #define at91_mci_read(host, reg) __raw_readl((host)->baseaddr + (reg)) + #define at91_mci_write(host, reg, val) __raw_writel((val), (host)->baseaddr + (reg)) +@@ -131,7 +129,7 @@ + /* + * Copy from sg to a dma block - used for transfers + */ +-static inline void at91mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data) ++static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data) + { + unsigned int len, i, size; + unsigned *dmabuf = host->buffer; +@@ -151,7 +149,7 @@ + + sg = &data->sg[i]; + +- sgbuffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; ++ sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; + amount = min(size, sg->length); + size -= amount; + +@@ -180,7 +178,7 @@ + /* + * Prepare a dma read + */ +-static void at91mci_pre_dma_read(struct at91mci_host *host) ++static void at91_mci_pre_dma_read(struct at91mci_host *host) + { + int i; + struct scatterlist *sg; +@@ -228,7 +226,7 @@ + sg = &data->sg[host->transfer_index++]; + pr_debug("sg = %p\n", sg); + +- sg->dma_address = dma_map_page(NULL, sg->page, sg->offset, sg->length, DMA_FROM_DEVICE); ++ sg->dma_address = dma_map_page(NULL, sg_page(sg), sg->offset, sg->length, DMA_FROM_DEVICE); + + pr_debug("dma address = %08X, length = %d\n", sg->dma_address, sg->length); + +@@ -248,7 +246,7 @@ + /* + * Handle after a dma read + */ +-static void at91mci_post_dma_read(struct at91mci_host *host) ++static void at91_mci_post_dma_read(struct at91mci_host *host) + { + struct mmc_command *cmd; + struct mmc_data *data; +@@ -268,8 +266,6 @@ + } + + while (host->in_use_index < host->transfer_index) { +- unsigned int *buffer; +- + struct scatterlist *sg; + + pr_debug("finishing index %d\n", host->in_use_index); +@@ -280,29 +276,31 @@ + + dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE); + +- /* Swap the contents of the buffer */ +- buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; +- pr_debug("buffer = %p, length = %d\n", buffer, sg->length); +- + data->bytes_xfered += sg->length; + + if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ ++ unsigned int *buffer; + int index; + ++ /* Swap the contents of the buffer */ ++ buffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; ++ pr_debug("buffer = %p, length = %d\n", buffer, sg->length); ++ + for (index = 0; index < (sg->length / 4); index++) + buffer[index] = swab32(buffer[index]); ++ ++ kunmap_atomic(buffer, KM_BIO_SRC_IRQ); + } + +- kunmap_atomic(buffer, KM_BIO_SRC_IRQ); +- flush_dcache_page(sg->page); ++ flush_dcache_page(sg_page(sg)); + } + + /* Is there another transfer to trigger? */ + if (host->transfer_index < data->sg_len) +- at91mci_pre_dma_read(host); ++ at91_mci_pre_dma_read(host); + else { ++ at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); +- at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); + } + + pr_debug("post dma read done\n"); +@@ -323,7 +321,6 @@ + + /* Now wait for cmd ready */ + at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE); +- at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); + + cmd = host->cmd; + if (!cmd) return; +@@ -331,18 +328,53 @@ + data = cmd->data; + if (!data) return; + ++ if (cmd->data->blocks > 1) { ++ pr_debug("multiple write : wait for BLKE...\n"); ++ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE); ++ } else ++ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); ++ + data->bytes_xfered = host->total_length; + } + ++/*Handle after command sent ready*/ ++static int at91_mci_handle_cmdrdy(struct at91mci_host *host) ++{ ++ if (!host->cmd) ++ return 1; ++ else if (!host->cmd->data) { ++ if (host->flags & FL_SENT_STOP) { ++ /*After multi block write, we must wait for NOTBUSY*/ ++ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); ++ } else return 1; ++ } else if (host->cmd->data->flags & MMC_DATA_WRITE) { ++ /*After sendding multi-block-write command, start DMA transfer*/ ++ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE); ++ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE); ++ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); ++ } ++ ++ /* command not completed, have to wait */ ++ return 0; ++} ++ ++ + /* + * Enable the controller + */ + static void at91_mci_enable(struct at91mci_host *host) + { ++ unsigned int mr; ++ + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); + at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); + at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); +- at91_mci_write(host, AT91_MCI_MR, AT91_MCI_PDCMODE | 0x34a); ++ mr = AT91_MCI_PDCMODE | 0x34a; ++ ++ if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) ++ mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF; ++ ++ at91_mci_write(host, AT91_MCI_MR, mr); + + /* use Slot A or B (only one at same time) */ + at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b); +@@ -358,9 +390,8 @@ + + /* + * Send a command +- * return the interrupts to enable + */ +-static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd) ++static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd) + { + unsigned int cmdr, mr; + unsigned int block_length; +@@ -371,8 +402,7 @@ + + host->cmd = cmd; + +- /* Not sure if this is needed */ +-#if 0 ++ /* Needed for leaving busy state before CMD1 */ + if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { + pr_debug("Clearing timeout\n"); + at91_mci_write(host, AT91_MCI_ARGR, 0); +@@ -382,7 +412,7 @@ + pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR)); + } + } +-#endif ++ + cmdr = cmd->opcode; + + if (mmc_resp_type(cmd) == MMC_RSP_NONE) +@@ -398,6 +428,14 @@ + } + + if (data) { ++ ++ if ( data->blksz & 0x3 ) { ++ pr_debug("Unsupported block size\n"); ++ cmd->error = -EINVAL; ++ mmc_request_done(host->mmc, host->request); ++ return; ++ } ++ + block_length = data->blksz; + blocks = data->blocks; + +@@ -409,7 +447,7 @@ + + if (data->flags & MMC_DATA_STREAM) + cmdr |= AT91_MCI_TRTYP_STREAM; +- if (data->flags & MMC_DATA_MULTI) ++ if (data->blocks > 1) + cmdr |= AT91_MCI_TRTYP_MULTIPLE; + } + else { +@@ -439,50 +477,48 @@ + at91_mci_write(host, ATMEL_PDC_TCR, 0); + at91_mci_write(host, ATMEL_PDC_TNPR, 0); + at91_mci_write(host, ATMEL_PDC_TNCR, 0); ++ ier = AT91_MCI_CMDRDY; ++ } else { ++ /* zero block length and PDC mode */ ++ mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; ++ at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE); ++ ++ /* ++ * Disable the PDC controller ++ */ ++ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); + +- at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); +- at91_mci_write(host, AT91_MCI_CMDR, cmdr); +- return AT91_MCI_CMDRDY; +- } +- +- mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; /* zero block length and PDC mode */ +- at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE); +- +- /* +- * Disable the PDC controller +- */ +- at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); +- +- if (cmdr & AT91_MCI_TRCMD_START) { +- data->bytes_xfered = 0; +- host->transfer_index = 0; +- host->in_use_index = 0; +- if (cmdr & AT91_MCI_TRDIR) { +- /* +- * Handle a read +- */ +- host->buffer = NULL; +- host->total_length = 0; ++ if (cmdr & AT91_MCI_TRCMD_START) { ++ data->bytes_xfered = 0; ++ host->transfer_index = 0; ++ host->in_use_index = 0; ++ if (cmdr & AT91_MCI_TRDIR) { ++ /* ++ * Handle a read ++ */ ++ host->buffer = NULL; ++ host->total_length = 0; + +- at91mci_pre_dma_read(host); +- ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; +- } +- else { +- /* +- * Handle a write +- */ +- host->total_length = block_length * blocks; +- host->buffer = dma_alloc_coherent(NULL, +- host->total_length, +- &host->physical_address, GFP_KERNEL); +- +- at91mci_sg_to_dma(host, data); +- +- pr_debug("Transmitting %d bytes\n", host->total_length); +- +- at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); +- at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4); +- ier = AT91_MCI_TXBUFE; ++ at91_mci_pre_dma_read(host); ++ ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; ++ } ++ else { ++ /* ++ * Handle a write ++ */ ++ host->total_length = block_length * blocks; ++ host->buffer = dma_alloc_coherent(NULL, ++ host->total_length, ++ &host->physical_address, GFP_KERNEL); ++ ++ at91_mci_sg_to_dma(host, data); ++ ++ pr_debug("Transmitting %d bytes\n", host->total_length); ++ ++ at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); ++ at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4); ++ ier = AT91_MCI_CMDRDY; ++ } + } + } + +@@ -497,39 +533,24 @@ + if (cmdr & AT91_MCI_TRCMD_START) { + if (cmdr & AT91_MCI_TRDIR) + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); +- else +- at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); + } +- return ier; +-} +- +-/* +- * Wait for a command to complete +- */ +-static void at91mci_process_command(struct at91mci_host *host, struct mmc_command *cmd) +-{ +- unsigned int ier; +- +- ier = at91_mci_send_command(host, cmd); + +- pr_debug("setting ier to %08X\n", ier); +- +- /* Stop on errors or the required value */ ++ /* Enable selected interrupts */ + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier); + } + + /* + * Process the next step in the request + */ +-static void at91mci_process_next(struct at91mci_host *host) ++static void at91_mci_process_next(struct at91mci_host *host) + { + if (!(host->flags & FL_SENT_COMMAND)) { + host->flags |= FL_SENT_COMMAND; +- at91mci_process_command(host, host->request->cmd); ++ at91_mci_send_command(host, host->request->cmd); + } + else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) { + host->flags |= FL_SENT_STOP; +- at91mci_process_command(host, host->request->stop); ++ at91_mci_send_command(host, host->request->stop); + } + else + mmc_request_done(host->mmc, host->request); +@@ -538,7 +559,7 @@ + /* + * Handle a command that has been completed + */ +-static void at91mci_completed_command(struct at91mci_host *host) ++static void at91_mci_completed_command(struct at91mci_host *host) + { + struct mmc_command *cmd = host->cmd; + unsigned int status; +@@ -560,30 +581,26 @@ + pr_debug("Status = %08X [%08X %08X %08X %08X]\n", + status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); + +- if (status & (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE | +- AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE | +- AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)) { ++ if (status & AT91_MCI_ERRORS) { + if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) { +- cmd->error = MMC_ERR_NONE; ++ cmd->error = 0; + } + else { + if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE)) +- cmd->error = MMC_ERR_TIMEOUT; ++ cmd->error = -ETIMEDOUT; + else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE)) +- cmd->error = MMC_ERR_BADCRC; +- else if (status & (AT91_MCI_OVRE | AT91_MCI_UNRE)) +- cmd->error = MMC_ERR_FIFO; ++ cmd->error = -EILSEQ; + else +- cmd->error = MMC_ERR_FAILED; ++ cmd->error = -EIO; + + pr_debug("Error detected and set to %d (cmd = %d, retries = %d)\n", + cmd->error, cmd->opcode, cmd->retries); + } + } + else +- cmd->error = MMC_ERR_NONE; ++ cmd->error = 0; + +- at91mci_process_next(host); ++ at91_mci_process_next(host); + } + + /* +@@ -595,7 +612,7 @@ + host->request = mrq; + host->flags = 0; + +- at91mci_process_next(host); ++ at91_mci_process_next(host); + } + + /* +@@ -663,15 +680,15 @@ + + int_status = at91_mci_read(host, AT91_MCI_SR); + int_mask = at91_mci_read(host, AT91_MCI_IMR); +- ++ + pr_debug("MCI irq: status = %08X, %08X, %08X\n", int_status, int_mask, + int_status & int_mask); +- ++ + int_status = int_status & int_mask; + + if (int_status & AT91_MCI_ERRORS) { + completed = 1; +- ++ + if (int_status & AT91_MCI_UNRE) + pr_debug("MMC: Underrun error\n"); + if (int_status & AT91_MCI_OVRE) +@@ -698,29 +715,33 @@ + at91_mci_handle_transmitted(host); + } + ++ if (int_status & AT91_MCI_ENDRX) { ++ pr_debug("ENDRX\n"); ++ at91_mci_post_dma_read(host); ++ } ++ + if (int_status & AT91_MCI_RXBUFF) { + pr_debug("RX buffer full\n"); +- at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY); ++ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); ++ at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_RXBUFF | AT91_MCI_ENDRX); ++ completed = 1; + } + + if (int_status & AT91_MCI_ENDTX) + pr_debug("Transmit has ended\n"); + +- if (int_status & AT91_MCI_ENDRX) { +- pr_debug("Receive has ended\n"); +- at91mci_post_dma_read(host); +- } +- + if (int_status & AT91_MCI_NOTBUSY) { + pr_debug("Card is ready\n"); +- at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY); ++ completed = 1; + } + + if (int_status & AT91_MCI_DTIP) + pr_debug("Data transfer in progress\n"); + +- if (int_status & AT91_MCI_BLKE) ++ if (int_status & AT91_MCI_BLKE) { + pr_debug("Block transfer has ended\n"); ++ completed = 1; ++ } + + if (int_status & AT91_MCI_TXRDY) + pr_debug("Ready to transmit\n"); +@@ -730,14 +751,14 @@ + + if (int_status & AT91_MCI_CMDRDY) { + pr_debug("Command ready\n"); +- completed = 1; ++ completed = at91_mci_handle_cmdrdy(host); + } + } + + if (completed) { + pr_debug("Completed command\n"); + at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); +- at91mci_completed_command(host); ++ at91_mci_completed_command(host); + } else + at91_mci_write(host, AT91_MCI_IDR, int_status); + +@@ -819,7 +840,6 @@ + mmc->f_min = 375000; + mmc->f_max = 25000000; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; +- mmc->caps = MMC_CAP_BYTEBLOCK; + + mmc->max_blk_size = 4095; + mmc->max_blk_count = mmc->max_req_size; +@@ -830,11 +850,11 @@ + host->bus_mode = 0; + host->board = pdev->dev.platform_data; + if (host->board->wire4) { +-#ifdef SUPPORT_4WIRE +- mmc->caps |= MMC_CAP_4_BIT_DATA; +-#else +- printk("AT91 MMC: 4 wire bus mode not supported by this driver - using 1 wire\n"); +-#endif ++ if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) ++ mmc->caps |= MMC_CAP_4_BIT_DATA; ++ else ++ printk("AT91 MMC: 4 wire bus mode not supported" ++ " - using 1 wire\n"); + } + + /* +@@ -886,8 +906,10 @@ + /* + * Add host to MMC layer + */ +- if (host->board->det_pin) ++ if (host->board->det_pin) { + host->present = !at91_get_gpio_value(host->board->det_pin); ++ device_init_wakeup(&pdev->dev, 1); ++ } + else + host->present = -1; + +@@ -922,7 +944,8 @@ + + host = mmc_priv(mmc); + +- if (host->present != -1) { ++ if (host->board->det_pin) { ++ device_init_wakeup(&pdev->dev, 0); + free_irq(host->board->det_pin, host); + cancel_delayed_work(&host->mmc->detect); + } +@@ -949,8 +972,12 @@ + static int at91_mci_suspend(struct platform_device *pdev, pm_message_t state) + { + struct mmc_host *mmc = platform_get_drvdata(pdev); ++ struct at91mci_host *host = mmc_priv(mmc); + int ret = 0; + ++ if (host->board->det_pin && device_may_wakeup(&pdev->dev)) ++ enable_irq_wake(host->board->det_pin); ++ + if (mmc) + ret = mmc_suspend_host(mmc, state); + +@@ -960,8 +987,12 @@ + static int at91_mci_resume(struct platform_device *pdev) + { + struct mmc_host *mmc = platform_get_drvdata(pdev); ++ struct at91mci_host *host = mmc_priv(mmc); + int ret = 0; + ++ if (host->board->det_pin && device_may_wakeup(&pdev->dev)) ++ disable_irq_wake(host->board->det_pin); ++ + if (mmc) + ret = mmc_resume_host(mmc); + +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/au1xmmc.c linux-2.6.22.18-mv/drivers/mmc/host/au1xmmc.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/au1xmmc.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/au1xmmc.c 2008-01-29 03:26:10.000000000 -0800 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/au1xmmc.c - AU1XX0 MMC driver ++ * linux/drivers/mmc/host/au1xmmc.c - AU1XX0 MMC driver + * + * Copyright (c) 2005, Advanced Micro Devices, Inc. + * +@@ -40,13 +40,13 @@ + #include + #include + #include ++#include + + #include + #include + #include + #include + #include +-#include + + #include + #include "au1xmmc.h" +@@ -186,7 +186,7 @@ + } + + static int au1xmmc_send_command(struct au1xmmc_host *host, int wait, +- struct mmc_command *cmd, unsigned int flags) ++ struct mmc_command *cmd, struct mmc_data *data) + { + u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT); + +@@ -208,19 +208,21 @@ + default: + printk(KERN_INFO "au1xmmc: unhandled response type %02x\n", + mmc_resp_type(cmd)); +- return MMC_ERR_INVALID; ++ return -EINVAL; + } + +- if (flags & MMC_DATA_READ) { +- if (flags & MMC_DATA_MULTI) +- mmccmd |= SD_CMD_CT_4; +- else +- mmccmd |= SD_CMD_CT_2; +- } else if (flags & MMC_DATA_WRITE) { +- if (flags & MMC_DATA_MULTI) +- mmccmd |= SD_CMD_CT_3; +- else +- mmccmd |= SD_CMD_CT_1; ++ if (data) { ++ if (data->flags & MMC_DATA_READ) { ++ if (data->blocks > 1) ++ mmccmd |= SD_CMD_CT_4; ++ else ++ mmccmd |= SD_CMD_CT_2; ++ } else if (data->flags & MMC_DATA_WRITE) { ++ if (data->blocks > 1) ++ mmccmd |= SD_CMD_CT_3; ++ else ++ mmccmd |= SD_CMD_CT_1; ++ } + } + + au_writel(cmd->arg, HOST_CMDARG(host)); +@@ -253,7 +255,7 @@ + IRQ_ON(host, SD_CONFIG_CR); + } + +- return MMC_ERR_NONE; ++ return 0; + } + + static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status) +@@ -278,7 +280,7 @@ + while((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB)) + status = au_readl(HOST_STATUS(host)); + +- data->error = MMC_ERR_NONE; ++ data->error = 0; + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma.dir); + + /* Process any errors */ +@@ -288,14 +290,14 @@ + crc |= ((status & 0x07) == 0x02) ? 0 : 1; + + if (crc) +- data->error = MMC_ERR_BADCRC; ++ data->error = -EILSEQ; + + /* Clear the CRC bits */ + au_writel(SD_STATUS_WC | SD_STATUS_RC, HOST_STATUS(host)); + + data->bytes_xfered = 0; + +- if (data->error == MMC_ERR_NONE) { ++ if (!data->error) { + if (host->flags & HOST_F_DMA) { + u32 chan = DMA_CHANNEL(host); + +@@ -338,7 +340,7 @@ + + /* This is the pointer to the data buffer */ + sg = &data->sg[host->pio.index]; +- sg_ptr = page_address(sg->page) + sg->offset + host->pio.offset; ++ sg_ptr = sg_virt(sg) + host->pio.offset; + + /* This is the space left inside the buffer */ + sg_len = data->sg[host->pio.index].length - host->pio.offset; +@@ -398,7 +400,7 @@ + + if (host->pio.index < host->dma.len) { + sg = &data->sg[host->pio.index]; +- sg_ptr = page_address(sg->page) + sg->offset + host->pio.offset; ++ sg_ptr = sg_virt(sg) + host->pio.offset; + + /* This is the space left inside the buffer */ + sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset; +@@ -475,7 +477,7 @@ + return; + + cmd = mrq->cmd; +- cmd->error = MMC_ERR_NONE; ++ cmd->error = 0; + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { +@@ -512,11 +514,11 @@ + /* Figure out errors */ + + if (status & (SD_STATUS_SC | SD_STATUS_WC | SD_STATUS_RC)) +- cmd->error = MMC_ERR_BADCRC; ++ cmd->error = -EILSEQ; + + trans = host->flags & (HOST_F_XMIT | HOST_F_RECV); + +- if (!trans || cmd->error != MMC_ERR_NONE) { ++ if (!trans || cmd->error) { + + IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA|SD_CONFIG_RF); + tasklet_schedule(&host->finish_task); +@@ -589,7 +591,7 @@ + data->sg_len, host->dma.dir); + + if (host->dma.len == 0) +- return MMC_ERR_TIMEOUT; ++ return -ETIMEDOUT; + + au_writel(data->blksz - 1, HOST_BLKSIZE(host)); + +@@ -611,14 +613,11 @@ + + if (host->flags & HOST_F_XMIT){ + ret = au1xxx_dbdma_put_source_flags(channel, +- (void *) (page_address(sg->page) + +- sg->offset), +- len, flags); ++ (void *) sg_virt(sg), len, flags); + } + else { + ret = au1xxx_dbdma_put_dest_flags(channel, +- (void *) (page_address(sg->page) + +- sg->offset), ++ (void *) sg_virt(sg), + len, flags); + } + +@@ -640,11 +639,11 @@ + //IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF); + } + +- return MMC_ERR_NONE; ++ return 0; + + dataerr: + dma_unmap_sg(mmc_dev(host->mmc),data->sg,data->sg_len,host->dma.dir); +- return MMC_ERR_TIMEOUT; ++ return -ETIMEDOUT; + } + + /* static void au1xmmc_request +@@ -656,7 +655,7 @@ + + struct au1xmmc_host *host = mmc_priv(mmc); + unsigned int flags = 0; +- int ret = MMC_ERR_NONE; ++ int ret = 0; + + WARN_ON(irqs_disabled()); + WARN_ON(host->status != HOST_S_IDLE); +@@ -672,10 +671,10 @@ + ret = au1xmmc_prepare_data(host, mrq->data); + } + +- if (ret == MMC_ERR_NONE) +- ret = au1xmmc_send_command(host, 0, mrq->cmd, flags); ++ if (!ret) ++ ret = au1xmmc_send_command(host, 0, mrq->cmd, mrq->data); + +- if (ret != MMC_ERR_NONE) { ++ if (ret) { + mrq->cmd->error = ret; + au1xmmc_finish_request(host); + } +@@ -764,10 +763,10 @@ + + if (host->mrq && (status & STATUS_TIMEOUT)) { + if (status & SD_STATUS_RAT) +- host->mrq->cmd->error = MMC_ERR_TIMEOUT; ++ host->mrq->cmd->error = -ETIMEDOUT; + + else if (status & SD_STATUS_DT) +- host->mrq->data->error = MMC_ERR_TIMEOUT; ++ host->mrq->data->error = -ETIMEDOUT; + + /* In PIO mode, interrupts might still be enabled */ + IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH); +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/imxmmc.c linux-2.6.22.18-mv/drivers/mmc/host/imxmmc.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/imxmmc.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/imxmmc.c 2008-01-29 03:26:10.000000000 -0800 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/imxmmc.c - Motorola i.MX MMCI driver ++ * linux/drivers/mmc/host/imxmmc.c - Motorola i.MX MMCI driver + * + * Copyright (C) 2004 Sascha Hauer, Pengutronix + * Copyright (C) 2006 Pavel Pisa, PiKRON +@@ -262,7 +262,7 @@ + } + + /* Convert back to virtual address */ +- host->data_ptr = (u16*)(page_address(data->sg->page) + data->sg->offset); ++ host->data_ptr = (u16*)sg_virt(sg); + host->data_cnt = 0; + + clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events); +@@ -428,11 +428,11 @@ + if ( stat & STATUS_ERR_MASK ) { + dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",stat); + if(stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR)) +- data->error = MMC_ERR_BADCRC; ++ data->error = -EILSEQ; + else if(stat & STATUS_TIME_OUT_READ) +- data->error = MMC_ERR_TIMEOUT; ++ data->error = -ETIMEDOUT; + else +- data->error = MMC_ERR_FAILED; ++ data->error = -EIO; + } else { + data->bytes_xfered = host->dma_size; + } +@@ -458,10 +458,10 @@ + + if (stat & STATUS_TIME_OUT_RESP) { + dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n"); +- cmd->error = MMC_ERR_TIMEOUT; ++ cmd->error = -ETIMEDOUT; + } else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) { + dev_dbg(mmc_dev(host->mmc), "cmd crc error\n"); +- cmd->error = MMC_ERR_BADCRC; ++ cmd->error = -EILSEQ; + } + + if(cmd->flags & MMC_RSP_PRESENT) { +@@ -482,7 +482,7 @@ + dev_dbg(mmc_dev(host->mmc), "RESP 0x%08x, 0x%08x, 0x%08x, 0x%08x, error %d\n", + cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error); + +- if (data && (cmd->error == MMC_ERR_NONE) && !(stat & STATUS_ERR_MASK)) { ++ if (data && !cmd->error && !(stat & STATUS_ERR_MASK)) { + if (host->req->data->flags & MMC_DATA_WRITE) { + + /* Wait for FIFO to be empty before starting DMA write */ +@@ -491,7 +491,7 @@ + if(imxmci_busy_wait_for_status(host, &stat, + STATUS_APPL_BUFF_FE, + 40, "imxmci_cmd_done DMA WR") < 0) { +- cmd->error = MMC_ERR_FIFO; ++ cmd->error = -EIO; + imxmci_finish_data(host, stat); + if(host->req) + imxmci_finish_request(host, host->req); +@@ -884,9 +884,21 @@ + } + } + ++static int imxmci_get_ro(struct mmc_host *mmc) ++{ ++ struct imxmci_host *host = mmc_priv(mmc); ++ ++ if (host->pdata && host->pdata->get_ro) ++ return host->pdata->get_ro(mmc_dev(mmc)); ++ /* Host doesn't support read only detection so assume writeable */ ++ return 0; ++} ++ ++ + static const struct mmc_host_ops imxmci_ops = { + .request = imxmci_request, + .set_ios = imxmci_set_ios, ++ .get_ro = imxmci_get_ro, + }; + + static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr) +@@ -913,7 +925,7 @@ + { + struct imxmci_host *host = (struct imxmci_host *)data; + +- if( host->pdata->card_present() != host->present ) { ++ if( host->pdata->card_present(mmc_dev(host->mmc)) != host->present ) { + host->present ^= 1; + dev_info(mmc_dev(host->mmc), "card %s\n", + host->present ? "inserted" : "removed"); +@@ -963,7 +975,7 @@ + mmc->f_min = 150000; + mmc->f_max = CLK_RATE/2; + mmc->ocr_avail = MMC_VDD_32_33; +- mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_BYTEBLOCK; ++ mmc->caps = MMC_CAP_4_BIT_DATA; + + /* MMC core transfer sizes tunable parameters */ + mmc->max_hw_segs = 64; +@@ -1022,7 +1034,7 @@ + if (ret) + goto out; + +- host->present = host->pdata->card_present(); ++ host->present = host->pdata->card_present(mmc_dev(mmc)); + init_timer(&host->timer); + host->timer.data = (unsigned long)host; + host->timer.function = imxmci_check_status; +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/Kconfig linux-2.6.22.18-mv/drivers/mmc/host/Kconfig +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/Kconfig 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/Kconfig 2008-02-21 09:40:02.000000000 -0800 +@@ -35,6 +35,23 @@ + + If unsure, say N. + ++config MMC_RICOH_MMC ++ tristate "Ricoh MMC Controller Disabler (EXPERIMENTAL)" ++ depends on PCI && EXPERIMENTAL && MMC_SDHCI ++ help ++ This selects the disabler for the Ricoh MMC Controller. This ++ proprietary controller is unnecessary because the SDHCI driver ++ supports MMC cards on the SD controller, but if it is not ++ disabled, it will steal the MMC cards away - rendering them ++ useless. It is safe to select this driver even if you don't ++ have a Ricoh based card reader. ++ ++ ++ To compile this driver as a module, choose M here: ++ the module will be called ricoh_mmc. ++ ++ If unsure, say Y. ++ + config MMC_OMAP + tristate "TI OMAP Multimedia Card Interface support" + depends on ARCH_OMAP +@@ -100,3 +117,15 @@ + To compile this driver as a module, choose M here: the + module will be called tifm_sd. + ++config MMC_SPI ++ tristate "MMC/SD over SPI (EXPERIMENTAL)" ++ depends on MMC && SPI_MASTER && !HIGHMEM && EXPERIMENTAL ++ select CRC_ITU_T ++ help ++ Some systems accss MMC/SD cards using a SPI controller instead of ++ using a "native" MMC/SD controller. This has a disadvantage of ++ being relatively high overhead, but a compensating advantage of ++ working on many systems without dedicated MMC/SD controllers. ++ ++ If unsure, or if your system has no SPI master driver, say N. ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/Makefile linux-2.6.22.18-mv/drivers/mmc/host/Makefile +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/Makefile 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/Makefile 2008-01-29 03:26:10.000000000 -0800 +@@ -10,9 +10,12 @@ + obj-$(CONFIG_MMC_PXA) += pxamci.o + obj-$(CONFIG_MMC_IMX) += imxmmc.o + obj-$(CONFIG_MMC_SDHCI) += sdhci.o ++obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o + obj-$(CONFIG_MMC_WBSD) += wbsd.o + obj-$(CONFIG_MMC_AU1X) += au1xmmc.o + obj-$(CONFIG_MMC_OMAP) += omap.o + obj-$(CONFIG_MMC_AT91) += at91_mci.o + obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o ++obj-$(CONFIG_MMC_SPI) += mmc_spi.o ++obj-$(CONFIG_MMC_MVSDMMC) += ../../../arch/arm/plat-feroceon/mv_drivers_lsp/mv_sdio/ + +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/mmci.c linux-2.6.22.18-mv/drivers/mmc/host/mmci.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/mmci.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/mmci.c 2008-01-29 03:26:10.000000000 -0800 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/mmci.c - ARM PrimeCell MMCI PL180/1 driver ++ * linux/drivers/mmc/host/mmci.c - ARM PrimeCell MMCI PL180/1 driver + * + * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. + * +@@ -16,14 +16,15 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + + #include + #include + #include +-#include + #include + #include + +@@ -154,11 +155,11 @@ + } + if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { + if (status & MCI_DATACRCFAIL) +- data->error = MMC_ERR_BADCRC; ++ data->error = -EILSEQ; + else if (status & MCI_DATATIMEOUT) +- data->error = MMC_ERR_TIMEOUT; ++ data->error = -ETIMEDOUT; + else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN)) +- data->error = MMC_ERR_FIFO; ++ data->error = -EIO; + status |= MCI_DATAEND; + + /* +@@ -166,7 +167,7 @@ + * partially written to a page is properly coherent. + */ + if (host->sg_len && data->flags & MMC_DATA_READ) +- flush_dcache_page(host->sg_ptr->page); ++ flush_dcache_page(sg_page(host->sg_ptr)); + } + if (status & MCI_DATAEND) { + mmci_stop_data(host); +@@ -193,12 +194,12 @@ + cmd->resp[3] = readl(base + MMCIRESPONSE3); + + if (status & MCI_CMDTIMEOUT) { +- cmd->error = MMC_ERR_TIMEOUT; ++ cmd->error = -ETIMEDOUT; + } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) { +- cmd->error = MMC_ERR_BADCRC; ++ cmd->error = -EILSEQ; + } + +- if (!cmd->data || cmd->error != MMC_ERR_NONE) { ++ if (!cmd->data || cmd->error) { + if (host->data) + mmci_stop_data(host); + mmci_request_end(host, cmd->mrq); +@@ -318,7 +319,7 @@ + * page, ensure that the data cache is coherent. + */ + if (status & MCI_RXACTIVE) +- flush_dcache_page(host->sg_ptr->page); ++ flush_dcache_page(sg_page(host->sg_ptr)); + + if (!mmci_next_sg(host)) + break; +@@ -391,6 +392,14 @@ + + WARN_ON(host->mrq != NULL); + ++ if (mrq->data && !is_power_of_2(mrq->data->blksz)) { ++ printk(KERN_ERR "%s: Unsupported block size (%d bytes)\n", ++ mmc_hostname(mmc), mrq->data->blksz); ++ mrq->cmd->error = -EINVAL; ++ mmc_request_done(mmc, mrq); ++ return; ++ } ++ + spin_lock_irq(&host->lock); + + host->mrq = mrq; +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/mmci.h linux-2.6.22.18-mv/drivers/mmc/host/mmci.h +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/mmci.h 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/mmci.h 2008-01-29 03:26:10.000000000 -0800 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/mmci.h - ARM PrimeCell MMCI PL180/1 driver ++ * linux/drivers/mmc/host/mmci.h - ARM PrimeCell MMCI PL180/1 driver + * + * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. + * +@@ -169,7 +169,7 @@ + struct scatterlist *sg = host->sg_ptr; + + local_irq_save(*flags); +- return kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; ++ return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; + } + + static inline void mmci_kunmap_atomic(struct mmci_host *host, void *buffer, unsigned long *flags) +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/mmc_spi.c linux-2.6.22.18-mv/drivers/mmc/host/mmc_spi.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/mmc_spi.c 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/mmc_spi.c 2008-01-29 03:26:10.000000000 -0800 +@@ -0,0 +1,1423 @@ ++/* ++ * mmc_spi.c - Access SD/MMC cards through SPI master controllers ++ * ++ * (C) Copyright 2005, Intec Automation, ++ * Mike Lavender (mike at steroidmicros) ++ * (C) Copyright 2006-2007, David Brownell ++ * (C) Copyright 2007, Axis Communications, ++ * Hans-Peter Nilsson (hp at axis.com) ++ * (C) Copyright 2007, ATRON electronic GmbH, ++ * Jan Nikitenko ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include /* for R1_SPI_* bit values */ ++ ++#include ++#include ++ ++#include ++ ++ ++/* NOTES: ++ * ++ * - For now, we won't try to interoperate with a real mmc/sd/sdio ++ * controller, although some of them do have hardware support for ++ * SPI protocol. The main reason for such configs would be mmc-ish ++ * cards like DataFlash, which don't support that "native" protocol. ++ * ++ * We don't have a "DataFlash/MMC/SD/SDIO card slot" abstraction to ++ * switch between driver stacks, and in any case if "native" mode ++ * is available, it will be faster and hence preferable. ++ * ++ * - MMC depends on a different chipselect management policy than the ++ * SPI interface currently supports for shared bus segments: it needs ++ * to issue multiple spi_message requests with the chipselect active, ++ * using the results of one message to decide the next one to issue. ++ * ++ * Pending updates to the programming interface, this driver expects ++ * that it not share the bus with other drivers (precluding conflicts). ++ * ++ * - We tell the controller to keep the chipselect active from the ++ * beginning of an mmc_host_ops.request until the end. So beware ++ * of SPI controller drivers that mis-handle the cs_change flag! ++ * ++ * However, many cards seem OK with chipselect flapping up/down ++ * during that time ... at least on unshared bus segments. ++ */ ++ ++ ++/* ++ * Local protocol constants, internal to data block protocols. ++ */ ++ ++/* Response tokens used to ack each block written: */ ++#define SPI_MMC_RESPONSE_CODE(x) ((x) & 0x1f) ++#define SPI_RESPONSE_ACCEPTED ((2 << 1)|1) ++#define SPI_RESPONSE_CRC_ERR ((5 << 1)|1) ++#define SPI_RESPONSE_WRITE_ERR ((6 << 1)|1) ++ ++/* Read and write blocks start with these tokens and end with crc; ++ * on error, read tokens act like a subset of R2_SPI_* values. ++ */ ++#define SPI_TOKEN_SINGLE 0xfe /* single block r/w, multiblock read */ ++#define SPI_TOKEN_MULTI_WRITE 0xfc /* multiblock write */ ++#define SPI_TOKEN_STOP_TRAN 0xfd /* terminate multiblock write */ ++ ++#define MMC_SPI_BLOCKSIZE 512 ++ ++ ++/* These fixed timeouts come from the latest SD specs, which say to ignore ++ * the CSD values. The R1B value is for card erase (e.g. the "I forgot the ++ * card's password" scenario); it's mostly applied to STOP_TRANSMISSION after ++ * reads which takes nowhere near that long. Older cards may be able to use ++ * shorter timeouts ... but why bother? ++ */ ++#define readblock_timeout ktime_set(0, 100 * 1000 * 1000) ++#define writeblock_timeout ktime_set(0, 250 * 1000 * 1000) ++#define r1b_timeout ktime_set(3, 0) ++ ++ ++/****************************************************************************/ ++ ++/* ++ * Local Data Structures ++ */ ++ ++/* "scratch" is per-{command,block} data exchanged with the card */ ++struct scratch { ++ u8 status[29]; ++ u8 data_token; ++ __be16 crc_val; ++}; ++ ++struct mmc_spi_host { ++ struct mmc_host *mmc; ++ struct spi_device *spi; ++ ++ unsigned char power_mode; ++ u16 powerup_msecs; ++ ++ struct mmc_spi_platform_data *pdata; ++ ++ /* for bulk data transfers */ ++ struct spi_transfer token, t, crc, early_status; ++ struct spi_message m; ++ ++ /* for status readback */ ++ struct spi_transfer status; ++ struct spi_message readback; ++ ++ /* underlying DMA-aware controller, or null */ ++ struct device *dma_dev; ++ ++ /* buffer used for commands and for message "overhead" */ ++ struct scratch *data; ++ dma_addr_t data_dma; ++ ++ /* Specs say to write ones most of the time, even when the card ++ * has no need to read its input data; and many cards won't care. ++ * This is our source of those ones. ++ */ ++ void *ones; ++ dma_addr_t ones_dma; ++}; ++ ++ ++/****************************************************************************/ ++ ++/* ++ * MMC-over-SPI protocol glue, used by the MMC stack interface ++ */ ++ ++static inline int mmc_cs_off(struct mmc_spi_host *host) ++{ ++ /* chipselect will always be inactive after setup() */ ++ return spi_setup(host->spi); ++} ++ ++static int ++mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len) ++{ ++ int status; ++ ++ if (len > sizeof(*host->data)) { ++ WARN_ON(1); ++ return -EIO; ++ } ++ ++ host->status.len = len; ++ ++ if (host->dma_dev) ++ dma_sync_single_for_device(host->dma_dev, ++ host->data_dma, sizeof(*host->data), ++ DMA_FROM_DEVICE); ++ ++ status = spi_sync(host->spi, &host->readback); ++ if (status == 0) ++ status = host->readback.status; ++ ++ if (host->dma_dev) ++ dma_sync_single_for_cpu(host->dma_dev, ++ host->data_dma, sizeof(*host->data), ++ DMA_FROM_DEVICE); ++ ++ return status; ++} ++ ++static int ++mmc_spi_skip(struct mmc_spi_host *host, ktime_t timeout, unsigned n, u8 byte) ++{ ++ u8 *cp = host->data->status; ++ ++ timeout = ktime_add(timeout, ktime_get()); ++ ++ while (1) { ++ int status; ++ unsigned i; ++ ++ status = mmc_spi_readbytes(host, n); ++ if (status < 0) ++ return status; ++ ++ for (i = 0; i < n; i++) { ++ if (cp[i] != byte) ++ return cp[i]; ++ } ++ ++ /* REVISIT investigate msleep() to avoid busy-wait I/O ++ * in at least some cases. ++ */ ++ if (ktime_to_ns(ktime_sub(ktime_get(), timeout)) > 0) ++ break; ++ } ++ return -ETIMEDOUT; ++} ++ ++static inline int ++mmc_spi_wait_unbusy(struct mmc_spi_host *host, ktime_t timeout) ++{ ++ return mmc_spi_skip(host, timeout, sizeof(host->data->status), 0); ++} ++ ++static int mmc_spi_readtoken(struct mmc_spi_host *host) ++{ ++ return mmc_spi_skip(host, readblock_timeout, 1, 0xff); ++} ++ ++ ++/* ++ * Note that for SPI, cmd->resp[0] is not the same data as "native" protocol ++ * hosts return! The low byte holds R1_SPI bits. The next byte may hold ++ * R2_SPI bits ... for SEND_STATUS, or after data read errors. ++ * ++ * cmd->resp[1] holds any four-byte response, for R3 (READ_OCR) and on ++ * newer cards R7 (IF_COND). ++ */ ++ ++static char *maptype(struct mmc_command *cmd) ++{ ++ switch (mmc_spi_resp_type(cmd)) { ++ case MMC_RSP_SPI_R1: return "R1"; ++ case MMC_RSP_SPI_R1B: return "R1B"; ++ case MMC_RSP_SPI_R2: return "R2/R5"; ++ case MMC_RSP_SPI_R3: return "R3/R4/R7"; ++ default: return "?"; ++ } ++} ++ ++/* return zero, else negative errno after setting cmd->error */ ++static int mmc_spi_response_get(struct mmc_spi_host *host, ++ struct mmc_command *cmd, int cs_on) ++{ ++ u8 *cp = host->data->status; ++ u8 *end = cp + host->t.len; ++ int value = 0; ++ char tag[32]; ++ ++ snprintf(tag, sizeof(tag), " ... CMD%d response SPI_%s", ++ cmd->opcode, maptype(cmd)); ++ ++ /* Except for data block reads, the whole response will already ++ * be stored in the scratch buffer. It's somewhere after the ++ * command and the first byte we read after it. We ignore that ++ * first byte. After STOP_TRANSMISSION command it may include ++ * two data bits, but otherwise it's all ones. ++ */ ++ cp += 8; ++ while (cp < end && *cp == 0xff) ++ cp++; ++ ++ /* Data block reads (R1 response types) may need more data... */ ++ if (cp == end) { ++ unsigned i; ++ ++ cp = host->data->status; ++ ++ /* Card sends N(CR) (== 1..8) bytes of all-ones then one ++ * status byte ... and we already scanned 2 bytes. ++ * ++ * REVISIT block read paths use nasty byte-at-a-time I/O ++ * so it can always DMA directly into the target buffer. ++ * It'd probably be better to memcpy() the first chunk and ++ * avoid extra i/o calls... ++ */ ++ for (i = 2; i < 9; i++) { ++ value = mmc_spi_readbytes(host, 1); ++ if (value < 0) ++ goto done; ++ if (*cp != 0xff) ++ goto checkstatus; ++ } ++ value = -ETIMEDOUT; ++ goto done; ++ } ++ ++checkstatus: ++ if (*cp & 0x80) { ++ dev_dbg(&host->spi->dev, "%s: INVALID RESPONSE, %02x\n", ++ tag, *cp); ++ value = -EBADR; ++ goto done; ++ } ++ ++ cmd->resp[0] = *cp++; ++ cmd->error = 0; ++ ++ /* Status byte: the entire seven-bit R1 response. */ ++ if (cmd->resp[0] != 0) { ++ if ((R1_SPI_PARAMETER | R1_SPI_ADDRESS ++ | R1_SPI_ILLEGAL_COMMAND) ++ & cmd->resp[0]) ++ value = -EINVAL; ++ else if (R1_SPI_COM_CRC & cmd->resp[0]) ++ value = -EILSEQ; ++ else if ((R1_SPI_ERASE_SEQ | R1_SPI_ERASE_RESET) ++ & cmd->resp[0]) ++ value = -EIO; ++ /* else R1_SPI_IDLE, "it's resetting" */ ++ } ++ ++ switch (mmc_spi_resp_type(cmd)) { ++ ++ /* SPI R1B == R1 + busy; STOP_TRANSMISSION (for multiblock reads) ++ * and less-common stuff like various erase operations. ++ */ ++ case MMC_RSP_SPI_R1B: ++ /* maybe we read all the busy tokens already */ ++ while (cp < end && *cp == 0) ++ cp++; ++ if (cp == end) ++ mmc_spi_wait_unbusy(host, r1b_timeout); ++ break; ++ ++ /* SPI R2 == R1 + second status byte; SEND_STATUS ++ * SPI R5 == R1 + data byte; IO_RW_DIRECT ++ */ ++ case MMC_RSP_SPI_R2: ++ cmd->resp[0] |= *cp << 8; ++ break; ++ ++ /* SPI R3, R4, or R7 == R1 + 4 bytes */ ++ case MMC_RSP_SPI_R3: ++ cmd->resp[1] = be32_to_cpu(get_unaligned((u32 *)cp)); ++ break; ++ ++ /* SPI R1 == just one status byte */ ++ case MMC_RSP_SPI_R1: ++ break; ++ ++ default: ++ dev_dbg(&host->spi->dev, "bad response type %04x\n", ++ mmc_spi_resp_type(cmd)); ++ if (value >= 0) ++ value = -EINVAL; ++ goto done; ++ } ++ ++ if (value < 0) ++ dev_dbg(&host->spi->dev, "%s: resp %04x %08x\n", ++ tag, cmd->resp[0], cmd->resp[1]); ++ ++ /* disable chipselect on errors and some success cases */ ++ if (value >= 0 && cs_on) ++ return value; ++done: ++ if (value < 0) ++ cmd->error = value; ++ mmc_cs_off(host); ++ return value; ++} ++ ++/* Issue command and read its response. ++ * Returns zero on success, negative for error. ++ * ++ * On error, caller must cope with mmc core retry mechanism. That ++ * means immediate low-level resubmit, which affects the bus lock... ++ */ ++static int ++mmc_spi_command_send(struct mmc_spi_host *host, ++ struct mmc_request *mrq, ++ struct mmc_command *cmd, int cs_on) ++{ ++ struct scratch *data = host->data; ++ u8 *cp = data->status; ++ u32 arg = cmd->arg; ++ int status; ++ struct spi_transfer *t; ++ ++ /* We can handle most commands (except block reads) in one full ++ * duplex I/O operation before either starting the next transfer ++ * (data block or command) or else deselecting the card. ++ * ++ * First, write 7 bytes: ++ * - an all-ones byte to ensure the card is ready ++ * - opcode byte (plus start and transmission bits) ++ * - four bytes of big-endian argument ++ * - crc7 (plus end bit) ... always computed, it's cheap ++ * ++ * We init the whole buffer to all-ones, which is what we need ++ * to write while we're reading (later) response data. ++ */ ++ memset(cp++, 0xff, sizeof(data->status)); ++ ++ *cp++ = 0x40 | cmd->opcode; ++ *cp++ = (u8)(arg >> 24); ++ *cp++ = (u8)(arg >> 16); ++ *cp++ = (u8)(arg >> 8); ++ *cp++ = (u8)arg; ++ *cp++ = (crc7(0, &data->status[1], 5) << 1) | 0x01; ++ ++ /* Then, read up to 13 bytes (while writing all-ones): ++ * - N(CR) (== 1..8) bytes of all-ones ++ * - status byte (for all response types) ++ * - the rest of the response, either: ++ * + nothing, for R1 or R1B responses ++ * + second status byte, for R2 responses ++ * + four data bytes, for R3 and R7 responses ++ * ++ * Finally, read some more bytes ... in the nice cases we know in ++ * advance how many, and reading 1 more is always OK: ++ * - N(EC) (== 0..N) bytes of all-ones, before deselect/finish ++ * - N(RC) (== 1..N) bytes of all-ones, before next command ++ * - N(WR) (== 1..N) bytes of all-ones, before data write ++ * ++ * So in those cases one full duplex I/O of at most 21 bytes will ++ * handle the whole command, leaving the card ready to receive a ++ * data block or new command. We do that whenever we can, shaving ++ * CPU and IRQ costs (especially when using DMA or FIFOs). ++ * ++ * There are two other cases, where it's not generally practical ++ * to rely on a single I/O: ++ * ++ * - R1B responses need at least N(EC) bytes of all-zeroes. ++ * ++ * In this case we can *try* to fit it into one I/O, then ++ * maybe read more data later. ++ * ++ * - Data block reads are more troublesome, since a variable ++ * number of padding bytes precede the token and data. ++ * + N(CX) (== 0..8) bytes of all-ones, before CSD or CID ++ * + N(AC) (== 1..many) bytes of all-ones ++ * ++ * In this case we currently only have minimal speedups here: ++ * when N(CR) == 1 we can avoid I/O in response_get(). ++ */ ++ if (cs_on && (mrq->data->flags & MMC_DATA_READ)) { ++ cp += 2; /* min(N(CR)) + status */ ++ /* R1 */ ++ } else { ++ cp += 10; /* max(N(CR)) + status + min(N(RC),N(WR)) */ ++ if (cmd->flags & MMC_RSP_SPI_S2) /* R2/R5 */ ++ cp++; ++ else if (cmd->flags & MMC_RSP_SPI_B4) /* R3/R4/R7 */ ++ cp += 4; ++ else if (cmd->flags & MMC_RSP_BUSY) /* R1B */ ++ cp = data->status + sizeof(data->status); ++ /* else: R1 (most commands) */ ++ } ++ ++ dev_dbg(&host->spi->dev, " mmc_spi: CMD%d, resp %s\n", ++ cmd->opcode, maptype(cmd)); ++ ++ /* send command, leaving chipselect active */ ++ spi_message_init(&host->m); ++ ++ t = &host->t; ++ memset(t, 0, sizeof(*t)); ++ t->tx_buf = t->rx_buf = data->status; ++ t->tx_dma = t->rx_dma = host->data_dma; ++ t->len = cp - data->status; ++ t->cs_change = 1; ++ spi_message_add_tail(t, &host->m); ++ ++ if (host->dma_dev) { ++ host->m.is_dma_mapped = 1; ++ dma_sync_single_for_device(host->dma_dev, ++ host->data_dma, sizeof(*host->data), ++ DMA_BIDIRECTIONAL); ++ } ++ status = spi_sync(host->spi, &host->m); ++ if (status == 0) ++ status = host->m.status; ++ ++ if (host->dma_dev) ++ dma_sync_single_for_cpu(host->dma_dev, ++ host->data_dma, sizeof(*host->data), ++ DMA_BIDIRECTIONAL); ++ if (status < 0) { ++ dev_dbg(&host->spi->dev, " ... write returned %d\n", status); ++ cmd->error = status; ++ return status; ++ } ++ ++ /* after no-data commands and STOP_TRANSMISSION, chipselect off */ ++ return mmc_spi_response_get(host, cmd, cs_on); ++} ++ ++/* Build data message with up to four separate transfers. For TX, we ++ * start by writing the data token. And in most cases, we finish with ++ * a status transfer. ++ * ++ * We always provide TX data for data and CRC. The MMC/SD protocol ++ * requires us to write ones; but Linux defaults to writing zeroes; ++ * so we explicitly initialize it to all ones on RX paths. ++ * ++ * We also handle DMA mapping, so the underlying SPI controller does ++ * not need to (re)do it for each message. ++ */ ++static void ++mmc_spi_setup_data_message( ++ struct mmc_spi_host *host, ++ int multiple, ++ enum dma_data_direction direction) ++{ ++ struct spi_transfer *t; ++ struct scratch *scratch = host->data; ++ dma_addr_t dma = host->data_dma; ++ ++ spi_message_init(&host->m); ++ if (dma) ++ host->m.is_dma_mapped = 1; ++ ++ /* for reads, readblock() skips 0xff bytes before finding ++ * the token; for writes, this transfer issues that token. ++ */ ++ if (direction == DMA_TO_DEVICE) { ++ t = &host->token; ++ memset(t, 0, sizeof(*t)); ++ t->len = 1; ++ if (multiple) ++ scratch->data_token = SPI_TOKEN_MULTI_WRITE; ++ else ++ scratch->data_token = SPI_TOKEN_SINGLE; ++ t->tx_buf = &scratch->data_token; ++ if (dma) ++ t->tx_dma = dma + offsetof(struct scratch, data_token); ++ spi_message_add_tail(t, &host->m); ++ } ++ ++ /* Body of transfer is buffer, then CRC ... ++ * either TX-only, or RX with TX-ones. ++ */ ++ t = &host->t; ++ memset(t, 0, sizeof(*t)); ++ t->tx_buf = host->ones; ++ t->tx_dma = host->ones_dma; ++ /* length and actual buffer info are written later */ ++ spi_message_add_tail(t, &host->m); ++ ++ t = &host->crc; ++ memset(t, 0, sizeof(*t)); ++ t->len = 2; ++ if (direction == DMA_TO_DEVICE) { ++ /* the actual CRC may get written later */ ++ t->tx_buf = &scratch->crc_val; ++ if (dma) ++ t->tx_dma = dma + offsetof(struct scratch, crc_val); ++ } else { ++ t->tx_buf = host->ones; ++ t->tx_dma = host->ones_dma; ++ t->rx_buf = &scratch->crc_val; ++ if (dma) ++ t->rx_dma = dma + offsetof(struct scratch, crc_val); ++ } ++ spi_message_add_tail(t, &host->m); ++ ++ /* ++ * A single block read is followed by N(EC) [0+] all-ones bytes ++ * before deselect ... don't bother. ++ * ++ * Multiblock reads are followed by N(AC) [1+] all-ones bytes before ++ * the next block is read, or a STOP_TRANSMISSION is issued. We'll ++ * collect that single byte, so readblock() doesn't need to. ++ * ++ * For a write, the one-byte data response follows immediately, then ++ * come zero or more busy bytes, then N(WR) [1+] all-ones bytes. ++ * Then single block reads may deselect, and multiblock ones issue ++ * the next token (next data block, or STOP_TRAN). We can try to ++ * minimize I/O ops by using a single read to collect end-of-busy. ++ */ ++ if (multiple || direction == DMA_TO_DEVICE) { ++ t = &host->early_status; ++ memset(t, 0, sizeof(*t)); ++ t->len = (direction == DMA_TO_DEVICE) ++ ? sizeof(scratch->status) ++ : 1; ++ t->tx_buf = host->ones; ++ t->tx_dma = host->ones_dma; ++ t->rx_buf = scratch->status; ++ if (dma) ++ t->rx_dma = dma + offsetof(struct scratch, status); ++ t->cs_change = 1; ++ spi_message_add_tail(t, &host->m); ++ } ++} ++ ++/* ++ * Write one block: ++ * - caller handled preceding N(WR) [1+] all-ones bytes ++ * - data block ++ * + token ++ * + data bytes ++ * + crc16 ++ * - an all-ones byte ... card writes a data-response byte ++ * - followed by N(EC) [0+] all-ones bytes, card writes zero/'busy' ++ * ++ * Return negative errno, else success. ++ */ ++static int ++mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t) ++{ ++ struct spi_device *spi = host->spi; ++ int status, i; ++ struct scratch *scratch = host->data; ++ ++ if (host->mmc->use_spi_crc) ++ scratch->crc_val = cpu_to_be16( ++ crc_itu_t(0, t->tx_buf, t->len)); ++ if (host->dma_dev) ++ dma_sync_single_for_device(host->dma_dev, ++ host->data_dma, sizeof(*scratch), ++ DMA_BIDIRECTIONAL); ++ ++ status = spi_sync(spi, &host->m); ++ if (status == 0) ++ status = host->m.status; ++ ++ if (status != 0) { ++ dev_dbg(&spi->dev, "write error (%d)\n", status); ++ return status; ++ } ++ ++ if (host->dma_dev) ++ dma_sync_single_for_cpu(host->dma_dev, ++ host->data_dma, sizeof(*scratch), ++ DMA_BIDIRECTIONAL); ++ ++ /* ++ * Get the transmission data-response reply. It must follow ++ * immediately after the data block we transferred. This reply ++ * doesn't necessarily tell whether the write operation succeeded; ++ * it just says if the transmission was ok and whether *earlier* ++ * writes succeeded; see the standard. ++ */ ++ switch (SPI_MMC_RESPONSE_CODE(scratch->status[0])) { ++ case SPI_RESPONSE_ACCEPTED: ++ status = 0; ++ break; ++ case SPI_RESPONSE_CRC_ERR: ++ /* host shall then issue MMC_STOP_TRANSMISSION */ ++ status = -EILSEQ; ++ break; ++ case SPI_RESPONSE_WRITE_ERR: ++ /* host shall then issue MMC_STOP_TRANSMISSION, ++ * and should MMC_SEND_STATUS to sort it out ++ */ ++ status = -EIO; ++ break; ++ default: ++ status = -EPROTO; ++ break; ++ } ++ if (status != 0) { ++ dev_dbg(&spi->dev, "write error %02x (%d)\n", ++ scratch->status[0], status); ++ return status; ++ } ++ ++ t->tx_buf += t->len; ++ if (host->dma_dev) ++ t->tx_dma += t->len; ++ ++ /* Return when not busy. If we didn't collect that status yet, ++ * we'll need some more I/O. ++ */ ++ for (i = 1; i < sizeof(scratch->status); i++) { ++ if (scratch->status[i] != 0) ++ return 0; ++ } ++ return mmc_spi_wait_unbusy(host, writeblock_timeout); ++} ++ ++/* ++ * Read one block: ++ * - skip leading all-ones bytes ... either ++ * + N(AC) [1..f(clock,CSD)] usually, else ++ * + N(CX) [0..8] when reading CSD or CID ++ * - data block ++ * + token ... if error token, no data or crc ++ * + data bytes ++ * + crc16 ++ * ++ * After single block reads, we're done; N(EC) [0+] all-ones bytes follow ++ * before dropping chipselect. ++ * ++ * For multiblock reads, caller either reads the next block or issues a ++ * STOP_TRANSMISSION command. ++ */ ++static int ++mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t) ++{ ++ struct spi_device *spi = host->spi; ++ int status; ++ struct scratch *scratch = host->data; ++ ++ /* At least one SD card sends an all-zeroes byte when N(CX) ++ * applies, before the all-ones bytes ... just cope with that. ++ */ ++ status = mmc_spi_readbytes(host, 1); ++ if (status < 0) ++ return status; ++ status = scratch->status[0]; ++ if (status == 0xff || status == 0) ++ status = mmc_spi_readtoken(host); ++ ++ if (status == SPI_TOKEN_SINGLE) { ++ if (host->dma_dev) { ++ dma_sync_single_for_device(host->dma_dev, ++ host->data_dma, sizeof(*scratch), ++ DMA_BIDIRECTIONAL); ++ dma_sync_single_for_device(host->dma_dev, ++ t->rx_dma, t->len, ++ DMA_FROM_DEVICE); ++ } ++ ++ status = spi_sync(spi, &host->m); ++ if (status == 0) ++ status = host->m.status; ++ ++ if (host->dma_dev) { ++ dma_sync_single_for_cpu(host->dma_dev, ++ host->data_dma, sizeof(*scratch), ++ DMA_BIDIRECTIONAL); ++ dma_sync_single_for_cpu(host->dma_dev, ++ t->rx_dma, t->len, ++ DMA_FROM_DEVICE); ++ } ++ ++ } else { ++ dev_dbg(&spi->dev, "read error %02x (%d)\n", status, status); ++ ++ /* we've read extra garbage, timed out, etc */ ++ if (status < 0) ++ return status; ++ ++ /* low four bits are an R2 subset, fifth seems to be ++ * vendor specific ... map them all to generic error.. ++ */ ++ return -EIO; ++ } ++ ++ if (host->mmc->use_spi_crc) { ++ u16 crc = crc_itu_t(0, t->rx_buf, t->len); ++ ++ be16_to_cpus(&scratch->crc_val); ++ if (scratch->crc_val != crc) { ++ dev_dbg(&spi->dev, "read - crc error: crc_val=0x%04x, " ++ "computed=0x%04x len=%d\n", ++ scratch->crc_val, crc, t->len); ++ return -EILSEQ; ++ } ++ } ++ ++ t->rx_buf += t->len; ++ if (host->dma_dev) ++ t->rx_dma += t->len; ++ ++ return 0; ++} ++ ++/* ++ * An MMC/SD data stage includes one or more blocks, optional CRCs, ++ * and inline handshaking. That handhaking makes it unlike most ++ * other SPI protocol stacks. ++ */ ++static void ++mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, ++ struct mmc_data *data, u32 blk_size) ++{ ++ struct spi_device *spi = host->spi; ++ struct device *dma_dev = host->dma_dev; ++ struct spi_transfer *t; ++ enum dma_data_direction direction; ++ struct scatterlist *sg; ++ unsigned n_sg; ++ int multiple = (data->blocks > 1); ++ ++ if (data->flags & MMC_DATA_READ) ++ direction = DMA_FROM_DEVICE; ++ else ++ direction = DMA_TO_DEVICE; ++ mmc_spi_setup_data_message(host, multiple, direction); ++ t = &host->t; ++ ++ /* Handle scatterlist segments one at a time, with synch for ++ * each 512-byte block ++ */ ++ for (sg = data->sg, n_sg = data->sg_len; n_sg; n_sg--, sg++) { ++ int status = 0; ++ dma_addr_t dma_addr = 0; ++ void *kmap_addr; ++ unsigned length = sg->length; ++ enum dma_data_direction dir = direction; ++ ++ /* set up dma mapping for controller drivers that might ++ * use DMA ... though they may fall back to PIO ++ */ ++ if (dma_dev) { ++ /* never invalidate whole *shared* pages ... */ ++ if ((sg->offset != 0 || length != PAGE_SIZE) ++ && dir == DMA_FROM_DEVICE) ++ dir = DMA_BIDIRECTIONAL; ++ ++ dma_addr = dma_map_page(dma_dev, sg_page(sg), 0, ++ PAGE_SIZE, dir); ++ if (direction == DMA_TO_DEVICE) ++ t->tx_dma = dma_addr + sg->offset; ++ else ++ t->rx_dma = dma_addr + sg->offset; ++ } ++ ++ /* allow pio too; we don't allow highmem */ ++ kmap_addr = kmap(sg_page(sg)); ++ if (direction == DMA_TO_DEVICE) ++ t->tx_buf = kmap_addr + sg->offset; ++ else ++ t->rx_buf = kmap_addr + sg->offset; ++ ++ /* transfer each block, and update request status */ ++ while (length) { ++ t->len = min(length, blk_size); ++ ++ dev_dbg(&host->spi->dev, ++ " mmc_spi: %s block, %d bytes\n", ++ (direction == DMA_TO_DEVICE) ++ ? "write" ++ : "read", ++ t->len); ++ ++ if (direction == DMA_TO_DEVICE) ++ status = mmc_spi_writeblock(host, t); ++ else ++ status = mmc_spi_readblock(host, t); ++ if (status < 0) ++ break; ++ ++ data->bytes_xfered += t->len; ++ length -= t->len; ++ ++ if (!multiple) ++ break; ++ } ++ ++ /* discard mappings */ ++ if (direction == DMA_FROM_DEVICE) ++ flush_kernel_dcache_page(sg_page(sg)); ++ kunmap(sg_page(sg)); ++ if (dma_dev) ++ dma_unmap_page(dma_dev, dma_addr, PAGE_SIZE, dir); ++ ++ if (status < 0) { ++ data->error = status; ++ dev_dbg(&spi->dev, "%s status %d\n", ++ (direction == DMA_TO_DEVICE) ++ ? "write" : "read", ++ status); ++ break; ++ } ++ } ++ ++ /* NOTE some docs describe an MMC-only SET_BLOCK_COUNT (CMD23) that ++ * can be issued before multiblock writes. Unlike its more widely ++ * documented analogue for SD cards (SET_WR_BLK_ERASE_COUNT, ACMD23), ++ * that can affect the STOP_TRAN logic. Complete (and current) ++ * MMC specs should sort that out before Linux starts using CMD23. ++ */ ++ if (direction == DMA_TO_DEVICE && multiple) { ++ struct scratch *scratch = host->data; ++ int tmp; ++ const unsigned statlen = sizeof(scratch->status); ++ ++ dev_dbg(&spi->dev, " mmc_spi: STOP_TRAN\n"); ++ ++ /* Tweak the per-block message we set up earlier by morphing ++ * it to hold single buffer with the token followed by some ++ * all-ones bytes ... skip N(BR) (0..1), scan the rest for ++ * "not busy any longer" status, and leave chip selected. ++ */ ++ INIT_LIST_HEAD(&host->m.transfers); ++ list_add(&host->early_status.transfer_list, ++ &host->m.transfers); ++ ++ memset(scratch->status, 0xff, statlen); ++ scratch->status[0] = SPI_TOKEN_STOP_TRAN; ++ ++ host->early_status.tx_buf = host->early_status.rx_buf; ++ host->early_status.tx_dma = host->early_status.rx_dma; ++ host->early_status.len = statlen; ++ ++ if (host->dma_dev) ++ dma_sync_single_for_device(host->dma_dev, ++ host->data_dma, sizeof(*scratch), ++ DMA_BIDIRECTIONAL); ++ ++ tmp = spi_sync(spi, &host->m); ++ if (tmp == 0) ++ tmp = host->m.status; ++ ++ if (host->dma_dev) ++ dma_sync_single_for_cpu(host->dma_dev, ++ host->data_dma, sizeof(*scratch), ++ DMA_BIDIRECTIONAL); ++ ++ if (tmp < 0) { ++ if (!data->error) ++ data->error = tmp; ++ return; ++ } ++ ++ /* Ideally we collected "not busy" status with one I/O, ++ * avoiding wasteful byte-at-a-time scanning... but more ++ * I/O is often needed. ++ */ ++ for (tmp = 2; tmp < statlen; tmp++) { ++ if (scratch->status[tmp] != 0) ++ return; ++ } ++ tmp = mmc_spi_wait_unbusy(host, writeblock_timeout); ++ if (tmp < 0 && !data->error) ++ data->error = tmp; ++ } ++} ++ ++/****************************************************************************/ ++ ++/* ++ * MMC driver implementation -- the interface to the MMC stack ++ */ ++ ++static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct mmc_spi_host *host = mmc_priv(mmc); ++ int status = -EINVAL; ++ ++#ifdef DEBUG ++ /* MMC core and layered drivers *MUST* issue SPI-aware commands */ ++ { ++ struct mmc_command *cmd; ++ int invalid = 0; ++ ++ cmd = mrq->cmd; ++ if (!mmc_spi_resp_type(cmd)) { ++ dev_dbg(&host->spi->dev, "bogus command\n"); ++ cmd->error = -EINVAL; ++ invalid = 1; ++ } ++ ++ cmd = mrq->stop; ++ if (cmd && !mmc_spi_resp_type(cmd)) { ++ dev_dbg(&host->spi->dev, "bogus STOP command\n"); ++ cmd->error = -EINVAL; ++ invalid = 1; ++ } ++ ++ if (invalid) { ++ dump_stack(); ++ mmc_request_done(host->mmc, mrq); ++ return; ++ } ++ } ++#endif ++ ++ /* issue command; then optionally data and stop */ ++ status = mmc_spi_command_send(host, mrq, mrq->cmd, mrq->data != NULL); ++ if (status == 0 && mrq->data) { ++ mmc_spi_data_do(host, mrq->cmd, mrq->data, mrq->data->blksz); ++ if (mrq->stop) ++ status = mmc_spi_command_send(host, mrq, mrq->stop, 0); ++ else ++ mmc_cs_off(host); ++ } ++ ++ mmc_request_done(host->mmc, mrq); ++} ++ ++/* See Section 6.4.1, in SD "Simplified Physical Layer Specification 2.0" ++ * ++ * NOTE that here we can't know that the card has just been powered up; ++ * not all MMC/SD sockets support power switching. ++ * ++ * FIXME when the card is still in SPI mode, e.g. from a previous kernel, ++ * this doesn't seem to do the right thing at all... ++ */ ++static void mmc_spi_initsequence(struct mmc_spi_host *host) ++{ ++ /* Try to be very sure any previous command has completed; ++ * wait till not-busy, skip debris from any old commands. ++ */ ++ mmc_spi_wait_unbusy(host, r1b_timeout); ++ mmc_spi_readbytes(host, 10); ++ ++ /* ++ * Do a burst with chipselect active-high. We need to do this to ++ * meet the requirement of 74 clock cycles with both chipselect ++ * and CMD (MOSI) high before CMD0 ... after the card has been ++ * powered up to Vdd(min), and so is ready to take commands. ++ * ++ * Some cards are particularly needy of this (e.g. Viking "SD256") ++ * while most others don't seem to care. ++ * ++ * Note that this is one of the places MMC/SD plays games with the ++ * SPI protocol. Another is that when chipselect is released while ++ * the card returns BUSY status, the clock must issue several cycles ++ * with chipselect high before the card will stop driving its output. ++ */ ++ host->spi->mode |= SPI_CS_HIGH; ++ if (spi_setup(host->spi) != 0) { ++ /* Just warn; most cards work without it. */ ++ dev_warn(&host->spi->dev, ++ "can't change chip-select polarity\n"); ++ host->spi->mode &= ~SPI_CS_HIGH; ++ } else { ++ mmc_spi_readbytes(host, 18); ++ ++ host->spi->mode &= ~SPI_CS_HIGH; ++ if (spi_setup(host->spi) != 0) { ++ /* Wot, we can't get the same setup we had before? */ ++ dev_err(&host->spi->dev, ++ "can't restore chip-select polarity\n"); ++ } ++ } ++} ++ ++static char *mmc_powerstring(u8 power_mode) ++{ ++ switch (power_mode) { ++ case MMC_POWER_OFF: return "off"; ++ case MMC_POWER_UP: return "up"; ++ case MMC_POWER_ON: return "on"; ++ } ++ return "?"; ++} ++ ++static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct mmc_spi_host *host = mmc_priv(mmc); ++ ++ if (host->power_mode != ios->power_mode) { ++ int canpower; ++ ++ canpower = host->pdata && host->pdata->setpower; ++ ++ dev_dbg(&host->spi->dev, "mmc_spi: power %s (%d)%s\n", ++ mmc_powerstring(ios->power_mode), ++ ios->vdd, ++ canpower ? ", can switch" : ""); ++ ++ /* switch power on/off if possible, accounting for ++ * max 250msec powerup time if needed. ++ */ ++ if (canpower) { ++ switch (ios->power_mode) { ++ case MMC_POWER_OFF: ++ case MMC_POWER_UP: ++ host->pdata->setpower(&host->spi->dev, ++ ios->vdd); ++ if (ios->power_mode == MMC_POWER_UP) ++ msleep(host->powerup_msecs); ++ } ++ } ++ ++ /* See 6.4.1 in the simplified SD card physical spec 2.0 */ ++ if (ios->power_mode == MMC_POWER_ON) ++ mmc_spi_initsequence(host); ++ ++ /* If powering down, ground all card inputs to avoid power ++ * delivery from data lines! On a shared SPI bus, this ++ * will probably be temporary; 6.4.2 of the simplified SD ++ * spec says this must last at least 1msec. ++ * ++ * - Clock low means CPOL 0, e.g. mode 0 ++ * - MOSI low comes from writing zero ++ * - Chipselect is usually active low... ++ */ ++ if (canpower && ios->power_mode == MMC_POWER_OFF) { ++ int mres; ++ ++ host->spi->mode &= ~(SPI_CPOL|SPI_CPHA); ++ mres = spi_setup(host->spi); ++ if (mres < 0) ++ dev_dbg(&host->spi->dev, ++ "switch to SPI mode 0 failed\n"); ++ ++ if (spi_w8r8(host->spi, 0x00) < 0) ++ dev_dbg(&host->spi->dev, ++ "put spi signals to low failed\n"); ++ ++ /* ++ * Now clock should be low due to spi mode 0; ++ * MOSI should be low because of written 0x00; ++ * chipselect should be low (it is active low) ++ * power supply is off, so now MMC is off too! ++ * ++ * FIXME no, chipselect can be high since the ++ * device is inactive and SPI_CS_HIGH is clear... ++ */ ++ msleep(10); ++ if (mres == 0) { ++ host->spi->mode |= (SPI_CPOL|SPI_CPHA); ++ mres = spi_setup(host->spi); ++ if (mres < 0) ++ dev_dbg(&host->spi->dev, ++ "switch back to SPI mode 3" ++ " failed\n"); ++ } ++ } ++ ++ host->power_mode = ios->power_mode; ++ } ++ ++ if (host->spi->max_speed_hz != ios->clock && ios->clock != 0) { ++ int status; ++ ++ host->spi->max_speed_hz = ios->clock; ++ status = spi_setup(host->spi); ++ dev_dbg(&host->spi->dev, ++ "mmc_spi: clock to %d Hz, %d\n", ++ host->spi->max_speed_hz, status); ++ } ++} ++ ++static int mmc_spi_get_ro(struct mmc_host *mmc) ++{ ++ struct mmc_spi_host *host = mmc_priv(mmc); ++ ++ if (host->pdata && host->pdata->get_ro) ++ return host->pdata->get_ro(mmc->parent); ++ /* board doesn't support read only detection; assume writeable */ ++ return 0; ++} ++ ++ ++static const struct mmc_host_ops mmc_spi_ops = { ++ .request = mmc_spi_request, ++ .set_ios = mmc_spi_set_ios, ++ .get_ro = mmc_spi_get_ro, ++}; ++ ++ ++/****************************************************************************/ ++ ++/* ++ * SPI driver implementation ++ */ ++ ++static irqreturn_t ++mmc_spi_detect_irq(int irq, void *mmc) ++{ ++ struct mmc_spi_host *host = mmc_priv(mmc); ++ u16 delay_msec = max(host->pdata->detect_delay, (u16)100); ++ ++ mmc_detect_change(mmc, msecs_to_jiffies(delay_msec)); ++ return IRQ_HANDLED; ++} ++ ++struct count_children { ++ unsigned n; ++ struct bus_type *bus; ++}; ++ ++static int maybe_count_child(struct device *dev, void *c) ++{ ++ struct count_children *ccp = c; ++ ++ if (dev->bus == ccp->bus) { ++ if (ccp->n) ++ return -EBUSY; ++ ccp->n++; ++ } ++ return 0; ++} ++ ++static int mmc_spi_probe(struct spi_device *spi) ++{ ++ void *ones; ++ struct mmc_host *mmc; ++ struct mmc_spi_host *host; ++ int status; ++ ++ /* MMC and SD specs only seem to care that sampling is on the ++ * rising edge ... meaning SPI modes 0 or 3. So either SPI mode ++ * should be legit. We'll use mode 0 since it seems to be a ++ * bit less troublesome on some hardware ... unclear why. ++ */ ++ spi->mode = SPI_MODE_0; ++ spi->bits_per_word = 8; ++ ++ status = spi_setup(spi); ++ if (status < 0) { ++ dev_dbg(&spi->dev, "needs SPI mode %02x, %d KHz; %d\n", ++ spi->mode, spi->max_speed_hz / 1000, ++ status); ++ return status; ++ } ++ ++ /* We can use the bus safely iff nobody else will interfere with us. ++ * Most commands consist of one SPI message to issue a command, then ++ * several more to collect its response, then possibly more for data ++ * transfer. Clocking access to other devices during that period will ++ * corrupt the command execution. ++ * ++ * Until we have software primitives which guarantee non-interference, ++ * we'll aim for a hardware-level guarantee. ++ * ++ * REVISIT we can't guarantee another device won't be added later... ++ */ ++ if (spi->master->num_chipselect > 1) { ++ struct count_children cc; ++ ++ cc.n = 0; ++ cc.bus = spi->dev.bus; ++ status = device_for_each_child(spi->dev.parent, &cc, ++ maybe_count_child); ++ if (status < 0) { ++ dev_err(&spi->dev, "can't share SPI bus\n"); ++ return status; ++ } ++ ++ dev_warn(&spi->dev, "ASSUMING SPI bus stays unshared!\n"); ++ } ++ ++ /* We need a supply of ones to transmit. This is the only time ++ * the CPU touches these, so cache coherency isn't a concern. ++ * ++ * NOTE if many systems use more than one MMC-over-SPI connector ++ * it'd save some memory to share this. That's evidently rare. ++ */ ++ status = -ENOMEM; ++ ones = kmalloc(MMC_SPI_BLOCKSIZE, GFP_KERNEL); ++ if (!ones) ++ goto nomem; ++ memset(ones, 0xff, MMC_SPI_BLOCKSIZE); ++ ++ mmc = mmc_alloc_host(sizeof(*host), &spi->dev); ++ if (!mmc) ++ goto nomem; ++ ++ mmc->ops = &mmc_spi_ops; ++ mmc->max_blk_size = MMC_SPI_BLOCKSIZE; ++ ++ /* As long as we keep track of the number of successfully ++ * transmitted blocks, we're good for multiwrite. ++ */ ++ mmc->caps = MMC_CAP_SPI | MMC_CAP_MULTIWRITE; ++ ++ /* SPI doesn't need the lowspeed device identification thing for ++ * MMC or SD cards, since it never comes up in open drain mode. ++ * That's good; some SPI masters can't handle very low speeds! ++ * ++ * However, low speed SDIO cards need not handle over 400 KHz; ++ * that's the only reason not to use a few MHz for f_min (until ++ * the upper layer reads the target frequency from the CSD). ++ */ ++ mmc->f_min = 400000; ++ mmc->f_max = spi->max_speed_hz; ++ ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ host->spi = spi; ++ ++ host->ones = ones; ++ ++ /* Platform data is used to hook up things like card sensing ++ * and power switching gpios. ++ */ ++ host->pdata = spi->dev.platform_data; ++ if (host->pdata) ++ mmc->ocr_avail = host->pdata->ocr_mask; ++ if (!mmc->ocr_avail) { ++ dev_warn(&spi->dev, "ASSUMING 3.2-3.4 V slot power\n"); ++ mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; ++ } ++ if (host->pdata && host->pdata->setpower) { ++ host->powerup_msecs = host->pdata->powerup_msecs; ++ if (!host->powerup_msecs || host->powerup_msecs > 250) ++ host->powerup_msecs = 250; ++ } ++ ++ dev_set_drvdata(&spi->dev, mmc); ++ ++ /* preallocate dma buffers */ ++ host->data = kmalloc(sizeof(*host->data), GFP_KERNEL); ++ if (!host->data) ++ goto fail_nobuf1; ++ ++ if (spi->master->dev.parent->dma_mask) { ++ struct device *dev = spi->master->dev.parent; ++ ++ host->dma_dev = dev; ++ host->ones_dma = dma_map_single(dev, ones, ++ MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE); ++ host->data_dma = dma_map_single(dev, host->data, ++ sizeof(*host->data), DMA_BIDIRECTIONAL); ++ ++ /* REVISIT in theory those map operations can fail... */ ++ ++ dma_sync_single_for_cpu(host->dma_dev, ++ host->data_dma, sizeof(*host->data), ++ DMA_BIDIRECTIONAL); ++ } ++ ++ /* setup message for status/busy readback */ ++ spi_message_init(&host->readback); ++ host->readback.is_dma_mapped = (host->dma_dev != NULL); ++ ++ spi_message_add_tail(&host->status, &host->readback); ++ host->status.tx_buf = host->ones; ++ host->status.tx_dma = host->ones_dma; ++ host->status.rx_buf = &host->data->status; ++ host->status.rx_dma = host->data_dma + offsetof(struct scratch, status); ++ host->status.cs_change = 1; ++ ++ /* register card detect irq */ ++ if (host->pdata && host->pdata->init) { ++ status = host->pdata->init(&spi->dev, mmc_spi_detect_irq, mmc); ++ if (status != 0) ++ goto fail_glue_init; ++ } ++ ++ status = mmc_add_host(mmc); ++ if (status != 0) ++ goto fail_add_host; ++ ++ dev_info(&spi->dev, "SD/MMC host %s%s%s%s\n", ++ mmc->class_dev.bus_id, ++ host->dma_dev ? "" : ", no DMA", ++ (host->pdata && host->pdata->get_ro) ++ ? "" : ", no WP", ++ (host->pdata && host->pdata->setpower) ++ ? "" : ", no poweroff"); ++ return 0; ++ ++fail_add_host: ++ mmc_remove_host (mmc); ++fail_glue_init: ++ if (host->dma_dev) ++ dma_unmap_single(host->dma_dev, host->data_dma, ++ sizeof(*host->data), DMA_BIDIRECTIONAL); ++ kfree(host->data); ++ ++fail_nobuf1: ++ mmc_free_host(mmc); ++ dev_set_drvdata(&spi->dev, NULL); ++ ++nomem: ++ kfree(ones); ++ return status; ++} ++ ++ ++static int __devexit mmc_spi_remove(struct spi_device *spi) ++{ ++ struct mmc_host *mmc = dev_get_drvdata(&spi->dev); ++ struct mmc_spi_host *host; ++ ++ if (mmc) { ++ host = mmc_priv(mmc); ++ ++ /* prevent new mmc_detect_change() calls */ ++ if (host->pdata && host->pdata->exit) ++ host->pdata->exit(&spi->dev, mmc); ++ ++ mmc_remove_host(mmc); ++ ++ if (host->dma_dev) { ++ dma_unmap_single(host->dma_dev, host->ones_dma, ++ MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE); ++ dma_unmap_single(host->dma_dev, host->data_dma, ++ sizeof(*host->data), DMA_BIDIRECTIONAL); ++ } ++ ++ kfree(host->data); ++ kfree(host->ones); ++ ++ spi->max_speed_hz = mmc->f_max; ++ mmc_free_host(mmc); ++ dev_set_drvdata(&spi->dev, NULL); ++ } ++ return 0; ++} ++ ++ ++static struct spi_driver mmc_spi_driver = { ++ .driver = { ++ .name = "mmc_spi", ++ .bus = &spi_bus_type, ++ .owner = THIS_MODULE, ++ }, ++ .probe = mmc_spi_probe, ++ .remove = __devexit_p(mmc_spi_remove), ++}; ++ ++ ++static int __init mmc_spi_init(void) ++{ ++ return spi_register_driver(&mmc_spi_driver); ++} ++module_init(mmc_spi_init); ++ ++ ++static void __exit mmc_spi_exit(void) ++{ ++ spi_unregister_driver(&mmc_spi_driver); ++} ++module_exit(mmc_spi_exit); ++ ++ ++MODULE_AUTHOR("Mike Lavender, David Brownell, " ++ "Hans-Peter Nilsson, Jan Nikitenko"); ++MODULE_DESCRIPTION("SPI SD/MMC host driver"); ++MODULE_LICENSE("GPL"); +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/omap.c linux-2.6.22.18-mv/drivers/mmc/host/omap.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/omap.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/omap.c 2008-01-29 03:26:10.000000000 -0800 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/media/mmc/omap.c ++ * linux/drivers/mmc/host/omap.c + * + * Copyright (C) 2004 Nokia Corporation + * Written by Tuukka Tikkanen and Juha Yrj?l? +@@ -24,10 +24,10 @@ + #include + #include + #include ++#include + + #include + #include +-#include + #include + + #include +@@ -263,7 +263,7 @@ + enum dma_data_direction dma_data_dir; + + BUG_ON(host->dma_ch < 0); +- if (data->error != MMC_ERR_NONE) ++ if (data->error) + omap_stop_dma(host->dma_ch); + /* Release DMA channel lazily */ + mod_timer(&host->dma_timer, jiffies + HZ); +@@ -368,7 +368,7 @@ + } + } + +- if (host->data == NULL || cmd->error != MMC_ERR_NONE) { ++ if (host->data == NULL || cmd->error) { + host->mrq = NULL; + clk_disable(host->fclk); + mmc_request_done(host->mmc, cmd->mrq); +@@ -383,7 +383,7 @@ + + sg = host->data->sg + host->sg_idx; + host->buffer_bytes_left = sg->length; +- host->buffer = page_address(sg->page) + sg->offset; ++ host->buffer = sg_virt(sg); + if (host->buffer_bytes_left > host->total_bytes_left) + host->buffer_bytes_left = host->total_bytes_left; + } +@@ -475,14 +475,14 @@ + if (status & OMAP_MMC_STAT_DATA_TOUT) { + dev_dbg(mmc_dev(host->mmc), "data timeout\n"); + if (host->data) { +- host->data->error |= MMC_ERR_TIMEOUT; ++ host->data->error = -ETIMEDOUT; + transfer_error = 1; + } + } + + if (status & OMAP_MMC_STAT_DATA_CRC) { + if (host->data) { +- host->data->error |= MMC_ERR_BADCRC; ++ host->data->error = -EILSEQ; + dev_dbg(mmc_dev(host->mmc), + "data CRC error, bytes left %d\n", + host->total_bytes_left); +@@ -504,7 +504,7 @@ + dev_err(mmc_dev(host->mmc), + "command timeout, CMD %d\n", + host->cmd->opcode); +- host->cmd->error = MMC_ERR_TIMEOUT; ++ host->cmd->error = -ETIMEDOUT; + end_command = 1; + } + } +@@ -514,7 +514,7 @@ + dev_err(mmc_dev(host->mmc), + "command CRC error (CMD%d, arg 0x%08x)\n", + host->cmd->opcode, host->cmd->arg); +- host->cmd->error = MMC_ERR_BADCRC; ++ host->cmd->error = -EILSEQ; + end_command = 1; + } else + dev_err(mmc_dev(host->mmc), +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/pxamci.c linux-2.6.22.18-mv/drivers/mmc/host/pxamci.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/pxamci.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/pxamci.c 2008-01-29 03:26:10.000000000 -0800 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/pxa.c - PXA MMCI driver ++ * linux/drivers/mmc/host/pxa.c - PXA MMCI driver + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * +@@ -23,11 +23,12 @@ + #include + #include + #include ++#include ++#include + #include + + #include + #include +-#include + #include + + #include +@@ -44,6 +45,8 @@ + spinlock_t lock; + struct resource *res; + void __iomem *base; ++ struct clk *clk; ++ unsigned long clkrate; + int irq; + int dma; + unsigned int clkrt; +@@ -119,7 +122,7 @@ + writel(nob, host->base + MMC_NOB); + writel(data->blksz, host->base + MMC_BLKLEN); + +- clks = (unsigned long long)data->timeout_ns * CLOCKRATE; ++ clks = (unsigned long long)data->timeout_ns * host->clkrate; + do_div(clks, 1000000000UL); + timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt); + writel((timeout + 255) / 256, host->base + MMC_RDTO); +@@ -142,6 +145,10 @@ + host->dma_dir); + + for (i = 0; i < host->dma_len; i++) { ++ unsigned int length = sg_dma_len(&data->sg[i]); ++ host->sg_cpu[i].dcmd = dcmd | length; ++ if (length & 31 && !(data->flags & MMC_DATA_READ)) ++ host->sg_cpu[i].dcmd |= DCMD_ENDIRQEN; + if (data->flags & MMC_DATA_READ) { + host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO; + host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); +@@ -149,7 +156,6 @@ + host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]); + host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO; + } +- host->sg_cpu[i].dcmd = dcmd | sg_dma_len(&data->sg[i]); + host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) * + sizeof(struct pxa_dma_desc); + } +@@ -226,7 +232,7 @@ + } + + if (stat & STAT_TIME_OUT_RESPONSE) { +- cmd->error = MMC_ERR_TIMEOUT; ++ cmd->error = -ETIMEDOUT; + } else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) { + #ifdef CONFIG_PXA27x + /* +@@ -239,11 +245,11 @@ + pr_debug("ignoring CRC from command %d - *risky*\n", cmd->opcode); + } else + #endif +- cmd->error = MMC_ERR_BADCRC; ++ cmd->error = -EILSEQ; + } + + pxamci_disable_irq(host, END_CMD_RES); +- if (host->data && cmd->error == MMC_ERR_NONE) { ++ if (host->data && !cmd->error) { + pxamci_enable_irq(host, DATA_TRAN_DONE); + } else { + pxamci_finish_request(host, host->mrq); +@@ -264,9 +270,9 @@ + host->dma_dir); + + if (stat & STAT_READ_TIME_OUT) +- data->error = MMC_ERR_TIMEOUT; ++ data->error = -ETIMEDOUT; + else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR)) +- data->error = MMC_ERR_BADCRC; ++ data->error = -EILSEQ; + + /* + * There appears to be a hardware design bug here. There seems to +@@ -274,7 +280,7 @@ + * This means that if there was an error on any block, we mark all + * data blocks as being in error. + */ +- if (data->error == MMC_ERR_NONE) ++ if (!data->error) + data->bytes_xfered = data->blocks * data->blksz; + else + data->bytes_xfered = 0; +@@ -284,7 +290,7 @@ + host->data = NULL; + if (host->mrq->stop) { + pxamci_stop_clock(host); +- pxamci_start_cmd(host, host->mrq->stop, 0); ++ pxamci_start_cmd(host, host->mrq->stop, host->cmdat); + } else { + pxamci_finish_request(host, host->mrq); + } +@@ -298,7 +304,7 @@ + unsigned int ireg; + int handled = 0; + +- ireg = readl(host->base + MMC_I_REG); ++ ireg = readl(host->base + MMC_I_REG) & ~readl(host->base + MMC_I_MASK); + + if (ireg) { + unsigned stat = readl(host->base + MMC_STAT); +@@ -309,6 +315,10 @@ + handled |= pxamci_cmd_done(host, stat); + if (ireg & DATA_TRAN_DONE) + handled |= pxamci_data_done(host, stat); ++ if (ireg & SDIO_INT) { ++ mmc_signal_sdio_irq(host->mmc); ++ handled = 1; ++ } + } + + return IRQ_RETVAL(handled); +@@ -358,18 +368,25 @@ + struct pxamci_host *host = mmc_priv(mmc); + + if (ios->clock) { +- unsigned int clk = CLOCKRATE / ios->clock; +- if (CLOCKRATE / clk > ios->clock) ++ unsigned long rate = host->clkrate; ++ unsigned int clk = rate / ios->clock; ++ ++ /* ++ * clk might result in a lower divisor than we ++ * desire. check for that condition and adjust ++ * as appropriate. ++ */ ++ if (rate / clk > ios->clock) + clk <<= 1; + host->clkrt = fls(clk) - 1; +- pxa_set_cken(CKEN_MMC, 1); ++ clk_enable(host->clk); + + /* + * we write clkrt on the next command + */ + } else { + pxamci_stop_clock(host); +- pxa_set_cken(CKEN_MMC, 0); ++ clk_disable(host->clk); + } + + if (host->power_mode != ios->power_mode) { +@@ -382,20 +399,46 @@ + host->cmdat |= CMDAT_INIT; + } + ++ if (ios->bus_width == MMC_BUS_WIDTH_4) ++ host->cmdat |= CMDAT_SD_4DAT; ++ else ++ host->cmdat &= ~CMDAT_SD_4DAT; ++ + pr_debug("PXAMCI: clkrt = %x cmdat = %x\n", + host->clkrt, host->cmdat); + } + ++static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable) ++{ ++ struct pxamci_host *pxa_host = mmc_priv(host); ++ ++ if (enable) ++ pxamci_enable_irq(pxa_host, SDIO_INT); ++ else ++ pxamci_disable_irq(pxa_host, SDIO_INT); ++} ++ + static const struct mmc_host_ops pxamci_ops = { +- .request = pxamci_request, +- .get_ro = pxamci_get_ro, +- .set_ios = pxamci_set_ios, ++ .request = pxamci_request, ++ .get_ro = pxamci_get_ro, ++ .set_ios = pxamci_set_ios, ++ .enable_sdio_irq = pxamci_enable_sdio_irq, + }; + + static void pxamci_dma_irq(int dma, void *devid) + { +- printk(KERN_ERR "DMA%d: IRQ???\n", dma); +- DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; ++ struct pxamci_host *host = devid; ++ int dcsr = DCSR(dma); ++ DCSR(dma) = dcsr & ~DCSR_STOPIRQEN; ++ ++ if (dcsr & DCSR_ENDINTR) { ++ writel(BUF_PART_FULL, host->base + MMC_PRTBUF); ++ } else { ++ printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", ++ mmc_hostname(host->mmc), dma, dcsr); ++ host->data->error = -EIO; ++ pxamci_data_done(host, 0); ++ } + } + + static irqreturn_t pxamci_detect_irq(int irq, void *devid) +@@ -429,8 +472,6 @@ + } + + mmc->ops = &pxamci_ops; +- mmc->f_min = CLOCKRATE_MIN; +- mmc->f_max = CLOCKRATE_MAX; + + /* + * We can do SG-DMA, but we don't because we never know how much +@@ -444,9 +485,9 @@ + mmc->max_seg_size = PAGE_SIZE; + + /* +- * Block length register is 10 bits. ++ * Block length register is only 10 bits before PXA27x. + */ +- mmc->max_blk_size = 1023; ++ mmc->max_blk_size = (cpu_is_pxa21x() || cpu_is_pxa25x()) ? 1023 : 2048; + + /* + * Block count register is 16 bits. +@@ -457,9 +498,31 @@ + host->mmc = mmc; + host->dma = -1; + host->pdata = pdev->dev.platform_data; ++ ++ host->clk = clk_get(&pdev->dev, "MMCCLK"); ++ if (IS_ERR(host->clk)) { ++ ret = PTR_ERR(host->clk); ++ host->clk = NULL; ++ goto out; ++ } ++ ++ host->clkrate = clk_get_rate(host->clk); ++ ++ /* ++ * Calculate minimum clock rate, rounding up. ++ */ ++ mmc->f_min = (host->clkrate + 63) / 64; ++ mmc->f_max = host->clkrate; ++ + mmc->ocr_avail = host->pdata ? + host->pdata->ocr_mask : + MMC_VDD_32_33|MMC_VDD_33_34; ++ mmc->caps = 0; ++ host->cmdat = 0; ++ if (!cpu_is_pxa21x() && !cpu_is_pxa25x()) { ++ mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; ++ host->cmdat |= CMDAT_SDIO_INT_EN; ++ } + + host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); + if (!host->sg_cpu) { +@@ -515,6 +578,8 @@ + iounmap(host->base); + if (host->sg_cpu) + dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); ++ if (host->clk) ++ clk_put(host->clk); + } + if (mmc) + mmc_free_host(mmc); +@@ -549,6 +614,8 @@ + iounmap(host->base); + dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + ++ clk_put(host->clk); ++ + release_resource(host->res); + + mmc_free_host(mmc); +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/pxamci.h linux-2.6.22.18-mv/drivers/mmc/host/pxamci.h +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/pxamci.h 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/pxamci.h 2008-01-29 03:26:10.000000000 -0800 +@@ -1,25 +1,3 @@ +-#undef MMC_STRPCL +-#undef MMC_STAT +-#undef MMC_CLKRT +-#undef MMC_SPI +-#undef MMC_CMDAT +-#undef MMC_RESTO +-#undef MMC_RDTO +-#undef MMC_BLKLEN +-#undef MMC_NOB +-#undef MMC_PRTBUF +-#undef MMC_I_MASK +-#undef END_CMD_RES +-#undef PRG_DONE +-#undef DATA_TRAN_DONE +-#undef MMC_I_REG +-#undef MMC_CMD +-#undef MMC_ARGH +-#undef MMC_ARGL +-#undef MMC_RES +-#undef MMC_RXFIFO +-#undef MMC_TXFIFO +- + #define MMC_STRPCL 0x0000 + #define STOP_CLOCK (1 << 0) + #define START_CLOCK (2 << 0) +@@ -47,6 +25,8 @@ + #define SPI_EN (1 << 0) + + #define MMC_CMDAT 0x0010 ++#define CMDAT_SDIO_INT_EN (1 << 11) ++#define CMDAT_SD_4DAT (1 << 8) + #define CMDAT_DMAEN (1 << 7) + #define CMDAT_INIT (1 << 6) + #define CMDAT_BUSY (1 << 5) +@@ -108,17 +88,3 @@ + #define MMC_RXFIFO 0x0040 /* 8 bit */ + + #define MMC_TXFIFO 0x0044 /* 8 bit */ +- +-/* +- * The base MMC clock rate +- */ +-#ifdef CONFIG_PXA27x +-#define CLOCKRATE_MIN 304688 +-#define CLOCKRATE_MAX 19500000 +-#else +-#define CLOCKRATE_MIN 312500 +-#define CLOCKRATE_MAX 20000000 +-#endif +- +-#define CLOCKRATE CLOCKRATE_MAX +- +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/ricoh_mmc.c linux-2.6.22.18-mv/drivers/mmc/host/ricoh_mmc.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/ricoh_mmc.c 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/ricoh_mmc.c 2008-01-29 03:26:10.000000000 -0800 +@@ -0,0 +1,151 @@ ++/* ++ * ricoh_mmc.c - Dummy driver to disable the Rioch MMC controller. ++ * ++ * Copyright (C) 2007 Philip Langdale, 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 as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++/* ++ * This is a conceptually ridiculous driver, but it is required by the way ++ * the Ricoh multi-function R5C832 works. This chip implements firewire ++ * and four different memory card controllers. Two of those controllers are ++ * an SDHCI controller and a proprietary MMC controller. The linux SDHCI ++ * driver supports MMC cards but the chip detects MMC cards in hardware ++ * and directs them to the MMC controller - so the SDHCI driver never sees ++ * them. To get around this, we must disable the useless MMC controller. ++ * At that point, the SDHCI controller will start seeing them. As a bonus, ++ * a detection event occurs immediately, even if the MMC card is already ++ * in the reader. ++ * ++ * The relevant registers live on the firewire function, so this is unavoidably ++ * ugly. Such is life. ++ */ ++ ++#include ++ ++#define DRIVER_NAME "ricoh-mmc" ++ ++static const struct pci_device_id pci_ids[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_RICOH, ++ .device = PCI_DEVICE_ID_RICOH_R5C843, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { /* end: all zeroes */ }, ++}; ++ ++MODULE_DEVICE_TABLE(pci, pci_ids); ++ ++static int __devinit ricoh_mmc_probe(struct pci_dev *pdev, ++ const struct pci_device_id *ent) ++{ ++ u8 rev; ++ ++ struct pci_dev *fw_dev = NULL; ++ ++ BUG_ON(pdev == NULL); ++ BUG_ON(ent == NULL); ++ ++ pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev); ++ ++ printk(KERN_INFO DRIVER_NAME ++ ": Ricoh MMC controller found at %s [%04x:%04x] (rev %x)\n", ++ pci_name(pdev), (int)pdev->vendor, (int)pdev->device, ++ (int)rev); ++ ++ while ((fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) { ++ if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) && ++ pdev->bus == fw_dev->bus) { ++ u8 write_enable; ++ u8 disable; ++ ++ pci_read_config_byte(fw_dev, 0xCB, &disable); ++ if (disable & 0x02) { ++ printk(KERN_INFO DRIVER_NAME ++ ": Controller already disabled. Nothing to do.\n"); ++ return -ENODEV; ++ } ++ ++ pci_read_config_byte(fw_dev, 0xCA, &write_enable); ++ pci_write_config_byte(fw_dev, 0xCA, 0x57); ++ pci_write_config_byte(fw_dev, 0xCB, disable | 0x02); ++ pci_write_config_byte(fw_dev, 0xCA, write_enable); ++ ++ pci_set_drvdata(pdev, fw_dev); ++ ++ printk(KERN_INFO DRIVER_NAME ++ ": Controller is now disabled.\n"); ++ ++ break; ++ } ++ } ++ ++ if (pci_get_drvdata(pdev) == NULL) { ++ printk(KERN_WARNING DRIVER_NAME ++ ": Main firewire function not found. Cannot disable controller.\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static void __devexit ricoh_mmc_remove(struct pci_dev *pdev) ++{ ++ u8 write_enable; ++ u8 disable; ++ struct pci_dev *fw_dev = NULL; ++ ++ fw_dev = pci_get_drvdata(pdev); ++ BUG_ON(fw_dev == NULL); ++ ++ pci_read_config_byte(fw_dev, 0xCA, &write_enable); ++ pci_read_config_byte(fw_dev, 0xCB, &disable); ++ pci_write_config_byte(fw_dev, 0xCA, 0x57); ++ pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02); ++ pci_write_config_byte(fw_dev, 0xCA, write_enable); ++ ++ printk(KERN_INFO DRIVER_NAME ++ ": Controller is now re-enabled.\n"); ++ ++ pci_set_drvdata(pdev, NULL); ++} ++ ++static struct pci_driver ricoh_mmc_driver = { ++ .name = DRIVER_NAME, ++ .id_table = pci_ids, ++ .probe = ricoh_mmc_probe, ++ .remove = __devexit_p(ricoh_mmc_remove), ++}; ++ ++/*****************************************************************************\ ++ * * ++ * Driver init/exit * ++ * * ++\*****************************************************************************/ ++ ++static int __init ricoh_mmc_drv_init(void) ++{ ++ printk(KERN_INFO DRIVER_NAME ++ ": Ricoh MMC Controller disabling driver\n"); ++ printk(KERN_INFO DRIVER_NAME ": Copyright(c) Philip Langdale\n"); ++ ++ return pci_register_driver(&ricoh_mmc_driver); ++} ++ ++static void __exit ricoh_mmc_drv_exit(void) ++{ ++ pci_unregister_driver(&ricoh_mmc_driver); ++} ++ ++module_init(ricoh_mmc_drv_init); ++module_exit(ricoh_mmc_drv_exit); ++ ++MODULE_AUTHOR("Philip Langdale "); ++MODULE_DESCRIPTION("Ricoh MMC Controller disabling driver"); ++MODULE_LICENSE("GPL"); ++ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/sdhci.c linux-2.6.22.18-mv/drivers/mmc/host/sdhci.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/sdhci.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/sdhci.c 2008-01-29 03:26:10.000000000 -0800 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/sdhci.c - Secure Digital Host Controller Interface driver ++ * linux/drivers/mmc/host/sdhci.c - Secure Digital Host Controller Interface driver + * + * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. + * +@@ -7,17 +7,20 @@ + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. ++ * ++ * Thanks to the following companies for their support: ++ * ++ * - JMicron (hardware and technical support) + */ + + #include + #include + #include + #include ++#include + + #include + +-#include +- + #include "sdhci.h" + + #define DRIVER_NAME "sdhci" +@@ -25,15 +28,31 @@ + #define DBG(f, x...) \ + pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) + +-static unsigned int debug_nodma = 0; +-static unsigned int debug_forcedma = 0; + static unsigned int debug_quirks = 0; + ++/* ++ * Different quirks to handle when the hardware deviates from a strict ++ * interpretation of the SDHCI specification. ++ */ ++ ++/* Controller doesn't honor resets unless we touch the clock register */ + #define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0) ++/* Controller has bad caps bits, but really supports DMA */ + #define SDHCI_QUIRK_FORCE_DMA (1<<1) + /* Controller doesn't like some resets when there is no card inserted. */ + #define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) ++/* Controller doesn't like clearing the power reg before a change */ + #define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3) ++/* Controller has flaky internal state so reset it on each ios change */ ++#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4) ++/* Controller has an unusable DMA engine */ ++#define SDHCI_QUIRK_BROKEN_DMA (1<<5) ++/* Controller can only DMA from 32-bit aligned addresses */ ++#define SDHCI_QUIRK_32BIT_DMA_ADDR (1<<6) ++/* Controller can only DMA chunk sizes that are a multiple of 32 bits */ ++#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<7) ++/* Controller needs to be reset after each request to stay stable */ ++#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<8) + + static const struct pci_device_id pci_ids[] __devinitdata = { + { +@@ -67,9 +86,56 @@ + .device = PCI_DEVICE_ID_ENE_CB712_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, +- .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE, ++ .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE | ++ SDHCI_QUIRK_BROKEN_DMA, ++ }, ++ { ++ .vendor = 0x1947, ++ .device = 0x4743, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = 0, ++ }, ++ ++#if 0 ++ { ++ .vendor = PCI_VENDOR_ID_ENE, ++ .device = PCI_DEVICE_ID_ENE_CB712_SD_2, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE | ++ SDHCI_QUIRK_BROKEN_DMA, ++ }, ++ ++ { ++ .vendor = PCI_VENDOR_ID_ENE, ++ .device = PCI_DEVICE_ID_ENE_CB714_SD, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE | ++ SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS, + }, + ++ { ++ .vendor = PCI_VENDOR_ID_ENE, ++ .device = PCI_DEVICE_ID_ENE_CB714_SD_2, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE | ++ SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS, ++ }, ++ ++ { ++ .vendor = PCI_VENDOR_ID_JMICRON, ++ .device = PCI_DEVICE_ID_JMICRON_JMB38X_SD, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SDHCI_QUIRK_32BIT_DMA_ADDR | ++ SDHCI_QUIRK_32BIT_DMA_SIZE | ++ SDHCI_QUIRK_RESET_AFTER_REQUEST, ++ }, ++#endif ++ + { /* Generic SD host controller */ + PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) + }, +@@ -105,7 +171,7 @@ + readb(host->ioaddr + SDHCI_POWER_CONTROL), + readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL)); + printk(KERN_DEBUG DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", +- readb(host->ioaddr + SDHCI_WALK_UP_CONTROL), ++ readb(host->ioaddr + SDHCI_WAKE_UP_CONTROL), + readw(host->ioaddr + SDHCI_CLOCK_CONTROL)); + printk(KERN_DEBUG DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", + readb(host->ioaddr + SDHCI_TIMEOUT_CONTROL), +@@ -203,7 +269,7 @@ + + static inline char* sdhci_sg_to_buffer(struct sdhci_host* host) + { +- return page_address(host->cur_sg->page) + host->cur_sg->offset; ++ return sg_virt_addr(host->cur_sg); + } + + static inline int sdhci_next_sg(struct sdhci_host* host) +@@ -353,16 +419,14 @@ + if (data == NULL) + return; + +- DBG("blksz %04x blks %04x flags %08x\n", +- data->blksz, data->blocks, data->flags); +- DBG("tsac %d ms nsac %d clk\n", +- data->timeout_ns / 1000000, data->timeout_clks); +- + /* Sanity checks */ + BUG_ON(data->blksz * data->blocks > 524288); + BUG_ON(data->blksz > host->mmc->max_blk_size); + BUG_ON(data->blocks > 65535); + ++ host->data = data; ++ host->data_early = 0; ++ + /* timeout in us */ + target_timeout = data->timeout_ns / 1000 + + data->timeout_clks / host->clock; +@@ -394,7 +458,29 @@ + + writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL); + +- if (host->flags & SDHCI_USE_DMA) { ++ if (host->flags & SDHCI_USE_DMA) ++ host->flags |= SDHCI_REQ_USE_DMA; ++ ++ if (unlikely((host->flags & SDHCI_REQ_USE_DMA) && ++ (host->chip->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE) && ++ ((data->blksz * data->blocks) & 0x3))) { ++ DBG("Reverting to PIO because of transfer size (%d)\n", ++ data->blksz * data->blocks); ++ host->flags &= ~SDHCI_REQ_USE_DMA; ++ } ++ ++ /* ++ * The assumption here being that alignment is the same after ++ * translation to device address space. ++ */ ++ if (unlikely((host->flags & SDHCI_REQ_USE_DMA) && ++ (host->chip->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && ++ (data->sg->offset & 0x3))) { ++ DBG("Reverting to PIO because of bad alignment\n"); ++ host->flags &= ~SDHCI_REQ_USE_DMA; ++ } ++ ++ if (host->flags & SDHCI_REQ_USE_DMA) { + int count; + + count = pci_map_sg(host->chip->pdev, data->sg, data->sg_len, +@@ -421,17 +507,17 @@ + { + u16 mode; + +- WARN_ON(host->data); +- + if (data == NULL) + return; + ++ WARN_ON(!host->data); ++ + mode = SDHCI_TRNS_BLK_CNT_EN; + if (data->blocks > 1) + mode |= SDHCI_TRNS_MULTI; + if (data->flags & MMC_DATA_READ) + mode |= SDHCI_TRNS_READ; +- if (host->flags & SDHCI_USE_DMA) ++ if (host->flags & SDHCI_REQ_USE_DMA) + mode |= SDHCI_TRNS_DMA; + + writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE); +@@ -447,7 +533,7 @@ + data = host->data; + host->data = NULL; + +- if (host->flags & SDHCI_USE_DMA) { ++ if (host->flags & SDHCI_REQ_USE_DMA) { + pci_unmap_sg(host->chip->pdev, data->sg, data->sg_len, + (data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE); + } +@@ -455,27 +541,25 @@ + /* + * Controller doesn't count down when in single block mode. + */ +- if ((data->blocks == 1) && (data->error == MMC_ERR_NONE)) +- blocks = 0; ++ if (data->blocks == 1) ++ blocks = (data->error == 0) ? 0 : 1; + else + blocks = readw(host->ioaddr + SDHCI_BLOCK_COUNT); + data->bytes_xfered = data->blksz * (data->blocks - blocks); + +- if ((data->error == MMC_ERR_NONE) && blocks) { ++ if (!data->error && blocks) { + printk(KERN_ERR "%s: Controller signalled completion even " + "though there were blocks left.\n", + mmc_hostname(host->mmc)); +- data->error = MMC_ERR_FAILED; ++ data->error = -EIO; + } + +- DBG("Ending data transfer (%d bytes)\n", data->bytes_xfered); +- + if (data->stop) { + /* + * The controller needs a reset of internal state machines + * upon error conditions. + */ +- if (data->error != MMC_ERR_NONE) { ++ if (data->error) { + sdhci_reset(host, SDHCI_RESET_CMD); + sdhci_reset(host, SDHCI_RESET_DATA); + } +@@ -493,8 +577,6 @@ + + WARN_ON(host->cmd); + +- DBG("Sending cmd (%x)\n", cmd->opcode); +- + /* Wait max 10 ms */ + timeout = 10; + +@@ -512,7 +594,7 @@ + printk(KERN_ERR "%s: Controller never released " + "inhibit bit(s).\n", mmc_hostname(host->mmc)); + sdhci_dumpregs(host); +- cmd->error = MMC_ERR_FAILED; ++ cmd->error = -EIO; + tasklet_schedule(&host->finish_tasklet); + return; + } +@@ -533,7 +615,7 @@ + if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { + printk(KERN_ERR "%s: Unsupported response type!\n", + mmc_hostname(host->mmc)); +- cmd->error = MMC_ERR_INVALID; ++ cmd->error = -EINVAL; + tasklet_schedule(&host->finish_tasklet); + return; + } +@@ -580,13 +662,12 @@ + } + } + +- host->cmd->error = MMC_ERR_NONE; ++ host->cmd->error = 0; + +- DBG("Ending cmd (%x)\n", host->cmd->opcode); ++ if (host->data && host->data_early) ++ sdhci_finish_data(host); + +- if (host->cmd->data) +- host->data = host->cmd->data; +- else ++ if (!host->cmd->data) + tasklet_schedule(&host->finish_tasklet); + + host->cmd = NULL; +@@ -598,6 +679,7 @@ + u16 clk; + unsigned long timeout; + ++ printk("sdhci_set_clock: clock=%d\n",clock); + if (clock == host->clock) + return; + +@@ -702,7 +784,7 @@ + host->mrq = mrq; + + if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { +- host->mrq->cmd->error = MMC_ERR_TIMEOUT; ++ host->mrq->cmd->error = -ENOMEDIUM; + tasklet_schedule(&host->finish_tasklet); + } else + sdhci_send_command(host, mrq->cmd); +@@ -751,6 +833,14 @@ + + writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); + ++ /* ++ * Some (ENE) controllers go apeshit on some ios operation, ++ * signalling timeout and CRC errors even on CMD0. Resetting ++ * it on each ios seems to solve the problem. ++ */ ++ if(host->chip->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) ++ sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); ++ + mmiowb(); + spin_unlock_irqrestore(&host->lock, flags); + } +@@ -772,10 +862,35 @@ + return !(present & SDHCI_WRITE_PROTECT); + } + ++static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) ++{ ++ struct sdhci_host *host; ++ unsigned long flags; ++ u32 ier; ++ ++ host = mmc_priv(mmc); ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ ier = readl(host->ioaddr + SDHCI_INT_ENABLE); ++ ++ ier &= ~SDHCI_INT_CARD_INT; ++ if (enable) ++ ier |= SDHCI_INT_CARD_INT; ++ ++ writel(ier, host->ioaddr + SDHCI_INT_ENABLE); ++ writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE); ++ ++ mmiowb(); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ + static const struct mmc_host_ops sdhci_ops = { + .request = sdhci_request, + .set_ios = sdhci_set_ios, + .get_ro = sdhci_get_ro, ++ .enable_sdio_irq = sdhci_enable_sdio_irq, + }; + + /*****************************************************************************\ +@@ -803,7 +918,7 @@ + sdhci_reset(host, SDHCI_RESET_CMD); + sdhci_reset(host, SDHCI_RESET_DATA); + +- host->mrq->cmd->error = MMC_ERR_FAILED; ++ host->mrq->cmd->error = -ENOMEDIUM; + tasklet_schedule(&host->finish_tasklet); + } + } +@@ -827,15 +942,14 @@ + + mrq = host->mrq; + +- DBG("Ending request, cmd (%x)\n", mrq->cmd->opcode); +- + /* + * The controller needs a reset of internal state machines + * upon error conditions. + */ +- if ((mrq->cmd->error != MMC_ERR_NONE) || +- (mrq->data && ((mrq->data->error != MMC_ERR_NONE) || +- (mrq->data->stop && (mrq->data->stop->error != MMC_ERR_NONE))))) { ++ if (mrq->cmd->error || ++ (mrq->data && (mrq->data->error || ++ (mrq->data->stop && mrq->data->stop->error))) || ++ (host->chip->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) { + + /* Some controllers need this kick or reset won't work here */ + if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) { +@@ -880,13 +994,13 @@ + sdhci_dumpregs(host); + + if (host->data) { +- host->data->error = MMC_ERR_TIMEOUT; ++ host->data->error = -ETIMEDOUT; + sdhci_finish_data(host); + } else { + if (host->cmd) +- host->cmd->error = MMC_ERR_TIMEOUT; ++ host->cmd->error = -ETIMEDOUT; + else +- host->mrq->cmd->error = MMC_ERR_TIMEOUT; ++ host->mrq->cmd->error = -ETIMEDOUT; + + tasklet_schedule(&host->finish_tasklet); + } +@@ -907,27 +1021,23 @@ + BUG_ON(intmask == 0); + + if (!host->cmd) { +- printk(KERN_ERR "%s: Got command interrupt even though no " +- "command operation was in progress.\n", +- mmc_hostname(host->mmc)); ++ printk(KERN_ERR "%s: Got command interrupt 0x%08x even " ++ "though no command operation was in progress.\n", ++ mmc_hostname(host->mmc), (unsigned)intmask); + sdhci_dumpregs(host); + return; + } + +- if (intmask & SDHCI_INT_RESPONSE) +- sdhci_finish_command(host); +- else { +- if (intmask & SDHCI_INT_TIMEOUT) +- host->cmd->error = MMC_ERR_TIMEOUT; +- else if (intmask & SDHCI_INT_CRC) +- host->cmd->error = MMC_ERR_BADCRC; +- else if (intmask & (SDHCI_INT_END_BIT | SDHCI_INT_INDEX)) +- host->cmd->error = MMC_ERR_FAILED; +- else +- host->cmd->error = MMC_ERR_INVALID; ++ if (intmask & SDHCI_INT_TIMEOUT) ++ host->cmd->error = -ETIMEDOUT; ++ else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | ++ SDHCI_INT_INDEX)) ++ host->cmd->error = -EILSEQ; + ++ if (host->cmd->error) + tasklet_schedule(&host->finish_tasklet); +- } ++ else if (intmask & SDHCI_INT_RESPONSE) ++ sdhci_finish_command(host); + } + + static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) +@@ -942,22 +1052,20 @@ + if (intmask & SDHCI_INT_DATA_END) + return; + +- printk(KERN_ERR "%s: Got data interrupt even though no " +- "data operation was in progress.\n", +- mmc_hostname(host->mmc)); ++ printk(KERN_ERR "%s: Got data interrupt 0x%08x even " ++ "though no data operation was in progress.\n", ++ mmc_hostname(host->mmc), (unsigned)intmask); + sdhci_dumpregs(host); + + return; + } + + if (intmask & SDHCI_INT_DATA_TIMEOUT) +- host->data->error = MMC_ERR_TIMEOUT; +- else if (intmask & SDHCI_INT_DATA_CRC) +- host->data->error = MMC_ERR_BADCRC; +- else if (intmask & SDHCI_INT_DATA_END_BIT) +- host->data->error = MMC_ERR_FAILED; ++ host->data->error = -ETIMEDOUT; ++ else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT)) ++ host->data->error = -EILSEQ; + +- if (host->data->error != MMC_ERR_NONE) ++ if (host->data->error) + sdhci_finish_data(host); + else { + if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) +@@ -972,8 +1080,18 @@ + writel(readl(host->ioaddr + SDHCI_DMA_ADDRESS), + host->ioaddr + SDHCI_DMA_ADDRESS); + +- if (intmask & SDHCI_INT_DATA_END) +- sdhci_finish_data(host); ++ if (intmask & SDHCI_INT_DATA_END) { ++ if (host->cmd) { ++ /* ++ * Data managed to finish before the ++ * command completed. Make sure we do ++ * things in the proper order. ++ */ ++ host->data_early = 1; ++ } else { ++ sdhci_finish_data(host); ++ } ++ } + } + } + +@@ -982,6 +1100,7 @@ + irqreturn_t result; + struct sdhci_host* host = dev_id; + u32 intmask; ++ int cardint = 0; + + spin_lock(&host->lock); + +@@ -1016,13 +1135,20 @@ + + intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK); + ++ intmask &= ~SDHCI_INT_ERROR; ++ + if (intmask & SDHCI_INT_BUS_POWER) { + printk(KERN_ERR "%s: Card is consuming too much power!\n", + mmc_hostname(host->mmc)); + writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS); + } + +- intmask &= SDHCI_INT_BUS_POWER; ++ intmask &= ~SDHCI_INT_BUS_POWER; ++ ++ if (intmask & SDHCI_INT_CARD_INT) ++ cardint = 1; ++ ++ intmask &= ~SDHCI_INT_CARD_INT; + + if (intmask) { + printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n", +@@ -1038,6 +1164,12 @@ + out: + spin_unlock(&host->lock); + ++ /* ++ * We have to delay this as it calls back into the driver. ++ */ ++ if (cardint) ++ mmc_signal_sdio_irq(host->mmc); ++ + return result; + } + +@@ -1136,6 +1268,8 @@ + * * + \*****************************************************************************/ + ++static int highspeed=1; ++ + static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) + { + int ret; +@@ -1215,7 +1349,7 @@ + + version = readw(host->ioaddr + SDHCI_HOST_VERSION); + version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; +- if (version != 0) { ++ if (version > 1) { + printk(KERN_ERR "%s: Unknown controller version (%d). " + "You may experience problems.\n", host->slot_descr, + version); +@@ -1223,20 +1357,26 @@ + + caps = readl(host->ioaddr + SDHCI_CAPABILITIES); + +- if (debug_nodma) +- DBG("DMA forced off\n"); +- else if (debug_forcedma) { +- DBG("DMA forced on\n"); +- host->flags |= SDHCI_USE_DMA; +- } else if (chip->quirks & SDHCI_QUIRK_FORCE_DMA) ++ if (chip->quirks & SDHCI_QUIRK_FORCE_DMA) + host->flags |= SDHCI_USE_DMA; +- else if ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) +- DBG("Controller doesn't have DMA interface\n"); + else if (!(caps & SDHCI_CAN_DO_DMA)) + DBG("Controller doesn't have DMA capability\n"); + else + host->flags |= SDHCI_USE_DMA; + ++ if ((chip->quirks & SDHCI_QUIRK_BROKEN_DMA) && ++ (host->flags & SDHCI_USE_DMA)) { ++ DBG("Disabling DMA as it is marked broken\n"); ++ host->flags &= ~SDHCI_USE_DMA; ++ } ++ ++ if (((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) && ++ (host->flags & SDHCI_USE_DMA)) { ++ printk(KERN_WARNING "%s: Will use DMA " ++ "mode even though HW doesn't fully " ++ "claim to support it.\n", host->slot_descr); ++ } ++ + if (host->flags & SDHCI_USE_DMA) { + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { + printk(KERN_WARNING "%s: No suitable DMA available. " +@@ -1277,9 +1417,10 @@ + mmc->ops = &sdhci_ops; + mmc->f_min = host->max_clk / 256; + mmc->f_max = host->max_clk; +- mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; ++ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_SDIO_IRQ; + +- if (caps & SDHCI_CAN_DO_HISPD) ++ ++ if ((caps & SDHCI_CAN_DO_HISPD) && (highspeed)) /* maen */ + mmc->caps |= MMC_CAP_SD_HIGHSPEED; + + mmc->ocr_avail = 0; +@@ -1326,12 +1467,11 @@ + */ + mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT; + if (mmc->max_blk_size >= 3) { +- printk(KERN_ERR "%s: Invalid maximum block size.\n", ++ printk(KERN_WARNING "%s: Invalid maximum block size, assuming 512\n", + host->slot_descr); +- ret = -ENODEV; +- goto unmap; +- } +- mmc->max_blk_size = 512 << mmc->max_blk_size; ++ mmc->max_blk_size = 512; ++ } else ++ mmc->max_blk_size = 512 << mmc->max_blk_size; + + /* + * Maximum block count. +@@ -1531,14 +1671,12 @@ + module_init(sdhci_drv_init); + module_exit(sdhci_drv_exit); + +-module_param(debug_nodma, uint, 0444); +-module_param(debug_forcedma, uint, 0444); ++module_param(highspeed, int, 0); ++ + module_param(debug_quirks, uint, 0444); + + MODULE_AUTHOR("Pierre Ossman "); + MODULE_DESCRIPTION("Secure Digital Host Controller Interface driver"); + MODULE_LICENSE("GPL"); + +-MODULE_PARM_DESC(debug_nodma, "Forcefully disable DMA transfers. (default 0)"); +-MODULE_PARM_DESC(debug_forcedma, "Forcefully enable DMA transfers. (default 0)"); + MODULE_PARM_DESC(debug_quirks, "Force certain quirks."); +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/sdhci.h linux-2.6.22.18-mv/drivers/mmc/host/sdhci.h +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/sdhci.h 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/sdhci.h 2008-01-29 03:26:10.000000000 -0800 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/sdhci.h - Secure Digital Host Controller Interface driver ++ * linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver + * + * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. + * +@@ -81,7 +81,7 @@ + + #define SDHCI_BLOCK_GAP_CONTROL 0x2A + +-#define SDHCI_WALK_UP_CONTROL 0x2B ++#define SDHCI_WAKE_UP_CONTROL 0x2B + + #define SDHCI_CLOCK_CONTROL 0x2C + #define SDHCI_DIVIDER_SHIFT 8 +@@ -107,6 +107,7 @@ + #define SDHCI_INT_CARD_INSERT 0x00000040 + #define SDHCI_INT_CARD_REMOVE 0x00000080 + #define SDHCI_INT_CARD_INT 0x00000100 ++#define SDHCI_INT_ERROR 0x00008000 + #define SDHCI_INT_TIMEOUT 0x00010000 + #define SDHCI_INT_CRC 0x00020000 + #define SDHCI_INT_END_BIT 0x00040000 +@@ -170,7 +171,8 @@ + spinlock_t lock; /* Mutex */ + + int flags; /* Host attributes */ +-#define SDHCI_USE_DMA (1<<0) ++#define SDHCI_USE_DMA (1<<0) /* Host is DMA capable */ ++#define SDHCI_REQ_USE_DMA (1<<1) /* Use DMA for this req. */ + + unsigned int max_clk; /* Max possible freq (MHz) */ + unsigned int timeout_clk; /* Timeout freq (KHz) */ +@@ -181,6 +183,7 @@ + struct mmc_request *mrq; /* Current request */ + struct mmc_command *cmd; /* Current command */ + struct mmc_data *data; /* Current data request */ ++ int data_early:1; /* Data finished before cmd */ + + struct scatterlist *cur_sg; /* We're working on this */ + int num_sg; /* Entries left */ +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/tifm_sd.c linux-2.6.22.18-mv/drivers/mmc/host/tifm_sd.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/tifm_sd.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/tifm_sd.c 2008-01-29 03:26:10.000000000 -0800 +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + + #define DRIVER_NAME "tifm_sd" +@@ -191,7 +192,7 @@ + } + off = sg[host->sg_pos].offset + host->block_pos; + +- pg = nth_page(sg[host->sg_pos].page, off >> PAGE_SHIFT); ++ pg = nth_page(sg_page(&sg[host->sg_pos]), off >> PAGE_SHIFT); + p_off = offset_in_page(off); + p_cnt = PAGE_SIZE - p_off; + p_cnt = min(p_cnt, cnt); +@@ -240,18 +241,18 @@ + } + off = sg[host->sg_pos].offset + host->block_pos; + +- pg = nth_page(sg[host->sg_pos].page, off >> PAGE_SHIFT); ++ pg = nth_page(sg_page(&sg[host->sg_pos]), off >> PAGE_SHIFT); + p_off = offset_in_page(off); + p_cnt = PAGE_SIZE - p_off; + p_cnt = min(p_cnt, cnt); + p_cnt = min(p_cnt, t_size); + + if (r_data->flags & MMC_DATA_WRITE) +- tifm_sd_copy_page(host->bounce_buf.page, ++ tifm_sd_copy_page(sg_page(&host->bounce_buf), + r_data->blksz - t_size, + pg, p_off, p_cnt); + else if (r_data->flags & MMC_DATA_READ) +- tifm_sd_copy_page(pg, p_off, host->bounce_buf.page, ++ tifm_sd_copy_page(pg, p_off, sg_page(&host->bounce_buf), + r_data->blksz - t_size, p_cnt); + + t_size -= p_cnt; +@@ -404,14 +405,14 @@ + struct tifm_dev *sock = host->dev; + struct mmc_command *cmd = host->req->cmd; + +- if (cmd->error != MMC_ERR_NONE) ++ if (cmd->error) + goto finish_request; + + if (!(host->cmd_flags & CMD_READY)) + return; + + if (cmd->data) { +- if (cmd->data->error != MMC_ERR_NONE) { ++ if (cmd->data->error) { + if ((host->cmd_flags & SCMD_ACTIVE) + && !(host->cmd_flags & SCMD_READY)) + return; +@@ -504,7 +505,7 @@ + { + struct tifm_sd *host; + unsigned int host_status = 0; +- int cmd_error = MMC_ERR_NONE; ++ int cmd_error = 0; + struct mmc_command *cmd = NULL; + unsigned long flags; + +@@ -521,15 +522,15 @@ + writel(host_status & TIFM_MMCSD_ERRMASK, + sock->addr + SOCK_MMCSD_STATUS); + if (host_status & TIFM_MMCSD_CTO) +- cmd_error = MMC_ERR_TIMEOUT; ++ cmd_error = -ETIMEDOUT; + else if (host_status & TIFM_MMCSD_CCRC) +- cmd_error = MMC_ERR_BADCRC; ++ cmd_error = -EILSEQ; + + if (cmd->data) { + if (host_status & TIFM_MMCSD_DTO) +- cmd->data->error = MMC_ERR_TIMEOUT; ++ cmd->data->error = -ETIMEDOUT; + else if (host_status & TIFM_MMCSD_DCRC) +- cmd->data->error = MMC_ERR_BADCRC; ++ cmd->data->error = -EILSEQ; + } + + writel(TIFM_FIFO_INT_SETALL, +@@ -626,14 +627,21 @@ + + spin_lock_irqsave(&sock->lock, flags); + if (host->eject) { +- spin_unlock_irqrestore(&sock->lock, flags); ++ mrq->cmd->error = -ENOMEDIUM; + goto err_out; + } + + if (host->req) { + printk(KERN_ERR "%s : unfinished request detected\n", + sock->dev.bus_id); +- spin_unlock_irqrestore(&sock->lock, flags); ++ mrq->cmd->error = -ETIMEDOUT; ++ goto err_out; ++ } ++ ++ if (mrq->data && !is_power_of_2(mrq->data->blksz)) { ++ printk(KERN_ERR "%s: Unsupported block size (%d bytes)\n", ++ sock->dev.bus_id, mrq->data->blksz); ++ mrq->cmd->error = -EINVAL; + goto err_out; + } + +@@ -722,7 +730,7 @@ + return; + + err_out: +- mrq->cmd->error = MMC_ERR_TIMEOUT; ++ spin_unlock_irqrestore(&sock->lock, flags); + mmc_request_done(mmc, mrq); + } + +@@ -1012,9 +1020,9 @@ + writel(TIFM_FIFO_INT_SETALL, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); + writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); +- host->req->cmd->error = MMC_ERR_TIMEOUT; ++ host->req->cmd->error = -ENOMEDIUM; + if (host->req->stop) +- host->req->stop->error = MMC_ERR_TIMEOUT; ++ host->req->stop->error = -ENOMEDIUM; + tasklet_schedule(&host->finish_tasklet); + } + spin_unlock_irqrestore(&sock->lock, flags); +diff -ruN linux-2.6.22.18-mv-clean/drivers/mmc/host/wbsd.c linux-2.6.22.18-mv/drivers/mmc/host/wbsd.c +--- linux-2.6.22.18-mv-clean/drivers/mmc/host/wbsd.c 2008-02-10 23:31:19.000000000 -0800 ++++ linux-2.6.22.18-mv/drivers/mmc/host/wbsd.c 2008-01-29 03:26:10.000000000 -0800 +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver ++ * linux/drivers/mmc/host/wbsd.c - Winbond W83L51xD SD/MMC driver + * + * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved. + * +@@ -33,10 +33,10 @@ + #include + #include + #include ++#include + + #include + #include +-#include + + #include "wbsd.h" + +@@ -207,8 +207,6 @@ + { + unsigned long dmaflags; + +- DBGF("Ending request, cmd (%x)\n", mrq->cmd->opcode); +- + if (host->dma >= 0) { + /* + * Release ISA DMA controller. +@@ -271,7 +269,7 @@ + + static inline char *wbsd_sg_to_buffer(struct wbsd_host *host) + { +- return page_address(host->cur_sg->page) + host->cur_sg->offset; ++ return sg_virt(host->cur_sg); +