openwrtv3/target/linux/ramips/patches-4.14/0046-mmc-MIPS-ralink-add-sdhci-for-mt7620a-SoC.patch
André Draszik ace1686200 ramips: mt7620: eMMC: stop invalid memory access if only one device is defined
pdev->id is -1 when only one device exists, and is used:
* as an index into drv_mode[] to determine whether to use
  PIO or DMA mode (via host->id)
* as an index into msdc_6575_host[], to store the
  mmc_priv() data.

Obviously, -1 is not a valid index in either case, causing
us to read invalid memory, and memory corruption,
respectively.

The invalid memory read is causing non-deterministic
behaviour, in particular in the v4.4 kernel it still
picked DMA mode, but in the v4.9 it now always picks
PIO mode.
Also, PIO mode doesn't work, causing the following:

/ # echo 3 > /proc/sys/vm/drop_caches
[ 3845.249237] sh (128): drop_caches: 3

/ # /root/usr/lib/libc.so
[ 3846.096070] do_page_fault(): sending SIGSEGV to libc.so for invalid read access from 7f9cb5a0
[ 3846.104758] epc = 779b0ea4 in libc.so[7792f000+c3000]
[ 3846.109907] ra  = 779a8004 in libc.so[7792f000+c3000]
Segmentation fault

/ # /root/usr/lib/libc.so
musl libc (mipsel-sf)
Version 1.1.16-git-40-g54807d47
Dynamic Program Loader
Usage: /root/usr/lib/libc.so [options] [--] pathname [args]

(i.e. initial page-in of any binary causes a segfault,
subsequent access works.)

While this change doesn't fix PIO mode, it at least makes
us deterministically use DMA (which works), and it also
stops us from corrupting memory.

Signed-off-by: André Draszik <git@andred.net>
2018-04-04 08:29:17 +02:00

4832 lines
164 KiB
Diff

From 23147af14531cbdada194b94120ef8774f46292d Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Thu, 13 Nov 2014 19:08:40 +0100
Subject: [PATCH 46/53] mmc: MIPS: ralink: add sdhci for mt7620a SoC
Signed-off-by: John Crispin <blogic@openwrt.org>
---
drivers/mmc/host/Kconfig | 2 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/mtk-mmc/Kconfig | 16 +
drivers/mmc/host/mtk-mmc/Makefile | 42 +
drivers/mmc/host/mtk-mmc/board.h | 137 ++
drivers/mmc/host/mtk-mmc/dbg.c | 347 ++++
drivers/mmc/host/mtk-mmc/dbg.h | 156 ++
drivers/mmc/host/mtk-mmc/mt6575_sd.h | 1001 +++++++++++
drivers/mmc/host/mtk-mmc/sd.c | 3060 ++++++++++++++++++++++++++++++++++
9 files changed, 4762 insertions(+)
create mode 100644 drivers/mmc/host/mtk-mmc/Kconfig
create mode 100644 drivers/mmc/host/mtk-mmc/Makefile
create mode 100644 drivers/mmc/host/mtk-mmc/board.h
create mode 100644 drivers/mmc/host/mtk-mmc/dbg.c
create mode 100644 drivers/mmc/host/mtk-mmc/dbg.h
create mode 100644 drivers/mmc/host/mtk-mmc/mt6575_sd.h
create mode 100644 drivers/mmc/host/mtk-mmc/sd.c
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -899,3 +899,5 @@ config MMC_SDHCI_XENON
This selects Marvell Xenon eMMC/SD/SDIO SDHCI.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
+
+source "drivers/mmc/host/mtk-mmc/Kconfig"
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -3,6 +3,7 @@
# Makefile for MMC/SD host controller drivers
#
+obj-$(CONFIG_MTK_MMC) += mtk-mmc/
obj-$(CONFIG_MMC_ARMMMCI) += armmmci.o
armmmci-y := mmci.o
armmmci-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o
--- /dev/null
+++ b/drivers/mmc/host/mtk-mmc/Kconfig
@@ -0,0 +1,16 @@
+config MTK_MMC
+ tristate "MTK SD/MMC"
+ depends on !MTD_NAND_RALINK
+
+config MTK_AEE_KDUMP
+ bool "MTK AEE KDUMP"
+ depends on MTK_MMC
+
+config MTK_MMC_CD_POLL
+ bool "Card Detect with Polling"
+ depends on MTK_MMC
+
+config MTK_MMC_EMMC_8BIT
+ bool "eMMC 8-bit support"
+ depends on MTK_MMC && RALINK_MT7628
+
--- /dev/null
+++ b/drivers/mmc/host/mtk-mmc/Makefile
@@ -0,0 +1,42 @@
+# Copyright Statement:
+#
+# This software/firmware and related documentation ("MediaTek Software") are
+# protected under relevant copyright laws. The information contained herein
+# is confidential and proprietary to MediaTek Inc. and/or its licensors.
+# Without the prior written permission of MediaTek inc. and/or its licensors,
+# any reproduction, modification, use or disclosure of MediaTek Software,
+# and information contained herein, in whole or in part, shall be strictly prohibited.
+#
+# MediaTek Inc. (C) 2010. All rights reserved.
+#
+# BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+# THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+# RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+# AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+# NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+# SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+# SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+# THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+# THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+# CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+# SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+# STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+# CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+# AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+# OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+# MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+#
+# The following software/firmware and/or related documentation ("MediaTek Software")
+# have been modified by MediaTek Inc. All revisions are subject to any receiver's
+# applicable license agreements with MediaTek Inc.
+
+obj-$(CONFIG_MTK_MMC) += mtk_sd.o
+mtk_sd-objs := sd.o dbg.o
+ifeq ($(CONFIG_MTK_AEE_KDUMP),y)
+EXTRA_CFLAGS += -DMT6575_SD_DEBUG
+endif
+
+clean:
+ @rm -f *.o modules.order .*.cmd
--- /dev/null
+++ b/drivers/mmc/host/mtk-mmc/board.h
@@ -0,0 +1,137 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ */
+/* MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#ifndef __ARCH_ARM_MACH_BOARD_H
+#define __ARCH_ARM_MACH_BOARD_H
+
+#include <generated/autoconf.h>
+#include <linux/pm.h>
+/* --- chhung */
+// #include <mach/mt6575.h>
+// #include <board-custom.h>
+/* end of chhung */
+
+typedef void (*sdio_irq_handler_t)(void*); /* external irq handler */
+typedef void (*pm_callback_t)(pm_message_t state, void *data);
+
+#define MSDC_CD_PIN_EN (1 << 0) /* card detection pin is wired */
+#define MSDC_WP_PIN_EN (1 << 1) /* write protection pin is wired */
+#define MSDC_RST_PIN_EN (1 << 2) /* emmc reset pin is wired */
+#define MSDC_SDIO_IRQ (1 << 3) /* use internal sdio irq (bus) */
+#define MSDC_EXT_SDIO_IRQ (1 << 4) /* use external sdio irq */
+#define MSDC_REMOVABLE (1 << 5) /* removable slot */
+#define MSDC_SYS_SUSPEND (1 << 6) /* suspended by system */
+#define MSDC_HIGHSPEED (1 << 7) /* high-speed mode support */
+#define MSDC_UHS1 (1 << 8) /* uhs-1 mode support */
+#define MSDC_DDR (1 << 9) /* ddr mode support */
+
+
+#define MSDC_SMPL_RISING (0)
+#define MSDC_SMPL_FALLING (1)
+
+#define MSDC_CMD_PIN (0)
+#define MSDC_DAT_PIN (1)
+#define MSDC_CD_PIN (2)
+#define MSDC_WP_PIN (3)
+#define MSDC_RST_PIN (4)
+
+enum {
+ MSDC_CLKSRC_48MHZ = 0,
+// MSDC_CLKSRC_26MHZ = 0,
+// MSDC_CLKSRC_197MHZ = 1,
+// MSDC_CLKSRC_208MHZ = 2
+};
+
+struct msdc_hw {
+ unsigned char clk_src; /* host clock source */
+ unsigned char cmd_edge; /* command latch edge */
+ unsigned char data_edge; /* data latch edge */
+ unsigned char clk_drv; /* clock pad driving */
+ unsigned char cmd_drv; /* command pad driving */
+ unsigned char dat_drv; /* data pad driving */
+ unsigned long flags; /* hardware capability flags */
+ unsigned long data_pins; /* data pins */
+ unsigned long data_offset; /* data address offset */
+
+ /* config gpio pull mode */
+ void (*config_gpio_pin)(int type, int pull);
+
+ /* external power control for card */
+ void (*ext_power_on)(void);
+ void (*ext_power_off)(void);
+
+ /* external sdio irq operations */
+ void (*request_sdio_eirq)(sdio_irq_handler_t sdio_irq_handler, void *data);
+ void (*enable_sdio_eirq)(void);
+ void (*disable_sdio_eirq)(void);
+
+ /* external cd irq operations */
+ void (*request_cd_eirq)(sdio_irq_handler_t cd_irq_handler, void *data);
+ void (*enable_cd_eirq)(void);
+ void (*disable_cd_eirq)(void);
+ int (*get_cd_status)(void);
+
+ /* power management callback for external module */
+ void (*register_pm)(pm_callback_t pm_cb, void *data);
+};
+
+extern struct msdc_hw msdc0_hw;
+extern struct msdc_hw msdc1_hw;
+extern struct msdc_hw msdc2_hw;
+extern struct msdc_hw msdc3_hw;
+
+/*GPS driver*/
+#define GPS_FLAG_FORCE_OFF 0x0001
+struct mt3326_gps_hardware {
+ int (*ext_power_on)(int);
+ int (*ext_power_off)(int);
+};
+extern struct mt3326_gps_hardware mt3326_gps_hw;
+
+/* NAND driver */
+struct mt6575_nand_host_hw {
+ unsigned int nfi_bus_width; /* NFI_BUS_WIDTH */
+ unsigned int nfi_access_timing; /* NFI_ACCESS_TIMING */
+ unsigned int nfi_cs_num; /* NFI_CS_NUM */
+ unsigned int nand_sec_size; /* NAND_SECTOR_SIZE */
+ unsigned int nand_sec_shift; /* NAND_SECTOR_SHIFT */
+ unsigned int nand_ecc_size;
+ unsigned int nand_ecc_bytes;
+ unsigned int nand_ecc_mode;
+};
+extern struct mt6575_nand_host_hw mt6575_nand_hw;
+
+#endif /* __ARCH_ARM_MACH_BOARD_H */
+
--- /dev/null
+++ b/drivers/mmc/host/mtk-mmc/dbg.c
@@ -0,0 +1,347 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ *
+ * MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+// #include <mach/mt6575_gpt.h> /* --- by chhung */
+#include "dbg.h"
+#include "mt6575_sd.h"
+#include <linux/seq_file.h>
+
+static char cmd_buf[256];
+
+/* for debug zone */
+unsigned int sd_debug_zone[4]={
+ 0,
+ 0,
+ 0,
+ 0
+};
+
+/* mode select */
+u32 dma_size[4]={
+ 512,
+ 512,
+ 512,
+ 512
+};
+msdc_mode drv_mode[4]={
+ MODE_SIZE_DEP, /* using DMA or not depend on the size */
+ MODE_SIZE_DEP,
+ MODE_SIZE_DEP,
+ MODE_SIZE_DEP
+};
+
+#if defined (MT6575_SD_DEBUG)
+/* for driver profile */
+#define TICKS_ONE_MS (13000)
+u32 gpt_enable = 0;
+u32 sdio_pro_enable = 0; /* make sure gpt is enabled */
+u32 sdio_pro_time = 0; /* no more than 30s */
+struct sdio_profile sdio_perfomance = {0};
+
+#if 0 /* --- chhung */
+void msdc_init_gpt(void)
+{
+ GPT_CONFIG config;
+
+ config.num = GPT6;
+ config.mode = GPT_FREE_RUN;
+ config.clkSrc = GPT_CLK_SRC_SYS;
+ config.clkDiv = GPT_CLK_DIV_1; /* 13MHz GPT6 */
+
+ if (GPT_Config(config) == FALSE )
+ return;
+
+ GPT_Start(GPT6);
+}
+#endif /* end of --- */
+
+u32 msdc_time_calc(u32 old_L32, u32 old_H32, u32 new_L32, u32 new_H32)
+{
+ u32 ret = 0;
+
+ if (new_H32 == old_H32) {
+ ret = new_L32 - old_L32;
+ } else if(new_H32 == (old_H32 + 1)) {
+ if (new_L32 > old_L32) {
+ printk("msdc old_L<0x%x> new_L<0x%x>\n", old_L32, new_L32);
+ }
+ ret = (0xffffffff - old_L32);
+ ret += new_L32;
+ } else {
+ printk("msdc old_H<0x%x> new_H<0x%x>\n", old_H32, new_H32);
+ }
+
+ return ret;
+}
+
+void msdc_sdio_profile(struct sdio_profile* result)
+{
+ struct cmd_profile* cmd;
+ u32 i;
+
+ printk("sdio === performance dump ===\n");
+ printk("sdio === total execute tick<%d> time<%dms> Tx<%dB> Rx<%dB>\n",
+ result->total_tc, result->total_tc / TICKS_ONE_MS,
+ result->total_tx_bytes, result->total_rx_bytes);
+
+ /* CMD52 Dump */
+ cmd = &result->cmd52_rx;
+ printk("sdio === CMD52 Rx <%d>times tick<%d> Max<%d> Min<%d> Aver<%d>\n", cmd->count, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count);
+ cmd = &result->cmd52_tx;
+ printk("sdio === CMD52 Tx <%d>times tick<%d> Max<%d> Min<%d> Aver<%d>\n", cmd->count, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count);
+
+ /* CMD53 Rx bytes + block mode */
+ for (i=0; i<512; i++) {
+ cmd = &result->cmd53_rx_byte[i];
+ if (cmd->count) {
+ printk("sdio<%6d><%3dB>_Rx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count,
+ cmd->tot_bytes, (cmd->tot_bytes/10)*13 / (cmd->tot_tc/10));
+ }
+ }
+ for (i=0; i<100; i++) {
+ cmd = &result->cmd53_rx_blk[i];
+ if (cmd->count) {
+ printk("sdio<%6d><%3d>B_Rx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count,
+ cmd->tot_bytes, (cmd->tot_bytes/10)*13 / (cmd->tot_tc/10));
+ }
+ }
+
+ /* CMD53 Tx bytes + block mode */
+ for (i=0; i<512; i++) {
+ cmd = &result->cmd53_tx_byte[i];
+ if (cmd->count) {
+ printk("sdio<%6d><%3dB>_Tx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count,
+ cmd->tot_bytes, (cmd->tot_bytes/10)*13 / (cmd->tot_tc/10));
+ }
+ }
+ for (i=0; i<100; i++) {
+ cmd = &result->cmd53_tx_blk[i];
+ if (cmd->count) {
+ printk("sdio<%6d><%3d>B_Tx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count,
+ cmd->tot_bytes, (cmd->tot_bytes/10)*13 / (cmd->tot_tc/10));
+ }
+ }
+
+ printk("sdio === performance dump done ===\n");
+}
+
+//========= sdio command table ===========
+void msdc_performance(u32 opcode, u32 sizes, u32 bRx, u32 ticks)
+{
+ struct sdio_profile* result = &sdio_perfomance;
+ struct cmd_profile* cmd;
+ u32 block;
+
+ if (sdio_pro_enable == 0) {
+ return;
+ }
+
+ if (opcode == 52) {
+ cmd = bRx ? &result->cmd52_rx : &result->cmd52_tx;
+ } else if (opcode == 53) {
+ if (sizes < 512) {
+ cmd = bRx ? &result->cmd53_rx_byte[sizes] : &result->cmd53_tx_byte[sizes];
+ } else {
+ block = sizes / 512;
+ if (block >= 99) {
+ printk("cmd53 error blocks\n");
+ while(1);
+ }
+ cmd = bRx ? &result->cmd53_rx_blk[block] : &result->cmd53_tx_blk[block];
+ }
+ } else {
+ return;
+ }
+
+ /* update the members */
+ if (ticks > cmd->max_tc){
+ cmd->max_tc = ticks;
+ }
+ if (cmd->min_tc == 0 || ticks < cmd->min_tc) {
+ cmd->min_tc = ticks;
+ }
+ cmd->tot_tc += ticks;
+ cmd->tot_bytes += sizes;
+ cmd->count ++;
+
+ if (bRx) {
+ result->total_rx_bytes += sizes;
+ } else {
+ result->total_tx_bytes += sizes;
+ }
+ result->total_tc += ticks;
+
+ /* dump when total_tc > 30s */
+ if (result->total_tc >= sdio_pro_time * TICKS_ONE_MS * 1000) {
+ msdc_sdio_profile(result);
+ memset(result, 0 , sizeof(struct sdio_profile));
+ }
+}
+
+//========== driver proc interface ===========
+static int msdc_debug_proc_read(struct seq_file *s, void *p)
+{
+ seq_printf(s, "\n=========================================\n");
+ seq_printf(s, "Index<0> + Id + Zone\n");
+ seq_printf(s, "-> PWR<9> WRN<8> | FIO<7> OPS<6> FUN<5> CFG<4> | INT<3> RSP<2> CMD<1> DMA<0>\n");
+ seq_printf(s, "-> echo 0 3 0x3ff >msdc_bebug -> host[3] debug zone set to 0x3ff\n");
+ seq_printf(s, "-> MSDC[0] Zone: 0x%.8x\n", sd_debug_zone[0]);
+ seq_printf(s, "-> MSDC[1] Zone: 0x%.8x\n", sd_debug_zone[1]);
+ seq_printf(s, "-> MSDC[2] Zone: 0x%.8x\n", sd_debug_zone[2]);
+ seq_printf(s, "-> MSDC[3] Zone: 0x%.8x\n", sd_debug_zone[3]);
+
+ seq_printf(s, "Index<1> + ID:4|Mode:4 + DMA_SIZE\n");
+ seq_printf(s, "-> 0)PIO 1)DMA 2)SIZE\n");
+ seq_printf(s, "-> echo 1 22 0x200 >msdc_bebug -> host[2] size mode, dma when >= 512\n");
+ seq_printf(s, "-> MSDC[0] mode<%d> size<%d>\n", drv_mode[0], dma_size[0]);
+ seq_printf(s, "-> MSDC[1] mode<%d> size<%d>\n", drv_mode[1], dma_size[1]);
+ seq_printf(s, "-> MSDC[2] mode<%d> size<%d>\n", drv_mode[2], dma_size[2]);
+ seq_printf(s, "-> MSDC[3] mode<%d> size<%d>\n", drv_mode[3], dma_size[3]);
+
+ seq_printf(s, "Index<3> + SDIO_PROFILE + TIME\n");
+ seq_printf(s, "-> echo 3 1 0x1E >msdc_bebug -> enable sdio_profile, 30s\n");
+ seq_printf(s, "-> SDIO_PROFILE<%d> TIME<%ds>\n", sdio_pro_enable, sdio_pro_time);
+ seq_printf(s, "=========================================\n\n");
+
+ return 0;
+}
+
+static ssize_t msdc_debug_proc_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *data)
+{
+ int ret;
+
+ int cmd, p1, p2;
+ int id, zone;
+ int mode, size;
+
+ if (count == 0)return -1;
+ if(count > 255)count = 255;
+
+ ret = copy_from_user(cmd_buf, buf, count);
+ if (ret < 0)return -1;
+
+ cmd_buf[count] = '\0';
+ printk("msdc Write %s\n", cmd_buf);
+
+ sscanf(cmd_buf, "%x %x %x", &cmd, &p1, &p2);
+
+ if(cmd == SD_TOOL_ZONE) {
+ id = p1; zone = p2; zone &= 0x3ff;
+ printk("msdc host_id<%d> zone<0x%.8x>\n", id, zone);
+ if(id >=0 && id<=3){
+ sd_debug_zone[id] = zone;
+ }
+ else if(id == 4){
+ sd_debug_zone[0] = sd_debug_zone[1] = zone;
+ sd_debug_zone[2] = sd_debug_zone[3] = zone;
+ }
+ else{
+ printk("msdc host_id error when set debug zone\n");
+ }
+ } else if (cmd == SD_TOOL_DMA_SIZE) {
+ id = p1>>4; mode = (p1&0xf); size = p2;
+ if(id >=0 && id<=3){
+ drv_mode[id] = mode;
+ dma_size[id] = p2;
+ }
+ else if(id == 4){
+ drv_mode[0] = drv_mode[1] = mode;
+ drv_mode[2] = drv_mode[3] = mode;
+ dma_size[0] = dma_size[1] = p2;
+ dma_size[2] = dma_size[3] = p2;
+ }
+ else{
+ printk("msdc host_id error when select mode\n");
+ }
+ } else if (cmd == SD_TOOL_SDIO_PROFILE) {
+ if (p1 == 1) { /* enable profile */
+ if (gpt_enable == 0) {
+ // msdc_init_gpt(); /* --- by chhung */
+ gpt_enable = 1;
+ }
+ sdio_pro_enable = 1;
+ if (p2 == 0) p2 = 1; if (p2 >= 30) p2 = 30;
+ sdio_pro_time = p2 ;
+ } else if (p1 == 0) {
+ /* todo */
+ sdio_pro_enable = 0;
+ }
+ }
+
+ return count;
+}
+
+static int msdc_debug_show(struct inode *inode, struct file *file)
+{
+ return single_open(file, msdc_debug_proc_read, NULL);
+}
+
+static const struct file_operations msdc_debug_fops = {
+ .owner = THIS_MODULE,
+ .open = msdc_debug_show,
+ .read = seq_read,
+ .write = msdc_debug_proc_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int msdc_debug_proc_init(void)
+{
+ struct proc_dir_entry *de = proc_create("msdc_debug", 0667, NULL, &msdc_debug_fops);
+
+ if (!de || IS_ERR(de))
+ printk("!! Create MSDC debug PROC fail !!\n");
+
+ return 0 ;
+}
+EXPORT_SYMBOL_GPL(msdc_debug_proc_init);
+#endif
--- /dev/null
+++ b/drivers/mmc/host/mtk-mmc/dbg.h
@@ -0,0 +1,156 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ *
+ * MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+#ifndef __MT_MSDC_DEUBG__
+#define __MT_MSDC_DEUBG__
+
+//==========================
+extern u32 sdio_pro_enable;
+/* for a type command, e.g. CMD53, 2 blocks */
+struct cmd_profile {
+ u32 max_tc; /* Max tick count */
+ u32 min_tc;
+ u32 tot_tc; /* total tick count */
+ u32 tot_bytes;
+ u32 count; /* the counts of the command */
+};
+
+/* dump when total_tc and total_bytes */
+struct sdio_profile {
+ u32 total_tc; /* total tick count of CMD52 and CMD53 */
+ u32 total_tx_bytes; /* total bytes of CMD53 Tx */
+ u32 total_rx_bytes; /* total bytes of CMD53 Rx */
+
+ /*CMD52*/
+ struct cmd_profile cmd52_tx;
+ struct cmd_profile cmd52_rx;
+
+ /*CMD53 in byte unit */
+ struct cmd_profile cmd53_tx_byte[512];
+ struct cmd_profile cmd53_rx_byte[512];
+
+ /*CMD53 in block unit */
+ struct cmd_profile cmd53_tx_blk[100];
+ struct cmd_profile cmd53_rx_blk[100];
+};
+
+//==========================
+typedef enum {
+ SD_TOOL_ZONE = 0,
+ SD_TOOL_DMA_SIZE = 1,
+ SD_TOOL_PM_ENABLE = 2,
+ SD_TOOL_SDIO_PROFILE = 3,
+} msdc_dbg;
+
+typedef enum {
+ MODE_PIO = 0,
+ MODE_DMA = 1,
+ MODE_SIZE_DEP = 2,
+} msdc_mode;
+extern msdc_mode drv_mode[4];
+extern u32 dma_size[4];
+
+/* Debug message event */
+#define DBG_EVT_NONE (0) /* No event */
+#define DBG_EVT_DMA (1 << 0) /* DMA related event */
+#define DBG_EVT_CMD (1 << 1) /* MSDC CMD related event */
+#define DBG_EVT_RSP (1 << 2) /* MSDC CMD RSP related event */
+#define DBG_EVT_INT (1 << 3) /* MSDC INT event */
+#define DBG_EVT_CFG (1 << 4) /* MSDC CFG event */
+#define DBG_EVT_FUC (1 << 5) /* Function event */
+#define DBG_EVT_OPS (1 << 6) /* Read/Write operation event */
+#define DBG_EVT_FIO (1 << 7) /* FIFO operation event */
+#define DBG_EVT_WRN (1 << 8) /* Warning event */
+#define DBG_EVT_PWR (1 << 9) /* Power event */
+#define DBG_EVT_ALL (0xffffffff)
+
+#define DBG_EVT_MASK (DBG_EVT_ALL)
+
+extern unsigned int sd_debug_zone[4];
+#define TAG "msdc"
+#if 0 /* +++ chhung */
+#define BUG_ON(x) \
+do { \
+ if (x) { \
+ printk("[BUG] %s LINE:%d FILE:%s\n", #x, __LINE__, __FILE__); \
+ while(1); \
+ } \
+}while(0)
+#endif /* end of +++ */
+
+#define N_MSG(evt, fmt, args...)
+/*
+do { \
+ if ((DBG_EVT_##evt) & sd_debug_zone[host->id]) { \
+ printk(KERN_ERR TAG"%d -> "fmt" <- %s() : L<%d> PID<%s><0x%x>\n", \
+ host->id, ##args , __FUNCTION__, __LINE__, current->comm, current->pid); \
+ } \
+} while(0)
+*/
+
+#define ERR_MSG(fmt, args...) \
+do { \
+ printk(KERN_ERR TAG"%d -> "fmt" <- %s() : L<%d> PID<%s><0x%x>\n", \
+ host->id, ##args , __FUNCTION__, __LINE__, current->comm, current->pid); \
+} while(0);
+
+#if 1
+//defined CONFIG_MTK_MMC_CD_POLL
+#define INIT_MSG(fmt, args...)
+#define IRQ_MSG(fmt, args...)
+#else
+#define INIT_MSG(fmt, args...) \
+do { \
+ printk(KERN_ERR TAG"%d -> "fmt" <- %s() : L<%d> PID<%s><0x%x>\n", \
+ host->id, ##args , __FUNCTION__, __LINE__, current->comm, current->pid); \
+} while(0);
+
+/* PID in ISR in not corrent */
+#define IRQ_MSG(fmt, args...) \
+do { \
+ printk(KERN_ERR TAG"%d -> "fmt" <- %s() : L<%d>\n", \
+ host->id, ##args , __FUNCTION__, __LINE__); \
+} while(0);
+#endif
+
+int msdc_debug_proc_init(void);
+
+#if 0 /* --- chhung */
+void msdc_init_gpt(void);
+extern void GPT_GetCounter64(UINT32 *cntL32, UINT32 *cntH32);
+#endif /* end of --- */
+u32 msdc_time_calc(u32 old_L32, u32 old_H32, u32 new_L32, u32 new_H32);
+void msdc_performance(u32 opcode, u32 sizes, u32 bRx, u32 ticks);
+
+#endif
--- /dev/null
+++ b/drivers/mmc/host/mtk-mmc/mt6575_sd.h
@@ -0,0 +1,1002 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ */
+/* MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#ifndef MT6575_SD_H
+#define MT6575_SD_H
+
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/mmc/host.h>
+
+// #include <mach/mt6575_reg_base.h> /* --- by chhung */
+
+/*--------------------------------------------------------------------------*/
+/* Common Macro */
+/*--------------------------------------------------------------------------*/
+#define REG_ADDR(x) ((volatile u32*)(base + OFFSET_##x))
+
+/*--------------------------------------------------------------------------*/
+/* Common Definition */
+/*--------------------------------------------------------------------------*/
+#define MSDC_FIFO_SZ (128)
+#define MSDC_FIFO_THD (64) // (128)
+#define MSDC_NUM (4)
+
+#define MSDC_MS (0)
+#define MSDC_SDMMC (1)
+
+#define MSDC_MODE_UNKNOWN (0)
+#define MSDC_MODE_PIO (1)
+#define MSDC_MODE_DMA_BASIC (2)
+#define MSDC_MODE_DMA_DESC (3)
+#define MSDC_MODE_DMA_ENHANCED (4)
+#define MSDC_MODE_MMC_STREAM (5)
+
+#define MSDC_BUS_1BITS (0)
+#define MSDC_BUS_4BITS (1)
+#define MSDC_BUS_8BITS (2)
+
+#define MSDC_BRUST_8B (3)
+#define MSDC_BRUST_16B (4)
+#define MSDC_BRUST_32B (5)
+#define MSDC_BRUST_64B (6)
+
+#define MSDC_PIN_PULL_NONE (0)
+#define MSDC_PIN_PULL_DOWN (1)
+#define MSDC_PIN_PULL_UP (2)
+#define MSDC_PIN_KEEP (3)
+
+#define MSDC_MAX_SCLK (48000000) /* +/- by chhung */
+#define MSDC_MIN_SCLK (260000)
+
+#define MSDC_AUTOCMD12 (0x0001)
+#define MSDC_AUTOCMD23 (0x0002)
+#define MSDC_AUTOCMD19 (0x0003)
+
+#define MSDC_EMMC_BOOTMODE0 (0) /* Pull low CMD mode */
+#define MSDC_EMMC_BOOTMODE1 (1) /* Reset CMD mode */
+
+enum {
+ RESP_NONE = 0,
+ RESP_R1,
+ RESP_R2,
+ RESP_R3,
+ RESP_R4,
+ RESP_R5,
+ RESP_R6,
+ RESP_R7,
+ RESP_R1B
+};
+
+/*--------------------------------------------------------------------------*/
+/* Register Offset */
+/*--------------------------------------------------------------------------*/
+#define OFFSET_MSDC_CFG (0x0)
+#define OFFSET_MSDC_IOCON (0x04)
+#define OFFSET_MSDC_PS (0x08)
+#define OFFSET_MSDC_INT (0x0c)
+#define OFFSET_MSDC_INTEN (0x10)
+#define OFFSET_MSDC_FIFOCS (0x14)
+#define OFFSET_MSDC_TXDATA (0x18)
+#define OFFSET_MSDC_RXDATA (0x1c)
+#define OFFSET_SDC_CFG (0x30)
+#define OFFSET_SDC_CMD (0x34)
+#define OFFSET_SDC_ARG (0x38)
+#define OFFSET_SDC_STS (0x3c)
+#define OFFSET_SDC_RESP0 (0x40)
+#define OFFSET_SDC_RESP1 (0x44)
+#define OFFSET_SDC_RESP2 (0x48)
+#define OFFSET_SDC_RESP3 (0x4c)
+#define OFFSET_SDC_BLK_NUM (0x50)
+#define OFFSET_SDC_CSTS (0x58)
+#define OFFSET_SDC_CSTS_EN (0x5c)
+#define OFFSET_SDC_DCRC_STS (0x60)
+#define OFFSET_EMMC_CFG0 (0x70)
+#define OFFSET_EMMC_CFG1 (0x74)
+#define OFFSET_EMMC_STS (0x78)
+#define OFFSET_EMMC_IOCON (0x7c)
+#define OFFSET_SDC_ACMD_RESP (0x80)
+#define OFFSET_SDC_ACMD19_TRG (0x84)
+#define OFFSET_SDC_ACMD19_STS (0x88)
+#define OFFSET_MSDC_DMA_SA (0x90)
+#define OFFSET_MSDC_DMA_CA (0x94)
+#define OFFSET_MSDC_DMA_CTRL (0x98)
+#define OFFSET_MSDC_DMA_CFG (0x9c)
+#define OFFSET_MSDC_DBG_SEL (0xa0)
+#define OFFSET_MSDC_DBG_OUT (0xa4)
+#define OFFSET_MSDC_PATCH_BIT (0xb0)
+#define OFFSET_MSDC_PATCH_BIT1 (0xb4)
+#define OFFSET_MSDC_PAD_CTL0 (0xe0)
+#define OFFSET_MSDC_PAD_CTL1 (0xe4)
+#define OFFSET_MSDC_PAD_CTL2 (0xe8)
+#define OFFSET_MSDC_PAD_TUNE (0xec)
+#define OFFSET_MSDC_DAT_RDDLY0 (0xf0)
+#define OFFSET_MSDC_DAT_RDDLY1 (0xf4)
+#define OFFSET_MSDC_HW_DBG (0xf8)
+#define OFFSET_MSDC_VERSION (0x100)
+#define OFFSET_MSDC_ECO_VER (0x104)
+
+/*--------------------------------------------------------------------------*/
+/* Register Address */
+/*--------------------------------------------------------------------------*/
+
+/* common register */
+#define MSDC_CFG REG_ADDR(MSDC_CFG)
+#define MSDC_IOCON REG_ADDR(MSDC_IOCON)
+#define MSDC_PS REG_ADDR(MSDC_PS)
+#define MSDC_INT REG_ADDR(MSDC_INT)
+#define MSDC_INTEN REG_ADDR(MSDC_INTEN)
+#define MSDC_FIFOCS REG_ADDR(MSDC_FIFOCS)
+#define MSDC_TXDATA REG_ADDR(MSDC_TXDATA)
+#define MSDC_RXDATA REG_ADDR(MSDC_RXDATA)
+#define MSDC_PATCH_BIT0 REG_ADDR(MSDC_PATCH_BIT)
+
+/* sdmmc register */
+#define SDC_CFG REG_ADDR(SDC_CFG)
+#define SDC_CMD REG_ADDR(SDC_CMD)
+#define SDC_ARG REG_ADDR(SDC_ARG)
+#define SDC_STS REG_ADDR(SDC_STS)
+#define SDC_RESP0 REG_ADDR(SDC_RESP0)
+#define SDC_RESP1 REG_ADDR(SDC_RESP1)
+#define SDC_RESP2 REG_ADDR(SDC_RESP2)
+#define SDC_RESP3 REG_ADDR(SDC_RESP3)
+#define SDC_BLK_NUM REG_ADDR(SDC_BLK_NUM)
+#define SDC_CSTS REG_ADDR(SDC_CSTS)
+#define SDC_CSTS_EN REG_ADDR(SDC_CSTS_EN)
+#define SDC_DCRC_STS REG_ADDR(SDC_DCRC_STS)
+
+/* emmc register*/
+#define EMMC_CFG0 REG_ADDR(EMMC_CFG0)
+#define EMMC_CFG1 REG_ADDR(EMMC_CFG1)
+#define EMMC_STS REG_ADDR(EMMC_STS)
+#define EMMC_IOCON REG_ADDR(EMMC_IOCON)
+
+/* auto command register */
+#define SDC_ACMD_RESP REG_ADDR(SDC_ACMD_RESP)
+#define SDC_ACMD19_TRG REG_ADDR(SDC_ACMD19_TRG)
+#define SDC_ACMD19_STS REG_ADDR(SDC_ACMD19_STS)
+
+/* dma register */
+#define MSDC_DMA_SA REG_ADDR(MSDC_DMA_SA)
+#define MSDC_DMA_CA REG_ADDR(MSDC_DMA_CA)
+#define MSDC_DMA_CTRL REG_ADDR(MSDC_DMA_CTRL)
+#define MSDC_DMA_CFG REG_ADDR(MSDC_DMA_CFG)
+
+/* pad ctrl register */
+#define MSDC_PAD_CTL0 REG_ADDR(MSDC_PAD_CTL0)
+#define MSDC_PAD_CTL1 REG_ADDR(MSDC_PAD_CTL1)
+#define MSDC_PAD_CTL2 REG_ADDR(MSDC_PAD_CTL2)
+
+/* data read delay */
+#define MSDC_DAT_RDDLY0 REG_ADDR(MSDC_DAT_RDDLY0)
+#define MSDC_DAT_RDDLY1 REG_ADDR(MSDC_DAT_RDDLY1)
+
+/* debug register */
+#define MSDC_DBG_SEL REG_ADDR(MSDC_DBG_SEL)
+#define MSDC_DBG_OUT REG_ADDR(MSDC_DBG_OUT)
+
+/* misc register */
+#define MSDC_PATCH_BIT REG_ADDR(MSDC_PATCH_BIT)
+#define MSDC_PATCH_BIT1 REG_ADDR(MSDC_PATCH_BIT1)
+#define MSDC_PAD_TUNE REG_ADDR(MSDC_PAD_TUNE)
+#define MSDC_HW_DBG REG_ADDR(MSDC_HW_DBG)
+#define MSDC_VERSION REG_ADDR(MSDC_VERSION)
+#define MSDC_ECO_VER REG_ADDR(MSDC_ECO_VER) /* ECO Version */
+
+/*--------------------------------------------------------------------------*/
+/* Register Mask */
+/*--------------------------------------------------------------------------*/
+
+/* MSDC_CFG mask */
+#define MSDC_CFG_MODE (0x1 << 0) /* RW */
+#define MSDC_CFG_CKPDN (0x1 << 1) /* RW */
+#define MSDC_CFG_RST (0x1 << 2) /* RW */
+#define MSDC_CFG_PIO (0x1 << 3) /* RW */
+#define MSDC_CFG_CKDRVEN (0x1 << 4) /* RW */
+#define MSDC_CFG_BV18SDT (0x1 << 5) /* RW */
+#define MSDC_CFG_BV18PSS (0x1 << 6) /* R */
+#define MSDC_CFG_CKSTB (0x1 << 7) /* R */
+#define MSDC_CFG_CKDIV (0xff << 8) /* RW */
+#define MSDC_CFG_CKMOD (0x3 << 16) /* RW */
+
+/* MSDC_IOCON mask */
+#define MSDC_IOCON_SDR104CKS (0x1 << 0) /* RW */
+#define MSDC_IOCON_RSPL (0x1 << 1) /* RW */
+#define MSDC_IOCON_DSPL (0x1 << 2) /* RW */
+#define MSDC_IOCON_DDLSEL (0x1 << 3) /* RW */
+#define MSDC_IOCON_DDR50CKD (0x1 << 4) /* RW */
+#define MSDC_IOCON_DSPLSEL (0x1 << 5) /* RW */
+#define MSDC_IOCON_D0SPL (0x1 << 16) /* RW */
+#define MSDC_IOCON_D1SPL (0x1 << 17) /* RW */
+#define MSDC_IOCON_D2SPL (0x1 << 18) /* RW */
+#define MSDC_IOCON_D3SPL (0x1 << 19) /* RW */
+#define MSDC_IOCON_D4SPL (0x1 << 20) /* RW */
+#define MSDC_IOCON_D5SPL (0x1 << 21) /* RW */
+#define MSDC_IOCON_D6SPL (0x1 << 22) /* RW */
+#define MSDC_IOCON_D7SPL (0x1 << 23) /* RW */
+#define MSDC_IOCON_RISCSZ (0x3 << 24) /* RW */
+
+/* MSDC_PS mask */
+#define MSDC_PS_CDEN (0x1 << 0) /* RW */
+#define MSDC_PS_CDSTS (0x1 << 1) /* R */
+#define MSDC_PS_CDDEBOUNCE (0xf << 12) /* RW */
+#define MSDC_PS_DAT (0xff << 16) /* R */
+#define MSDC_PS_CMD (0x1 << 24) /* R */
+#define MSDC_PS_WP (0x1UL<< 31) /* R */
+
+/* MSDC_INT mask */
+#define MSDC_INT_MMCIRQ (0x1 << 0) /* W1C */
+#define MSDC_INT_CDSC (0x1 << 1) /* W1C */
+#define MSDC_INT_ACMDRDY (0x1 << 3) /* W1C */
+#define MSDC_INT_ACMDTMO (0x1 << 4) /* W1C */
+#define MSDC_INT_ACMDCRCERR (0x1 << 5) /* W1C */
+#define MSDC_INT_DMAQ_EMPTY (0x1 << 6) /* W1C */
+#define MSDC_INT_SDIOIRQ (0x1 << 7) /* W1C */
+#define MSDC_INT_CMDRDY (0x1 << 8) /* W1C */
+#define MSDC_INT_CMDTMO (0x1 << 9) /* W1C */
+#define MSDC_INT_RSPCRCERR (0x1 << 10) /* W1C */
+#define MSDC_INT_CSTA (0x1 << 11) /* R */
+#define MSDC_INT_XFER_COMPL (0x1 << 12) /* W1C */
+#define MSDC_INT_DXFER_DONE (0x1 << 13) /* W1C */
+#define MSDC_INT_DATTMO (0x1 << 14) /* W1C */
+#define MSDC_INT_DATCRCERR (0x1 << 15) /* W1C */
+#define MSDC_INT_ACMD19_DONE (0x1 << 16) /* W1C */
+
+/* MSDC_INTEN mask */
+#define MSDC_INTEN_MMCIRQ (0x1 << 0) /* RW */
+#define MSDC_INTEN_CDSC (0x1 << 1) /* RW */
+#define MSDC_INTEN_ACMDRDY (0x1 << 3) /* RW */
+#define MSDC_INTEN_ACMDTMO (0x1 << 4) /* RW */
+#define MSDC_INTEN_ACMDCRCERR (0x1 << 5) /* RW */
+#define MSDC_INTEN_DMAQ_EMPTY (0x1 << 6) /* RW */
+#define MSDC_INTEN_SDIOIRQ (0x1 << 7) /* RW */
+#define MSDC_INTEN_CMDRDY (0x1 << 8) /* RW */
+#define MSDC_INTEN_CMDTMO (0x1 << 9) /* RW */
+#define MSDC_INTEN_RSPCRCERR (0x1 << 10) /* RW */
+#define MSDC_INTEN_CSTA (0x1 << 11) /* RW */
+#define MSDC_INTEN_XFER_COMPL (0x1 << 12) /* RW */
+#define MSDC_INTEN_DXFER_DONE (0x1 << 13) /* RW */
+#define MSDC_INTEN_DATTMO (0x1 << 14) /* RW */
+#define MSDC_INTEN_DATCRCERR (0x1 << 15) /* RW */
+#define MSDC_INTEN_ACMD19_DONE (0x1 << 16) /* RW */
+
+/* MSDC_FIFOCS mask */
+#define MSDC_FIFOCS_RXCNT (0xff << 0) /* R */
+#define MSDC_FIFOCS_TXCNT (0xff << 16) /* R */
+#define MSDC_FIFOCS_CLR (0x1UL<< 31) /* RW */
+
+/* SDC_CFG mask */
+#define SDC_CFG_SDIOINTWKUP (0x1 << 0) /* RW */
+#define SDC_CFG_INSWKUP (0x1 << 1) /* RW */
+#define SDC_CFG_BUSWIDTH (0x3 << 16) /* RW */
+#define SDC_CFG_SDIO (0x1 << 19) /* RW */
+#define SDC_CFG_SDIOIDE (0x1 << 20) /* RW */
+#define SDC_CFG_INTATGAP (0x1 << 21) /* RW */
+#define SDC_CFG_DTOC (0xffUL << 24) /* RW */
+
+/* SDC_CMD mask */
+#define SDC_CMD_OPC (0x3f << 0) /* RW */
+#define SDC_CMD_BRK (0x1 << 6) /* RW */
+#define SDC_CMD_RSPTYP (0x7 << 7) /* RW */
+#define SDC_CMD_DTYP (0x3 << 11) /* RW */
+#define SDC_CMD_DTYP (0x3 << 11) /* RW */
+#define SDC_CMD_RW (0x1 << 13) /* RW */
+#define SDC_CMD_STOP (0x1 << 14) /* RW */
+#define SDC_CMD_GOIRQ (0x1 << 15) /* RW */
+#define SDC_CMD_BLKLEN (0xfff<< 16) /* RW */
+#define SDC_CMD_AUTOCMD (0x3 << 28) /* RW */
+#define SDC_CMD_VOLSWTH (0x1 << 30) /* RW */
+
+/* SDC_STS mask */
+#define SDC_STS_SDCBUSY (0x1 << 0) /* RW */
+#define SDC_STS_CMDBUSY (0x1 << 1) /* RW */
+#define SDC_STS_SWR_COMPL (0x1 << 31) /* RW */
+
+/* SDC_DCRC_STS mask */
+#define SDC_DCRC_STS_NEG (0xf << 8) /* RO */
+#define SDC_DCRC_STS_POS (0xff << 0) /* RO */
+
+/* EMMC_CFG0 mask */
+#define EMMC_CFG0_BOOTSTART (0x1 << 0) /* W */
+#define EMMC_CFG0_BOOTSTOP (0x1 << 1) /* W */
+#define EMMC_CFG0_BOOTMODE (0x1 << 2) /* RW */
+#define EMMC_CFG0_BOOTACKDIS (0x1 << 3) /* RW */
+#define EMMC_CFG0_BOOTWDLY (0x7 << 12) /* RW */
+#define EMMC_CFG0_BOOTSUPP (0x1 << 15) /* RW */
+
+/* EMMC_CFG1 mask */
+#define EMMC_CFG1_BOOTDATTMC (0xfffff << 0) /* RW */
+#define EMMC_CFG1_BOOTACKTMC (0xfffUL << 20) /* RW */
+
+/* EMMC_STS mask */
+#define EMMC_STS_BOOTCRCERR (0x1 << 0) /* W1C */
+#define EMMC_STS_BOOTACKERR (0x1 << 1) /* W1C */
+#define EMMC_STS_BOOTDATTMO (0x1 << 2) /* W1C */
+#define EMMC_STS_BOOTACKTMO (0x1 << 3) /* W1C */
+#define EMMC_STS_BOOTUPSTATE (0x1 << 4) /* R */
+#define EMMC_STS_BOOTACKRCV (0x1 << 5) /* W1C */
+#define EMMC_STS_BOOTDATRCV (0x1 << 6) /* R */
+
+/* EMMC_IOCON mask */
+#define EMMC_IOCON_BOOTRST (0x1 << 0) /* RW */
+
+/* SDC_ACMD19_TRG mask */
+#define SDC_ACMD19_TRG_TUNESEL (0xf << 0) /* RW */
+
+/* MSDC_DMA_CTRL mask */
+#define MSDC_DMA_CTRL_START (0x1 << 0) /* W */
+#define MSDC_DMA_CTRL_STOP (0x1 << 1) /* W */
+#define MSDC_DMA_CTRL_RESUME (0x1 << 2) /* W */
+#define MSDC_DMA_CTRL_MODE (0x1 << 8) /* RW */
+#define MSDC_DMA_CTRL_LASTBUF (0x1 << 10) /* RW */
+#define MSDC_DMA_CTRL_BRUSTSZ (0x7 << 12) /* RW */
+#define MSDC_DMA_CTRL_XFERSZ (0xffffUL << 16)/* RW */
+
+/* MSDC_DMA_CFG mask */
+#define MSDC_DMA_CFG_STS (0x1 << 0) /* R */
+#define MSDC_DMA_CFG_DECSEN (0x1 << 1) /* RW */
+#define MSDC_DMA_CFG_BDCSERR (0x1 << 4) /* R */
+#define MSDC_DMA_CFG_GPDCSERR (0x1 << 5) /* R */
+
+/* MSDC_PATCH_BIT mask */
+#define MSDC_PATCH_BIT_WFLSMODE (0x1 << 0) /* RW */
+#define MSDC_PATCH_BIT_ODDSUPP (0x1 << 1) /* RW */
+#define MSDC_PATCH_BIT_CKGEN_CK (0x1 << 6) /* E2: Fixed to 1 */
+#define MSDC_PATCH_BIT_IODSSEL (0x1 << 16) /* RW */
+#define MSDC_PATCH_BIT_IOINTSEL (0x1 << 17) /* RW */
+#define MSDC_PATCH_BIT_BUSYDLY (0xf << 18) /* RW */
+#define MSDC_PATCH_BIT_WDOD (0xf << 22) /* RW */
+#define MSDC_PATCH_BIT_IDRTSEL (0x1 << 26) /* RW */
+#define MSDC_PATCH_BIT_CMDFSEL (0x1 << 27) /* RW */
+#define MSDC_PATCH_BIT_INTDLSEL (0x1 << 28) /* RW */
+#define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */
+#define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */
+
+/* MSDC_PATCH_BIT1 mask */
+#define MSDC_PATCH_BIT1_WRDAT_CRCS (0x7 << 3)
+#define MSDC_PATCH_BIT1_CMD_RSP (0x7 << 0)
+
+/* MSDC_PAD_CTL0 mask */
+#define MSDC_PAD_CTL0_CLKDRVN (0x7 << 0) /* RW */
+#define MSDC_PAD_CTL0_CLKDRVP (0x7 << 4) /* RW */
+#define MSDC_PAD_CTL0_CLKSR (0x1 << 8) /* RW */
+#define MSDC_PAD_CTL0_CLKPD (0x1 << 16) /* RW */
+#define MSDC_PAD_CTL0_CLKPU (0x1 << 17) /* RW */
+#define MSDC_PAD_CTL0_CLKSMT (0x1 << 18) /* RW */
+#define MSDC_PAD_CTL0_CLKIES (0x1 << 19) /* RW */
+#define MSDC_PAD_CTL0_CLKTDSEL (0xf << 20) /* RW */
+#define MSDC_PAD_CTL0_CLKRDSEL (0xffUL<< 24) /* RW */
+
+/* MSDC_PAD_CTL1 mask */
+#define MSDC_PAD_CTL1_CMDDRVN (0x7 << 0) /* RW */
+#define MSDC_PAD_CTL1_CMDDRVP (0x7 << 4) /* RW */
+#define MSDC_PAD_CTL1_CMDSR (0x1 << 8) /* RW */
+#define MSDC_PAD_CTL1_CMDPD (0x1 << 16) /* RW */
+#define MSDC_PAD_CTL1_CMDPU (0x1 << 17) /* RW */
+#define MSDC_PAD_CTL1_CMDSMT (0x1 << 18) /* RW */
+#define MSDC_PAD_CTL1_CMDIES (0x1 << 19) /* RW */
+#define MSDC_PAD_CTL1_CMDTDSEL (0xf << 20) /* RW */
+#define MSDC_PAD_CTL1_CMDRDSEL (0xffUL<< 24) /* RW */
+
+/* MSDC_PAD_CTL2 mask */
+#define MSDC_PAD_CTL2_DATDRVN (0x7 << 0) /* RW */
+#define MSDC_PAD_CTL2_DATDRVP (0x7 << 4) /* RW */
+#define MSDC_PAD_CTL2_DATSR (0x1 << 8) /* RW */
+#define MSDC_PAD_CTL2_DATPD (0x1 << 16) /* RW */
+#define MSDC_PAD_CTL2_DATPU (0x1 << 17) /* RW */
+#define MSDC_PAD_CTL2_DATIES (0x1 << 19) /* RW */
+#define MSDC_PAD_CTL2_DATSMT (0x1 << 18) /* RW */
+#define MSDC_PAD_CTL2_DATTDSEL (0xf << 20) /* RW */
+#define MSDC_PAD_CTL2_DATRDSEL (0xffUL<< 24) /* RW */
+
+/* MSDC_PAD_TUNE mask */
+#define MSDC_PAD_TUNE_DATWRDLY (0x1F << 0) /* RW */
+#define MSDC_PAD_TUNE_DATRRDLY (0x1F << 8) /* RW */
+#define MSDC_PAD_TUNE_CMDRDLY (0x1F << 16) /* RW */
+#define MSDC_PAD_TUNE_CMDRRDLY (0x1FUL << 22) /* RW */
+#define MSDC_PAD_TUNE_CLKTXDLY (0x1FUL << 27) /* RW */
+
+/* MSDC_DAT_RDDLY0/1 mask */
+#define MSDC_DAT_RDDLY0_D0 (0x1F << 0) /* RW */
+#define MSDC_DAT_RDDLY0_D1 (0x1F << 8) /* RW */
+#define MSDC_DAT_RDDLY0_D2 (0x1F << 16) /* RW */
+#define MSDC_DAT_RDDLY0_D3 (0x1F << 24) /* RW */
+
+#define MSDC_DAT_RDDLY1_D4 (0x1F << 0) /* RW */
+#define MSDC_DAT_RDDLY1_D5 (0x1F << 8) /* RW */
+#define MSDC_DAT_RDDLY1_D6 (0x1F << 16) /* RW */
+#define MSDC_DAT_RDDLY1_D7 (0x1F << 24) /* RW */
+
+#define MSDC_CKGEN_MSDC_DLY_SEL (0x1F<<10)
+#define MSDC_INT_DAT_LATCH_CK_SEL (0x7<<7)
+#define MSDC_CKGEN_MSDC_CK_SEL (0x1<<6)
+#define CARD_READY_FOR_DATA (1<<8)
+#define CARD_CURRENT_STATE(x) ((x&0x00001E00)>>9)
+
+/*--------------------------------------------------------------------------*/
+/* Descriptor Structure */
+/*--------------------------------------------------------------------------*/
+typedef struct {
+ u32 hwo:1; /* could be changed by hw */
+ u32 bdp:1;
+ u32 rsv0:6;
+ u32 chksum:8;
+ u32 intr:1;
+ u32 rsv1:15;
+ void *next;
+ void *ptr;
+ u32 buflen:16;
+ u32 extlen:8;
+ u32 rsv2:8;
+ u32 arg;
+ u32 blknum;
+ u32 cmd;
+} gpd_t;
+
+typedef struct {
+ u32 eol:1;
+ u32 rsv0:7;
+ u32 chksum:8;
+ u32 rsv1:1;
+ u32 blkpad:1;
+ u32 dwpad:1;
+ u32 rsv2:13;
+ void *next;
+ void *ptr;
+ u32 buflen:16;
+ u32 rsv3:16;
+} bd_t;
+
+/*--------------------------------------------------------------------------*/
+/* Register Debugging Structure */
+/*--------------------------------------------------------------------------*/
+
+typedef struct {
+ u32 msdc:1;
+ u32 ckpwn:1;
+ u32 rst:1;
+ u32 pio:1;
+ u32 ckdrven:1;
+ u32 start18v:1;
+ u32 pass18v:1;
+ u32 ckstb:1;
+ u32 ckdiv:8;
+ u32 ckmod:2;
+ u32 pad:14;
+} msdc_cfg_reg;
+typedef struct {
+ u32 sdr104cksel:1;
+ u32 rsmpl:1;
+ u32 dsmpl:1;
+ u32 ddlysel:1;
+ u32 ddr50ckd:1;
+ u32 dsplsel:1;
+ u32 pad1:10;
+ u32 d0spl:1;
+ u32 d1spl:1;
+ u32 d2spl:1;
+ u32 d3spl:1;
+ u32 d4spl:1;
+ u32 d5spl:1;
+ u32 d6spl:1;
+ u32 d7spl:1;
+ u32 riscsz:1;
+ u32 pad2:7;
+} msdc_iocon_reg;
+typedef struct {
+ u32 cden:1;
+ u32 cdsts:1;
+ u32 pad1:10;
+ u32 cddebounce:4;
+ u32 dat:8;
+ u32 cmd:1;
+ u32 pad2:6;
+ u32 wp:1;
+} msdc_ps_reg;
+typedef struct {
+ u32 mmcirq:1;
+ u32 cdsc:1;
+ u32 pad1:1;
+ u32 atocmdrdy:1;
+ u32 atocmdtmo:1;
+ u32 atocmdcrc:1;
+ u32 dmaqempty:1;
+ u32 sdioirq:1;
+ u32 cmdrdy:1;
+ u32 cmdtmo:1;
+ u32 rspcrc:1;
+ u32 csta:1;
+ u32 xfercomp:1;
+ u32 dxferdone:1;
+ u32 dattmo:1;
+ u32 datcrc:1;
+ u32 atocmd19done:1;
+ u32 pad2:15;
+} msdc_int_reg;
+typedef struct {
+ u32 mmcirq:1;
+ u32 cdsc:1;
+ u32 pad1:1;
+ u32 atocmdrdy:1;
+ u32 atocmdtmo:1;
+ u32 atocmdcrc:1;
+ u32 dmaqempty:1;
+ u32 sdioirq:1;
+ u32 cmdrdy:1;
+ u32 cmdtmo:1;
+ u32 rspcrc:1;
+ u32 csta:1;
+ u32 xfercomp:1;
+ u32 dxferdone:1;
+ u32 dattmo:1;
+ u32 datcrc:1;
+ u32 atocmd19done:1;
+ u32 pad2:15;
+} msdc_inten_reg;
+typedef struct {
+ u32 rxcnt:8;
+ u32 pad1:8;
+ u32 txcnt:8;
+ u32 pad2:7;
+ u32 clr:1;
+} msdc_fifocs_reg;
+typedef struct {
+ u32 val;
+} msdc_txdat_reg;
+typedef struct {
+ u32 val;
+} msdc_rxdat_reg;
+typedef struct {
+ u32 sdiowkup:1;
+ u32 inswkup:1;
+ u32 pad1:14;
+ u32 buswidth:2;
+ u32 pad2:1;
+ u32 sdio:1;
+ u32 sdioide:1;
+ u32 intblkgap:1;
+ u32 pad4:2;
+ u32 dtoc:8;
+} sdc_cfg_reg;
+typedef struct {
+ u32 cmd:6;
+ u32 brk:1;
+ u32 rsptyp:3;
+ u32 pad1:1;
+ u32 dtype:2;
+ u32 rw:1;
+ u32 stop:1;
+ u32 goirq:1;
+ u32 blklen:12;
+ u32 atocmd:2;
+ u32 volswth:1;
+ u32 pad2:1;
+} sdc_cmd_reg;
+typedef struct {
+ u32 arg;
+} sdc_arg_reg;
+typedef struct {
+ u32 sdcbusy:1;
+ u32 cmdbusy:1;
+ u32 pad:29;
+ u32 swrcmpl:1;
+} sdc_sts_reg;
+typedef struct {
+ u32 val;
+} sdc_resp0_reg;
+typedef struct {
+ u32 val;
+} sdc_resp1_reg;
+typedef struct {
+ u32 val;
+} sdc_resp2_reg;
+typedef struct {
+ u32 val;
+} sdc_resp3_reg;
+typedef struct {
+ u32 num;
+} sdc_blknum_reg;
+typedef struct {
+ u32 sts;
+} sdc_csts_reg;
+typedef struct {
+ u32 sts;
+} sdc_cstsen_reg;
+typedef struct {
+ u32 datcrcsts:8;
+ u32 ddrcrcsts:4;
+ u32 pad:20;
+} sdc_datcrcsts_reg;
+typedef struct {
+ u32 bootstart:1;
+ u32 bootstop:1;
+ u32 bootmode:1;
+ u32 pad1:9;
+ u32 bootwaidly:3;
+ u32 bootsupp:1;
+ u32 pad2:16;
+} emmc_cfg0_reg;
+typedef struct {
+ u32 bootcrctmc:16;
+ u32 pad:4;
+ u32 bootacktmc:12;
+} emmc_cfg1_reg;
+typedef struct {
+ u32 bootcrcerr:1;
+ u32 bootackerr:1;
+ u32 bootdattmo:1;
+ u32 bootacktmo:1;
+ u32 bootupstate:1;
+ u32 bootackrcv:1;
+ u32 bootdatrcv:1;
+ u32 pad:25;
+} emmc_sts_reg;
+typedef struct {
+ u32 bootrst:1;
+ u32 pad:31;
+} emmc_iocon_reg;
+typedef struct {
+ u32 val;
+} msdc_acmd_resp_reg;
+typedef struct {
+ u32 tunesel:4;
+ u32 pad:28;
+} msdc_acmd19_trg_reg;
+typedef struct {
+ u32 val;
+} msdc_acmd19_sts_reg;
+typedef struct {
+ u32 addr;
+} msdc_dma_sa_reg;
+typedef struct {
+ u32 addr;
+} msdc_dma_ca_reg;
+typedef struct {
+ u32 start:1;
+ u32 stop:1;
+ u32 resume:1;
+ u32 pad1:5;
+ u32 mode:1;
+ u32 pad2:1;
+ u32 lastbuf:1;
+ u32 pad3:1;
+ u32 brustsz:3;
+ u32 pad4:1;
+ u32 xfersz:16;
+} msdc_dma_ctrl_reg;
+typedef struct {
+ u32 status:1;
+ u32 decsen:1;
+ u32 pad1:2;
+ u32 bdcsen:1;
+ u32 gpdcsen:1;
+ u32 pad2:26;
+} msdc_dma_cfg_reg;
+typedef struct {
+ u32 sel:16;
+ u32 pad2:16;
+} msdc_dbg_sel_reg;
+typedef struct {
+ u32 val;
+} msdc_dbg_out_reg;
+typedef struct {
+ u32 clkdrvn:3;
+ u32 rsv0:1;
+ u32 clkdrvp:3;
+ u32 rsv1:1;
+ u32 clksr:1;
+ u32 rsv2:7;
+ u32 clkpd:1;
+ u32 clkpu:1;
+ u32 clksmt:1;
+ u32 clkies:1;
+ u32 clktdsel:4;
+ u32 clkrdsel:8;
+} msdc_pad_ctl0_reg;
+typedef struct {
+ u32 cmddrvn:3;
+ u32 rsv0:1;
+ u32 cmddrvp:3;
+ u32 rsv1:1;
+ u32 cmdsr:1;
+ u32 rsv2:7;
+ u32 cmdpd:1;
+ u32 cmdpu:1;
+ u32 cmdsmt:1;
+ u32 cmdies:1;
+ u32 cmdtdsel:4;
+ u32 cmdrdsel:8;
+} msdc_pad_ctl1_reg;
+typedef struct {
+ u32 datdrvn:3;
+ u32 rsv0:1;
+ u32 datdrvp:3;
+ u32 rsv1:1;
+ u32 datsr:1;
+ u32 rsv2:7;
+ u32 datpd:1;
+ u32 datpu:1;
+ u32 datsmt:1;
+ u32 daties:1;
+ u32 dattdsel:4;
+ u32 datrdsel:8;
+} msdc_pad_ctl2_reg;
+typedef struct {
+ u32 wrrxdly:3;
+ u32 pad1:5;
+ u32 rdrxdly:8;
+ u32 pad2:16;
+} msdc_pad_tune_reg;
+typedef struct {
+ u32 dat0:5;
+ u32 rsv0:3;
+ u32 dat1:5;
+ u32 rsv1:3;
+ u32 dat2:5;
+ u32 rsv2:3;
+ u32 dat3:5;
+ u32 rsv3:3;
+} msdc_dat_rddly0;
+typedef struct {
+ u32 dat4:5;
+ u32 rsv4:3;
+ u32 dat5:5;
+ u32 rsv5:3;
+ u32 dat6:5;
+ u32 rsv6:3;
+ u32 dat7:5;
+ u32 rsv7:3;
+} msdc_dat_rddly1;
+typedef struct {
+ u32 dbg0sel:8;
+ u32 dbg1sel:6;
+ u32 pad1:2;
+ u32 dbg2sel:6;
+ u32 pad2:2;
+ u32 dbg3sel:6;
+ u32 pad3:2;
+} msdc_hw_dbg_reg;
+typedef struct {
+ u32 val;
+} msdc_version_reg;
+typedef struct {
+ u32 val;
+} msdc_eco_ver_reg;
+
+struct msdc_regs {
+ msdc_cfg_reg msdc_cfg; /* base+0x00h */
+ msdc_iocon_reg msdc_iocon; /* base+0x04h */
+ msdc_ps_reg msdc_ps; /* base+0x08h */
+ msdc_int_reg msdc_int; /* base+0x0ch */
+ msdc_inten_reg msdc_inten; /* base+0x10h */
+ msdc_fifocs_reg msdc_fifocs; /* base+0x14h */
+ msdc_txdat_reg msdc_txdat; /* base+0x18h */
+ msdc_rxdat_reg msdc_rxdat; /* base+0x1ch */
+ u32 rsv1[4];
+ sdc_cfg_reg sdc_cfg; /* base+0x30h */
+ sdc_cmd_reg sdc_cmd; /* base+0x34h */
+ sdc_arg_reg sdc_arg; /* base+0x38h */
+ sdc_sts_reg sdc_sts; /* base+0x3ch */
+ sdc_resp0_reg sdc_resp0; /* base+0x40h */
+ sdc_resp1_reg sdc_resp1; /* base+0x44h */
+ sdc_resp2_reg sdc_resp2; /* base+0x48h */
+ sdc_resp3_reg sdc_resp3; /* base+0x4ch */
+ sdc_blknum_reg sdc_blknum; /* base+0x50h */
+ u32 rsv2[1];
+ sdc_csts_reg sdc_csts; /* base+0x58h */
+ sdc_cstsen_reg sdc_cstsen; /* base+0x5ch */
+ sdc_datcrcsts_reg sdc_dcrcsta; /* base+0x60h */
+ u32 rsv3[3];
+ emmc_cfg0_reg emmc_cfg0; /* base+0x70h */
+ emmc_cfg1_reg emmc_cfg1; /* base+0x74h */
+ emmc_sts_reg emmc_sts; /* base+0x78h */
+ emmc_iocon_reg emmc_iocon; /* base+0x7ch */
+ msdc_acmd_resp_reg acmd_resp; /* base+0x80h */
+ msdc_acmd19_trg_reg acmd19_trg; /* base+0x84h */
+ msdc_acmd19_sts_reg acmd19_sts; /* base+0x88h */
+ u32 rsv4[1];
+ msdc_dma_sa_reg dma_sa; /* base+0x90h */
+ msdc_dma_ca_reg dma_ca; /* base+0x94h */
+ msdc_dma_ctrl_reg dma_ctrl; /* base+0x98h */
+ msdc_dma_cfg_reg dma_cfg; /* base+0x9ch */
+ msdc_dbg_sel_reg dbg_sel; /* base+0xa0h */
+ msdc_dbg_out_reg dbg_out; /* base+0xa4h */
+ u32 rsv5[2];
+ u32 patch0; /* base+0xb0h */
+ u32 patch1; /* base+0xb4h */
+ u32 rsv6[10];
+ msdc_pad_ctl0_reg pad_ctl0; /* base+0xe0h */
+ msdc_pad_ctl1_reg pad_ctl1; /* base+0xe4h */
+ msdc_pad_ctl2_reg pad_ctl2; /* base+0xe8h */
+ msdc_pad_tune_reg pad_tune; /* base+0xech */
+ msdc_dat_rddly0 dat_rddly0; /* base+0xf0h */
+ msdc_dat_rddly1 dat_rddly1; /* base+0xf4h */
+ msdc_hw_dbg_reg hw_dbg; /* base+0xf8h */
+ u32 rsv7[1];
+ msdc_version_reg version; /* base+0x100h */
+ msdc_eco_ver_reg eco_ver; /* base+0x104h */
+};
+
+struct scatterlist_ex {
+ u32 cmd;
+ u32 arg;
+ u32 sglen;
+ struct scatterlist *sg;
+};
+
+#define DMA_FLAG_NONE (0x00000000)
+#define DMA_FLAG_EN_CHKSUM (0x00000001)
+#define DMA_FLAG_PAD_BLOCK (0x00000002)
+#define DMA_FLAG_PAD_DWORD (0x00000004)
+
+struct msdc_dma {
+ u32 flags; /* flags */
+ u32 xfersz; /* xfer size in bytes */
+ u32 sglen; /* size of scatter list */
+ u32 blklen; /* block size */
+ struct scatterlist *sg; /* I/O scatter list */
+ struct scatterlist_ex *esg; /* extended I/O scatter list */
+ u8 mode; /* dma mode */
+ u8 burstsz; /* burst size */
+ u8 intr; /* dma done interrupt */
+ u8 padding; /* padding */
+ u32 cmd; /* enhanced mode command */
+ u32 arg; /* enhanced mode arg */
+ u32 rsp; /* enhanced mode command response */
+ u32 autorsp; /* auto command response */
+
+ gpd_t *gpd; /* pointer to gpd array */
+ bd_t *bd; /* pointer to bd array */
+ dma_addr_t gpd_addr; /* the physical address of gpd array */
+ dma_addr_t bd_addr; /* the physical address of bd array */
+ u32 used_gpd; /* the number of used gpd elements */
+ u32 used_bd; /* the number of used bd elements */
+};
+
+struct msdc_host
+{
+ struct msdc_hw *hw;
+
+ struct mmc_host *mmc; /* mmc structure */
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ struct mmc_request *mrq;
+ int cmd_rsp;
+ int cmd_rsp_done;
+ int cmd_r1b_done;
+
+ int error;
+ spinlock_t lock; /* mutex */
+ struct semaphore sem;
+
+ u32 blksz; /* host block size */
+ u32 base; /* host base address */
+ int id; /* host id */
+ int pwr_ref; /* core power reference count */
+
+ u32 xfer_size; /* total transferred size */
+
+ struct msdc_dma dma; /* dma channel */
+ u32 dma_addr; /* dma transfer address */
+ u32 dma_left_size; /* dma transfer left size */
+ u32 dma_xfer_size; /* dma transfer size in bytes */
+ int dma_xfer; /* dma transfer mode */
+
+ u32 timeout_ns; /* data timeout ns */
+ u32 timeout_clks; /* data timeout clks */
+
+ atomic_t abort; /* abort transfer */
+
+ int irq; /* host interrupt */
+
+ struct tasklet_struct card_tasklet;
+#if 0
+ struct work_struct card_workqueue;
+#else
+ struct delayed_work card_delaywork;
+#endif
+
+ struct completion cmd_done;
+ struct completion xfer_done;
+ struct pm_message pm_state;
+
+ u32 mclk; /* mmc subsystem clock */
+ u32 hclk; /* host clock speed */
+ u32 sclk; /* SD/MS clock speed */
+ u8 core_clkon; /* Host core clock on ? */
+ u8 card_clkon; /* Card clock on ? */
+ u8 core_power; /* core power */
+ u8 power_mode; /* host power mode */
+ u8 card_inserted; /* card inserted ? */
+ u8 suspend; /* host suspended ? */
+ u8 reserved;
+ u8 app_cmd; /* for app command */
+ u32 app_cmd_arg;
+ u64 starttime;
+};
+
+static inline unsigned int uffs(unsigned int x)
+{
+ unsigned int r = 1;
+
+ if (!x)
+ return 0;
+ if (!(x & 0xffff)) {
+ x >>= 16;
+ r += 16;
+ }
+ if (!(x & 0xff)) {
+ x >>= 8;
+ r += 8;
+ }
+ if (!(x & 0xf)) {
+ x >>= 4;
+ r += 4;
+ }
+ if (!(x & 3)) {
+ x >>= 2;
+ r += 2;
+ }
+ if (!(x & 1)) {
+ x >>= 1;
+ r += 1;
+ }
+ return r;
+}
+#define sdr_read8(reg) __raw_readb(reg)
+#define sdr_read16(reg) __raw_readw(reg)
+#define sdr_read32(reg) __raw_readl(reg)
+#define sdr_write8(reg,val) __raw_writeb(val,reg)
+#define sdr_write16(reg,val) __raw_writew(val,reg)
+#define sdr_write32(reg,val) __raw_writel(val,reg)
+
+#define sdr_set_bits(reg,bs) ((*(volatile u32*)(reg)) |= (u32)(bs))
+#define sdr_clr_bits(reg,bs) ((*(volatile u32*)(reg)) &= ~((u32)(bs)))
+
+#define sdr_set_field(reg,field,val) \
+ do { \
+ volatile unsigned int tv = sdr_read32(reg); \
+ tv &= ~(field); \
+ tv |= ((val) << (uffs((unsigned int)field) - 1)); \
+ sdr_write32(reg,tv); \
+ } while(0)
+#define sdr_get_field(reg,field,val) \
+ do { \
+ volatile unsigned int tv = sdr_read32(reg); \
+ val = ((tv & (field)) >> (uffs((unsigned int)field) - 1)); \
+ } while(0)
+
+#endif
+
--- /dev/null
+++ b/drivers/mmc/host/mtk-mmc/sd.c
@@ -0,0 +1,3068 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ *
+ * MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
+#include <linux/dma-mapping.h>
+
+/* +++ by chhung */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+
+#define MSDC_SMPL_FALLING (1)
+#define MSDC_CD_PIN_EN (1 << 0) /* card detection pin is wired */
+#define MSDC_WP_PIN_EN (1 << 1) /* write protection pin is wired */
+#define MSDC_REMOVABLE (1 << 5) /* removable slot */
+#define MSDC_SYS_SUSPEND (1 << 6) /* suspended by system */
+#define MSDC_HIGHSPEED (1 << 7)
+
+//#define IRQ_SDC 14 //MT7620 /*FIXME*/
+#ifdef CONFIG_SOC_MT7621
+#define RALINK_SYSCTL_BASE 0xbe000000
+#define RALINK_MSDC_BASE 0xbe130000
+#else
+#define RALINK_SYSCTL_BASE 0xb0000000
+#define RALINK_MSDC_BASE 0xb0130000
+#endif
+#define IRQ_SDC 22 /*FIXME*/
+
+#include <asm/dma.h>
+/* end of +++ */
+
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#if 0 /* --- by chhung */
+#include <mach/board.h>
+#include <mach/mt6575_devs.h>
+#include <mach/mt6575_typedefs.h>
+#include <mach/mt6575_clock_manager.h>
+#include <mach/mt6575_pm_ldo.h>
+//#include <mach/mt6575_pll.h>
+//#include <mach/mt6575_gpio.h>
+//#include <mach/mt6575_gpt_sw.h>
+#include <asm/tcm.h>
+// #include <mach/mt6575_gpt.h>
+#endif /* end of --- */
+
+#include "mt6575_sd.h"
+#include "dbg.h"
+
+/* +++ by chhung */
+#include "board.h"
+/* end of +++ */
+
+#if 0 /* --- by chhung */
+#define isb() __asm__ __volatile__ ("" : : : "memory")
+#define dsb() __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" \
+ : : "r" (0) : "memory")
+#define dmb() __asm__ __volatile__ ("" : : : "memory")
+#endif /* end of --- */
+
+#define DRV_NAME "mtk-sd"
+
+#define HOST_MAX_NUM (1) /* +/- by chhung */
+
+#if defined (CONFIG_SOC_MT7620)
+#define HOST_MAX_MCLK (48000000) /* +/- by chhung */
+#elif defined (CONFIG_SOC_MT7621)
+#define HOST_MAX_MCLK (50000000) /* +/- by chhung */
+#endif
+#define HOST_MIN_MCLK (260000)
+
+#define HOST_MAX_BLKSZ (2048)
+
+#define MSDC_OCR_AVAIL (MMC_VDD_28_29 | MMC_VDD_29_30 | MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33)
+
+#define GPIO_PULL_DOWN (0)
+#define GPIO_PULL_UP (1)
+
+#if 0 /* --- by chhung */
+#define MSDC_CLKSRC_REG (0xf100000C)
+#define PDN_REG (0xF1000010)
+#endif /* end of --- */
+
+#define DEFAULT_DEBOUNCE (8) /* 8 cycles */
+#define DEFAULT_DTOC (40) /* data timeout counter. 65536x40 sclk. */
+
+#define CMD_TIMEOUT (HZ/10) /* 100ms */
+#define DAT_TIMEOUT (HZ/2 * 5) /* 500ms x5 */
+
+#define MAX_DMA_CNT (64 * 1024 - 512) /* a single transaction for WIFI may be 50K*/
+
+#define MAX_GPD_NUM (1 + 1) /* one null gpd */
+#define MAX_BD_NUM (1024)
+#define MAX_BD_PER_GPD (MAX_BD_NUM)
+
+#define MAX_HW_SGMTS (MAX_BD_NUM)
+#define MAX_PHY_SGMTS (MAX_BD_NUM)
+#define MAX_SGMT_SZ (MAX_DMA_CNT)
+#define MAX_REQ_SZ (MAX_SGMT_SZ * 8)
+
+#ifdef MT6575_SD_DEBUG
+static struct msdc_regs *msdc_reg[HOST_MAX_NUM];
+#endif
+
+static int mtk_sw_poll;
+
+static int cd_active_low = 1;
+
+//=================================
+#define PERI_MSDC0_PDN (15)
+//#define PERI_MSDC1_PDN (16)
+//#define PERI_MSDC2_PDN (17)
+//#define PERI_MSDC3_PDN (18)
+
+struct msdc_host *msdc_6575_host[] = {NULL,NULL,NULL,NULL};
+#if 0 /* --- by chhung */
+/* gate means clock power down */
+static int g_clk_gate = 0;
+#define msdc_gate_clock(id) \
+ do { \
+ g_clk_gate &= ~(1 << ((id) + PERI_MSDC0_PDN)); \
+ } while(0)
+/* not like power down register. 1 means clock on. */
+#define msdc_ungate_clock(id) \
+ do { \
+ g_clk_gate |= 1 << ((id) + PERI_MSDC0_PDN); \
+ } while(0)
+
+// do we need sync object or not
+void msdc_clk_status(int * status)
+{
+ *status = g_clk_gate;
+}
+#endif /* end of --- */
+
+/* +++ by chhung */
+struct msdc_hw msdc0_hw = {
+ .clk_src = 0,
+ .cmd_edge = MSDC_SMPL_FALLING,
+ .data_edge = MSDC_SMPL_FALLING,
+ .clk_drv = 4,
+ .cmd_drv = 4,
+ .dat_drv = 4,
+ .data_pins = 4,
+ .data_offset = 0,
+ .flags = MSDC_SYS_SUSPEND | MSDC_CD_PIN_EN | MSDC_REMOVABLE | MSDC_HIGHSPEED,
+// .flags = MSDC_SYS_SUSPEND | MSDC_WP_PIN_EN | MSDC_CD_PIN_EN | MSDC_REMOVABLE,
+};
+
+static struct resource mtk_sd_resources[] = {
+ [0] = {
+ .start = RALINK_MSDC_BASE,
+ .end = RALINK_MSDC_BASE+0x3fff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_SDC, /*FIXME*/
+ .end = IRQ_SDC, /*FIXME*/
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device mtk_sd_device = {
+ .name = "mtk-sd",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mtk_sd_resources),
+ .resource = mtk_sd_resources,
+};
+/* end of +++ */
+
+static int msdc_rsp[] = {
+ 0, /* RESP_NONE */
+ 1, /* RESP_R1 */
+ 2, /* RESP_R2 */
+ 3, /* RESP_R3 */
+ 4, /* RESP_R4 */
+ 1, /* RESP_R5 */
+ 1, /* RESP_R6 */
+ 1, /* RESP_R7 */
+ 7, /* RESP_R1b */
+};
+
+/* For Inhanced DMA */
+#define msdc_init_gpd_ex(gpd,extlen,cmd,arg,blknum) \
+ do { \
+ ((gpd_t*)gpd)->extlen = extlen; \
+ ((gpd_t*)gpd)->cmd = cmd; \
+ ((gpd_t*)gpd)->arg = arg; \
+ ((gpd_t*)gpd)->blknum = blknum; \
+ }while(0)
+
+#define msdc_init_bd(bd, blkpad, dwpad, dptr, dlen) \
+ do { \
+ BUG_ON(dlen > 0xFFFFUL); \
+ ((bd_t*)bd)->blkpad = blkpad; \
+ ((bd_t*)bd)->dwpad = dwpad; \
+ ((bd_t*)bd)->ptr = (void*)dptr; \
+ ((bd_t*)bd)->buflen = dlen; \
+ }while(0)
+
+#define msdc_txfifocnt() ((sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_TXCNT) >> 16)
+#define msdc_rxfifocnt() ((sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_RXCNT) >> 0)
+#define msdc_fifo_write32(v) sdr_write32(MSDC_TXDATA, (v))
+#define msdc_fifo_write8(v) sdr_write8(MSDC_TXDATA, (v))
+#define msdc_fifo_read32() sdr_read32(MSDC_RXDATA)
+#define msdc_fifo_read8() sdr_read8(MSDC_RXDATA)
+
+
+#define msdc_dma_on() sdr_clr_bits(MSDC_CFG, MSDC_CFG_PIO)
+#define msdc_dma_off() sdr_set_bits(MSDC_CFG, MSDC_CFG_PIO)
+
+#define msdc_retry(expr,retry,cnt) \
+ do { \
+ int backup = cnt; \
+ while (retry) { \
+ if (!(expr)) break; \
+ if (cnt-- == 0) { \
+ retry--; mdelay(1); cnt = backup; \
+ } \
+ } \
+ WARN_ON(retry == 0); \
+ } while(0)
+
+#if 0 /* --- by chhung */
+#define msdc_reset() \
+ do { \
+ int retry = 3, cnt = 1000; \
+ sdr_set_bits(MSDC_CFG, MSDC_CFG_RST); \
+ dsb(); \
+ msdc_retry(sdr_read32(MSDC_CFG) & MSDC_CFG_RST, retry, cnt); \
+ } while(0)
+#else
+#define msdc_reset() \
+ do { \
+ int retry = 3, cnt = 1000; \
+ sdr_set_bits(MSDC_CFG, MSDC_CFG_RST); \
+ msdc_retry(sdr_read32(MSDC_CFG) & MSDC_CFG_RST, retry, cnt); \
+ } while(0)
+#endif /* end of +/- */
+
+#define msdc_clr_int() \
+ do { \
+ volatile u32 val = sdr_read32(MSDC_INT); \
+ sdr_write32(MSDC_INT, val); \
+ } while(0)
+
+#define msdc_clr_fifo() \
+ do { \
+ int retry = 3, cnt = 1000; \
+ sdr_set_bits(MSDC_FIFOCS, MSDC_FIFOCS_CLR); \
+ msdc_retry(sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_CLR, retry, cnt); \
+ } while(0)
+
+#define msdc_irq_save(val) \
+ do { \
+ val = sdr_read32(MSDC_INTEN); \
+ sdr_clr_bits(MSDC_INTEN, val); \
+ } while(0)
+
+#define msdc_irq_restore(val) \
+ do { \
+ sdr_set_bits(MSDC_INTEN, val); \
+ } while(0)
+
+/* clock source for host: global */
+#if defined (CONFIG_SOC_MT7620)
+static u32 hclks[] = {48000000}; /* +/- by chhung */
+#elif defined (CONFIG_SOC_MT7621)
+static u32 hclks[] = {50000000}; /* +/- by chhung */
+#endif
+
+//============================================
+// the power for msdc host controller: global
+// always keep the VMC on.
+//============================================
+#define msdc_vcore_on(host) \
+ do { \
+ INIT_MSG("[+]VMC ref. count<%d>", ++host->pwr_ref); \
+ (void)hwPowerOn(MT65XX_POWER_LDO_VMC, VOL_3300, "SD"); \
+ } while (0)
+#define msdc_vcore_off(host) \
+ do { \
+ INIT_MSG("[-]VMC ref. count<%d>", --host->pwr_ref); \
+ (void)hwPowerDown(MT65XX_POWER_LDO_VMC, "SD"); \
+ } while (0)
+
+//====================================
+// the vdd output for card: global
+// always keep the VMCH on.
+//====================================
+#define msdc_vdd_on(host) \
+ do { \
+ (void)hwPowerOn(MT65XX_POWER_LDO_VMCH, VOL_3300, "SD"); \
+ } while (0)
+#define msdc_vdd_off(host) \
+ do { \
+ (void)hwPowerDown(MT65XX_POWER_LDO_VMCH, "SD"); \
+ } while (0)
+
+#define sdc_is_busy() (sdr_read32(SDC_STS) & SDC_STS_SDCBUSY)
+#define sdc_is_cmd_busy() (sdr_read32(SDC_STS) & SDC_STS_CMDBUSY)
+
+#define sdc_send_cmd(cmd,arg) \
+ do { \
+ sdr_write32(SDC_ARG, (arg)); \
+ sdr_write32(SDC_CMD, (cmd)); \
+ } while(0)
+
+// can modify to read h/w register.
+//#define is_card_present(h) ((sdr_read32(MSDC_PS) & MSDC_PS_CDSTS) ? 0 : 1);
+#define is_card_present(h) (((struct msdc_host*)(h))->card_inserted)
+
+/* +++ by chhung */
+#ifndef __ASSEMBLY__
+#define PHYSADDR(a) (((unsigned long)(a)) & 0x1fffffff)
+#else
+#define PHYSADDR(a) ((a) & 0x1fffffff)
+#endif
+/* end of +++ */
+static unsigned int msdc_do_command(struct msdc_host *host,
+ struct mmc_command *cmd,
+ int tune,
+ unsigned long timeout);
+
+static int msdc_tune_cmdrsp(struct msdc_host*host,struct mmc_command *cmd);
+
+#ifdef MT6575_SD_DEBUG
+static void msdc_dump_card_status(struct msdc_host *host, u32 status)
+{
+ static char *state[] = {
+ "Idle", /* 0 */
+ "Ready", /* 1 */
+ "Ident", /* 2 */
+ "Stby", /* 3 */
+ "Tran", /* 4 */
+ "Data", /* 5 */
+ "Rcv", /* 6 */
+ "Prg", /* 7 */
+ "Dis", /* 8 */
+ "Reserved", /* 9 */
+ "Reserved", /* 10 */
+ "Reserved", /* 11 */
+ "Reserved", /* 12 */
+ "Reserved", /* 13 */
+ "Reserved", /* 14 */
+ "I/O mode", /* 15 */
+ };
+ if (status & R1_OUT_OF_RANGE)
+ N_MSG(RSP, "[CARD_STATUS] Out of Range");
+ if (status & R1_ADDRESS_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Address Error");
+ if (status & R1_BLOCK_LEN_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Block Len Error");
+ if (status & R1_ERASE_SEQ_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Erase Seq Error");
+ if (status & R1_ERASE_PARAM)
+ N_MSG(RSP, "[CARD_STATUS] Erase Param");
+ if (status & R1_WP_VIOLATION)
+ N_MSG(RSP, "[CARD_STATUS] WP Violation");
+ if (status & R1_CARD_IS_LOCKED)
+ N_MSG(RSP, "[CARD_STATUS] Card is Locked");
+ if (status & R1_LOCK_UNLOCK_FAILED)
+ N_MSG(RSP, "[CARD_STATUS] Lock/Unlock Failed");
+ if (status & R1_COM_CRC_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Command CRC Error");
+ if (status & R1_ILLEGAL_COMMAND)
+ N_MSG(RSP, "[CARD_STATUS] Illegal Command");
+ if (status & R1_CARD_ECC_FAILED)
+ N_MSG(RSP, "[CARD_STATUS] Card ECC Failed");
+ if (status & R1_CC_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] CC Error");
+ if (status & R1_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Error");
+ if (status & R1_UNDERRUN)
+ N_MSG(RSP, "[CARD_STATUS] Underrun");
+ if (status & R1_OVERRUN)
+ N_MSG(RSP, "[CARD_STATUS] Overrun");
+ if (status & R1_CID_CSD_OVERWRITE)
+ N_MSG(RSP, "[CARD_STATUS] CID/CSD Overwrite");
+ if (status & R1_WP_ERASE_SKIP)
+ N_MSG(RSP, "[CARD_STATUS] WP Eraser Skip");
+ if (status & R1_CARD_ECC_DISABLED)
+ N_MSG(RSP, "[CARD_STATUS] Card ECC Disabled");
+ if (status & R1_ERASE_RESET)
+ N_MSG(RSP, "[CARD_STATUS] Erase Reset");
+ if (status & R1_READY_FOR_DATA)
+ N_MSG(RSP, "[CARD_STATUS] Ready for Data");
+ if (status & R1_SWITCH_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Switch error");
+ if (status & R1_APP_CMD)
+ N_MSG(RSP, "[CARD_STATUS] App Command");
+
+ N_MSG(RSP, "[CARD_STATUS] '%s' State", state[R1_CURRENT_STATE(status)]);
+}
+
+static void msdc_dump_ocr_reg(struct msdc_host *host, u32 resp)
+{
+ if (resp & (1 << 7))
+ N_MSG(RSP, "[OCR] Low Voltage Range");
+ if (resp & (1 << 15))
+ N_MSG(RSP, "[OCR] 2.7-2.8 volt");
+ if (resp & (1 << 16))
+ N_MSG(RSP, "[OCR] 2.8-2.9 volt");
+ if (resp & (1 << 17))
+ N_MSG(RSP, "[OCR] 2.9-3.0 volt");
+ if (resp & (1 << 18))
+ N_MSG(RSP, "[OCR] 3.0-3.1 volt");
+ if (resp & (1 << 19))
+ N_MSG(RSP, "[OCR] 3.1-3.2 volt");
+ if (resp & (1 << 20))
+ N_MSG(RSP, "[OCR] 3.2-3.3 volt");
+ if (resp & (1 << 21))
+ N_MSG(RSP, "[OCR] 3.3-3.4 volt");
+ if (resp & (1 << 22))
+ N_MSG(RSP, "[OCR] 3.4-3.5 volt");
+ if (resp & (1 << 23))
+ N_MSG(RSP, "[OCR] 3.5-3.6 volt");
+ if (resp & (1 << 24))
+ N_MSG(RSP, "[OCR] Switching to 1.8V Accepted (S18A)");
+ if (resp & (1 << 30))
+ N_MSG(RSP, "[OCR] Card Capacity Status (CCS)");
+ if (resp & (1 << 31))
+ N_MSG(RSP, "[OCR] Card Power Up Status (Idle)");
+ else
+ N_MSG(RSP, "[OCR] Card Power Up Status (Busy)");
+}
+
+static void msdc_dump_rca_resp(struct msdc_host *host, u32 resp)
+{
+ u32 status = (((resp >> 15) & 0x1) << 23) |
+ (((resp >> 14) & 0x1) << 22) |
+ (((resp >> 13) & 0x1) << 19) |
+ (resp & 0x1fff);
+
+ N_MSG(RSP, "[RCA] 0x%.4x", resp >> 16);
+ msdc_dump_card_status(host, status);
+}
+
+static void msdc_dump_io_resp(struct msdc_host *host, u32 resp)
+{
+ u32 flags = (resp >> 8) & 0xFF;
+ char *state[] = {"DIS", "CMD", "TRN", "RFU"};
+
+ if (flags & (1 << 7))
+ N_MSG(RSP, "[IO] COM_CRC_ERR");
+ if (flags & (1 << 6))
+ N_MSG(RSP, "[IO] Illgal command");
+ if (flags & (1 << 3))
+ N_MSG(RSP, "[IO] Error");
+ if (flags & (1 << 2))
+ N_MSG(RSP, "[IO] RFU");
+ if (flags & (1 << 1))
+ N_MSG(RSP, "[IO] Function number error");
+ if (flags & (1 << 0))
+ N_MSG(RSP, "[IO] Out of range");
+
+ N_MSG(RSP, "[IO] State: %s, Data:0x%x", state[(resp >> 12) & 0x3], resp & 0xFF);
+}
+#endif
+
+static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks)
+{
+ u32 base = host->base;
+ u32 timeout, clk_ns;
+
+ host->timeout_ns = ns;
+ host->timeout_clks = clks;
+
+ clk_ns = 1000000000UL / host->sclk;
+ timeout = ns / clk_ns + clks;
+ timeout = timeout >> 16; /* in 65536 sclk cycle unit */
+ timeout = timeout > 1 ? timeout - 1 : 0;
+ timeout = timeout > 255 ? 255 : timeout;
+
+ sdr_set_field(SDC_CFG, SDC_CFG_DTOC, timeout);
+
+ N_MSG(OPS, "Set read data timeout: %dns %dclks -> %d x 65536 cycles",
+ ns, clks, timeout + 1);
+}
+
+/* msdc_eirq_sdio() will be called when EIRQ(for WIFI) */
+static void msdc_eirq_sdio(void *data)
+{
+ struct msdc_host *host = (struct msdc_host *)data;
+
+ N_MSG(INT, "SDIO EINT");
+
+ mmc_signal_sdio_irq(host->mmc);
+}
+
+/* msdc_eirq_cd will not be used! We not using EINT for card detection. */
+static void msdc_eirq_cd(void *data)
+{
+ struct msdc_host *host = (struct msdc_host *)data;
+
+ N_MSG(INT, "CD EINT");
+
+#if 0
+ tasklet_hi_schedule(&host->card_tasklet);
+#else
+ schedule_delayed_work(&host->card_delaywork, HZ);
+#endif
+}
+
+#if 0
+static void msdc_tasklet_card(unsigned long arg)
+{
+ struct msdc_host *host = (struct msdc_host *)arg;
+#else
+static void msdc_tasklet_card(struct work_struct *work)
+{
+ struct msdc_host *host = (struct msdc_host *)container_of(work,
+ struct msdc_host, card_delaywork.work);
+#endif
+ struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+ u32 inserted;
+ u32 status = 0;
+ //u32 change = 0;
+
+ spin_lock(&host->lock);
+
+ if (hw->get_cd_status) { // NULL
+ inserted = hw->get_cd_status();
+ } else {
+ status = sdr_read32(MSDC_PS);
+ if (cd_active_low)
+ inserted = (status & MSDC_PS_CDSTS) ? 0 : 1;
+ else
+ inserted = (status & MSDC_PS_CDSTS) ? 1 : 0;
+ }
+ if (host->mmc->caps & MMC_CAP_NEEDS_POLL)
+ inserted = 1;
+
+#if 0
+ change = host->card_inserted ^ inserted;
+ host->card_inserted = inserted;
+
+ if (change && !host->suspend) {
+ if (inserted) {
+ host->mmc->f_max = HOST_MAX_MCLK; // work around
+ }
+ mmc_detect_change(host->mmc, msecs_to_jiffies(20));
+ }
+#else /* Make sure: handle the last interrupt */
+ host->card_inserted = inserted;
+
+ if (!host->suspend) {
+ host->mmc->f_max = HOST_MAX_MCLK;
+ mmc_detect_change(host->mmc, msecs_to_jiffies(20));
+ }
+
+ IRQ_MSG("card found<%s>", inserted ? "inserted" : "removed");
+#endif
+
+ spin_unlock(&host->lock);
+}
+
+#if 0 /* --- by chhung */
+/* For E2 only */
+static u8 clk_src_bit[4] = {
+ 0, 3, 5, 7
+};
+
+static void msdc_select_clksrc(struct msdc_host* host, unsigned char clksrc)
+{
+ u32 val;
+ u32 base = host->base;
+
+ BUG_ON(clksrc > 3);
+ INIT_MSG("set clock source to <%d>", clksrc);
+
+ val = sdr_read32(MSDC_CLKSRC_REG);
+ if (sdr_read32(MSDC_ECO_VER) >= 4) {
+ val &= ~(0x3 << clk_src_bit[host->id]);
+ val |= clksrc << clk_src_bit[host->id];
+ } else {
+ val &= ~0x3; val |= clksrc;
+ }
+ sdr_write32(MSDC_CLKSRC_REG, val);
+
+ host->hclk = hclks[clksrc];
+ host->hw->clk_src = clksrc;
+}
+#endif /* end of --- */
+
+static void msdc_set_mclk(struct msdc_host *host, int ddr, unsigned int hz)
+{
+ //struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+ u32 mode;
+ u32 flags;
+ u32 div;
+ u32 sclk;
+ u32 hclk = host->hclk;
+ //u8 clksrc = hw->clk_src;
+
+ if (!hz) { // set mmc system clock to 0 ?
+ //ERR_MSG("set mclk to 0!!!");
+ msdc_reset();
+ return;
+ }
+
+ msdc_irq_save(flags);
+
+#if defined (CONFIG_MT7621_FPGA) || defined (CONFIG_MT7628_FPGA)
+ mode = 0x0; /* use divisor */
+ if (hz >= (hclk >> 1)) {
+ div = 0; /* mean div = 1/2 */
+ sclk = hclk >> 1; /* sclk = clk / 2 */
+ } else {
+ div = (hclk + ((hz << 2) - 1)) / (hz << 2);
+ sclk = (hclk >> 2) / div;
+ }
+#else
+ if (ddr) {
+ mode = 0x2; /* ddr mode and use divisor */
+ if (hz >= (hclk >> 2)) {
+ div = 1; /* mean div = 1/4 */
+ sclk = hclk >> 2; /* sclk = clk / 4 */
+ } else {
+ div = (hclk + ((hz << 2) - 1)) / (hz << 2);
+ sclk = (hclk >> 2) / div;
+ }
+ } else if (hz >= hclk) { /* bug fix */
+ mode = 0x1; /* no divisor and divisor is ignored */
+ div = 0;
+ sclk = hclk;
+ } else {
+ mode = 0x0; /* use divisor */
+ if (hz >= (hclk >> 1)) {
+ div = 0; /* mean div = 1/2 */
+ sclk = hclk >> 1; /* sclk = clk / 2 */
+ } else {
+ div = (hclk + ((hz << 2) - 1)) / (hz << 2);
+ sclk = (hclk >> 2) / div;
+ }
+ }
+#endif
+ /* set clock mode and divisor */
+ sdr_set_field(MSDC_CFG, MSDC_CFG_CKMOD, mode);
+ sdr_set_field(MSDC_CFG, MSDC_CFG_CKDIV, div);
+
+ /* wait clock stable */
+ while (!(sdr_read32(MSDC_CFG) & MSDC_CFG_CKSTB));
+
+ host->sclk = sclk;
+ host->mclk = hz;
+ msdc_set_timeout(host, host->timeout_ns, host->timeout_clks); // need?
+
+ INIT_MSG("================");
+ INIT_MSG("!!! Set<%dKHz> Source<%dKHz> -> sclk<%dKHz>", hz/1000, hclk/1000, sclk/1000);
+ INIT_MSG("================");
+
+ msdc_irq_restore(flags);
+}
+
+/* Fix me. when need to abort */
+static void msdc_abort_data(struct msdc_host *host)
+{
+ u32 base = host->base;
+ struct mmc_command *stop = host->mrq->stop;
+
+ ERR_MSG("Need to Abort. dma<%d>", host->dma_xfer);
+
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+
+ // need to check FIFO count 0 ?
+
+ if (stop) { /* try to stop, but may not success */
+ ERR_MSG("stop when abort CMD<%d>", stop->opcode);
+ (void)msdc_do_command(host, stop, 0, CMD_TIMEOUT);
+ }
+
+ //if (host->mclk >= 25000000) {
+ // msdc_set_mclk(host, 0, host->mclk >> 1);
+ //}
+}
+
+#if 0 /* --- by chhung */
+static void msdc_pin_config(struct msdc_host *host, int mode)
+{
+ struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+ int pull = (mode == MSDC_PIN_PULL_UP) ? GPIO_PULL_UP : GPIO_PULL_DOWN;
+
+ /* Config WP pin */
+ if (hw->flags & MSDC_WP_PIN_EN) {
+ if (hw->config_gpio_pin) /* NULL */
+ hw->config_gpio_pin(MSDC_WP_PIN, pull);
+ }
+
+ switch (mode) {
+ case MSDC_PIN_PULL_UP:
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPU, 1); /* Check & FIXME */
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPD, 0); /* Check & FIXME */
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPU, 1);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPD, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPU, 1);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPD, 0);
+ break;
+ case MSDC_PIN_PULL_DOWN:
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPU, 0); /* Check & FIXME */
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPD, 1); /* Check & FIXME */
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPU, 0);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPD, 1);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPU, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPD, 1);
+ break;
+ case MSDC_PIN_PULL_NONE:
+ default:
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPU, 0); /* Check & FIXME */
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPD, 0); /* Check & FIXME */
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPU, 0);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPD, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPU, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPD, 0);
+ break;
+ }
+
+ N_MSG(CFG, "Pins mode(%d), down(%d), up(%d)",
+ mode, MSDC_PIN_PULL_DOWN, MSDC_PIN_PULL_UP);
+}
+
+void msdc_pin_reset(struct msdc_host *host, int mode)
+{
+ struct msdc_hw *hw = (struct msdc_hw *)host->hw;
+ u32 base = host->base;
+ int pull = (mode == MSDC_PIN_PULL_UP) ? GPIO_PULL_UP : GPIO_PULL_DOWN;
+
+ /* Config reset pin */
+ if (hw->flags & MSDC_RST_PIN_EN) {
+ if (hw->config_gpio_pin) /* NULL */
+ hw->config_gpio_pin(MSDC_RST_PIN, pull);
+
+ if (mode == MSDC_PIN_PULL_UP) {
+ sdr_clr_bits(EMMC_IOCON, EMMC_IOCON_BOOTRST);
+ } else {
+ sdr_set_bits(EMMC_IOCON, EMMC_IOCON_BOOTRST);
+ }
+ }
+}
+
+static void msdc_core_power(struct msdc_host *host, int on)
+{
+ N_MSG(CFG, "Turn %s %s power (copower: %d -> %d)",
+ on ? "on" : "off", "core", host->core_power, on);
+
+ if (on && host->core_power == 0) {
+ msdc_vcore_on(host);
+ host->core_power = 1;
+ msleep(1);
+ } else if (!on && host->core_power == 1) {
+ msdc_vcore_off(host);
+ host->core_power = 0;
+ msleep(1);
+ }
+}
+
+static void msdc_host_power(struct msdc_host *host, int on)
+{
+ N_MSG(CFG, "Turn %s %s power ", on ? "on" : "off", "host");
+
+ if (on) {
+ //msdc_core_power(host, 1); // need do card detection.
+ msdc_pin_reset(host, MSDC_PIN_PULL_UP);
+ } else {
+ msdc_pin_reset(host, MSDC_PIN_PULL_DOWN);
+ //msdc_core_power(host, 0);
+ }
+}
+
+static void msdc_card_power(struct msdc_host *host, int on)
+{
+ N_MSG(CFG, "Turn %s %s power ", on ? "on" : "off", "card");
+
+ if (on) {
+ msdc_pin_config(host, MSDC_PIN_PULL_UP);
+ if (host->hw->ext_power_on) {
+ host->hw->ext_power_on();
+ } else {
+ //msdc_vdd_on(host); // need todo card detection.
+ }
+ msleep(1);
+ } else {
+ if (host->hw->ext_power_off) {
+ host->hw->ext_power_off();
+ } else {
+ //msdc_vdd_off(host);
+ }
+ msdc_pin_config(host, MSDC_PIN_PULL_DOWN);
+ msleep(1);
+ }
+}
+
+static void msdc_set_power_mode(struct msdc_host *host, u8 mode)
+{
+ N_MSG(CFG, "Set power mode(%d)", mode);
+
+ if (host->power_mode == MMC_POWER_OFF && mode != MMC_POWER_OFF) {
+ msdc_host_power(host, 1);
+ msdc_card_power(host, 1);
+ } else if (host->power_mode != MMC_POWER_OFF && mode == MMC_POWER_OFF) {
+ msdc_card_power(host, 0);
+ msdc_host_power(host, 0);
+ }
+ host->power_mode = mode;
+}
+#endif /* end of --- */
+
+#ifdef CONFIG_PM
+/*
+ register as callback function of WIFI(combo_sdio_register_pm) .
+ can called by msdc_drv_suspend/resume too.
+*/
+static void msdc_pm(pm_message_t state, void *data)
+{
+ struct msdc_host *host = (struct msdc_host *)data;
+ int evt = state.event;
+
+ if (evt == PM_EVENT_USER_RESUME || evt == PM_EVENT_USER_SUSPEND) {
+ INIT_MSG("USR_%s: suspend<%d> power<%d>",
+ evt == PM_EVENT_USER_RESUME ? "EVENT_USER_RESUME" : "EVENT_USER_SUSPEND",
+ host->suspend, host->power_mode);
+ }
+
+ if (evt == PM_EVENT_SUSPEND || evt == PM_EVENT_USER_SUSPEND) {
+ if (host->suspend) /* already suspend */ /* default 0*/
+ return;
+
+ /* for memory card. already power off by mmc */
+ if (evt == PM_EVENT_SUSPEND && host->power_mode == MMC_POWER_OFF)
+ return;
+
+ host->suspend = 1;
+ host->pm_state = state; /* default PMSG_RESUME */
+
+ INIT_MSG("%s Suspend", evt == PM_EVENT_SUSPEND ? "PM" : "USR");
+ if(host->hw->flags & MSDC_SYS_SUSPEND) /* set for card */
+ (void)mmc_suspend_host(host->mmc);
+ else {
+ // host->mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY; /* just for double confirm */ /* --- by chhung */
+ mmc_remove_host(host->mmc);
+ }
+ } else if (evt == PM_EVENT_RESUME || evt == PM_EVENT_USER_RESUME) {
+ if (!host->suspend){
+ //ERR_MSG("warning: already resume");
+ return;
+ }
+
+ /* No PM resume when USR suspend */
+ if (evt == PM_EVENT_RESUME && host->pm_state.event == PM_EVENT_USER_SUSPEND) {
+ ERR_MSG("PM Resume when in USR Suspend"); /* won't happen. */
+ return;
+ }
+
+ host->suspend = 0;
+ host->pm_state = state;
+
+ INIT_MSG("%s Resume", evt == PM_EVENT_RESUME ? "PM" : "USR");
+ if(host->hw->flags & MSDC_SYS_SUSPEND) { /* will not set for WIFI */
+ (void)mmc_resume_host(host->mmc);
+ }
+ else {
+ // host->mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY; /* --- by chhung */
+ mmc_add_host(host->mmc);
+ }
+ }
+}
+#endif
+
+/*--------------------------------------------------------------------------*/
+/* mmc_host_ops members */
+/*--------------------------------------------------------------------------*/
+static unsigned int msdc_command_start(struct msdc_host *host,
+ struct mmc_command *cmd,
+ int tune, /* not used */
+ unsigned long timeout)
+{
+ u32 base = host->base;
+ u32 opcode = cmd->opcode;
+ u32 rawcmd;
+ u32 wints = MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO |
+ MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO |
+ MSDC_INT_ACMD19_DONE;
+
+ u32 resp;
+ unsigned long tmo;
+
+ /* Protocol layer does not provide response type, but our hardware needs
+ * to know exact type, not just size!
+ */
+ if (opcode == MMC_SEND_OP_COND || opcode == SD_APP_OP_COND)
+ resp = RESP_R3;
+ else if (opcode == MMC_SET_RELATIVE_ADDR || opcode == SD_SEND_RELATIVE_ADDR)
+ resp = (mmc_cmd_type(cmd) == MMC_CMD_BCR) ? RESP_R6 : RESP_R1;
+ else if (opcode == MMC_FAST_IO)
+ resp = RESP_R4;
+ else if (opcode == MMC_GO_IRQ_STATE)
+ resp = RESP_R5;
+ else if (opcode == MMC_SELECT_CARD)
+ resp = (cmd->arg != 0) ? RESP_R1B : RESP_NONE;
+ else if (opcode == SD_IO_RW_DIRECT || opcode == SD_IO_RW_EXTENDED)
+ resp = RESP_R1; /* SDIO workaround. */
+ else if (opcode == SD_SEND_IF_COND && (mmc_cmd_type(cmd) == MMC_CMD_BCR))
+ resp = RESP_R1;
+ else {
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_R1:
+ resp = RESP_R1;
+ break;
+ case MMC_RSP_R1B:
+ resp = RESP_R1B;
+ break;
+ case MMC_RSP_R2:
+ resp = RESP_R2;
+ break;
+ case MMC_RSP_R3:
+ resp = RESP_R3;
+ break;
+ case MMC_RSP_NONE:
+ default:
+ resp = RESP_NONE;
+ break;
+ }
+ }
+
+ cmd->error = 0;
+ /* rawcmd :
+ * vol_swt << 30 | auto_cmd << 28 | blklen << 16 | go_irq << 15 |
+ * stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode
+ */
+ rawcmd = opcode | msdc_rsp[resp] << 7 | host->blksz << 16;
+
+ if (opcode == MMC_READ_MULTIPLE_BLOCK) {
+ rawcmd |= (2 << 11);
+ } else if (opcode == MMC_READ_SINGLE_BLOCK) {
+ rawcmd |= (1 << 11);
+ } else if (opcode == MMC_WRITE_MULTIPLE_BLOCK) {
+ rawcmd |= ((2 << 11) | (1 << 13));
+ } else if (opcode == MMC_WRITE_BLOCK) {
+ rawcmd |= ((1 << 11) | (1 << 13));
+ } else if (opcode == SD_IO_RW_EXTENDED) {
+ if (cmd->data->flags & MMC_DATA_WRITE)
+ rawcmd |= (1 << 13);
+ if (cmd->data->blocks > 1)
+ rawcmd |= (2 << 11);
+ else
+ rawcmd |= (1 << 11);
+ } else if (opcode == SD_IO_RW_DIRECT && cmd->flags == (unsigned int)-1) {
+ rawcmd |= (1 << 14);
+ } else if ((opcode == SD_APP_SEND_SCR) ||
+ (opcode == SD_APP_SEND_NUM_WR_BLKS) ||
+ (opcode == SD_SWITCH && (mmc_cmd_type(cmd) == MMC_CMD_ADTC)) ||
+ (opcode == SD_APP_SD_STATUS && (mmc_cmd_type(cmd) == MMC_CMD_ADTC)) ||
+ (opcode == MMC_SEND_EXT_CSD && (mmc_cmd_type(cmd) == MMC_CMD_ADTC))) {
+ rawcmd |= (1 << 11);
+ } else if (opcode == MMC_STOP_TRANSMISSION) {
+ rawcmd |= (1 << 14);
+ rawcmd &= ~(0x0FFF << 16);
+ }
+
+ N_MSG(CMD, "CMD<%d><0x%.8x> Arg<0x%.8x>", opcode , rawcmd, cmd->arg);
+
+ tmo = jiffies + timeout;
+
+ if (opcode == MMC_SEND_STATUS) {
+ for (;;) {
+ if (!sdc_is_cmd_busy())
+ break;
+
+ if (time_after(jiffies, tmo)) {
+ ERR_MSG("XXX cmd_busy timeout: before CMD<%d>", opcode);
+ cmd->error = (unsigned int)-ETIMEDOUT;
+ msdc_reset();
+ goto end;
+ }
+ }
+ }else {
+ for (;;) {
+ if (!sdc_is_busy())
+ break;
+ if (time_after(jiffies, tmo)) {
+ ERR_MSG("XXX sdc_busy timeout: before CMD<%d>", opcode);
+ cmd->error = (unsigned int)-ETIMEDOUT;
+ msdc_reset();
+ goto end;
+ }
+ }
+ }
+
+ //BUG_ON(in_interrupt());
+ host->cmd = cmd;
+ host->cmd_rsp = resp;
+
+ init_completion(&host->cmd_done);
+
+ sdr_set_bits(MSDC_INTEN, wints);
+ sdc_send_cmd(rawcmd, cmd->arg);
+
+end:
+ return cmd->error;
+}
+
+static unsigned int msdc_command_resp(struct msdc_host *host,
+ struct mmc_command *cmd,
+ int tune,
+ unsigned long timeout)
+{
+ u32 base = host->base;
+ u32 opcode = cmd->opcode;
+ //u32 rawcmd;
+ u32 resp;
+ u32 wints = MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO |
+ MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO |
+ MSDC_INT_ACMD19_DONE;
+
+ resp = host->cmd_rsp;
+
+ BUG_ON(in_interrupt());
+ //init_completion(&host->cmd_done);
+ //sdr_set_bits(MSDC_INTEN, wints);
+
+ spin_unlock(&host->lock);
+ if(!wait_for_completion_timeout(&host->cmd_done, 10*timeout)){
+ ERR_MSG("XXX CMD<%d> wait_for_completion timeout ARG<0x%.8x>", opcode, cmd->arg);
+ cmd->error = (unsigned int)-ETIMEDOUT;
+ msdc_reset();
+ }
+ spin_lock(&host->lock);
+
+ sdr_clr_bits(MSDC_INTEN, wints);
+ host->cmd = NULL;
+
+//end:
+#ifdef MT6575_SD_DEBUG
+ switch (resp) {
+ case RESP_NONE:
+ N_MSG(RSP, "CMD_RSP(%d): %d RSP(%d)", opcode, cmd->error, resp);
+ break;
+ case RESP_R2:
+ N_MSG(RSP, "CMD_RSP(%d): %d RSP(%d)= %.8x %.8x %.8x %.8x",
+ opcode, cmd->error, resp, cmd->resp[0], cmd->resp[1],
+ cmd->resp[2], cmd->resp[3]);
+ break;
+ default: /* Response types 1, 3, 4, 5, 6, 7(1b) */
+ N_MSG(RSP, "CMD_RSP(%d): %d RSP(%d)= 0x%.8x",
+ opcode, cmd->error, resp, cmd->resp[0]);
+ if (cmd->error == 0) {
+ switch (resp) {
+ case RESP_R1:
+ case RESP_R1B:
+ msdc_dump_card_status(host, cmd->resp[0]);
+ break;
+ case RESP_R3:
+ msdc_dump_ocr_reg(host, cmd->resp[0]);
+ break;
+ case RESP_R5:
+ msdc_dump_io_resp(host, cmd->resp[0]);
+ break;
+ case RESP_R6:
+ msdc_dump_rca_resp(host, cmd->resp[0]);
+ break;
+ }
+ }
+ break;
+ }
+#endif
+
+ /* do we need to save card's RCA when SD_SEND_RELATIVE_ADDR */
+
+ if (!tune) {
+ return cmd->error;
+ }
+
+ /* memory card CRC */
+ if(host->hw->flags & MSDC_REMOVABLE && cmd->error == (unsigned int)(-EIO) ) {
+ if (sdr_read32(SDC_CMD) & 0x1800) { /* check if has data phase */
+ msdc_abort_data(host);
+ } else {
+ /* do basic: reset*/
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ }
+ cmd->error = msdc_tune_cmdrsp(host,cmd);
+ }
+
+ // check DAT0
+ /* if (resp == RESP_R1B) {
+ while ((sdr_read32(MSDC_PS) & 0x10000) != 0x10000);
+ } */
+ /* CMD12 Error Handle */
+
+ return cmd->error;
+}
+
+static unsigned int msdc_do_command(struct msdc_host *host,
+ struct mmc_command *cmd,
+ int tune,
+ unsigned long timeout)
+{
+ if (msdc_command_start(host, cmd, tune, timeout))
+ goto end;
+
+ if (msdc_command_resp(host, cmd, tune, timeout))
+ goto end;
+
+end:
+
+ N_MSG(CMD, " return<%d> resp<0x%.8x>", cmd->error, cmd->resp[0]);
+ return cmd->error;
+}
+
+/* The abort condition when PIO read/write
+ tmo:
+*/
+static int msdc_pio_abort(struct msdc_host *host, struct mmc_data *data, unsigned long tmo)
+{
+ int ret = 0;
+ u32 base = host->base;
+
+ if (atomic_read(&host->abort)) {
+ ret = 1;
+ }
+
+ if (time_after(jiffies, tmo)) {
+ data->error = (unsigned int)-ETIMEDOUT;
+ ERR_MSG("XXX PIO Data Timeout: CMD<%d>", host->mrq->cmd->opcode);
+ ret = 1;
+ }
+
+ if(ret) {
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ ERR_MSG("msdc pio find abort");
+ }
+ return ret;
+}
+
+/*
+ Need to add a timeout, or WDT timeout, system reboot.
+*/
+// pio mode data read/write
+static int msdc_pio_read(struct msdc_host *host, struct mmc_data *data)
+{
+ struct scatterlist *sg = data->sg;
+ u32 base = host->base;
+ u32 num = data->sg_len;
+ u32 *ptr;
+ u8 *u8ptr;
+ u32 left = 0;
+ u32 count, size = 0;
+ u32 wints = MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR ;
+ unsigned long tmo = jiffies + DAT_TIMEOUT;
+
+ sdr_set_bits(MSDC_INTEN, wints);
+ while (num) {
+ left = sg_dma_len(sg);
+ ptr = sg_virt(sg);
+ while (left) {
+ if ((left >= MSDC_FIFO_THD) && (msdc_rxfifocnt() >= MSDC_FIFO_THD)) {
+ count = MSDC_FIFO_THD >> 2;
+ do {
+ *ptr++ = msdc_fifo_read32();
+ } while (--count);
+ left -= MSDC_FIFO_THD;
+ } else if ((left < MSDC_FIFO_THD) && msdc_rxfifocnt() >= left) {
+ while (left > 3) {
+ *ptr++ = msdc_fifo_read32();
+ left -= 4;
+ }
+
+ u8ptr = (u8 *)ptr;
+ while(left) {
+ * u8ptr++ = msdc_fifo_read8();
+ left--;
+ }
+ }
+
+ if (msdc_pio_abort(host, data, tmo)) {
+ goto end;
+ }
+ }
+ size += sg_dma_len(sg);
+ sg = sg_next(sg); num--;
+ }
+end:
+ data->bytes_xfered += size;
+ N_MSG(FIO, " PIO Read<%d>bytes", size);
+
+ sdr_clr_bits(MSDC_INTEN, wints);
+ if(data->error) ERR_MSG("read pio data->error<%d> left<%d> size<%d>", data->error, left, size);
+ return data->error;
+}
+
+/* please make sure won't using PIO when size >= 512
+ which means, memory card block read/write won't using pio
+ then don't need to handle the CMD12 when data error.
+*/
+static int msdc_pio_write(struct msdc_host* host, struct mmc_data *data)
+{
+ u32 base = host->base;
+ struct scatterlist *sg = data->sg;
+ u32 num = data->sg_len;
+ u32 *ptr;
+ u8 *u8ptr;
+ u32 left;
+ u32 count, size = 0;
+ u32 wints = MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR ;
+ unsigned long tmo = jiffies + DAT_TIMEOUT;
+
+ sdr_set_bits(MSDC_INTEN, wints);
+ while (num) {
+ left = sg_dma_len(sg);
+ ptr = sg_virt(sg);
+
+ while (left) {
+ if (left >= MSDC_FIFO_SZ && msdc_txfifocnt() == 0) {
+ count = MSDC_FIFO_SZ >> 2;
+ do {
+ msdc_fifo_write32(*ptr); ptr++;
+ } while (--count);
+ left -= MSDC_FIFO_SZ;
+ } else if (left < MSDC_FIFO_SZ && msdc_txfifocnt() == 0) {
+ while (left > 3) {
+ msdc_fifo_write32(*ptr); ptr++;
+ left -= 4;
+ }
+
+ u8ptr = (u8*)ptr;
+ while(left){
+ msdc_fifo_write8(*u8ptr); u8ptr++;
+ left--;
+ }
+ }
+
+ if (msdc_pio_abort(host, data, tmo)) {
+ goto end;
+ }
+ }
+ size += sg_dma_len(sg);
+ sg = sg_next(sg); num--;
+ }
+end:
+ data->bytes_xfered += size;
+ N_MSG(FIO, " PIO Write<%d>bytes", size);
+ if(data->error) ERR_MSG("write pio data->error<%d>", data->error);
+
+ sdr_clr_bits(MSDC_INTEN, wints);
+ return data->error;
+}
+
+#if 0 /* --- by chhung */
+// DMA resume / start / stop
+static void msdc_dma_resume(struct msdc_host *host)
+{
+ u32 base = host->base;
+
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_RESUME, 1);
+
+ N_MSG(DMA, "DMA resume");
+}
+#endif /* end of --- */
+
+static void msdc_dma_start(struct msdc_host *host)
+{
+ u32 base = host->base;
+ u32 wints = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR ;
+
+ sdr_set_bits(MSDC_INTEN, wints);
+ //dsb(); /* --- by chhung */
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1);
+
+ N_MSG(DMA, "DMA start");
+}
+
+static void msdc_dma_stop(struct msdc_host *host)
+{
+ u32 base = host->base;
+ //u32 retries=500;
+ u32 wints = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR ;
+
+ N_MSG(DMA, "DMA status: 0x%.8x",sdr_read32(MSDC_DMA_CFG));
+ //while (sdr_read32(MSDC_DMA_CFG) & MSDC_DMA_CFG_STS);
+
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_STOP, 1);
+ while (sdr_read32(MSDC_DMA_CFG) & MSDC_DMA_CFG_STS);
+
+ //dsb(); /* --- by chhung */
+ sdr_clr_bits(MSDC_INTEN, wints); /* Not just xfer_comp */
+
+ N_MSG(DMA, "DMA stop");
+}
+
+#if 0 /* --- by chhung */
+/* dump a gpd list */
+static void msdc_dma_dump(struct msdc_host *host, struct msdc_dma *dma)
+{
+ gpd_t *gpd = dma->gpd;
+ bd_t *bd = dma->bd;
+ bd_t *ptr;
+ int i = 0;
+ int p_to_v;
+
+ if (dma->mode != MSDC_MODE_DMA_DESC) {
+ return;
+ }
+
+ ERR_MSG("try to dump gpd and bd");
+
+ /* dump gpd */
+ ERR_MSG(".gpd<0x%.8x> gpd_phy<0x%.8x>", (int)gpd, (int)dma->gpd_addr);
+ ERR_MSG("...hwo <%d>", gpd->hwo );
+ ERR_MSG("...bdp <%d>", gpd->bdp );
+ ERR_MSG("...chksum<0x%.8x>", gpd->chksum );
+ //ERR_MSG("...intr <0x%.8x>", gpd->intr );
+ ERR_MSG("...next <0x%.8x>", (int)gpd->next );
+ ERR_MSG("...ptr <0x%.8x>", (int)gpd->ptr );
+ ERR_MSG("...buflen<0x%.8x>", gpd->buflen );
+ //ERR_MSG("...extlen<0x%.8x>", gpd->extlen );
+ //ERR_MSG("...arg <0x%.8x>", gpd->arg );
+ //ERR_MSG("...blknum<0x%.8x>", gpd->blknum );
+ //ERR_MSG("...cmd <0x%.8x>", gpd->cmd );
+
+ /* dump bd */
+ ERR_MSG(".bd<0x%.8x> bd_phy<0x%.8x> gpd_ptr<0x%.8x>", (int)bd, (int)dma->bd_addr, (int)gpd->ptr);
+ ptr = bd;
+ p_to_v = ((u32)bd - (u32)dma->bd_addr);
+ while (1) {
+ ERR_MSG(".bd[%d]", i); i++;
+ ERR_MSG("...eol <%d>", ptr->eol );
+ ERR_MSG("...chksum<0x%.8x>", ptr->chksum );
+ //ERR_MSG("...blkpad<0x%.8x>", ptr->blkpad );
+ //ERR_MSG("...dwpad <0x%.8x>", ptr->dwpad );
+ ERR_MSG("...next <0x%.8x>", (int)ptr->next );
+ ERR_MSG("...ptr <0x%.8x>", (int)ptr->ptr );
+ ERR_MSG("...buflen<0x%.8x>", (int)ptr->buflen );
+
+ if (ptr->eol == 1) {
+ break;
+ }
+
+ /* find the next bd, virtual address of ptr->next */
+ /* don't need to enable when use malloc */
+ //BUG_ON( (ptr->next + p_to_v)!=(ptr+1) );
+ //ERR_MSG(".next bd<0x%.8x><0x%.8x>", (ptr->next + p_to_v), (ptr+1));
+ ptr++;
+ }
+
+ ERR_MSG("dump gpd and bd finished");
+}
+#endif /* end of --- */
+
+/* calc checksum */
+static u8 msdc_dma_calcs(u8 *buf, u32 len)
+{
+ u32 i, sum = 0;
+ for (i = 0; i < len; i++) {
+ sum += buf[i];
+ }
+ return 0xFF - (u8)sum;
+}
+
+/* gpd bd setup + dma registers */
+static int msdc_dma_config(struct msdc_host *host, struct msdc_dma *dma)
+{
+ u32 base = host->base;
+ u32 sglen = dma->sglen;
+ //u32 i, j, num, bdlen, arg, xfersz;
+ u32 j, num, bdlen;
+ u8 blkpad, dwpad, chksum;
+ struct scatterlist *sg = dma->sg;
+ gpd_t *gpd;
+ bd_t *bd;
+
+ switch (dma->mode) {
+ case MSDC_MODE_DMA_BASIC:
+ BUG_ON(dma->xfersz > 65535);
+ BUG_ON(dma->sglen != 1);
+ sdr_write32(MSDC_DMA_SA, PHYSADDR(sg_dma_address(sg)));
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_LASTBUF, 1);
+//#if defined (CONFIG_RALINK_MT7620)
+ if (ralink_soc == MT762X_SOC_MT7620A)
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_XFERSZ, sg_dma_len(sg));
+//#elif defined (CONFIG_RALINK_MT7621) || defined (CONFIG_RALINK_MT7628)
+ else
+ sdr_write32((volatile u32*)(RALINK_MSDC_BASE+0xa8), sg_dma_len(sg));
+//#endif
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_BRUSTSZ, dma->burstsz);
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_MODE, 0);
+ break;
+ case MSDC_MODE_DMA_DESC:
+ blkpad = (dma->flags & DMA_FLAG_PAD_BLOCK) ? 1 : 0;
+ dwpad = (dma->flags & DMA_FLAG_PAD_DWORD) ? 1 : 0;
+ chksum = (dma->flags & DMA_FLAG_EN_CHKSUM) ? 1 : 0;
+
+ /* calculate the required number of gpd */
+ num = (sglen + MAX_BD_PER_GPD - 1) / MAX_BD_PER_GPD;
+ BUG_ON(num !=1 );
+
+ gpd = dma->gpd;
+ bd = dma->bd;
+ bdlen = sglen;
+
+ /* modify gpd*/
+ //gpd->intr = 0;
+ gpd->hwo = 1; /* hw will clear it */
+ gpd->bdp = 1;
+ gpd->chksum = 0; /* need to clear first. */
+ gpd->chksum = (chksum ? msdc_dma_calcs((u8 *)gpd, 16) : 0);
+
+ /* modify bd*/
+ for (j = 0; j < bdlen; j++) {
+ msdc_init_bd(&bd[j], blkpad, dwpad, sg_dma_address(sg), sg_dma_len(sg));
+ if(j == bdlen - 1) {
+ bd[j].eol = 1; /* the last bd */
+ } else {
+ bd[j].eol = 0;
+ }
+ bd[j].chksum = 0; /* checksume need to clear first */
+ bd[j].chksum = (chksum ? msdc_dma_calcs((u8 *)(&bd[j]), 16) : 0);
+ sg++;
+ }
+
+ dma->used_gpd += 2;
+ dma->used_bd += bdlen;
+
+ sdr_set_field(MSDC_DMA_CFG, MSDC_DMA_CFG_DECSEN, chksum);
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_BRUSTSZ, dma->burstsz);
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_MODE, 1);
+
+ sdr_write32(MSDC_DMA_SA, PHYSADDR((u32)dma->gpd_addr));
+ break;
+
+ default:
+ break;
+ }
+
+ N_MSG(DMA, "DMA_CTRL = 0x%x", sdr_read32(MSDC_DMA_CTRL));
+ N_MSG(DMA, "DMA_CFG = 0x%x", sdr_read32(MSDC_DMA_CFG));
+ N_MSG(DMA, "DMA_SA = 0x%x", sdr_read32(MSDC_DMA_SA));
+
+ return 0;
+}
+
+static void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma,
+ struct scatterlist *sg, unsigned int sglen)
+{
+ BUG_ON(sglen > MAX_BD_NUM); /* not support currently */
+
+ dma->sg = sg;
+ dma->flags = DMA_FLAG_EN_CHKSUM;
+ //dma->flags = DMA_FLAG_NONE; /* CHECKME */
+ dma->sglen = sglen;
+ dma->xfersz = host->xfer_size;
+ dma->burstsz = MSDC_BRUST_64B;
+
+ if (sglen == 1 && sg_dma_len(sg) <= MAX_DMA_CNT)
+ dma->mode = MSDC_MODE_DMA_BASIC;
+ else
+ dma->mode = MSDC_MODE_DMA_DESC;
+
+ N_MSG(DMA, "DMA mode<%d> sglen<%d> xfersz<%d>", dma->mode, dma->sglen, dma->xfersz);
+
+ msdc_dma_config(host, dma);
+
+ /*if (dma->mode == MSDC_MODE_DMA_DESC) {
+ //msdc_dma_dump(host, dma);
+ } */
+}
+
+/* set block number before send command */
+static void msdc_set_blknum(struct msdc_host *host, u32 blknum)
+{
+ u32 base = host->base;
+
+ sdr_write32(SDC_BLK_NUM, blknum);
+}
+
+static int msdc_do_request(struct mmc_host*mmc, struct mmc_request*mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ u32 base = host->base;
+ //u32 intsts = 0;
+ unsigned int left=0;
+ int dma = 0, read = 1, dir = DMA_FROM_DEVICE, send_type=0;
+
+ #define SND_DAT 0
+ #define SND_CMD 1
+
+ BUG_ON(mmc == NULL);
+ BUG_ON(mrq == NULL);
+
+ host->error = 0;
+ atomic_set(&host->abort, 0);
+
+ cmd = mrq->cmd;
+ data = mrq->cmd->data;
+
+#if 0 /* --- by chhung */
+ //if(host->id ==1){
+ N_MSG(OPS, "enable clock!");
+ msdc_ungate_clock(host->id);
+ //}
+#endif /* end of --- */
+
+ if (!data) {
+ send_type=SND_CMD;
+ if (msdc_do_command(host, cmd, 1, CMD_TIMEOUT) != 0) {
+ goto done;
+ }
+ } else {
+ BUG_ON(data->blksz > HOST_MAX_BLKSZ);
+ send_type=SND_DAT;
+
+ data->error = 0;
+ read = data->flags & MMC_DATA_READ ? 1 : 0;
+ host->data = data;
+ host->xfer_size = data->blocks * data->blksz;
+ host->blksz = data->blksz;
+
+ /* deside the transfer mode */
+ if (drv_mode[host->id] == MODE_PIO) {
+ host->dma_xfer = dma = 0;
+ } else if (drv_mode[host->id] == MODE_DMA) {
+ host->dma_xfer = dma = 1;
+ } else if (drv_mode[host->id] == MODE_SIZE_DEP) {
+ host->dma_xfer = dma = ((host->xfer_size >= dma_size[host->id]) ? 1 : 0);
+ }
+
+ if (read) {
+ if ((host->timeout_ns != data->timeout_ns) ||
+ (host->timeout_clks != data->timeout_clks)) {
+ msdc_set_timeout(host, data->timeout_ns, data->timeout_clks);
+ }
+ }
+
+ msdc_set_blknum(host, data->blocks);
+ //msdc_clr_fifo(); /* no need */
+
+ if (dma) {
+ msdc_dma_on(); /* enable DMA mode first!! */
+ init_completion(&host->xfer_done);
+
+ /* start the command first*/
+ if (msdc_command_start(host, cmd, 1, CMD_TIMEOUT) != 0)
+ goto done;
+
+ dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ (void)dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len, dir);
+ msdc_dma_setup(host, &host->dma, data->sg, data->sg_len);
+
+ /* then wait command done */
+ if (msdc_command_resp(host, cmd, 1, CMD_TIMEOUT) != 0)
+ goto done;
+
+ /* for read, the data coming too fast, then CRC error
+ start DMA no business with CRC. */
+ //init_completion(&host->xfer_done);
+ msdc_dma_start(host);
+
+ spin_unlock(&host->lock);
+ if(!wait_for_completion_timeout(&host->xfer_done, DAT_TIMEOUT)){
+ ERR_MSG("XXX CMD<%d> wait xfer_done<%d> timeout!!", cmd->opcode, data->blocks * data->blksz);
+ ERR_MSG(" DMA_SA = 0x%x", sdr_read32(MSDC_DMA_SA));
+ ERR_MSG(" DMA_CA = 0x%x", sdr_read32(MSDC_DMA_CA));
+ ERR_MSG(" DMA_CTRL = 0x%x", sdr_read32(MSDC_DMA_CTRL));
+ ERR_MSG(" DMA_CFG = 0x%x", sdr_read32(MSDC_DMA_CFG));
+ data->error = (unsigned int)-ETIMEDOUT;
+
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ }
+ spin_lock(&host->lock);
+ msdc_dma_stop(host);
+ } else {
+ /* Firstly: send command */
+ if (msdc_do_command(host, cmd, 1, CMD_TIMEOUT) != 0) {
+ goto done;
+ }
+
+ /* Secondly: pio data phase */
+ if (read) {
+ if (msdc_pio_read(host, data)){
+ goto done;
+ }
+ } else {
+ if (msdc_pio_write(host, data)) {
+ goto done;
+ }
+ }
+
+ /* For write case: make sure contents in fifo flushed to device */
+ if (!read) {
+ while (1) {
+ left=msdc_txfifocnt();
+ if (left == 0) {
+ break;
+ }
+ if (msdc_pio_abort(host, data, jiffies + DAT_TIMEOUT)) {
+ break;
+ /* Fix me: what about if data error, when stop ? how to? */
+ }
+ }
+ } else {
+ /* Fix me: read case: need to check CRC error */
+ }
+
+ /* For write case: SDCBUSY and Xfer_Comp will assert when DAT0 not busy.
+ For read case : SDCBUSY and Xfer_Comp will assert when last byte read out from FIFO.
+ */
+
+ /* try not to wait xfer_comp interrupt.
+ the next command will check SDC_BUSY.
+ SDC_BUSY means xfer_comp assert
+ */
+
+ } // PIO mode
+
+ /* Last: stop transfer */
+ if (data->stop){
+ if (msdc_do_command(host, data->stop, 0, CMD_TIMEOUT) != 0) {
+ goto done;
+ }
+ }
+ }
+
+done:
+ if (data != NULL) {
+ host->data = NULL;
+ host->dma_xfer = 0;
+ if (dma != 0) {
+ msdc_dma_off();
+ host->dma.used_bd = 0;
+ host->dma.used_gpd = 0;
+ dma_unmap_sg(mmc_dev(mmc), data->sg, data->sg_len, dir);
+ }
+ host->blksz = 0;
+
+#if 0 // don't stop twice!
+ if(host->hw->flags & MSDC_REMOVABLE && data->error) {
+ msdc_abort_data(host);
+ /* reset in IRQ, stop command has issued. -> No need */
+ }
+#endif
+
+ N_MSG(OPS, "CMD<%d> data<%s %s> blksz<%d> block<%d> error<%d>",cmd->opcode, (dma? "dma":"pio"),
+ (read ? "read ":"write") ,data->blksz, data->blocks, data->error);
+ }
+
+#if 0 /* --- by chhung */
+#if 1
+ //if(host->id==1) {
+ if(send_type==SND_CMD) {
+ if(cmd->opcode == MMC_SEND_STATUS) {
+ if((cmd->resp[0] & CARD_READY_FOR_DATA) ||(CARD_CURRENT_STATE(cmd->resp[0]) != 7)){
+ N_MSG(OPS,"disable clock, CMD13 IDLE");
+ msdc_gate_clock(host->id);
+ }
+ } else {
+ N_MSG(OPS,"disable clock, CMD<%d>", cmd->opcode);
+ msdc_gate_clock(host->id);
+ }
+ } else {
+ if(read) {
+ N_MSG(OPS,"disable clock!!! Read CMD<%d>",cmd->opcode);
+ msdc_gate_clock(host->id);
+ }
+ }
+ //}
+#else
+ msdc_gate_clock(host->id);
+#endif
+#endif /* end of --- */
+
+ if (mrq->cmd->error) host->error = 0x001;
+ if (mrq->data && mrq->data->error) host->error |= 0x010;
+ if (mrq->stop && mrq->stop->error) host->error |= 0x100;
+
+ //if (host->error) ERR_MSG("host->error<%d>", host->error);
+
+ return host->error;
+}
+
+static int msdc_app_cmd(struct mmc_host *mmc, struct msdc_host *host)
+{
+ struct mmc_command cmd;
+ struct mmc_request mrq;
+ u32 err;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_APP_CMD;
+#if 0 /* bug: we meet mmc->card is null when ACMD6 */
+ cmd.arg = mmc->card->rca << 16;
+#else
+ cmd.arg = host->app_cmd_arg;
+#endif
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ mrq.cmd = &cmd; cmd.mrq = &mrq;
+ cmd.data = NULL;
+
+ err = msdc_do_command(host, &cmd, 0, CMD_TIMEOUT);
+ return err;
+}
+
+static int msdc_tune_cmdrsp(struct msdc_host*host, struct mmc_command *cmd)
+{
+ int result = -1;
+ u32 base = host->base;
+ u32 rsmpl, cur_rsmpl, orig_rsmpl;
+ u32 rrdly, cur_rrdly = 0xffffffff, orig_rrdly;
+ u32 skip = 1;
+
+ /* ==== don't support 3.0 now ====
+ 1: R_SMPL[1]
+ 2: PAD_CMD_RESP_RXDLY[26:22]
+ ==========================*/
+
+ // save the previous tune result
+ sdr_get_field(MSDC_IOCON, MSDC_IOCON_RSPL, orig_rsmpl);
+ sdr_get_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY, orig_rrdly);
+
+ rrdly = 0;
+ do {
+ for (rsmpl = 0; rsmpl < 2; rsmpl++) {
+ /* Lv1: R_SMPL[1] */
+ cur_rsmpl = (orig_rsmpl + rsmpl) % 2;
+ if (skip == 1) {
+ skip = 0;
+ continue;
+ }
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_RSPL, cur_rsmpl);
+
+ if (host->app_cmd) {
+ result = msdc_app_cmd(host->mmc, host);
+ if (result) {
+ ERR_MSG("TUNE_CMD app_cmd<%d> failed: RESP_RXDLY<%d>,R_SMPL<%d>",
+ host->mrq->cmd->opcode, cur_rrdly, cur_rsmpl);
+ continue;
+ }
+ }
+ result = msdc_do_command(host, cmd, 0, CMD_TIMEOUT); // not tune.
+ ERR_MSG("TUNE_CMD<%d> %s PAD_CMD_RESP_RXDLY[26:22]<%d> R_SMPL[1]<%d>", cmd->opcode,
+ (result == 0) ? "PASS" : "FAIL", cur_rrdly, cur_rsmpl);
+
+ if (result == 0) {
+ return 0;
+ }
+ if (result != (unsigned int)(-EIO)) {
+ ERR_MSG("TUNE_CMD<%d> Error<%d> not -EIO", cmd->opcode, result);
+ return result;
+ }
+
+ /* should be EIO */
+ if (sdr_read32(SDC_CMD) & 0x1800) { /* check if has data phase */
+ msdc_abort_data(host);
+ }
+ }
+
+ /* Lv2: PAD_CMD_RESP_RXDLY[26:22] */
+ cur_rrdly = (orig_rrdly + rrdly + 1) % 32;
+ sdr_set_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY, cur_rrdly);
+ }while (++rrdly < 32);
+
+ return result;
+}
+
+/* Support SD2.0 Only */
+static int msdc_tune_bread(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 base = host->base;
+ u32 ddr=0;
+ u32 dcrc=0;
+ u32 rxdly, cur_rxdly0, cur_rxdly1;
+ u32 dsmpl, cur_dsmpl, orig_dsmpl;
+ u32 cur_dat0, cur_dat1, cur_dat2, cur_dat3;
+ u32 cur_dat4, cur_dat5, cur_dat6, cur_dat7;
+ u32 orig_dat0, orig_dat1, orig_dat2, orig_dat3;
+ u32 orig_dat4, orig_dat5, orig_dat6, orig_dat7;
+ int result = -1;
+ u32 skip = 1;
+
+ sdr_get_field(MSDC_IOCON, MSDC_IOCON_DSPL, orig_dsmpl);
+
+ /* Tune Method 2. */
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
+
+ rxdly = 0;
+ do {
+ for (dsmpl = 0; dsmpl < 2; dsmpl++) {
+ cur_dsmpl = (orig_dsmpl + dsmpl) % 2;
+ if (skip == 1) {
+ skip = 0;
+ continue;
+ }
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, cur_dsmpl);
+
+ if (host->app_cmd) {
+ result = msdc_app_cmd(host->mmc, host);
+ if (result) {
+ ERR_MSG("TUNE_BREAD app_cmd<%d> failed", host->mrq->cmd->opcode);
+ continue;
+ }
+ }
+ result = msdc_do_request(mmc,mrq);
+
+ sdr_get_field(SDC_DCRC_STS, SDC_DCRC_STS_POS|SDC_DCRC_STS_NEG, dcrc); /* RO */
+ if (!ddr) dcrc &= ~SDC_DCRC_STS_NEG;
+ ERR_MSG("TUNE_BREAD<%s> dcrc<0x%x> DATRDDLY0/1<0x%x><0x%x> dsmpl<0x%x>",
+ (result == 0 && dcrc == 0) ? "PASS" : "FAIL", dcrc,
+ sdr_read32(MSDC_DAT_RDDLY0), sdr_read32(MSDC_DAT_RDDLY1), cur_dsmpl);
+
+ /* Fix me: result is 0, but dcrc is still exist */
+ if (result == 0 && dcrc == 0) {
+ goto done;
+ } else {
+ /* there is a case: command timeout, and data phase not processed */
+ if (mrq->data->error != 0 && mrq->data->error != (unsigned int)(-EIO)) {
+ ERR_MSG("TUNE_READ: result<0x%x> cmd_error<%d> data_error<%d>",
+ result, mrq->cmd->error, mrq->data->error);
+ goto done;
+ }
+ }
+ }
+
+ cur_rxdly0 = sdr_read32(MSDC_DAT_RDDLY0);
+ cur_rxdly1 = sdr_read32(MSDC_DAT_RDDLY1);
+
+ /* E1 ECO. YD: Reverse */
+ if (sdr_read32(MSDC_ECO_VER) >= 4) {
+ orig_dat0 = (cur_rxdly0 >> 24) & 0x1F;
+ orig_dat1 = (cur_rxdly0 >> 16) & 0x1F;
+ orig_dat2 = (cur_rxdly0 >> 8) & 0x1F;
+ orig_dat3 = (cur_rxdly0 >> 0) & 0x1F;
+ orig_dat4 = (cur_rxdly1 >> 24) & 0x1F;
+ orig_dat5 = (cur_rxdly1 >> 16) & 0x1F;
+ orig_dat6 = (cur_rxdly1 >> 8) & 0x1F;
+ orig_dat7 = (cur_rxdly1 >> 0) & 0x1F;
+ } else {
+ orig_dat0 = (cur_rxdly0 >> 0) & 0x1F;
+ orig_dat1 = (cur_rxdly0 >> 8) & 0x1F;
+ orig_dat2 = (cur_rxdly0 >> 16) & 0x1F;
+ orig_dat3 = (cur_rxdly0 >> 24) & 0x1F;
+ orig_dat4 = (cur_rxdly1 >> 0) & 0x1F;
+ orig_dat5 = (cur_rxdly1 >> 8) & 0x1F;
+ orig_dat6 = (cur_rxdly1 >> 16) & 0x1F;
+ orig_dat7 = (cur_rxdly1 >> 24) & 0x1F;
+ }
+
+ if (ddr) {
+ cur_dat0 = (dcrc & (1 << 0) || dcrc & (1 << 8)) ? ((orig_dat0 + 1) % 32) : orig_dat0;
+ cur_dat1 = (dcrc & (1 << 1) || dcrc & (1 << 9)) ? ((orig_dat1 + 1) % 32) : orig_dat1;
+ cur_dat2 = (dcrc & (1 << 2) || dcrc & (1 << 10)) ? ((orig_dat2 + 1) % 32) : orig_dat2;
+ cur_dat3 = (dcrc & (1 << 3) || dcrc & (1 << 11)) ? ((orig_dat3 + 1) % 32) : orig_dat3;
+ } else {
+ cur_dat0 = (dcrc & (1 << 0)) ? ((orig_dat0 + 1) % 32) : orig_dat0;
+ cur_dat1 = (dcrc & (1 << 1)) ? ((orig_dat1 + 1) % 32) : orig_dat1;
+ cur_dat2 = (dcrc & (1 << 2)) ? ((orig_dat2 + 1) % 32) : orig_dat2;
+ cur_dat3 = (dcrc & (1 << 3)) ? ((orig_dat3 + 1) % 32) : orig_dat3;
+ }
+ cur_dat4 = (dcrc & (1 << 4)) ? ((orig_dat4 + 1) % 32) : orig_dat4;
+ cur_dat5 = (dcrc & (1 << 5)) ? ((orig_dat5 + 1) % 32) : orig_dat5;
+ cur_dat6 = (dcrc & (1 << 6)) ? ((orig_dat6 + 1) % 32) : orig_dat6;
+ cur_dat7 = (dcrc & (1 << 7)) ? ((orig_dat7 + 1) % 32) : orig_dat7;
+
+ cur_rxdly0 = (cur_dat0 << 24) | (cur_dat1 << 16) | (cur_dat2 << 8) | (cur_dat3 << 0);
+ cur_rxdly1 = (cur_dat4 << 24) | (cur_dat5 << 16) | (cur_dat6 << 8) | (cur_dat7 << 0);
+
+ sdr_write32(MSDC_DAT_RDDLY0, cur_rxdly0);
+ sdr_write32(MSDC_DAT_RDDLY1, cur_rxdly1);
+
+ } while (++rxdly < 32);
+
+done:
+ return result;
+}
+
+static int msdc_tune_bwrite(struct mmc_host *mmc,struct mmc_request *mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 base = host->base;
+
+ u32 wrrdly, cur_wrrdly = 0xffffffff, orig_wrrdly;
+ u32 dsmpl, cur_dsmpl, orig_dsmpl;
+ u32 rxdly, cur_rxdly0;
+ u32 orig_dat0, orig_dat1, orig_dat2, orig_dat3;
+ u32 cur_dat0, cur_dat1, cur_dat2, cur_dat3;
+ int result = -1;
+ u32 skip = 1;
+
+ // MSDC_IOCON_DDR50CKD need to check. [Fix me]
+
+ sdr_get_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATWRDLY, orig_wrrdly);
+ sdr_get_field(MSDC_IOCON, MSDC_IOCON_DSPL, orig_dsmpl );
+
+ /* Tune Method 2. just DAT0 */
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
+ cur_rxdly0 = sdr_read32(MSDC_DAT_RDDLY0);
+
+ /* E1 ECO. YD: Reverse */
+ if (sdr_read32(MSDC_ECO_VER) >= 4) {
+ orig_dat0 = (cur_rxdly0 >> 24) & 0x1F;
+ orig_dat1 = (cur_rxdly0 >> 16) & 0x1F;
+ orig_dat2 = (cur_rxdly0 >> 8) & 0x1F;
+ orig_dat3 = (cur_rxdly0 >> 0) & 0x1F;
+ } else {
+ orig_dat0 = (cur_rxdly0 >> 0) & 0x1F;
+ orig_dat1 = (cur_rxdly0 >> 8) & 0x1F;
+ orig_dat2 = (cur_rxdly0 >> 16) & 0x1F;
+ orig_dat3 = (cur_rxdly0 >> 24) & 0x1F;
+ }
+
+ rxdly = 0;
+ do {
+ wrrdly = 0;
+ do {
+ for (dsmpl = 0; dsmpl < 2; dsmpl++) {
+ cur_dsmpl = (orig_dsmpl + dsmpl) % 2;
+ if (skip == 1) {
+ skip = 0;
+ continue;
+ }
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, cur_dsmpl);
+
+ if (host->app_cmd) {
+ result = msdc_app_cmd(host->mmc, host);
+ if (result) {
+ ERR_MSG("TUNE_BWRITE app_cmd<%d> failed", host->mrq->cmd->opcode);
+ continue;
+ }
+ }
+ result = msdc_do_request(mmc,mrq);
+
+ ERR_MSG("TUNE_BWRITE<%s> DSPL<%d> DATWRDLY<%d> MSDC_DAT_RDDLY0<0x%x>",
+ result == 0 ? "PASS" : "FAIL",
+ cur_dsmpl, cur_wrrdly, cur_rxdly0);
+
+ if (result == 0) {
+ goto done;
+ }
+ else {
+ /* there is a case: command timeout, and data phase not processed */
+ if (mrq->data->error != (unsigned int)(-EIO)) {
+ ERR_MSG("TUNE_READ: result<0x%x> cmd_error<%d> data_error<%d>",
+ result, mrq->cmd->error, mrq->data->error);
+ goto done;
+ }
+ }
+ }
+ cur_wrrdly = (orig_wrrdly + wrrdly + 1) % 32;
+ sdr_set_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATWRDLY, cur_wrrdly);
+ } while (++wrrdly < 32);
+
+ cur_dat0 = (orig_dat0 + rxdly) % 32; /* only adjust bit-1 for crc */
+ cur_dat1 = orig_dat1;
+ cur_dat2 = orig_dat2;
+ cur_dat3 = orig_dat3;
+
+ cur_rxdly0 = (cur_dat0 << 24) | (cur_dat1 << 16) | (cur_dat2 << 8) | (cur_dat3 << 0);
+ sdr_write32(MSDC_DAT_RDDLY0, cur_rxdly0);
+ } while (++rxdly < 32);
+
+done:
+ return result;
+}
+
+static int msdc_get_card_status(struct mmc_host *mmc, struct msdc_host *host, u32 *status)
+{
+ struct mmc_command cmd;
+ struct mmc_request mrq;
+ u32 err;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_SEND_STATUS;
+ if (mmc->card) {
+ cmd.arg = mmc->card->rca << 16;
+ } else {
+ ERR_MSG("cmd13 mmc card is null");
+ cmd.arg = host->app_cmd_arg;
+ }
+ cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ mrq.cmd = &cmd; cmd.mrq = &mrq;
+ cmd.data = NULL;
+
+ err = msdc_do_command(host, &cmd, 1, CMD_TIMEOUT);
+
+ if (status) {
+ *status = cmd.resp[0];
+ }
+
+ return err;
+}
+
+static int msdc_check_busy(struct mmc_host *mmc, struct msdc_host *host)
+{
+ u32 err = 0;
+ u32 status = 0;
+
+ do {
+ err = msdc_get_card_status(mmc, host, &status);
+ if (err) return err;
+ /* need cmd12? */
+ ERR_MSG("cmd<13> resp<0x%x>", status);
+ } while (R1_CURRENT_STATE(status) == 7);
+
+ return err;
+}
+
+/* failed when msdc_do_request */
+static int msdc_tune_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ //u32 base = host->base;
+ int ret=0, read;
+
+ cmd = mrq->cmd;
+ data = mrq->cmd->data;
+
+ read = data->flags & MMC_DATA_READ ? 1 : 0;
+
+ if (read) {
+ if (data->error == (unsigned int)(-EIO)) {
+ ret = msdc_tune_bread(mmc,mrq);
+ }
+ } else {
+ ret = msdc_check_busy(mmc, host);
+ if (ret){
+ ERR_MSG("XXX cmd13 wait program done failed");
+ return ret;
+ }
+ /* CRC and TO */
+ /* Fix me: don't care card status? */
+ ret = msdc_tune_bwrite(mmc,mrq);
+ }
+
+ return ret;
+}
+
+/* ops.request */
+static void msdc_ops_request(struct mmc_host *mmc,struct mmc_request *mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+
+ //=== for sdio profile ===
+#if 0 /* --- by chhung */
+ u32 old_H32, old_L32, new_H32, new_L32;
+ u32 ticks = 0, opcode = 0, sizes = 0, bRx = 0;
+#endif /* end of --- */
+
+ if(host->mrq){
+ ERR_MSG("XXX host->mrq<0x%.8x>", (int)host->mrq);
+ BUG();
+ }
+
+ if (!is_card_present(host) || host->power_mode == MMC_POWER_OFF) {
+ ERR_MSG("cmd<%d> card<%d> power<%d>", mrq->cmd->opcode, is_card_present(host), host->power_mode);
+ mrq->cmd->error = (unsigned int)-ENOMEDIUM;
+
+#if 1
+ mrq->done(mrq); // call done directly.
+#else
+ mrq->cmd->retries = 0; // please don't retry.
+ mmc_request_done(mmc, mrq);
+#endif
+
+ return;
+ }
+
+ /* start to process */
+ spin_lock(&host->lock);
+#if 0 /* --- by chhung */
+ if (sdio_pro_enable) { //=== for sdio profile ===
+ if (mrq->cmd->opcode == 52 || mrq->cmd->opcode == 53) {
+ GPT_GetCounter64(&old_L32, &old_H32);
+ }
+ }
+#endif /* end of --- */
+
+ host->mrq = mrq;
+
+ if (msdc_do_request(mmc,mrq)) {
+ if(host->hw->flags & MSDC_REMOVABLE && ralink_soc == MT762X_SOC_MT7621AT && mrq->data && mrq->data->error) {
+ msdc_tune_request(mmc,mrq);
+ }
+ }
+
+ /* ==== when request done, check if app_cmd ==== */
+ if (mrq->cmd->opcode == MMC_APP_CMD) {
+ host->app_cmd = 1;
+ host->app_cmd_arg = mrq->cmd->arg; /* save the RCA */
+ } else {
+ host->app_cmd = 0;
+ //host->app_cmd_arg = 0;
+ }
+
+ host->mrq = NULL;
+
+#if 0 /* --- by chhung */
+ //=== for sdio profile ===
+ if (sdio_pro_enable) {
+ if (mrq->cmd->opcode == 52 || mrq->cmd->opcode == 53) {
+ GPT_GetCounter64(&new_L32, &new_H32);
+ ticks = msdc_time_calc(old_L32, old_H32, new_L32, new_H32);
+
+ opcode = mrq->cmd->opcode;
+ if (mrq->cmd->data) {
+ sizes = mrq->cmd->data->blocks * mrq->cmd->data->blksz;
+ bRx = mrq->cmd->data->flags & MMC_DATA_READ ? 1 : 0 ;
+ } else {
+ bRx = mrq->cmd->arg & 0x80000000 ? 1 : 0;
+ }
+
+ if (!mrq->cmd->error) {
+ msdc_performance(opcode, sizes, bRx, ticks);
+ }
+ }
+ }
+#endif /* end of --- */
+ spin_unlock(&host->lock);
+
+ mmc_request_done(mmc, mrq);
+
+ return;
+}
+
+/* called by ops.set_ios */
+static void msdc_set_buswidth(struct msdc_host *host, u32 width)
+{
+ u32 base = host->base;
+ u32 val = sdr_read32(SDC_CFG);
+
+ val &= ~SDC_CFG_BUSWIDTH;
+
+ switch (width) {
+ default:
+ case MMC_BUS_WIDTH_1:
+ width = 1;
+ val |= (MSDC_BUS_1BITS << 16);
+ break;
+ case MMC_BUS_WIDTH_4:
+ val |= (MSDC_BUS_4BITS << 16);
+ break;
+ case MMC_BUS_WIDTH_8:
+ val |= (MSDC_BUS_8BITS << 16);
+ break;
+ }
+
+ sdr_write32(SDC_CFG, val);
+
+ N_MSG(CFG, "Bus Width = %d", width);
+}
+
+/* ops.set_ios */
+static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct msdc_hw *hw=host->hw;
+ u32 base = host->base;
+ u32 ddr = 0;
+
+#ifdef MT6575_SD_DEBUG
+ static char *vdd[] = {
+ "1.50v", "1.55v", "1.60v", "1.65v", "1.70v", "1.80v", "1.90v",
+ "2.00v", "2.10v", "2.20v", "2.30v", "2.40v", "2.50v", "2.60v",
+ "2.70v", "2.80v", "2.90v", "3.00v", "3.10v", "3.20v", "3.30v",
+ "3.40v", "3.50v", "3.60v"
+ };
+ static char *power_mode[] = {
+ "OFF", "UP", "ON"
+ };
+ static char *bus_mode[] = {
+ "UNKNOWN", "OPENDRAIN", "PUSHPULL"
+ };
+ static char *timing[] = {
+ "LEGACY", "MMC_HS", "SD_HS"
+ };
+
+ printk("SET_IOS: CLK(%dkHz), BUS(%s), BW(%u), PWR(%s), VDD(%s), TIMING(%s)",
+ ios->clock / 1000, bus_mode[ios->bus_mode],
+ (ios->bus_width == MMC_BUS_WIDTH_4) ? 4 : 1,
+ power_mode[ios->power_mode], vdd[ios->vdd], timing[ios->timing]);
+#endif
+
+ msdc_set_buswidth(host, ios->bus_width);
+
+ /* Power control ??? */
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ case MMC_POWER_UP:
+ // msdc_set_power_mode(host, ios->power_mode); /* --- by chhung */
+ break;
+ case MMC_POWER_ON:
+ host->power_mode = MMC_POWER_ON;
+ break;
+ default:
+ break;
+ }
+
+ /* Clock control */
+ if (host->mclk != ios->clock) {
+ if(ios->clock > 25000000) {
+ //if (!(host->hw->flags & MSDC_REMOVABLE)) {
+ INIT_MSG("SD data latch edge<%d>", hw->data_edge);
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_RSPL, hw->cmd_edge);
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, hw->data_edge);
+ //} /* for tuning debug */
+ } else { /* default value */
+ sdr_write32(MSDC_IOCON, 0x00000000);
+ // sdr_write32(MSDC_DAT_RDDLY0, 0x00000000);
+ sdr_write32(MSDC_DAT_RDDLY0, 0x10101010); // for MT7620 E2 and afterward
+ sdr_write32(MSDC_DAT_RDDLY1, 0x00000000);
+ // sdr_write32(MSDC_PAD_TUNE, 0x00000000);
+ sdr_write32(MSDC_PAD_TUNE, 0x84101010); // for MT7620 E2 and afterward
+ }
+ msdc_set_mclk(host, ddr, ios->clock);
+ }
+}
+
+/* ops.get_ro */
+static int msdc_ops_get_ro(struct mmc_host *mmc)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 base = host->base;
+ unsigned long flags;
+ int ro = 0;
+
+ if (host->hw->flags & MSDC_WP_PIN_EN) { /* set for card */
+ spin_lock_irqsave(&host->lock, flags);
+ ro = (sdr_read32(MSDC_PS) >> 31);
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+ return ro;
+}
+
+/* ops.get_cd */
+static int msdc_ops_get_cd(struct mmc_host *mmc)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 base = host->base;
+ unsigned long flags;
+ int present = 1;
+
+ /* for sdio, MSDC_REMOVABLE not set, always return 1 */
+ if (!(host->hw->flags & MSDC_REMOVABLE)) {
+ /* For sdio, read H/W always get<1>, but may timeout some times */
+#if 1
+ host->card_inserted = 1;
+ return 1;
+#else
+ host->card_inserted = (host->pm_state.event == PM_EVENT_USER_RESUME) ? 1 : 0;
+ INIT_MSG("sdio ops_get_cd<%d>", host->card_inserted);
+ return host->card_inserted;
+#endif
+ }
+
+ /* MSDC_CD_PIN_EN set for card */
+ if (host->hw->flags & MSDC_CD_PIN_EN) {
+ spin_lock_irqsave(&host->lock, flags);
+#if 0
+ present = host->card_inserted; /* why not read from H/W: Fix me*/
+#else
+ // CD
+ if (cd_active_low)
+ present = (sdr_read32(MSDC_PS) & MSDC_PS_CDSTS) ? 0 : 1;
+ else
+ present = (sdr_read32(MSDC_PS) & MSDC_PS_CDSTS) ? 1 : 0;
+ if (host->mmc->caps & MMC_CAP_NEEDS_POLL)
+ present = 1;
+ host->card_inserted = present;
+#endif
+ spin_unlock_irqrestore(&host->lock, flags);
+ } else {
+ present = 0; /* TODO? Check DAT3 pins for card detection */
+ }
+
+ INIT_MSG("ops_get_cd return<%d>", present);
+ return present;
+}
+
+/* ops.enable_sdio_irq */
+static void msdc_ops_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+ u32 tmp;
+
+ if (hw->flags & MSDC_EXT_SDIO_IRQ) { /* yes for sdio */
+ if (enable) {
+ hw->enable_sdio_eirq(); /* combo_sdio_enable_eirq */
+ } else {
+ hw->disable_sdio_eirq(); /* combo_sdio_disable_eirq */
+ }
+ } else {
+ ERR_MSG("XXX "); /* so never enter here */
+ tmp = sdr_read32(SDC_CFG);
+ /* FIXME. Need to interrupt gap detection */
+ if (enable) {
+ tmp |= (SDC_CFG_SDIOIDE | SDC_CFG_SDIOINTWKUP);
+ } else {
+ tmp &= ~(SDC_CFG_SDIOIDE | SDC_CFG_SDIOINTWKUP);
+ }
+ sdr_write32(SDC_CFG, tmp);
+ }
+}
+
+static struct mmc_host_ops mt_msdc_ops = {
+ .request = msdc_ops_request,
+ .set_ios = msdc_ops_set_ios,
+ .get_ro = msdc_ops_get_ro,
+ .get_cd = msdc_ops_get_cd,
+ .enable_sdio_irq = msdc_ops_enable_sdio_irq,
+};
+
+/*--------------------------------------------------------------------------*/
+/* interrupt handler */
+/*--------------------------------------------------------------------------*/
+static irqreturn_t msdc_irq(int irq, void *dev_id)
+{
+ struct msdc_host *host = (struct msdc_host *)dev_id;
+ struct mmc_data *data = host->data;
+ struct mmc_command *cmd = host->cmd;
+ u32 base = host->base;
+
+ u32 cmdsts = MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO | MSDC_INT_CMDRDY |
+ MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO | MSDC_INT_ACMDRDY |
+ MSDC_INT_ACMD19_DONE;
+ u32 datsts = MSDC_INT_DATCRCERR |MSDC_INT_DATTMO;
+
+ u32 intsts = sdr_read32(MSDC_INT);
+ u32 inten = sdr_read32(MSDC_INTEN); inten &= intsts;
+
+ sdr_write32(MSDC_INT, intsts); /* clear interrupts */
+ /* MSG will cause fatal error */
+
+ /* card change interrupt */
+ if (intsts & MSDC_INT_CDSC){
+ if (mtk_sw_poll)
+ return IRQ_HANDLED;
+ IRQ_MSG("MSDC_INT_CDSC irq<0x%.8x>", intsts);
+#if 0 /* ---/+++ by chhung: fix slot mechanical bounce issue */
+ tasklet_hi_schedule(&host->card_tasklet);
+#else
+ schedule_delayed_work(&host->card_delaywork, HZ);
+#endif
+ /* tuning when plug card ? */
+ }
+
+ /* sdio interrupt */
+ if (intsts & MSDC_INT_SDIOIRQ){
+ IRQ_MSG("XXX MSDC_INT_SDIOIRQ"); /* seems not sdio irq */
+ //mmc_signal_sdio_irq(host->mmc);
+ }
+
+ /* transfer complete interrupt */
+ if (data != NULL) {
+ if (inten & MSDC_INT_XFER_COMPL) {
+ data->bytes_xfered = host->dma.xfersz;
+ complete(&host->xfer_done);
+ }
+
+ if (intsts & datsts) {
+ /* do basic reset, or stop command will sdc_busy */
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ atomic_set(&host->abort, 1); /* For PIO mode exit */
+
+ if (intsts & MSDC_INT_DATTMO){
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_DATTMO", host->mrq->cmd->opcode);
+ data->error = (unsigned int)-ETIMEDOUT;
+ }
+ else if (intsts & MSDC_INT_DATCRCERR){
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_DATCRCERR, SDC_DCRC_STS<0x%x>", host->mrq->cmd->opcode, sdr_read32(SDC_DCRC_STS));
+ data->error = (unsigned int)-EIO;
+ }
+
+ //if(sdr_read32(MSDC_INTEN) & MSDC_INT_XFER_COMPL) {
+ if (host->dma_xfer) {
+ complete(&host->xfer_done); /* Read CRC come fast, XFER_COMPL not enabled */
+ } /* PIO mode can't do complete, because not init */
+ }
+ }
+
+ /* command interrupts */
+ if ((cmd != NULL) && (intsts & cmdsts)) {
+ if ((intsts & MSDC_INT_CMDRDY) || (intsts & MSDC_INT_ACMDRDY) ||
+ (intsts & MSDC_INT_ACMD19_DONE)) {
+ u32 *rsp = &cmd->resp[0];
+
+ switch (host->cmd_rsp) {
+ case RESP_NONE:
+ break;
+ case RESP_R2:
+ *rsp++ = sdr_read32(SDC_RESP3); *rsp++ = sdr_read32(SDC_RESP2);
+ *rsp++ = sdr_read32(SDC_RESP1); *rsp++ = sdr_read32(SDC_RESP0);
+ break;
+ default: /* Response types 1, 3, 4, 5, 6, 7(1b) */
+ if ((intsts & MSDC_INT_ACMDRDY) || (intsts & MSDC_INT_ACMD19_DONE)) {
+ *rsp = sdr_read32(SDC_ACMD_RESP);
+ } else {
+ *rsp = sdr_read32(SDC_RESP0);
+ }
+ break;
+ }
+ } else if ((intsts & MSDC_INT_RSPCRCERR) || (intsts & MSDC_INT_ACMDCRCERR)) {
+ if(intsts & MSDC_INT_ACMDCRCERR){
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_ACMDCRCERR",cmd->opcode);
+ }
+ else {
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_RSPCRCERR",cmd->opcode);
+ }
+ cmd->error = (unsigned int)-EIO;
+ } else if ((intsts & MSDC_INT_CMDTMO) || (intsts & MSDC_INT_ACMDTMO)) {
+ if(intsts & MSDC_INT_ACMDTMO){
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_ACMDTMO",cmd->opcode);
+ }
+ else {
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_CMDTMO",cmd->opcode);
+ }
+ cmd->error = (unsigned int)-ETIMEDOUT;
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ }
+ complete(&host->cmd_done);
+ }
+
+ /* mmc irq interrupts */
+ if (intsts & MSDC_INT_MMCIRQ) {
+ printk(KERN_INFO "msdc[%d] MMCIRQ: SDC_CSTS=0x%.8x\r\n", host->id, sdr_read32(SDC_CSTS));
+ }
+
+#ifdef MT6575_SD_DEBUG
+ {
+ msdc_int_reg *int_reg = (msdc_int_reg*)&intsts;
+ N_MSG(INT, "IRQ_EVT(0x%x): MMCIRQ(%d) CDSC(%d), ACRDY(%d), ACTMO(%d), ACCRE(%d) AC19DN(%d)",
+ intsts,
+ int_reg->mmcirq,
+ int_reg->cdsc,
+ int_reg->atocmdrdy,
+ int_reg->atocmdtmo,
+ int_reg->atocmdcrc,
+ int_reg->atocmd19done);
+ N_MSG(INT, "IRQ_EVT(0x%x): SDIO(%d) CMDRDY(%d), CMDTMO(%d), RSPCRC(%d), CSTA(%d)",
+ intsts,
+ int_reg->sdioirq,
+ int_reg->cmdrdy,
+ int_reg->cmdtmo,
+ int_reg->rspcrc,
+ int_reg->csta);
+ N_MSG(INT, "IRQ_EVT(0x%x): XFCMP(%d) DXDONE(%d), DATTMO(%d), DATCRC(%d), DMAEMP(%d)",
+ intsts,
+ int_reg->xfercomp,
+ int_reg->dxferdone,
+ int_reg->dattmo,
+ int_reg->datcrc,
+ int_reg->dmaqempty);
+
+ }
+#endif
+
+ return IRQ_HANDLED;
+}
+
+/*--------------------------------------------------------------------------*/
+/* platform_driver members */
+/*--------------------------------------------------------------------------*/
+/* called by msdc_drv_probe/remove */
+static void msdc_enable_cd_irq(struct msdc_host *host, int enable)
+{
+ struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+
+ /* for sdio, not set */
+ if ((hw->flags & MSDC_CD_PIN_EN) == 0) {
+ /* Pull down card detection pin since it is not avaiable */
+ /*
+ if (hw->config_gpio_pin)
+ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_DOWN);
+ */
+ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN);
+ sdr_clr_bits(MSDC_INTEN, MSDC_INTEN_CDSC);
+ sdr_clr_bits(SDC_CFG, SDC_CFG_INSWKUP);
+ return;
+ }
+
+ N_MSG(CFG, "CD IRQ Eanable(%d)", enable);
+
+ if (enable) {
+ if (hw->enable_cd_eirq) { /* not set, never enter */
+ hw->enable_cd_eirq();
+ } else {
+ /* card detection circuit relies on the core power so that the core power
+ * shouldn't be turned off. Here adds a reference count to keep
+ * the core power alive.
+ */
+ //msdc_vcore_on(host); //did in msdc_init_hw()
+
+ if (hw->config_gpio_pin) /* NULL */
+ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_UP);
+
+ sdr_set_field(MSDC_PS, MSDC_PS_CDDEBOUNCE, DEFAULT_DEBOUNCE);
+ sdr_set_bits(MSDC_PS, MSDC_PS_CDEN);
+ sdr_set_bits(MSDC_INTEN, MSDC_INTEN_CDSC);
+ sdr_set_bits(SDC_CFG, SDC_CFG_INSWKUP); /* not in document! Fix me */
+ }
+ } else {
+ if (hw->disable_cd_eirq) {
+ hw->disable_cd_eirq();
+ } else {
+ if (hw->config_gpio_pin) /* NULL */
+ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_DOWN);
+
+ sdr_clr_bits(SDC_CFG, SDC_CFG_INSWKUP);
+ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN);
+ sdr_clr_bits(MSDC_INTEN, MSDC_INTEN_CDSC);
+
+ /* Here decreases a reference count to core power since card
+ * detection circuit is shutdown.
+ */
+ //msdc_vcore_off(host);
+ }
+ }
+}
+
+/* called by msdc_drv_probe */
+static void msdc_init_hw(struct msdc_host *host)
+{
+ u32 base = host->base;
+ struct msdc_hw *hw = host->hw;
+
+#ifdef MT6575_SD_DEBUG
+ msdc_reg[host->id] = (struct msdc_regs *)host->base;
+#endif
+
+ /* Power on */
+#if 0 /* --- by chhung */
+ msdc_vcore_on(host);
+ msdc_pin_reset(host, MSDC_PIN_PULL_UP);
+ msdc_select_clksrc(host, hw->clk_src);
+ enable_clock(PERI_MSDC0_PDN + host->id, "SD");
+ msdc_vdd_on(host);
+#endif /* end of --- */
+ /* Configure to MMC/SD mode */
+ sdr_set_field(MSDC_CFG, MSDC_CFG_MODE, MSDC_SDMMC);
+
+ /* Reset */
+ msdc_reset();
+ msdc_clr_fifo();
+
+ /* Disable card detection */
+ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN);
+
+ /* Disable and clear all interrupts */
+ sdr_clr_bits(MSDC_INTEN, sdr_read32(MSDC_INTEN));
+ sdr_write32(MSDC_INT, sdr_read32(MSDC_INT));
+
+#if 1
+ /* reset tuning parameter */
+ sdr_write32(MSDC_PAD_CTL0, 0x00090000);
+ sdr_write32(MSDC_PAD_CTL1, 0x000A0000);
+ sdr_write32(MSDC_PAD_CTL2, 0x000A0000);
+ // sdr_write32(MSDC_PAD_TUNE, 0x00000000);
+ sdr_write32(MSDC_PAD_TUNE, 0x84101010); // for MT7620 E2 and afterward
+ // sdr_write32(MSDC_DAT_RDDLY0, 0x00000000);
+ sdr_write32(MSDC_DAT_RDDLY0, 0x10101010); // for MT7620 E2 and afterward
+ sdr_write32(MSDC_DAT_RDDLY1, 0x00000000);
+ sdr_write32(MSDC_IOCON, 0x00000000);
+#if 0 // use MT7620 default value: 0x403c004f
+ sdr_write32(MSDC_PATCH_BIT0, 0x003C000F); /* bit0 modified: Rx Data Clock Source: 1 -> 2.0*/
+#endif
+
+ if (sdr_read32(MSDC_ECO_VER) >= 4) {
+ if (host->id == 1) {
+ sdr_set_field(MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_WRDAT_CRCS, 1);
+ sdr_set_field(MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_CMD_RSP, 1);
+
+ /* internal clock: latch read data */
+ sdr_set_bits(MSDC_PATCH_BIT0, MSDC_PATCH_BIT_CKGEN_CK);
+ }
+ }
+#endif
+
+ /* for safety, should clear SDC_CFG.SDIO_INT_DET_EN & set SDC_CFG.SDIO in
+ pre-loader,uboot,kernel drivers. and SDC_CFG.SDIO_INT_DET_EN will be only
+ set when kernel driver wants to use SDIO bus interrupt */
+ /* Configure to enable SDIO mode. it's must otherwise sdio cmd5 failed */
+ sdr_set_bits(SDC_CFG, SDC_CFG_SDIO);
+
+ /* disable detect SDIO device interupt function */
+ sdr_clr_bits(SDC_CFG, SDC_CFG_SDIOIDE);
+
+ /* eneable SMT for glitch filter */
+ sdr_set_bits(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKSMT);
+ sdr_set_bits(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDSMT);
+ sdr_set_bits(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATSMT);
+
+#if 1
+ /* set clk, cmd, dat pad driving */
+ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVN, hw->clk_drv);
+ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVP, hw->clk_drv);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVN, hw->cmd_drv);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVP, hw->cmd_drv);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVN, hw->dat_drv);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVP, hw->dat_drv);
+#else
+ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVN, 0);
+ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVP, 0);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVN, 0);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVP, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVN, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVP, 0);
+#endif
+
+ /* set sampling edge */
+
+ /* write crc timeout detection */
+ sdr_set_field(MSDC_PATCH_BIT0, 1 << 30, 1);
+
+ /* Configure to default data timeout */
+ sdr_set_field(SDC_CFG, SDC_CFG_DTOC, DEFAULT_DTOC);
+
+ msdc_set_buswidth(host, MMC_BUS_WIDTH_1);
+
+ N_MSG(FUC, "init hardware done!");
+}
+
+/* called by msdc_drv_remove */
+static void msdc_deinit_hw(struct msdc_host *host)
+{
+ u32 base = host->base;
+
+ /* Disable and clear all interrupts */
+ sdr_clr_bits(MSDC_INTEN, sdr_read32(MSDC_INTEN));
+ sdr_write32(MSDC_INT, sdr_read32(MSDC_INT));
+
+ /* Disable card detection */
+ msdc_enable_cd_irq(host, 0);
+ // msdc_set_power_mode(host, MMC_POWER_OFF); /* make sure power down */ /* --- by chhung */
+}
+
+/* init gpd and bd list in msdc_drv_probe */
+static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma)
+{
+ gpd_t *gpd = dma->gpd;
+ bd_t *bd = dma->bd;
+ bd_t *ptr, *prev;
+
+ /* we just support one gpd */
+ int bdlen = MAX_BD_PER_GPD;
+
+ /* init the 2 gpd */
+ memset(gpd, 0, sizeof(gpd_t) * 2);
+ //gpd->next = (void *)virt_to_phys(gpd + 1); /* pointer to a null gpd, bug! kmalloc <-> virt_to_phys */
+ //gpd->next = (dma->gpd_addr + 1); /* bug */
+ gpd->next = (void *)((u32)dma->gpd_addr + sizeof(gpd_t));
+
+ //gpd->intr = 0;
+ gpd->bdp = 1; /* hwo, cs, bd pointer */
+ //gpd->ptr = (void*)virt_to_phys(bd);
+ gpd->ptr = (void *)dma->bd_addr; /* physical address */
+
+ memset(bd, 0, sizeof(bd_t) * bdlen);
+ ptr = bd + bdlen - 1;
+ //ptr->eol = 1; /* 0 or 1 [Fix me]*/
+ //ptr->next = 0;
+
+ while (ptr != bd) {
+ prev = ptr - 1;
+ prev->next = (void *)(dma->bd_addr + sizeof(bd_t) *(ptr - bd));
+ ptr = prev;
+ }
+}
+
+static int msdc_drv_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ __iomem void *base;
+ struct mmc_host *mmc;
+ struct resource *mem;
+ struct msdc_host *host;
+ struct msdc_hw *hw;
+ int ret, irq;
+
+ pdev->dev.platform_data = &msdc0_hw;
+
+ if (of_property_read_bool(pdev->dev.of_node, "mtk,wp-en"))
+ msdc0_hw.flags |= MSDC_WP_PIN_EN;
+
+ /* Allocate MMC host for this device */
+ mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev);
+ if (!mmc) return -ENOMEM;
+
+ hw = (struct msdc_hw*)pdev->dev.platform_data;
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+
+ //BUG_ON((!hw) || (!mem) || (irq < 0)); /* --- by chhung */
+
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ /* Set host parameters to mmc */
+ mmc->ops = &mt_msdc_ops;
+ mmc->f_min = HOST_MIN_MCLK;
+ mmc->f_max = HOST_MAX_MCLK;
+ mmc->ocr_avail = MSDC_OCR_AVAIL;
+
+ /* For sd card: MSDC_SYS_SUSPEND | MSDC_WP_PIN_EN | MSDC_CD_PIN_EN | MSDC_REMOVABLE | MSDC_HIGHSPEED,
+ For sdio : MSDC_EXT_SDIO_IRQ | MSDC_HIGHSPEED */
+ if (hw->flags & MSDC_HIGHSPEED) {
+ mmc->caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
+ }
+ if (hw->data_pins == 4) { /* current data_pins are all 4*/
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+ } else if (hw->data_pins == 8) {
+ mmc->caps |= MMC_CAP_8_BIT_DATA;
+ }
+ if ((hw->flags & MSDC_SDIO_IRQ) || (hw->flags & MSDC_EXT_SDIO_IRQ))
+ mmc->caps |= MMC_CAP_SDIO_IRQ; /* yes for sdio */
+
+ cd_active_low = !of_property_read_bool(pdev->dev.of_node, "mediatek,cd-high");
+ mtk_sw_poll = of_property_read_bool(pdev->dev.of_node, "mediatek,cd-poll");
+
+ if (mtk_sw_poll)
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+ /* MMC core transfer sizes tunable parameters */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3,10,0)
+ mmc->max_segs = MAX_HW_SGMTS;
+#else
+ mmc->max_hw_segs = MAX_HW_SGMTS;
+ mmc->max_phys_segs = MAX_PHY_SGMTS;
+#endif
+ mmc->max_seg_size = MAX_SGMT_SZ;
+ mmc->max_blk_size = HOST_MAX_BLKSZ;
+ mmc->max_req_size = MAX_REQ_SZ;
+ mmc->max_blk_count = mmc->max_req_size;
+
+ host = mmc_priv(mmc);
+ host->hw = hw;
+ host->mmc = mmc;
+ BUG_ON(pdev->id < -1);
+ BUG_ON(pdev->id >= ARRAY_SIZE(drv_mode));
+ host->id = (pdev->id == -1) ? 0 : pdev->id;
+ host->error = 0;
+ host->irq = irq;
+ host->base = (unsigned long) base;
+ host->mclk = 0; /* mclk: the request clock of mmc sub-system */
+ host->hclk = hclks[hw->clk_src]; /* hclk: clock of clock source to msdc controller */
+ host->sclk = 0; /* sclk: the really clock after divition */
+ host->pm_state = PMSG_RESUME;
+ host->suspend = 0;
+ host->core_clkon = 0;
+ host->card_clkon = 0;
+ host->core_power = 0;
+ host->power_mode = MMC_POWER_OFF;
+// host->card_inserted = hw->flags & MSDC_REMOVABLE ? 0 : 1;
+ host->timeout_ns = 0;
+ host->timeout_clks = DEFAULT_DTOC * 65536;
+
+ host->mrq = NULL;
+ //init_MUTEX(&host->sem); /* we don't need to support multiple threads access */
+
+ host->dma.used_gpd = 0;
+ host->dma.used_bd = 0;
+
+ /* using dma_alloc_coherent*/ /* todo: using 1, for all 4 slots */
+ host->dma.gpd = dma_alloc_coherent(NULL, MAX_GPD_NUM * sizeof(gpd_t), &host->dma.gpd_addr, GFP_KERNEL);
+ host->dma.bd = dma_alloc_coherent(NULL, MAX_BD_NUM * sizeof(bd_t), &host->dma.bd_addr, GFP_KERNEL);
+ BUG_ON((!host->dma.gpd) || (!host->dma.bd));
+ msdc_init_gpd_bd(host, &host->dma);
+ /*for emmc*/
+ msdc_6575_host[pdev->id] = host;
+
+#if 0
+ tasklet_init(&host->card_tasklet, msdc_tasklet_card, (ulong)host);
+#else
+ INIT_DELAYED_WORK(&host->card_delaywork, msdc_tasklet_card);
+#endif
+ spin_lock_init(&host->lock);
+ msdc_init_hw(host);
+
+ if (ralink_soc == MT762X_SOC_MT7621AT)
+ ret = request_irq((unsigned int)irq, msdc_irq, 0, dev_name(&pdev->dev), host);
+ else
+ ret = request_irq((unsigned int)irq, msdc_irq, IRQF_TRIGGER_LOW, dev_name(&pdev->dev), host);
+
+ if (ret) goto release;
+ // mt65xx_irq_unmask(irq); /* --- by chhung */
+
+ if (hw->flags & MSDC_CD_PIN_EN) { /* not set for sdio */
+ if (hw->request_cd_eirq) { /* not set for MT6575 */
+ hw->request_cd_eirq(msdc_eirq_cd, (void*)host); /* msdc_eirq_cd will not be used! */
+ }
+ }
+
+ if (hw->request_sdio_eirq) /* set to combo_sdio_request_eirq() for WIFI */
+ hw->request_sdio_eirq(msdc_eirq_sdio, (void*)host); /* msdc_eirq_sdio() will be called when EIRQ */
+
+ if (hw->register_pm) {/* yes for sdio */
+#ifdef CONFIG_PM
+ hw->register_pm(msdc_pm, (void*)host); /* combo_sdio_register_pm() */
+#endif
+ if(hw->flags & MSDC_SYS_SUSPEND) { /* will not set for WIFI */
+ ERR_MSG("MSDC_SYS_SUSPEND and register_pm both set");
+ }
+ //mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY; /* pm not controlled by system but by client. */ /* --- by chhung */
+ }
+
+ platform_set_drvdata(pdev, mmc);
+
+ ret = mmc_add_host(mmc);
+ if (ret) goto free_irq;
+
+ /* Config card detection pin and enable interrupts */
+ if (hw->flags & MSDC_CD_PIN_EN) { /* set for card */
+ msdc_enable_cd_irq(host, 1);
+ } else {
+ msdc_enable_cd_irq(host, 0);
+ }
+
+ return 0;
+
+free_irq:
+ free_irq(irq, host);
+release:
+ platform_set_drvdata(pdev, NULL);
+ msdc_deinit_hw(host);
+
+#if 0
+ tasklet_kill(&host->card_tasklet);
+#else
+ cancel_delayed_work_sync(&host->card_delaywork);
+#endif
+
+ if (mem)
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+
+ mmc_free_host(mmc);
+
+ return ret;
+}
+
+/* 4 device share one driver, using "drvdata" to show difference */
+static int msdc_drv_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct msdc_host *host;
+ struct resource *mem;
+
+ mmc = platform_get_drvdata(pdev);
+ BUG_ON(!mmc);
+
+ host = mmc_priv(mmc);
+ BUG_ON(!host);
+
+ ERR_MSG("removed !!!");
+
+ platform_set_drvdata(pdev, NULL);
+ mmc_remove_host(host->mmc);
+ msdc_deinit_hw(host);
+
+#if 0
+ tasklet_kill(&host->card_tasklet);
+#else
+ cancel_delayed_work_sync(&host->card_delaywork);
+#endif
+ free_irq(host->irq, host);
+
+ dma_free_coherent(NULL, MAX_GPD_NUM * sizeof(gpd_t), host->dma.gpd, host->dma.gpd_addr);
+ dma_free_coherent(NULL, MAX_BD_NUM * sizeof(bd_t), host->dma.bd, host->dma.bd_addr);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (mem)
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+
+ mmc_free_host(host->mmc);
+
+ return 0;
+}
+
+/* Fix me: Power Flow */
+#ifdef CONFIG_PM
+static int msdc_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int ret = 0;
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct msdc_host *host = mmc_priv(mmc);
+
+ if (mmc && state.event == PM_EVENT_SUSPEND && (host->hw->flags & MSDC_SYS_SUSPEND)) { /* will set for card */
+ msdc_pm(state, (void*)host);
+ }
+
+ return ret;
+}
+
+static int msdc_drv_resume(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct msdc_host *host = mmc_priv(mmc);
+ struct pm_message state;
+
+ state.event = PM_EVENT_RESUME;
+ if (mmc && (host->hw->flags & MSDC_SYS_SUSPEND)) {/* will set for card */
+ msdc_pm(state, (void*)host);
+ }
+
+ /* This mean WIFI not controller by PM */
+
+ return ret;
+}
+#endif
+
+static const struct of_device_id mt7620_sdhci_match[] = {
+ { .compatible = "ralink,mt7620-sdhci" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt7620_sdhci_match);
+
+static struct platform_driver mt_msdc_driver = {
+ .probe = msdc_drv_probe,
+ .remove = msdc_drv_remove,
+#ifdef CONFIG_PM
+ .suspend = msdc_drv_suspend,
+ .resume = msdc_drv_resume,
+#endif
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = mt7620_sdhci_match,
+ },
+};
+
+/*--------------------------------------------------------------------------*/
+/* module init/exit */
+/*--------------------------------------------------------------------------*/
+static int __init mt_msdc_init(void)
+{
+ int ret;
+/* +++ by chhung */
+ u32 reg;
+
+#if defined (CONFIG_MTD_ANY_RALINK)
+ extern int ra_check_flash_type(void);
+ if(ra_check_flash_type() == 2) { /* NAND */
+ printk("%s: !!!!! SDXC Module Initialize Fail !!!!!", __func__);
+ return 0;
+ }
+#endif
+ printk("MTK MSDC device init.\n");
+ mtk_sd_device.dev.platform_data = &msdc0_hw;
+if (ralink_soc == MT762X_SOC_MT7620A || ralink_soc == MT762X_SOC_MT7621AT) {
+//#if defined (CONFIG_RALINK_MT7620) || defined (CONFIG_RALINK_MT7621)
+ reg = sdr_read32((volatile u32*)(RALINK_SYSCTL_BASE + 0x60)) & ~(0x3<<18);
+//#if defined (CONFIG_RALINK_MT7620)
+ if (ralink_soc == MT762X_SOC_MT7620A)
+ reg |= 0x1<<18;
+//#endif
+} else {
+//#elif defined (CONFIG_RALINK_MT7628)
+ /* TODO: maybe omitted when RAether already toggle AGPIO_CFG */
+ reg = sdr_read32((volatile u32*)(RALINK_SYSCTL_BASE + 0x3c));
+ reg |= 0x1e << 16;
+ sdr_write32((volatile u32*)(RALINK_SYSCTL_BASE + 0x3c), reg);
+
+ reg = sdr_read32((volatile u32*)(RALINK_SYSCTL_BASE + 0x60)) & ~(0x3<<10);
+#if defined (CONFIG_MTK_MMC_EMMC_8BIT)
+ reg |= 0x3<<26 | 0x3<<28 | 0x3<<30;
+ msdc0_hw.data_pins = 8,
+#endif
+//#endif
+}
+ sdr_write32((volatile u32*)(RALINK_SYSCTL_BASE + 0x60), reg);
+ //platform_device_register(&mtk_sd_device);
+/* end of +++ */
+
+ ret = platform_driver_register(&mt_msdc_driver);
+ if (ret) {
+ printk(KERN_ERR DRV_NAME ": Can't register driver");
+ return ret;
+ }
+ printk(KERN_INFO DRV_NAME ": MediaTek MT6575 MSDC Driver\n");
+
+#if defined (MT6575_SD_DEBUG)
+ msdc_debug_proc_init();
+#endif
+ return 0;
+}
+
+static void __exit mt_msdc_exit(void)
+{
+// platform_device_unregister(&mtk_sd_device);
+ platform_driver_unregister(&mt_msdc_driver);
+}
+
+module_init(mt_msdc_init);
+module_exit(mt_msdc_exit);
+MODULE_LICENSE("GPL");