2015-04-29 01:34:59

by 敬锐

[permalink] [raw]
Subject: [PATCH 00/12] mmc: core: add SD4.0 support

From: Micky Ching <[email protected]>

Add support for SD4.0 card, which introduce UHSII mode and tlp transfer.

Micky Ching (12):
mmc: core: add data structure define for SD4.0
mmc: core: modify mmc_app_cmd interface for SD4.0
mmc: core: add SD4.0 operation function
mmc: core: add tlp request handler for SD4.0
mmc: core: disable full power cycle for SD4.0
mmc: core: init SD4.0 mode before legacy mode
mmc: sdhci: add data structure for SD4.0
mmc: sdhci: add SD4.0 operations
mmc: sdhci: add tlp handler for SD4.0
mmc: sdhci: disable clock control for SD4.0 mode
mmc: sdhci: set DMA configure for SD4.0 mode
mmc: sdhci: add SD4.0 support

drivers/mmc/card/block.c | 2 +-
drivers/mmc/core/core.c | 174 ++++++++++++--
drivers/mmc/core/mmc.c | 63 +++--
drivers/mmc/core/sd.c | 282 ++++++++++++++++++----
drivers/mmc/core/sd.h | 1 +
drivers/mmc/core/sd_ops.c | 227 +++++++++++++++++-
drivers/mmc/core/sd_ops.h | 7 +
drivers/mmc/core/sdio.c | 103 ++++-----
drivers/mmc/host/sdhci.c | 579 ++++++++++++++++++++++++++++++++++++++++++----
drivers/mmc/host/sdhci.h | 136 ++++++++++-
include/linux/mmc/card.h | 13 ++
include/linux/mmc/core.h | 91 +++++++-
include/linux/mmc/host.h | 33 +++
include/linux/mmc/sd.h | 73 ++++++
14 files changed, 1572 insertions(+), 212 deletions(-)

--
1.9.1


2015-04-29 02:01:07

by 敬锐

[permalink] [raw]
Subject: [PATCH 01/12] mmc: core: add data structure define for SD4.0

From: Micky Ching <[email protected]>

The new data structure for SD4.0 including follows:

register: SD4.0 IO space register define.
protocol: host and card handshake data.
tlp: tlp request data structure for SD4.0 mode.
uhsii ios: UHSII bus control data structure.

Signed-off-by: Micky Ching <[email protected]>
Signed-off-by: Wei Wang <[email protected]>
---
include/linux/mmc/card.h | 13 +++++++
include/linux/mmc/core.h | 88 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/mmc/host.h | 33 ++++++++++++++++++
include/linux/mmc/sd.h | 73 +++++++++++++++++++++++++++++++++++++++
4 files changed, 207 insertions(+)

diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index a6cf4c0..77baf55 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -262,6 +262,7 @@ struct mmc_card {
#define MMC_CARD_REMOVED (1<<4) /* card has been removed */
#define MMC_STATE_DOING_BKOPS (1<<5) /* card is doing BKOPS */
#define MMC_STATE_SUSPENDED (1<<6) /* card is suspended */
+#define MMC_STATE_UHSII (1<<12) /* card is in UHS-II mode */
unsigned int quirks; /* card quirks */
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
@@ -279,6 +280,15 @@ struct mmc_card {
#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */
#define MMC_QUIRK_BROKEN_IRQ_POLLING (1<<11) /* Polling SDIO_CCCR_INTx could create a fake interrupt */

+ u8 node_id; /* Node ID for UHS-II card */
+ u8 lane_mode;
+ u8 n_data_gap;
+ u8 max_retry_num;
+ u8 n_fcu;
+ u8 n_lss_dir;
+ u8 n_lss_syn;
+ u16 max_blklen;
+
unsigned int erase_size; /* erase size in sectors */
unsigned int erase_shift; /* if erase unit is power 2 */
unsigned int pref_erase; /* in sectors */
@@ -423,6 +433,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR)
+#define mmc_card_uhsii(c) ((c)->state & MMC_STATE_UHSII)
#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED))
#define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS)
@@ -431,6 +442,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
+#define mmc_card_set_uhsii(c) ((c)->state |= MMC_STATE_UHSII)
+#define mmc_card_clr_uhsii(c) ((c)->state &= ~MMC_STATE_UHSII)
#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
#define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
#define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS)
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index de722d4e..337c6b8 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -15,6 +15,57 @@ struct request;
struct mmc_data;
struct mmc_request;

+struct mmc_tlp_block {
+ u16 header;
+
+#define UHSII_HD_NP 0x8000
+#define UHSII_HD_TYP_MASK 0x7000
+#define UHSII_HD_TYP_CCMD (0 << 12)
+#define UHSII_HD_TYP_DCMD (1 << 12)
+#define UHSII_HD_TYP_RES (2 << 12)
+#define UHSII_HD_TYP_DATA (3 << 12)
+#define UHSII_HD_TYP_MSG (7 << 12)
+#define UHSII_HD_DID_SHIFT 8
+#define UHSII_HD_DID_MASK (0x0F << UHSII_HD_DID_SHIFT)
+#define UHSII_HD_SID_SHIFT 4
+#define UHSII_HD_SID_MASK (0x0F << UHSII_HD_SID_SHIFT)
+
+#define UHSII_HD_DID(val) ((val) << UHSII_HD_DID_SHIFT & UHSII_HD_DID_MASK)
+#define UHSII_CHK_CCMD(val) (((val) & UHSII_HD_TYP_MASK) == UHSII_HD_TYP_CCMD)
+#define UHSII_CHK_DCMD(val) (((val) & UHSII_HD_TYP_MASK) == UHSII_HD_TYP_DCMD)
+
+ u16 argument;
+#define UHSII_ARG_DIR_WRITE 0x8000
+#define UHSII_ARG_DIR_READ 0
+#define UHSII_ARG_PLEN_SHIFT 12
+#define UHSII_ARG_PLEN_MASK (0x03 << UHSII_ARG_PLEN_SHIFT)
+#define UHSII_ARG_IOADR_MASK 0x0FFF
+#define UHSII_ARG_RES_NACK 0x8000
+#define UHSII_ARG_TMODE_SHIFT 11
+#define UHSII_ARG_TMODE_MASK (0x0F << UHSII_ARG_TMODE_SHIFT)
+#define UHSII_TMODE_DM_HD 0x08
+#define UHSII_TMODE_LM_SPECIFIED 0x04
+#define UHSII_TMODE_TLUM_BYTE 0x02
+#define UHSII_TMODE_DAM_FIX 0x01
+#define UHSII_ARG_APP_CMD 0x40
+#define UHSII_ARG_CMD_INDEX_MASK 0x3F
+
+#define UHSII_PLEN_BYTES(plen) ((plen) ? 2 << (plen) : 0)
+#define UHSII_PLEN_DWORDS(plen) ((plen) == 3 ? 4 : (plen))
+
+#define UHSII_GET_PLEN(tlp_block) \
+ ((u8)(((tlp_block)->argument & UHSII_ARG_PLEN_MASK) \
+ >> UHSII_ARG_PLEN_SHIFT))
+#define UHSII_GET_TMODE(tlp_block) \
+ ((u8)(((tlp_block)->argument & UHSII_ARG_TMODE_MASK) \
+ >> UHSII_ARG_TMODE_SHIFT))
+
+ u32 payload[4];
+#define UHSII_GET_GAP(tlp_block) \
+ ((u8)(((tlp_block)->payload[0] >> SD40_GAP_SHIFT) \
+ & SD40_GAP_MASK))
+};
+
struct mmc_command {
u32 opcode;
u32 arg;
@@ -101,6 +152,10 @@ struct mmc_command {

struct mmc_data *data; /* data segment associated with cmd */
struct mmc_request *mrq; /* associated request */
+
+ struct mmc_tlp_block tlp_send;
+ bool use_tlp;
+ bool app_cmd;
};

struct mmc_data {
@@ -125,18 +180,51 @@ struct mmc_data {
s32 host_cookie; /* host private data */
};

+struct mmc_tlp {
+ struct mmc_tlp_block *tlp_send;
+ struct mmc_tlp_block *tlp_back;
+
+ u8 cmd_type;
+#define UHSII_COMMAND_NORMAL 0x00
+#define UHSII_COMMAND_GO_DORMANT 0x03
+
+ unsigned int retries; /* max number of retries */
+ int error;
+};
+
struct mmc_host;
struct mmc_request {
struct mmc_command *sbc; /* SET_BLOCK_COUNT for multiblock */
struct mmc_command *cmd;
struct mmc_data *data;
struct mmc_command *stop;
+ struct mmc_tlp *tlp;

struct completion completion;
void (*done)(struct mmc_request *);/* completion function */
struct mmc_host *host;
};

+static inline void mmc_set_mrq_error_code(struct mmc_request *mrq, int err)
+{
+ if (mrq->cmd)
+ mrq->cmd->error = err;
+ else
+ mrq->tlp->error = err;
+}
+
+static inline int mmc_get_mrq_error_code(struct mmc_request *mrq)
+{
+ int err;
+
+ if (mrq->cmd)
+ err = mrq->cmd->error;
+ else
+ err = mrq->tlp->error;
+
+ return err;
+}
+
struct mmc_card;
struct mmc_async_req;

diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index b5bedae..2c4fcf5 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -78,6 +78,23 @@ struct mmc_ios {
#define MMC_SET_DRIVER_TYPE_D 3
};

+struct mmc_uhsii_ios {
+ u8 speed_range;
+#define SD_UHSII_SPEED_RANGE_A 0
+#define SD_UHSII_SPEED_RANGE_B 1
+
+ u8 n_fcu;
+
+ u8 pwr_ctl_mode;
+#define SD_UHSII_PWR_CTL_FAST_MODE 0
+#define SD_UHSII_PWR_CTL_LOW_PWR_MODE 1
+
+ u32 flags;
+#define SETTING_SPEED_RANGE (1 << 0)
+#define SETTING_N_FCU (1 << 1)
+#define SETTING_PWR_CTL_MODE (1 << 2)
+};
+
struct mmc_host_ops {
/*
* It is optional for the host to implement pre_req and post_req in
@@ -135,6 +152,10 @@ struct mmc_host_ops {
void (*hw_reset)(struct mmc_host *host);
void (*card_event)(struct mmc_host *host);

+ int (*switch_uhsii_if)(struct mmc_host *host);
+ int (*exit_dormant)(struct mmc_host *host);
+ void (*set_uhsii_ios)(struct mmc_host *host,
+ struct mmc_uhsii_ios *ios);
/*
* Optional callback to support controllers with HW issues for multiple
* I/O. Returns the number of supported blocks for the request.
@@ -257,6 +278,7 @@ struct mmc_host {
#define MMC_CAP_UHS_SDR104 (1 << 18) /* Host supports UHS SDR104 mode */
#define MMC_CAP_UHS_DDR50 (1 << 19) /* Host supports UHS DDR50 mode */
#define MMC_CAP_RUNTIME_RESUME (1 << 20) /* Resume at runtime_resume. */
+#define MMC_CAP_UHSII (1 << 21) /* Host supports UHS-II mode */
#define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */
#define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */
#define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */
@@ -285,6 +307,8 @@ struct mmc_host {
MMC_CAP2_HS400_1_2V)
#define MMC_CAP2_HSX00_1_2V (MMC_CAP2_HS200_1_2V_SDR | MMC_CAP2_HS400_1_2V)
#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
+#define MMC_CAP2_UHSII_RANGE_AB (1 << 18) /* Supported speed range */
+#define MMC_CAP2_UHSII_LOW_PWR (1 << 19) /* Support UHS-II low power */

mmc_pm_flag_t pm_caps; /* supported pm features */

@@ -300,6 +324,15 @@ struct mmc_host {
unsigned long clkgate_delay;
#endif

+ u8 lane_mode;
+ u8 max_gap;
+ u8 max_dap;
+ u8 n_data_gap;
+ u8 n_fcu;
+ u8 n_lss_dir;
+ u8 n_lss_syn;
+ struct mmc_uhsii_ios uhsii_ios;
+
/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
unsigned short max_segs; /* see blk_queue_max_segments */
diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
index 1ebcf9b..ec0e12d 100644
--- a/include/linux/mmc/sd.h
+++ b/include/linux/mmc/sd.h
@@ -91,4 +91,77 @@
#define SD_SWITCH_ACCESS_DEF 0
#define SD_SWITCH_ACCESS_HS 1

+#define UHSII_IOADR(base, reg) (((u16)(base) << 8) | (reg))
+#define UHSII_IOADR_BASE(arg) (((arg) >> 8) & 0x0F)
+#define UHSII_IOADR_OFFSET(arg) ((arg) & 0xFF)
+
+/* IOADR of Generic Capabilities Register (DW) */
+#define SD40_IOADR_GEN_CAP_L 0x00
+#define SD40_IOADR_GEN_CAP_H 0x01
+
+/* IOADR of PHY Capabilities Register (DW) */
+#define SD40_IOADR_PHY_CAP_L 0x02
+#define SD40_IOADR_PHY_CAP_H 0x03
+
+/* IOADR LINK/TRAN Capabilities Register (DW) */
+#define SD40_IOADR_LINK_CAP_L 0x04
+#define SD40_IOADR_LINK_CAP_H 0x05
+
+#define SD40_IOADR_GEN_SET_L 0x08
+#define SD40_IOADR_GEN_SET_H 0x09
+
+#define SD40_IOADR_PHY_SET_L 0x0A
+#define SD40_IOADR_PHY_SET_H 0x0B
+
+#define SD40_IOADR_LINK_SET_L 0x0C
+#define SD40_IOADR_LINK_SET_H 0x0D
+
+/* Node ID (First or Last) ENUMERATE */
+#define SD40_IDL_SHIFT 24
+#define SD40_IDL_MASK (0x0F << SD40_IDL_SHIFT)
+
+/* SD40 I/O Address space offset */
+#define SD40_IOADR_CFG_BASE 0x00 /*000h : 0FFh*/
+#define SD40_IOADR_INT_BASE 0x01 /*100h : 17Fh*/
+#define SD40_IOADR_ST_BASE 0x01 /*180h : 1FFh*/
+#define SD40_IOADR_CMD_BASE 0x02 /*200h : 2FFh*/
+#define SD40_IOADR_VENDOR_BASE 0x0F /*F00h : FFFh*/
+
+/* Command Register (CMD_REG). DW, Base on IOADR_CMD_BASE */
+#define SD40_FULL_RESET 0x00
+#define SD40_GO_DORMANT_STATE 0x01
+#define SD40_DEVICE_INIT 0x02
+#define SD40_ENUMERATE 0x03
+#define SD40_TRANS_ABORT 0x04
+
+/* DEVICE_INIT */
+#define SD40_GD_SHIFT 28
+#define SD40_GD_MASK (0x0F << SD40_GD_SHIFT)
+#define SD40_GAP_SHIFT 24
+#define SD40_GAP_MASK (0x0F << SD40_GAP_SHIFT)
+#define SD40_DAP_SHIFT 20
+#define SD40_DAP_MASK (0x0F << SD40_DAP_SHIFT)
+#define SD40_CF 0x80000
+
+#define SD40_GD(val) (((val) << SD40_GD_SHIFT) & SD40_GD_MASK)
+#define SD40_GAP(val) (((val) << SD40_GAP_SHIFT) & SD40_GAP_MASK)
+#define SD40_DAP(val) (((val) << SD40_DAP_SHIFT) & SD40_DAP_MASK)
+
+/* Generic Capabilities Register */
+#define SD40_LANE_MODE_SHIFT 8
+#define SD40_LANE_MODE_MASK (0x3F << SD40_LANE_MODE_SHIFT)
+#define SD40_LANE_MODE_2L_HD 0x01
+#define SD40_LANE_MODE_2D1U_FD 0x02
+#define SD40_LANE_MODE_1D2U_FD 0x04
+#define SD40_LANE_MODE_2D2U_FD 0x08
+
+#define SD40_APP_TYPE_MASK 0x07
+#define SD40_APP_TYPE_SD_MEMORY 0x01
+#define SD40_APP_TYPE_SDIO 0x02
+#define SD40_APP_TYPE_EMBEDDED 0x04
+
+/* Generic Settings Register */
+#define SD40_CONFIG_COMPLETE 0x80000000
+#define SD40_LOW_PWR_MODE 0x01
+
#endif /* LINUX_MMC_SD_H */
--
1.9.1

2015-04-29 01:34:24

by 敬锐

[permalink] [raw]
Subject: [PATCH 02/12] mmc: core: modify mmc_app_cmd interface for SD4.0

From: Micky Ching <[email protected]>

When card running in SD4.0 mode, ACMD is not need to send two command,
we only need to mark a flag for the CMD which is to be send.

Signed-off-by: Micky Ching <[email protected]>
Signed-off-by: Wei Wang <[email protected]>
---
drivers/mmc/card/block.c | 2 +-
drivers/mmc/core/sd_ops.c | 17 ++++++++++++-----
include/linux/mmc/core.h | 3 ++-
3 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 2fc4269..a2acf3c 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -532,7 +532,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
goto cmd_rel_host;

if (idata->ic.is_acmd) {
- err = mmc_app_cmd(card->host, card);
+ err = mmc_app_cmd(card->host, card, &cmd);
if (err)
goto cmd_rel_host;
}
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index 48d0c93..cd37971c 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -22,7 +22,8 @@
#include "core.h"
#include "sd_ops.h"

-int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
+int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card,
+ struct mmc_command *next_cmd)
{
int err;
struct mmc_command cmd = {0};
@@ -30,6 +31,11 @@ int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
BUG_ON(!host);
BUG_ON(card && (card->host != host));

+ if (card && mmc_card_uhsii(card)) {
+ next_cmd->app_cmd = true;
+ return 0;
+ }
+
cmd.opcode = MMC_APP_CMD;

if (card) {
@@ -82,7 +88,7 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
* we cannot use the retries field in mmc_command.
*/
for (i = 0;i <= retries;i++) {
- err = mmc_app_cmd(host, card);
+ err = mmc_app_cmd(host, card, cmd);
if (err) {
/* no point in retrying; no APP commands allowed */
if (mmc_host_is_spi(host)) {
@@ -162,7 +168,8 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;

for (i = 100; i; i--) {
- err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
+ err = mmc_wait_for_app_cmd(host, host->card,
+ &cmd, MMC_CMD_RETRIES);
if (err)
break;

@@ -260,7 +267,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)

/* NOTE: caller guarantees scr is heap-allocated */

- err = mmc_app_cmd(card->host, card);
+ err = mmc_app_cmd(card->host, card, &cmd);
if (err)
return err;

@@ -363,7 +370,7 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr)

/* NOTE: caller guarantees ssr is heap-allocated */

- err = mmc_app_cmd(card->host, card);
+ err = mmc_app_cmd(card->host, card, &cmd);
if (err)
return err;

diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 337c6b8..b9821f1 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -235,7 +235,8 @@ extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
extern int mmc_interrupt_hpi(struct mmc_card *);
extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
-extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
+extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *,
+ struct mmc_command *);
extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
struct mmc_command *, int);
extern void mmc_start_bkops(struct mmc_card *card, bool from_exception);
--
1.9.1

2015-04-29 01:37:13

by 敬锐

[permalink] [raw]
Subject: [PATCH 03/12] mmc: core: add SD4.0 operation function

From: Micky Ching <[email protected]>

SD4.0 add some new operations, which include follows:

UHSII interface detect: when UHSII interface is detected, the power is up.
go/exit dormant: enter or exit dormant state.
device init: device init CCMD.
enumerate: enumerate CCMD.
config space read/write CCMD.

when we send SD command in UHSII mode, we need to pack mmc_command to
mmc_tlp_block, using tlp to transfer cmd.

Signed-off-by: Micky Ching <[email protected]>
Signed-off-by: Wei Wang <[email protected]>
---
drivers/mmc/core/sd.c | 187 +++++++++++++++++++++++++++++++++++++++++
drivers/mmc/core/sd.h | 1 +
drivers/mmc/core/sd_ops.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/mmc/core/sd_ops.h | 7 ++
4 files changed, 405 insertions(+)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 31a9ef2..8dd35d9 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -707,6 +707,38 @@ struct device_type sd_type = {
.groups = sd_std_groups,
};

+static int mmc_sd_switch_uhsii_if(struct mmc_host *host)
+{
+ int err = 0;
+
+ if (!(host->caps & MMC_CAP_UHSII) || !host->ops->switch_uhsii_if)
+ return -ENXIO;
+
+ mmc_host_clk_hold(host);
+ err = host->ops->switch_uhsii_if(host);
+ mmc_host_clk_release(host);
+
+ if (!err)
+ host->ios.power_mode = MMC_POWER_ON;
+
+ mmc_delay(10);
+ return err;
+}
+
+static int mmc_sd_exit_dormant(struct mmc_host *host)
+{
+ int ret;
+
+ if (!(host->caps & MMC_CAP_UHSII) || !host->ops->exit_dormant)
+ return 0;
+
+ mmc_host_clk_hold(host);
+ ret = host->ops->exit_dormant(host);
+ mmc_host_clk_release(host);
+
+ return ret;
+}
+
/*
* Fetch CID from card.
*/
@@ -1211,6 +1243,161 @@ static const struct mmc_bus_ops mmc_sd_ops = {
.reset = mmc_sd_reset,
};

+static inline void mmc_set_uhsii_ios(struct mmc_host *host)
+{
+ struct mmc_uhsii_ios *ios = &host->uhsii_ios;
+
+ pr_debug("%s: speedrange %d nfcu %d\n",
+ mmc_hostname(host), ios->speed_range, ios->n_fcu);
+
+ host->ops->set_uhsii_ios(host, ios);
+}
+
+int mmc_sd_init_uhsii_card(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ int err = 0, i;
+ u32 cap[2], setting;
+
+ mmc_card_set_uhsii(card);
+
+ err = mmc_sd_switch_uhsii_if(host);
+ if (err) {
+ mmc_card_clr_uhsii(card);
+ return err;
+ }
+
+ pr_debug("%s: try UHS-II interface\n", mmc_hostname(host));
+
+ for (i = 0; i < 5; i++) {
+ err = mmc_sd_send_device_init_ccmd(card);
+ if (!err)
+ break;
+
+ msleep(20);
+ }
+ if (err)
+ goto poweroff;
+
+ err = mmc_sd_send_enumerate_ccmd(card);
+ if (err)
+ goto poweroff;
+
+ err = mmc_sd_read_cfg_ccmd(card, SD40_IOADR_GEN_CAP_L, 2, cap);
+ if (err)
+ goto poweroff;
+ card->lane_mode = (cap[0] & SD40_LANE_MODE_MASK) >>
+ SD40_LANE_MODE_SHIFT;
+ card->lane_mode &= host->lane_mode;
+ pr_debug("%s: card->lane_mode = 0x%x\n",
+ mmc_hostname(host), card->lane_mode);
+
+ err = mmc_sd_read_cfg_ccmd(card, SD40_IOADR_PHY_CAP_L, 2, cap);
+ if (err)
+ goto poweroff;
+ card->n_lss_dir = (cap[1] & 0xF0) >> 4;
+ card->n_lss_syn = cap[1] & 0x0F;
+ pr_debug("%s: card->n_lss_dir = %d, card->n_lss_syn = %d\n",
+ mmc_hostname(host), card->n_lss_dir, card->n_lss_syn);
+
+ err = mmc_sd_read_cfg_ccmd(card, SD40_IOADR_LINK_CAP_L, 2, cap);
+ if (err)
+ goto poweroff;
+ card->n_fcu = (cap[0] & 0xFF00) >> 8;
+ card->max_retry_num = (cap[0] & 0x30000) >> 16;
+ card->max_blklen = (cap[0] & 0xFFF00000) >> 20;
+ card->n_data_gap = cap[1] & 0xFF;
+ pr_debug("%s: card->n_fcu = %d\n", mmc_hostname(host), card->n_fcu);
+ pr_debug("%s: card->max_retry_num = %d\n",
+ mmc_hostname(host), card->max_retry_num);
+ pr_debug("%s: card->max_blklen = %d\n",
+ mmc_hostname(host), card->max_blklen);
+ pr_debug("%s: card->n_data_gap = %d\n",
+ mmc_hostname(host), card->n_data_gap);
+
+ if (card->n_lss_dir < host->n_lss_dir)
+ card->n_lss_dir = host->n_lss_dir;
+ if (card->n_lss_syn < host->n_lss_syn)
+ card->n_lss_syn = host->n_lss_syn;
+ setting = ((card->n_lss_dir << 4) & 0xF0) | (card->n_lss_syn & 0x0F);
+ err = mmc_sd_write_cfg_ccmd(card,
+ SD40_IOADR_PHY_SET_H, 1, &setting);
+ if (err)
+ goto poweroff;
+
+ if (card->n_data_gap < host->n_data_gap)
+ card->n_data_gap = host->n_data_gap;
+ setting = card->n_data_gap;
+ err = mmc_sd_write_cfg_ccmd(card,
+ SD40_IOADR_LINK_SET_H, 1, &setting);
+ if (err)
+ goto poweroff;
+
+ if (host->caps2 & MMC_CAP2_UHSII_LOW_PWR) {
+ /* Set low power mode */
+ setting = SD40_LOW_PWR_MODE;
+ err = mmc_sd_write_cfg_ccmd(card,
+ SD40_IOADR_GEN_SET_L, 1, &setting);
+ if (err)
+ goto poweroff;
+ host->uhsii_ios.pwr_ctl_mode = SD_UHSII_PWR_CTL_LOW_PWR_MODE;
+ host->uhsii_ios.flags = SETTING_PWR_CTL_MODE;
+ mmc_set_uhsii_ios(host);
+ }
+
+ if (host->n_fcu) {
+ if (!card->n_fcu || (card->n_fcu > host->n_fcu))
+ card->n_fcu = host->n_fcu;
+ }
+ setting = (card->n_fcu << 8) | (card->max_retry_num << 16) |
+ (card->max_blklen << 20);
+ err = mmc_sd_write_cfg_ccmd(card, SD40_IOADR_LINK_SET_L, 1, &setting);
+ if (err)
+ goto poweroff;
+
+ setting = SD40_CONFIG_COMPLETE;
+ err = mmc_sd_write_cfg_ccmd(card, SD40_IOADR_GEN_SET_H, 1, &setting);
+ if (err)
+ goto poweroff;
+
+ host->uhsii_ios.n_fcu = card->n_fcu;
+ host->uhsii_ios.flags = SETTING_N_FCU;
+ mmc_set_uhsii_ios(host);
+
+ if (host->caps2 & MMC_CAP2_UHSII_RANGE_AB) {
+ pr_debug("%s: Select Speed Range B\n", mmc_hostname(host));
+
+ setting = (1 << 6);
+ err = mmc_sd_write_cfg_ccmd(card,
+ SD40_IOADR_PHY_SET_L, 1, &setting);
+ if (err)
+ goto poweroff;
+
+ host->uhsii_ios.speed_range = SD_UHSII_SPEED_RANGE_B;
+ host->uhsii_ios.flags = SETTING_SPEED_RANGE;
+ mmc_set_uhsii_ios(host);
+
+ /* Enter dormant state */
+ err = mmc_sd_send_go_dormant_state_ccmd(card, 0);
+ if (err)
+ goto poweroff;
+
+ /* Exit dormant state */
+ err = mmc_sd_exit_dormant(host);
+ if (err)
+ goto poweroff;
+ }
+
+ pr_debug("%s: UHSII-interface init success\n", mmc_hostname(host));
+ return 0;
+
+poweroff:
+ mmc_power_off(host);
+ mmc_card_clr_uhsii(card);
+
+ return err;
+}
+
/*
* Starting point for SD card init.
*/
diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
index aab824a..4f3a6e0 100644
--- a/drivers/mmc/core/sd.h
+++ b/drivers/mmc/core/sd.h
@@ -12,5 +12,6 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
bool reinit);
unsigned mmc_sd_get_max_clock(struct mmc_card *card);
int mmc_sd_switch_hs(struct mmc_card *card);
+int mmc_sd_init_uhsii_card(struct mmc_card *card);

#endif
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index cd37971c..1f3dd30 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -400,3 +400,213 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr)

return 0;
}
+
+int mmc_sd_send_device_init_ccmd(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ struct mmc_request mrq = {NULL};
+ struct mmc_tlp tlp = {NULL};
+ struct mmc_tlp_block cmd = {0}, resp = {0};
+ int i;
+ u8 gd = 0, gap;
+ u16 ioadr = UHSII_IOADR(SD40_IOADR_CMD_BASE, SD40_DEVICE_INIT);
+ u32 payload;
+
+ payload = SD40_CF | SD40_GAP(host->max_gap) | SD40_DAP(host->max_dap);
+
+ mrq.tlp = &tlp;
+ tlp.tlp_send = &cmd;
+ tlp.tlp_back = &resp;
+ tlp.retries = 0;
+
+ tlp.tlp_send->header = UHSII_HD_NP | UHSII_HD_TYP_CCMD;
+ tlp.tlp_send->argument = UHSII_ARG_DIR_WRITE |
+ (1 << UHSII_ARG_PLEN_SHIFT) | (ioadr & UHSII_ARG_IOADR_MASK);
+
+ for (i = 0; i < 30; i++) {
+ tlp.tlp_send->payload[0] = payload;
+
+ mmc_wait_for_req(host, &mrq);
+
+ if (tlp.error)
+ return tlp.error;
+
+ if (tlp.tlp_back->payload[0] & SD40_CF)
+ return 0;
+
+ gap = UHSII_GET_GAP(tlp.tlp_back);
+ if (gap == host->max_gap) {
+ gd++;
+ payload |= (gd & SD40_GD_MASK) << SD40_GD_SHIFT;
+ }
+ }
+
+ return -ETIMEDOUT;
+}
+
+int mmc_sd_send_enumerate_ccmd(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ struct mmc_request mrq = {NULL};
+ struct mmc_tlp tlp = {NULL};
+ struct mmc_tlp_block cmd = {0}, resp = {0};
+ u16 ioadr = UHSII_IOADR(SD40_IOADR_CMD_BASE, SD40_ENUMERATE);
+
+ mrq.tlp = &tlp;
+ tlp.tlp_send = &cmd;
+ tlp.tlp_back = &resp;
+ tlp.retries = 0;
+
+ tlp.tlp_send->header = UHSII_HD_NP | UHSII_HD_TYP_CCMD;
+ tlp.tlp_send->argument = UHSII_ARG_DIR_WRITE |
+ (1 << UHSII_ARG_PLEN_SHIFT) | (ioadr & UHSII_ARG_IOADR_MASK);
+ tlp.tlp_send->payload[0] = 0;
+
+ mmc_wait_for_req(host, &mrq);
+
+ if (tlp.error)
+ return tlp.error;
+
+ card->node_id = (tlp.tlp_back->payload[0] & SD40_IDL_MASK)
+ >> SD40_IDL_SHIFT;
+
+ return 0;
+}
+
+int mmc_sd_send_go_dormant_state_ccmd(struct mmc_card *card, int hibernate)
+{
+ struct mmc_host *host = card->host;
+ struct mmc_request mrq = {NULL};
+ struct mmc_tlp tlp = {NULL};
+ struct mmc_tlp_block cmd = {0}, resp = {0};
+ u16 ioadr = UHSII_IOADR(SD40_IOADR_CMD_BASE, SD40_GO_DORMANT_STATE);
+
+ mrq.tlp = &tlp;
+ tlp.tlp_send = &cmd;
+ tlp.tlp_back = &resp;
+ tlp.retries = 0;
+ tlp.cmd_type = UHSII_COMMAND_GO_DORMANT;
+
+ tlp.tlp_send->header = UHSII_HD_NP | UHSII_HD_TYP_CCMD |
+ UHSII_HD_DID(card->node_id);
+ tlp.tlp_send->argument = UHSII_ARG_DIR_WRITE |
+ (1 << UHSII_ARG_PLEN_SHIFT) | (ioadr & UHSII_ARG_IOADR_MASK);
+ if (hibernate)
+ tlp.tlp_send->payload[0] = 0x80000000;
+ else
+ tlp.tlp_send->payload[0] = 0;
+
+ mmc_wait_for_req(host, &mrq);
+
+ if (tlp.error)
+ return tlp.error;
+
+ return 0;
+}
+
+int mmc_sd_read_cfg_ccmd(struct mmc_card *card, u8 offset, u8 plen, u32 *buf)
+{
+ struct mmc_host *host = card->host;
+ struct mmc_request mrq = {NULL};
+ struct mmc_tlp tlp = {NULL};
+ struct mmc_tlp_block cmd = {0}, resp = {0};
+ u16 ioadr = UHSII_IOADR(SD40_IOADR_CFG_BASE, offset);
+ int i;
+
+ mrq.tlp = &tlp;
+ tlp.tlp_send = &cmd;
+ tlp.tlp_back = &resp;
+ tlp.retries = 0;
+
+ tlp.tlp_send->header = UHSII_HD_NP | UHSII_HD_TYP_CCMD |
+ UHSII_HD_DID(card->node_id);
+ tlp.tlp_send->argument = UHSII_ARG_DIR_READ |
+ (plen << UHSII_ARG_PLEN_SHIFT) |
+ (ioadr & UHSII_ARG_IOADR_MASK);
+
+ mmc_wait_for_req(host, &mrq);
+
+ if (tlp.error)
+ return tlp.error;
+
+ for (i = 0; i < plen; i++)
+ buf[i] = tlp.tlp_back->payload[i];
+
+ return 0;
+}
+
+int mmc_sd_write_cfg_ccmd(struct mmc_card *card, u8 offset, u8 plen, u32 *buf)
+{
+ struct mmc_host *host = card->host;
+ struct mmc_request mrq = {NULL};
+ struct mmc_tlp tlp = {NULL};
+ struct mmc_tlp_block cmd = {0}, resp = {0};
+ u16 ioadr = UHSII_IOADR(SD40_IOADR_CFG_BASE, offset);
+ int i;
+
+ mrq.tlp = &tlp;
+ tlp.tlp_send = &cmd;
+ tlp.tlp_back = &resp;
+ tlp.retries = 0;
+
+ tlp.tlp_send->header = UHSII_HD_NP | UHSII_HD_TYP_CCMD |
+ UHSII_HD_DID(card->node_id);
+ tlp.tlp_send->argument = UHSII_ARG_DIR_WRITE |
+ (plen << UHSII_ARG_PLEN_SHIFT) |
+ (ioadr & UHSII_ARG_IOADR_MASK);
+ for (i = 0; i < plen; i++)
+ tlp.tlp_send->payload[i] = buf[i];
+
+ mmc_wait_for_req(host, &mrq);
+
+ if (tlp.error)
+ return tlp.error;
+
+ return 0;
+}
+
+void mmc_sd_tran_pack_ccmd(struct mmc_card *card, struct mmc_command *cmd)
+{
+ struct mmc_tlp_block *tlp_send = &cmd->tlp_send;
+
+ tlp_send->header = UHSII_HD_TYP_CCMD | UHSII_HD_DID(card->node_id);
+
+ tlp_send->argument = cmd->opcode & UHSII_ARG_CMD_INDEX_MASK;
+ if (cmd->app_cmd)
+ tlp_send->argument |= UHSII_ARG_APP_CMD;
+
+ tlp_send->payload[0] = cmd->arg;
+
+ pr_debug("%s: SDTRAN CCMD header = 0x%04x, arg = 0x%04x\n",
+ mmc_hostname(card->host), tlp_send->header, tlp_send->argument);
+}
+
+void mmc_sd_tran_pack_dcmd(struct mmc_card *card, struct mmc_command *cmd)
+{
+ u8 tmode = 0;
+ u32 tlen = 0;
+ struct mmc_tlp_block *tlp_send = &cmd->tlp_send;
+
+ tlp_send->header = UHSII_HD_TYP_DCMD | UHSII_HD_DID(card->node_id);
+
+ tlp_send->argument = cmd->opcode & UHSII_ARG_CMD_INDEX_MASK;
+ if (cmd->app_cmd)
+ tlp_send->argument |= UHSII_ARG_APP_CMD;
+ if (cmd->data && (cmd->data->flags & MMC_DATA_WRITE))
+ tlp_send->argument |= UHSII_ARG_DIR_WRITE;
+ if (mmc_op_multi(cmd->opcode)) {
+ tmode |= UHSII_TMODE_LM_SPECIFIED;
+ if (card->lane_mode & SD40_LANE_MODE_2L_HD)
+ tmode |= UHSII_TMODE_DM_HD;
+ if (cmd->data)
+ tlen = cmd->data->blocks;
+ }
+ tlp_send->argument |= tmode << UHSII_ARG_TMODE_SHIFT;
+
+ tlp_send->payload[0] = cmd->arg;
+ tlp_send->payload[1] = tlen;
+
+ pr_debug("%s: SDTRAN DCMD header = 0x%04x, arg = 0x%04x, TLEN = %d\n",
+ mmc_hostname(card->host), tlp_send->header, tlp_send->argument,
+ tlen);
+}
diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
index ffc2305..00aea01 100644
--- a/drivers/mmc/core/sd_ops.h
+++ b/drivers/mmc/core/sd_ops.h
@@ -20,6 +20,13 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp);
int mmc_app_sd_status(struct mmc_card *card, void *ssr);
+int mmc_sd_send_device_init_ccmd(struct mmc_card *card);
+int mmc_sd_send_enumerate_ccmd(struct mmc_card *card);
+int mmc_sd_send_go_dormant_state_ccmd(struct mmc_card *card, int hibernate);
+int mmc_sd_read_cfg_ccmd(struct mmc_card *card, u8 offset, u8 plen, u32 *buf);
+int mmc_sd_write_cfg_ccmd(struct mmc_card *card, u8 offset, u8 plen, u32 *buf);
+void mmc_sd_tran_pack_ccmd(struct mmc_card *card, struct mmc_command *cmd);
+void mmc_sd_tran_pack_dcmd(struct mmc_card *card, struct mmc_command *cmd);

#endif

--
1.9.1

2015-04-29 01:34:29

by 敬锐

[permalink] [raw]
Subject: [PATCH 04/12] mmc: core: add tlp request handler for SD4.0

From: Micky Ching <[email protected]>

when card is work in SD4.0 mode, we should send tlp instead of cmd.
add this function to handle tlp request.

Signed-off-by: Micky Ching <[email protected]>
Signed-off-by: Wei Wang <[email protected]>
---
drivers/mmc/core/core.c | 111 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 104 insertions(+), 7 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index c296bc0..ebb6fea 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -45,6 +45,7 @@
#include "mmc_ops.h"
#include "sd_ops.h"
#include "sdio_ops.h"
+#include "sd.h"

/* If the device is not responding */
#define MMC_CORE_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
@@ -131,7 +132,42 @@ static inline void mmc_should_fail_request(struct mmc_host *host,
void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
struct mmc_command *cmd = mrq->cmd;
- int err = cmd->error;
+ struct mmc_tlp *tlp = mrq->tlp;
+ int err;
+
+ if (tlp && cmd)
+ pr_debug("%s: cmd and native tlp conflict, done native tlp\n",
+ mmc_hostname(host));
+
+ if (tlp) {
+ err = tlp->error;
+ if (err && tlp->retries && !mmc_card_removed(host->card)) {
+ /*
+ * Request starter must handle retries - see
+ * mmc_wait_for_req_done().
+ */
+ if (mrq->done)
+ mrq->done(mrq);
+ } else {
+ led_trigger_event(host->led, LED_OFF);
+
+ pr_debug("%s: native TLP req done(%d) %04x %04x %08x %08x %08x %08x\n",
+ mmc_hostname(host), err,
+ tlp->tlp_back->header, tlp->tlp_back->argument,
+ tlp->tlp_back->payload[0],
+ tlp->tlp_back->payload[1],
+ tlp->tlp_back->payload[2],
+ tlp->tlp_back->payload[3]);
+
+ if (mrq->done)
+ mrq->done(mrq);
+
+ mmc_host_clk_release(host);
+ }
+ return;
+ }
+
+ err = cmd->error;

if (err && cmd->retries && mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
@@ -201,9 +237,18 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
mrq->sbc->arg, mrq->sbc->flags);
}

- pr_debug("%s: starting CMD%u arg %08x flags %08x\n",
- mmc_hostname(host), mrq->cmd->opcode,
- mrq->cmd->arg, mrq->cmd->flags);
+ if (mrq->tlp && mrq->cmd)
+ pr_debug("%s: cmd and native tlp conflict, start native tlp\n",
+ mmc_hostname(host));
+
+ if (mrq->tlp)
+ pr_debug("%s: starting native TLP header %04x argument %04x\n",
+ mmc_hostname(host), mrq->tlp->tlp_send->header,
+ mrq->tlp->tlp_send->argument);
+ else if (mrq->cmd)
+ pr_debug("%s: starting CMD%u arg %08x flags %08x\n",
+ mmc_hostname(host), mrq->cmd->opcode,
+ mrq->cmd->arg, mrq->cmd->flags);

if (mrq->data) {
pr_debug("%s: blksz %d blocks %d flags %08x "
@@ -221,6 +266,8 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
}

WARN_ON(!host->claimed);
+ if (!mrq->cmd)
+ goto start;

mrq->cmd->error = 0;
mrq->cmd->mrq = mrq;
@@ -250,6 +297,25 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
mrq->stop->mrq = mrq;
}
}
+
+ if (mmc_card_uhsii(host->card)) {
+ mrq->cmd->use_tlp = true;
+ if (mrq->data)
+ mmc_sd_tran_pack_dcmd(host->card, mrq->cmd);
+ else
+ mmc_sd_tran_pack_ccmd(host->card, mrq->cmd);
+
+ if (mrq->sbc) {
+ mrq->sbc->use_tlp = true;
+ mmc_sd_tran_pack_ccmd(host->card, mrq->sbc);
+ }
+ if (mrq->stop) {
+ mrq->stop->use_tlp = true;
+ mmc_sd_tran_pack_ccmd(host->card, mrq->stop);
+ }
+ }
+
+start:
mmc_host_clk_hold(host);
led_trigger_event(host->led, LED_FULL);
host->ops->request(host, mrq);
@@ -356,7 +422,7 @@ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)

err = mmc_start_request(host, mrq);
if (err) {
- mrq->cmd->error = err;
+ mmc_set_mrq_error_code(mrq, err);
mmc_wait_data_done(mrq);
}

@@ -372,7 +438,7 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)

err = mmc_start_request(host, mrq);
if (err) {
- mrq->cmd->error = err;
+ mmc_set_mrq_error_code(mrq, err);
complete(&mrq->completion);
}

@@ -436,7 +502,7 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
return err;
}

-static void mmc_wait_for_req_done(struct mmc_host *host,
+static void mmc_wait_for_cmd_req_done(struct mmc_host *host,
struct mmc_request *mrq)
{
struct mmc_command *cmd;
@@ -475,6 +541,37 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
}
}

+static void mmc_wait_for_tlp_req_done(struct mmc_host *host,
+ struct mmc_request *mrq)
+{
+ struct mmc_tlp *tlp;
+
+ while (1) {
+ wait_for_completion(&mrq->completion);
+
+ tlp = mrq->tlp;
+
+ if (!tlp->error || !tlp->retries ||
+ mmc_card_removed(host->card))
+ break;
+
+ pr_debug("%s: native TLP req failed: %d, retrying...\n",
+ mmc_hostname(host), tlp->error);
+ tlp->retries--;
+ tlp->error = 0;
+ host->ops->request(host, mrq);
+ }
+}
+
+static void mmc_wait_for_req_done(struct mmc_host *host,
+ struct mmc_request *mrq)
+{
+ if (mrq->cmd)
+ mmc_wait_for_cmd_req_done(host, mrq);
+ else
+ mmc_wait_for_tlp_req_done(host, mrq);
+}
+
/**
* mmc_pre_req - Prepare for a new request
* @host: MMC host to prepare command
--
1.9.1

2015-04-29 01:34:32

by 敬锐

[permalink] [raw]
Subject: [PATCH 05/12] mmc: core: disable full power cycle for SD4.0

From: Micky Ching <[email protected]>

We should not do power cycle when card is in SD4.0 mode,
if we power off, we should detect UHSII interface again.
so we can disable it when card is in UHSII mode.

Signed-off-by: Micky Ching <[email protected]>
Signed-off-by: Wei Wang <[email protected]>
---
drivers/mmc/core/core.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index ebb6fea..4c5433b 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1550,7 +1550,8 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
return 0;
}

- if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
+ if (!mmc_card_uhsii(host->card) &&
+ (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE)) {
bit = ffs(ocr) - 1;
ocr &= 3 << bit;
mmc_power_cycle(host, ocr);
--
1.9.1

2015-04-29 01:34:36

by 敬锐

[permalink] [raw]
Subject: [PATCH 06/12] mmc: core: init SD4.0 mode before legacy mode

From: Micky Ching <[email protected]>

We alloc card before init card, if init UHSII mode failed, then
try to init legacy mode.

Since we card is allocated before do init operations, so mmc/sdio
card init should do some modify. To reduce many diff hunks, the old
labels are reserved(we can remove them in the future).

Signed-off-by: Micky Ching <[email protected]>
Signed-off-by: Wei Wang <[email protected]>
---
drivers/mmc/core/core.c | 60 +++++++++++++++++++++-------
drivers/mmc/core/mmc.c | 63 +++++++++++++----------------
drivers/mmc/core/sd.c | 95 +++++++++++++++++++++++---------------------
drivers/mmc/core/sdio.c | 103 +++++++++++++++++++++---------------------------
4 files changed, 169 insertions(+), 152 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 4c5433b..d9e904f 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2435,16 +2435,8 @@ int mmc_hw_reset(struct mmc_host *host)
}
EXPORT_SYMBOL(mmc_hw_reset);

-static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
+static int mmc_reset_card(struct mmc_host *host)
{
- host->f_init = freq;
-
-#ifdef CONFIG_MMC_DEBUG
- pr_info("%s: %s: trying to init card at %u Hz\n",
- mmc_hostname(host), __func__, host->f_init);
-#endif
- mmc_power_up(host, host->ocr_avail);
-
/*
* Some eMMCs (with VCCQ always on) may not be reset after power up, so
* do a hardware reset if possible.
@@ -2473,6 +2465,20 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
return -EIO;
}

+static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
+{
+ host->f_init = freq;
+
+#ifdef CONFIG_MMC_DEBUG
+ pr_info("%s: %s: trying to init card at %u Hz\n",
+ mmc_hostname(host), __func__, host->f_init);
+#endif
+
+ mmc_power_up(host, host->ocr_avail);
+
+ return mmc_reset_card(host);
+}
+
int _mmc_detect_card_removed(struct mmc_host *host)
{
int ret;
@@ -2544,7 +2550,8 @@ void mmc_rescan(struct work_struct *work)
{
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
- int i;
+ struct mmc_card *card;
+ int i, err;

if (host->trigger_card_event && host->ops->card_event) {
host->ops->card_event(host);
@@ -2599,14 +2606,37 @@ void mmc_rescan(struct work_struct *work)
}

mmc_claim_host(host);
- for (i = 0; i < ARRAY_SIZE(freqs); i++) {
- if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
- break;
- if (freqs[i] <= host->f_min)
- break;
+
+ card = mmc_alloc_card(host, NULL);
+ if (IS_ERR(card)) {
+ mmc_release_host(host);
+ goto out;
}
+ host->card = card;
+
+ mmc_sd_init_uhsii_card(card);
+
+ if (mmc_card_uhsii(card)) {
+ err = mmc_reset_card(host);
+ } else {
+ for (i = 0; i < ARRAY_SIZE(freqs); i++) {
+ err = mmc_rescan_try_freq(host,
+ max(freqs[i], host->f_min));
+ if (!err)
+ break;
+ if (freqs[i] <= host->f_min)
+ break;
+ }
+ }
+
mmc_release_host(host);

+ /* Attach fail, free host->card */
+ if (err) {
+ mmc_remove_card(host->card);
+ host->card = NULL;
+ }
+
out:
if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index f36c76f..ddb8831 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1187,14 +1187,10 @@ static int mmc_hs200_tuning(struct mmc_card *card)

/*
* Handle the detection and initialisation of a card.
- *
- * In the case of a resume, "oldcard" will contain the card
- * we're trying to reinitialise.
*/
-static int mmc_init_card(struct mmc_host *host, u32 ocr,
- struct mmc_card *oldcard)
+static int mmc_do_init_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *card, bool reinit)
{
- struct mmc_card *card;
int err;
u32 cid[4];
u32 rocr;
@@ -1239,23 +1235,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
if (err)
goto err;

- if (oldcard) {
- if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
+ if (reinit) {
+ if (memcmp(cid, card->raw_cid, sizeof(cid)) != 0) {
err = -ENOENT;
goto err;
}
-
- card = oldcard;
} else {
- /*
- * Allocate card structure.
- */
- card = mmc_alloc_card(host, &mmc_type);
- if (IS_ERR(card)) {
- err = PTR_ERR(card);
- goto err;
- }
-
+ card->dev.type = &mmc_type;
card->ocr = ocr;
card->type = MMC_TYPE_MMC;
card->rca = 1;
@@ -1279,7 +1265,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}

- if (!oldcard) {
+ if (!reinit) {
/*
* Fetch CSD from card.
*/
@@ -1311,7 +1297,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
goto free_card;
}

- if (!oldcard) {
+ if (!reinit) {
/* Read extended CSD. */
err = mmc_read_ext_csd(card);
if (err)
@@ -1487,18 +1473,25 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
}

- if (!oldcard)
- host->card = card;
-
return 0;

free_card:
- if (!oldcard)
- mmc_remove_card(card);
err:
return err;
}

+static inline int mmc_init_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *card)
+{
+ return mmc_do_init_card(host, ocr, card, false);
+}
+
+static inline int mmc_reinit_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *card)
+{
+ return mmc_do_init_card(host, ocr, card, true);
+}
+
static int mmc_can_sleep(struct mmc_card *card)
{
return (card && card->ext_csd.rev >= 3);
@@ -1700,7 +1693,7 @@ static int _mmc_resume(struct mmc_host *host)
goto out;

mmc_power_up(host, host->card->ocr);
- err = mmc_init_card(host, host->card->ocr, host->card);
+ err = mmc_reinit_card(host, host->card->ocr, host->card);
mmc_card_clr_suspended(host->card);

out:
@@ -1787,7 +1780,7 @@ static int mmc_power_restore(struct mmc_host *host)
int ret;

mmc_claim_host(host);
- ret = mmc_init_card(host, host->card->ocr, host->card);
+ ret = mmc_reinit_card(host, host->card->ocr, host->card);
mmc_release_host(host);

return ret;
@@ -1851,12 +1844,15 @@ static const struct mmc_bus_ops mmc_ops = {
*/
int mmc_attach_mmc(struct mmc_host *host)
{
+ struct mmc_card *card;
int err;
u32 ocr, rocr;

- BUG_ON(!host);
+ BUG_ON(!host || !host->card);
WARN_ON(!host->claimed);

+ card = host->card;
+
/* Set correct bus mode for MMC before attempting attach */
if (!mmc_host_is_spi(host))
mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
@@ -1891,7 +1887,7 @@ int mmc_attach_mmc(struct mmc_host *host)
/*
* Detect and init the card.
*/
- err = mmc_init_card(host, rocr, NULL);
+ err = mmc_init_card(host, rocr, card);
if (err)
goto err;

@@ -1899,15 +1895,10 @@ int mmc_attach_mmc(struct mmc_host *host)
err = mmc_add_card(host->card);
mmc_claim_host(host);
if (err)
- goto remove_card;
+ goto err;

return 0;

-remove_card:
- mmc_release_host(host);
- mmc_remove_card(host->card);
- mmc_claim_host(host);
- host->card = NULL;
err:
mmc_detach_bus(host);

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 8dd35d9..5c00e23 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -863,12 +863,12 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
/* Erase init depends on CSD and SSR */
mmc_init_erase(card);

- /*
- * Fetch switch information from card.
- */
- err = mmc_read_switch(card);
- if (err)
- return err;
+ if (!mmc_card_uhsii(card)) {
+ /* Fetch switch information from card. */
+ err = mmc_read_switch(card);
+ if (err)
+ return err;
+ }
}

/*
@@ -922,14 +922,10 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card)

/*
* Handle the detection and initialisation of a card.
- *
- * In the case of a resume, "oldcard" will contain the card
- * we're trying to reinitialise.
*/
-static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
- struct mmc_card *oldcard)
+static int mmc_sd_do_init_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *card, bool reinit)
{
- struct mmc_card *card;
int err;
u32 cid[4];
u32 rocr = 0;
@@ -941,19 +937,11 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
if (err)
return err;

- if (oldcard) {
- if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
+ if (reinit) {
+ if (memcmp(cid, card->raw_cid, sizeof(cid)) != 0)
return -ENOENT;
-
- card = oldcard;
} else {
- /*
- * Allocate card structure.
- */
- card = mmc_alloc_card(host, &sd_type);
- if (IS_ERR(card))
- return PTR_ERR(card);
-
+ card->dev.type = &sd_type;
card->ocr = ocr;
card->type = MMC_TYPE_SD;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
@@ -974,7 +962,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
goto free_card;
}

- if (!oldcard) {
+ if (!reinit) {
err = mmc_sd_get_csd(host, card);
if (err)
goto free_card;
@@ -998,10 +986,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
goto free_card;
}

- err = mmc_sd_setup_card(host, card, oldcard != NULL);
+ err = mmc_sd_setup_card(host, card, reinit);
if (err)
goto free_card;

+ if (mmc_card_uhsii(card))
+ return 0;
+
/* Initialization sequence for UHS-I cards */
if (rocr & SD_ROCR_S18A) {
err = mmc_sd_init_uhs_card(card);
@@ -1035,16 +1026,23 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
}
}

- host->card = card;
return 0;
-
free_card:
- if (!oldcard)
- mmc_remove_card(card);
-
return err;
}

+static inline int mmc_sd_reinit_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *card)
+{
+ return mmc_sd_do_init_card(host, ocr, card, true);
+}
+
+static inline int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *card)
+{
+ return mmc_sd_do_init_card(host, ocr, card, false);
+}
+
/*
* Host is being removed. Free up the current card.
*/
@@ -1151,11 +1149,16 @@ static int _mmc_sd_resume(struct mmc_host *host)
if (!mmc_card_suspended(host->card))
goto out;

- mmc_power_up(host, host->card->ocr);
- err = mmc_sd_init_card(host, host->card->ocr, host->card);
- mmc_card_clr_suspended(host->card);
+ if (mmc_card_uhsii(host->card))
+ mmc_sd_init_uhsii_card(host->card);
+
+ if (!mmc_card_uhsii(host->card))
+ mmc_power_up(host, host->card->ocr);
+
+ err = mmc_sd_reinit_card(host, host->card->ocr, host->card);

out:
+ mmc_card_clr_suspended(host->card);
mmc_release_host(host);
return err;
}
@@ -1218,7 +1221,7 @@ static int mmc_sd_power_restore(struct mmc_host *host)
int ret;

mmc_claim_host(host);
- ret = mmc_sd_init_card(host, host->card->ocr, host->card);
+ ret = mmc_sd_reinit_card(host, host->card->ocr, host->card);
mmc_release_host(host);

return ret;
@@ -1405,13 +1408,22 @@ int mmc_attach_sd(struct mmc_host *host)
{
int err;
u32 ocr, rocr;
+ struct mmc_card *card;

- BUG_ON(!host);
+ BUG_ON(!host || !host->card);
WARN_ON(!host->claimed);

+ card = host->card;
+
+ /*
+ * some sd4.0 card may fail to response ACMD41 with arg=0.
+ */
err = mmc_send_app_op_cond(host, 0, &ocr);
- if (err)
- return err;
+ if (err) {
+ ocr = MMC_VDD_32_33 | MMC_VDD_33_34;
+ pr_debug("%s: using default ocr = 0x%08x\n",
+ mmc_hostname(host), ocr);
+ }

mmc_attach_bus(host, &mmc_sd_ops);
if (host->ocr_avail_sd)
@@ -1441,7 +1453,7 @@ int mmc_attach_sd(struct mmc_host *host)
/*
* Detect and init the card.
*/
- err = mmc_sd_init_card(host, rocr, NULL);
+ err = mmc_sd_init_card(host, rocr, card);
if (err)
goto err;

@@ -1449,15 +1461,10 @@ int mmc_attach_sd(struct mmc_host *host)
err = mmc_add_card(host->card);
mmc_claim_host(host);
if (err)
- goto remove_card;
+ goto err;

return 0;

-remove_card:
- mmc_release_host(host);
- mmc_remove_card(host->card);
- host->card = NULL;
- mmc_claim_host(host);
err:
mmc_detach_bus(host);

diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 5bc6c7d..d74f42b 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -575,18 +575,17 @@ out:

/*
* Handle the detection and initialisation of a card.
- *
- * In the case of a resume, "oldcard" will contain the card
- * we're trying to reinitialise.
*/
-static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
- struct mmc_card *oldcard, int powered_resume)
+static int mmc_sdio_do_init_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *card, int powered_resume, bool reinit)
{
- struct mmc_card *card;
int err;
+ unsigned short old_vendor = 0;
+ unsigned short old_device = 0;
int retries = 10;
u32 rocr = 0;
u32 ocr_card = ocr;
+ u32 raw_cid[4];

BUG_ON(!host);
WARN_ON(!host->claimed);
@@ -619,31 +618,20 @@ try_again:
goto err;
}

- /*
- * Allocate card structure.
- */
- card = mmc_alloc_card(host, NULL);
- if (IS_ERR(card)) {
- err = PTR_ERR(card);
- goto err;
- }
-
if ((rocr & R4_MEMORY_PRESENT) &&
- mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
+ mmc_sd_get_cid(host, ocr & rocr, raw_cid, NULL) == 0) {
card->type = MMC_TYPE_SD_COMBO;
-
- if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
- memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
- mmc_remove_card(card);
- return -ENOENT;
+ if (reinit) {
+ int cmp_cid = memcmp(raw_cid, card->raw_cid,
+ sizeof(raw_cid));
+ if (cmp_cid != 0 || card->type != MMC_TYPE_SD_COMBO)
+ return -ENOENT;
}
} else {
- card->type = MMC_TYPE_SDIO;
-
- if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
- mmc_remove_card(card);
+ if (reinit && card->type != MMC_TYPE_SDIO)
return -ENOENT;
- }
+
+ card->type = MMC_TYPE_SDIO;
}

/*
@@ -666,7 +654,6 @@ try_again:
sdio_reset(host);
mmc_go_idle(host);
mmc_send_if_cond(host, host->ocr_avail);
- mmc_remove_card(card);
retries--;
goto try_again;
} else if (err) {
@@ -684,20 +671,12 @@ try_again:
err = mmc_send_relative_addr(host, &card->rca);
if (err)
goto remove;
-
- /*
- * Update oldcard with the new RCA received from the SDIO
- * device -- we're doing this so that it's updated in the
- * "card" struct when oldcard overwrites that later.
- */
- if (oldcard)
- oldcard->rca = card->rca;
}

/*
* Read CSD, before selecting the card
*/
- if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
+ if (!reinit && card->type == MMC_TYPE_SD_COMBO) {
err = mmc_sd_get_csd(host, card);
if (err)
return err;
@@ -737,6 +716,11 @@ try_again:
if (err)
goto remove;

+ if (reinit) {
+ old_vendor = card->cis.vendor;
+ old_device = card->cis.device;
+ }
+
/*
* Read the common CIS tuples.
*/
@@ -744,20 +728,17 @@ try_again:
if (err)
goto remove;

- if (oldcard) {
- int same = (card->cis.vendor == oldcard->cis.vendor &&
- card->cis.device == oldcard->cis.device);
- mmc_remove_card(card);
+ if (reinit) {
+ int same = (card->cis.vendor == old_vendor &&
+ card->cis.device == old_device);
if (!same)
return -ENOENT;
-
- card = oldcard;
}
card->ocr = ocr_card;
mmc_fixup_device(card, NULL);

if (card->type == MMC_TYPE_SD_COMBO) {
- err = mmc_sd_setup_card(host, card, oldcard != NULL);
+ err = mmc_sd_setup_card(host, card, reinit);
/* handle as SDIO-only card if memory init failed */
if (err) {
mmc_go_idle(host);
@@ -776,6 +757,9 @@ try_again:
if (err)
goto remove;

+ if (mmc_card_uhsii(card))
+ return err;
+
/* Initialization sequence for UHS-I cards */
/* Only if card supports 1.8v and UHS signaling */
if ((ocr & R4_18V_PRESENT) && card->sw_caps.sd3_bus_mode) {
@@ -805,18 +789,23 @@ try_again:
goto remove;
}
finish:
- if (!oldcard)
- host->card = card;
- return 0;
-
remove:
- if (!oldcard)
- mmc_remove_card(card);
-
err:
return err;
}

+static inline int mmc_sdio_reinit_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *card, int powered_resume)
+{
+ return mmc_sdio_do_init_card(host, ocr, card, powered_resume, true);
+}
+
+static inline int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *card, int powered_resume)
+{
+ return mmc_sdio_do_init_card(host, ocr, card, powered_resume, false);
+}
+
/*
* Host is being removed. Free up the current card.
*/
@@ -973,7 +962,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
mmc_send_if_cond(host, host->card->ocr);
err = mmc_send_io_op_cond(host, 0, NULL);
if (!err)
- err = mmc_sdio_init_card(host, host->card->ocr,
+ err = mmc_sdio_reinit_card(host, host->card->ocr,
host->card,
mmc_card_keep_power(host));
} else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
@@ -1031,8 +1020,8 @@ static int mmc_sdio_power_restore(struct mmc_host *host)
if (ret)
goto out;

- ret = mmc_sdio_init_card(host, host->card->ocr, host->card,
- mmc_card_keep_power(host));
+ ret = mmc_sdio_reinit_card(host, host->card->ocr, host->card,
+ mmc_card_keep_power(host));
if (!ret && host->sdio_irqs)
mmc_signal_sdio_irq(host);

@@ -1078,9 +1067,11 @@ int mmc_attach_sdio(struct mmc_host *host)
u32 ocr, rocr;
struct mmc_card *card;

- BUG_ON(!host);
+ BUG_ON(!host || !host->card);
WARN_ON(!host->claimed);

+ card = host->card;
+
err = mmc_send_io_op_cond(host, 0, &ocr);
if (err)
return err;
@@ -1103,12 +1094,10 @@ int mmc_attach_sdio(struct mmc_host *host)
/*
* Detect and init the card.
*/
- err = mmc_sdio_init_card(host, rocr, NULL, 0);
+ err = mmc_sdio_init_card(host, rocr, card, 0);
if (err)
goto err;

- card = host->card;
-
/*
* Enable runtime PM only if supported by host+card+board
*/
--
1.9.1

2015-04-29 01:36:18

by 敬锐

[permalink] [raw]
Subject: [PATCH 07/12] mmc: sdhci: add data structure for SD4.0

From: Micky Ching <[email protected]>

add SD4.0 register define and host data structure for
handshake with SD4.0 card.

Signed-off-by: Micky Ching <[email protected]>
Signed-off-by: Wei Wang <[email protected]>
---
drivers/mmc/host/sdhci.h | 136 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 135 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index e639b7f..659eb64 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -73,6 +73,9 @@
#define SDHCI_DATA_LVL_MASK 0x00F00000
#define SDHCI_DATA_LVL_SHIFT 20
#define SDHCI_DATA_0_LVL_MASK 0x00100000
+#define SDHCI_IN_DORMANT_STATE 0x20000000
+#define SDHCI_LANE_SYNC 0x40000000
+#define SDHCI_STBL_DETECT 0x80000000

#define SDHCI_HOST_CONTROL 0x28
#define SDHCI_CTRL_LED 0x01
@@ -90,6 +93,8 @@
#define SDHCI_POWER_180 0x0A
#define SDHCI_POWER_300 0x0C
#define SDHCI_POWER_330 0x0E
+#define SDHCI_VDD1_SHIFT 0
+#define SDHCI_VDD2_SHIFT 4

#define SDHCI_BLOCK_GAP_CONTROL 0x2A

@@ -162,6 +167,7 @@
#define SDHCI_CTRL_UHS_SDR104 0x0003
#define SDHCI_CTRL_UHS_DDR50 0x0004
#define SDHCI_CTRL_HS400 0x0005 /* Non-standard */
+#define SDHCI_CTRL_UHSII 0x0007
#define SDHCI_CTRL_VDD_180 0x0008
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
@@ -170,6 +176,10 @@
#define SDHCI_CTRL_DRV_TYPE_D 0x0030
#define SDHCI_CTRL_EXEC_TUNING 0x0040
#define SDHCI_CTRL_TUNED_CLK 0x0080
+#define SDHCI_CTRL_UHSII_IF_ENABLE 0x0100
+#define SDHCI_CTRL_HOST_V4_ENABLE 0x1000
+#define SDHCI_CTRL_64BIT_ADDR_ENABLE 0x2000
+#define SDHCI_CTRL_ASYNC_INT_ENABLE 0x4000
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000

#define SDHCI_CAPABILITIES 0x40
@@ -194,6 +204,7 @@
#define SDHCI_SUPPORT_SDR50 0x00000001
#define SDHCI_SUPPORT_SDR104 0x00000002
#define SDHCI_SUPPORT_DDR50 0x00000004
+#define SDHCI_SUPPORT_UHSII 0x00000008
#define SDHCI_DRIVER_TYPE_A 0x00000010
#define SDHCI_DRIVER_TYPE_C 0x00000020
#define SDHCI_DRIVER_TYPE_D 0x00000040
@@ -205,6 +216,8 @@
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
#define SDHCI_CLOCK_MUL_SHIFT 16
#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
+#define SDHCI_CAN_DO_ADMA3 0x08000000
+#define SDHCI_CAN_VDD2_180 0x10000000

#define SDHCI_CAPABILITIES_1 0x44

@@ -230,7 +243,7 @@
#define SDHCI_ADMA_ADDRESS 0x58
#define SDHCI_ADMA_ADDRESS_HI 0x5C

-/* 60-FB reserved */
+/* 60-73 reserved */

#define SDHCI_PRESET_FOR_SDR12 0x66
#define SDHCI_PRESET_FOR_SDR25 0x68
@@ -245,6 +258,71 @@
#define SDHCI_PRESET_SDCLK_FREQ_MASK 0x3FF
#define SDHCI_PRESET_SDCLK_FREQ_SHIFT 0

+#define SDHCI_PRESET_VALUE_UHSII 0x74
+#define SDHCI_ADMA3_ID_ADDRESS 0x78
+
+#define SDHCI_UHSII_BLOCK_SIZE 0x80
+#define SDHCI_UHSII_BLOCK_COUNT 0x84
+
+#define SDHCI_UHSII_CMD_PACKET 0x88
+#define SDHCI_UHSII_CMD_PACK_LEN 20
+#define SDHCI_UHSII_CMD_HEADER (SDHCI_UHSII_CMD_PACKET)
+#define SDHCI_UHSII_CMD_ARGUMENT (SDHCI_UHSII_CMD_PACKET + 2)
+#define SDHCI_UHSII_CMD_PAYLOAD (SDHCI_UHSII_CMD_PACKET + 4)
+
+#define SDHCI_UHSII_TRANSFER_MODE 0x9C
+#define SDHCI_UHSII_TRNS_DMA 0x01
+#define SDHCI_UHSII_TRNS_BLK_CNT_EN 0x02
+#define SDHCI_UHSII_TRNS_WRITE 0x10
+#define SDHCI_UHSII_TRNS_BYTE_MODE 0x20
+#define SDHCI_UHSII_TRNS_WAIT_EBSY 0x4000
+#define SDHCI_UHSII_TRANS_2LANE_HD 0x8000
+#define SDHCI_UHSII_COMMAND 0x9E
+#define SDHCI_UHSII_DATA_PRESENT 0x0020
+#define SDHCI_UHSII_NORMAL_COMMAND (0 << 6)
+#define SDHCI_UHSII_TRANS_ABORT_CCMD (1 << 6)
+#define SDHCI_UHSII_ABORT_COMMAND (2 << 6)
+#define SDHCI_UHSII_GO_DORMANT (3 << 6)
+#define SDHCI_UHSII_COMMAND_LEN_SHIFT 8
+#define SDHCI_UHSII_COMMAND_LEN_MASK 0x1F
+
+#define SDHCI_UHSII_RESPONSE 0xA0
+#define SDHCI_UHSII_RESP_LEN 20
+#define SDHCI_UHSII_RESP_HEADER SDHCI_UHSII_RESPONSE
+#define SDHCI_UHSII_RESP_ARGUMENT (SDHCI_UHSII_RESPONSE + 2)
+#define SDHCI_UHSII_RESP_PAYLOAD (SDHCI_UHSII_RESPONSE + 4)
+
+#define SDHCI_UHSII_MSG_SEL 0xB4
+#define SDHCI_UHSII_MSG_REG 0xB8
+#define SDHCI_UHSII_DEV_INT_STATUS 0xBC
+#define SDHCI_UHSII_DEV_SEL 0xBE
+#define SDHCI_UHSII_INT_CODE 0xBF
+#define SDHCI_UHSII_SOFT_RESET 0xC0
+#define SDHCI_UHSII_HOST_FULL_RESET 0x0001
+#define SDHCI_UHSII_TIMER_CONTROL 0xC2
+#define SDHCI_UHSII_INT_STATUS 0xC4
+#define SDHCI_UHSII_INT_ENABLE 0xC8
+#define SDHCI_UHSII_SIGNAL_ENABLE 0xCC
+#define SDHCI_UHSII_INT_HEADER 0x00000001
+#define SDHCI_UHSII_INT_RES 0x00000002
+#define SDHCI_UHSII_INT_EXPIRED 0x00000004
+#define SDHCI_UHSII_INT_CRC 0x00000008
+#define SDHCI_UHSII_INT_FRAMING 0x00000010
+#define SDHCI_UHSII_INT_TID 0x00000020
+#define SDHCI_UHSII_INT_UNRECOVERABLE 0x00000080
+#define SDHCI_UHSII_INT_EBSY 0x00000100
+#define SDHCI_UHSII_INT_ADMA 0x00008000
+#define SDHCI_UHSII_INT_TO_RES 0x00010000
+#define SDHCI_UHSII_INT_TO_DEADLOCK 0x00020000
+#define SDHCI_UHSII_INT_TIMEOUT \
+ (SDHCI_UHSII_INT_TO_RES | SDHCI_UHSII_INT_TO_DEADLOCK)
+
+#define SDHCI_UHSII_SETTINGS_PTR 0xE0
+#define SDHCI_UHSII_HOST_CAPS_PTR 0xE2
+#define SDHCI_UHSII_TEST_REG_PTR 0xE4
+#define SDHCI_EMBEDDED_CONTROL_PTR 0xE6
+#define SDHCI_VENDOR_SPEC_AREA_PTR 0xE8
+
#define SDHCI_SLOT_INT_STATUS 0xFC

#define SDHCI_HOST_VERSION 0xFE
@@ -255,6 +333,42 @@
#define SDHCI_SPEC_100 0
#define SDHCI_SPEC_200 1
#define SDHCI_SPEC_300 2
+#define SDHCI_SPEC_400 3
+
+#define SDHCI_UHSII_GENERAL_REG 0x00
+#define SDHCI_UHSII_PHY_REG 0x04
+#define SDHCI_UHSII_LINK_REG_L 0x08
+#define SDHCI_UHSII_LINK_REG_H 0x0C
+
+/* UHS-II General Setting */
+#define SDHCI_UHSII_LOW_PWR_MODE 0x01
+
+/* UHS-II General Capabilities */
+#define SDHCI_UHSII_LANES_SHIFT 8
+#define SDHCI_UHSII_LANES_MASK 0x3F00
+#define SDHCI_UHSII_LANES_2L_HD 0x01
+#define SDHCI_UHSII_LANES_2D1U_FD 0x02
+#define SDHCI_UHSII_LANES_1D2U_FD 0x04
+#define SDHCI_UHSII_LANES_2D2U_FD 0x08
+#define SDHCI_UHSII_GAP_SHIFT 4
+#define SDHCI_UHSII_GAP_MASK 0xF0
+#define SDHCI_UHSII_DAP_SHIFT 0
+#define SDHCI_UHSII_DAP_MASK 0x0F
+
+/* UHS-II PHY Capabilities */
+#define SDHCI_UHSII_LSS_DIR_SHIFT 20
+#define SDHCI_UHSII_LSS_DIR_MASK (0x0F >> SDHCI_UHSII_LSS_DIR_SHIFT)
+#define SDHCI_UHSII_LSS_SYN_SHIFT 16
+#define SDHCI_UHSII_LSS_SYN_MASK (0x0F >> SDHCI_UHSII_LSS_SYN_SHIFT)
+#define SDHCI_UHSII_RANGE_SHIFT 6
+#define SDHCI_UHSII_RANGE_MASK 0xC0
+#define SDHCI_UHSII_RANGE_A 0x00
+#define SDHCI_UHSII_RANGE_AB 0x01
+
+/* UHS-II LINK/TRAN Capabilities */
+#define SDHCI_UHSII_DATA_GAP_MASK 0xFF
+#define SDHCI_UHSII_N_FCU_SHIFT 8
+#define SDHCI_UHSII_N_FCU_MASK 0xFF00

/*
* End of controller registers.
@@ -441,6 +555,7 @@ struct sdhci_host {
#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
+#define SDHCI_HOST_V4_ENABLED (1<<12)

unsigned int version; /* SDHCI spec. version */

@@ -451,11 +566,25 @@ struct sdhci_host {
unsigned int clock; /* Current clock (MHz) */
u8 pwr; /* Current voltage */

+ bool uhsii_if_enabled;
+ u8 lane_mode;
+ u8 max_gap;
+ u8 max_dap;
+ u8 n_data_gap;
+ u8 n_fcu;
+ u8 n_lss_dir;
+ u8 n_lss_syn;
+ u8 speed_range;
+
+ u16 uhsii_settings_ptr;
+ u16 uhsii_caps_ptr;
+
bool runtime_suspended; /* Host is runtime suspended */
bool bus_on; /* Bus power prevents runtime suspend */
bool preset_enabled; /* Preset is enabled */

struct mmc_request *mrq; /* Current request */
+ struct mmc_tlp *tlp; /* Current native TLP */
struct mmc_command *cmd; /* Current command */
struct mmc_data *data; /* Current data request */
unsigned int data_early:1; /* Data finished before cmd */
@@ -627,6 +756,11 @@ static inline u8 sdhci_readb(struct sdhci_host *host, int reg)

#endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */

+static inline u32 sdhci_raw_readl(struct sdhci_host *host, int reg)
+{
+ return __raw_readl(host->ioaddr + reg);
+}
+
extern struct sdhci_host *sdhci_alloc_host(struct device *dev,
size_t priv_size);
extern void sdhci_free_host(struct sdhci_host *host);
--
1.9.1

2015-04-29 01:36:14

by 敬锐

[permalink] [raw]
Subject: [PATCH 08/12] mmc: sdhci: add SD4.0 operations

From: Micky Ching <[email protected]>

SD4.0 operations include UHSII interface detect, go/exit dormant
and uhsii ios settings.

Signed-off-by: Micky Ching <[email protected]>
Signed-off-by: Wei Wang <[email protected]>
---
drivers/mmc/host/sdhci.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 182 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index c80287a..df1b88d 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -143,6 +143,32 @@ static void sdhci_dumpregs(struct sdhci_host *host)
* *
\*****************************************************************************/

+static int sdhci_checkw(struct sdhci_host *host, int reg,
+ u16 mask, u16 done, int msec)
+{
+ while (msec > 0) {
+ if ((sdhci_readw(host, reg) & mask) == done)
+ return 0;
+ mdelay(1);
+ msec--;
+ };
+ DBG("check %x(%x) %x failed\n", reg, mask, done);
+ return -ETIMEDOUT;
+}
+
+static int sdhci_checkl(struct sdhci_host *host, int reg,
+ u32 mask, u32 done, int msec)
+{
+ while (msec > 0) {
+ if ((sdhci_readl(host, reg) & mask) == done)
+ return 0;
+ mdelay(1);
+ msec--;
+ };
+ DBG("check %x(%x) %x failed\n", reg, mask, done);
+ return -ETIMEDOUT;
+}
+
static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
{
u32 present;
@@ -2224,6 +2250,158 @@ static void sdhci_card_event(struct mmc_host *mmc)
spin_unlock_irqrestore(&host->lock, flags);
}

+static int sdhci_switch_uhsii_if(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+ int err = 0;
+ u32 present;
+ u16 clk, ctrl2;
+ u8 pwr;
+
+ host->uhsii_if_enabled = false;
+ spin_lock_irqsave(&host->lock, flags);
+
+ clk = SDHCI_CLOCK_INT_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ if (sdhci_checkw(host, SDHCI_CLOCK_CONTROL,
+ SDHCI_CLOCK_INT_STABLE,
+ SDHCI_CLOCK_INT_STABLE, 20) < 0) {
+ pr_err("%s: Internal clock not ready.\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ err = -ETIMEDOUT;
+ goto out;
+ }
+
+ ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ ctrl2 |= SDHCI_CTRL_UHSII_IF_ENABLE;
+ sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2);
+
+ pwr = (SDHCI_POWER_ON | SDHCI_POWER_330) << SDHCI_VDD1_SHIFT;
+ pwr |= (SDHCI_POWER_ON | SDHCI_POWER_180) << SDHCI_VDD2_SHIFT;
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+
+ ctrl2 |= SDHCI_CTRL_UHSII;
+ sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2);
+
+ /* Wait Power Ramp Up Time */
+ spin_unlock_irqrestore(&host->lock, flags);
+ msleep(20);
+ spin_lock_irqsave(&host->lock, flags);
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ udelay(200);
+
+ present = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ if (present & SDHCI_STBL_DETECT) {
+ if (sdhci_checkl(host, SDHCI_PRESENT_STATE,
+ SDHCI_LANE_SYNC,
+ SDHCI_LANE_SYNC, 20) < 0) {
+ pr_err("%s: UHS-II PHY is not initialized\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ err = -ETIMEDOUT;
+ goto out;
+ }
+ host->uhsii_if_enabled = true;
+ } else {
+ pr_info("%s: UHS-II IF is not detected\n",
+ mmc_hostname(host->mmc));
+ goto out;
+ }
+
+out:
+ if (!host->uhsii_if_enabled) {
+ pwr = SDHCI_POWER_330 << SDHCI_VDD1_SHIFT;
+ pwr |= SDHCI_POWER_180 << SDHCI_VDD2_SHIFT;
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+ msleep(100);
+ spin_lock_irqsave(&host->lock, flags);
+
+ ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ ctrl2 &= ~SDHCI_CTRL_UHSII_IF_ENABLE;
+ ctrl2 &= ~SDHCI_CTRL_UHS_MASK;
+ sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2);
+
+ err = -ENXIO;
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+ return err;
+}
+
+static int sdhci_exit_dormant(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u16 clk;
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ udelay(200);
+
+ if (sdhci_checkl(host, SDHCI_PRESENT_STATE,
+ SDHCI_IN_DORMANT_STATE, 0, 100) < 0) {
+ pr_err("%s: Still in dormant state.\n",
+ mmc_hostname(host->mmc));
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void sdhci_set_uhsii_ios(struct mmc_host *mmc, struct mmc_uhsii_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u32 reg;
+
+ sdhci_runtime_pm_get(host);
+
+ /* speed range */
+ if (ios->flags & SETTING_SPEED_RANGE) {
+ reg = sdhci_readl(host,
+ host->uhsii_settings_ptr + SDHCI_UHSII_PHY_REG);
+ reg &= ~SDHCI_UHSII_RANGE_MASK;
+ reg |= (ios->speed_range << SDHCI_UHSII_RANGE_SHIFT) &
+ SDHCI_UHSII_RANGE_MASK;
+ sdhci_writel(host, reg,
+ host->uhsii_settings_ptr + SDHCI_UHSII_PHY_REG);
+ }
+
+ /* n_fcu */
+ if (ios->flags & SETTING_N_FCU) {
+ if (host->n_fcu) {
+ if (!ios->n_fcu || (ios->n_fcu > host->n_fcu))
+ ios->n_fcu = host->n_fcu;
+ }
+ reg = sdhci_readl(host,
+ host->uhsii_settings_ptr + SDHCI_UHSII_LINK_REG_L);
+ reg &= ~SDHCI_UHSII_N_FCU_MASK;
+ reg |= (ios->n_fcu << SDHCI_UHSII_N_FCU_SHIFT) &
+ SDHCI_UHSII_N_FCU_MASK;
+ sdhci_writel(host, reg,
+ host->uhsii_settings_ptr + SDHCI_UHSII_LINK_REG_L);
+ }
+
+ /* power control mode */
+ if (ios->flags & SETTING_PWR_CTL_MODE) {
+ reg = sdhci_readl(host,
+ host->uhsii_settings_ptr + SDHCI_UHSII_GENERAL_REG);
+ reg |= SDHCI_UHSII_LOW_PWR_MODE;
+ sdhci_writel(host, reg,
+ host->uhsii_settings_ptr + SDHCI_UHSII_GENERAL_REG);
+ }
+
+ sdhci_runtime_pm_put(host);
+}
+
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.post_req = sdhci_post_req,
@@ -2237,7 +2415,10 @@ static const struct mmc_host_ops sdhci_ops = {
.prepare_hs400_tuning = sdhci_prepare_hs400_tuning,
.execute_tuning = sdhci_execute_tuning,
.card_event = sdhci_card_event,
- .card_busy = sdhci_card_busy,
+ .card_busy = sdhci_card_busy,
+ .switch_uhsii_if = sdhci_switch_uhsii_if,
+ .exit_dormant = sdhci_exit_dormant,
+ .set_uhsii_ios = sdhci_set_uhsii_ios,
};

/*****************************************************************************\
--
1.9.1

2015-04-29 01:34:47

by 敬锐

[permalink] [raw]
Subject: [PATCH 09/12] mmc: sdhci: add tlp handler for SD4.0

From: Micky Ching <[email protected]>

SD4.0 mode using tlp for cmd/data transfer, add tlp functions to handle
this case.

Signed-off-by: Micky Ching <[email protected]>
Signed-off-by: Wei Wang <[email protected]>
---
drivers/mmc/host/sdhci.c | 244 ++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 220 insertions(+), 24 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index df1b88d..3c56944 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -976,6 +976,32 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
}

+static void sdhci_uhsii_set_transfer_mode(struct sdhci_host *host,
+ struct mmc_command *cmd)
+{
+ u16 mode = 0;
+ struct mmc_data *data = cmd->data;
+
+ if (UHSII_CHK_CCMD(cmd->tlp_send.header)) {
+ if (cmd->flags & MMC_RSP_BUSY)
+ mode |= SDHCI_UHSII_TRNS_WAIT_EBSY;
+ } else {
+ u8 tmode = (cmd->tlp_send.argument & UHSII_ARG_TMODE_MASK) >>
+ UHSII_ARG_TMODE_SHIFT;
+ if (tmode & UHSII_TMODE_DM_HD)
+ mode |= SDHCI_UHSII_TRANS_2LANE_HD;
+ mode |= SDHCI_UHSII_TRNS_BLK_CNT_EN;
+ mode |= SDHCI_UHSII_TRNS_WAIT_EBSY;
+
+ if (data->flags & MMC_DATA_WRITE)
+ mode |= SDHCI_UHSII_TRNS_WRITE;
+ if (host->flags & SDHCI_REQ_USE_DMA)
+ mode |= SDHCI_UHSII_TRNS_DMA;
+ }
+
+ sdhci_writew(host, mode, SDHCI_UHSII_TRANSFER_MODE);
+}
+
static void sdhci_finish_data(struct sdhci_host *host)
{
struct mmc_data *data;
@@ -1014,7 +1040,7 @@ static void sdhci_finish_data(struct sdhci_host *host)
* a) open-ended multiblock transfer (no CMD23)
* b) error in multiblock transfer
*/
- if (data->stop &&
+ if (!host->uhsii_if_enabled && data->stop &&
(data->error ||
!host->mrq->sbc)) {

@@ -1032,6 +1058,51 @@ static void sdhci_finish_data(struct sdhci_host *host)
tasklet_schedule(&host->finish_tasklet);
}

+static void sdhci_send_native_tlp(struct sdhci_host *host, struct mmc_tlp *tlp)
+{
+ int i;
+ unsigned long timeout;
+ u16 cmdreg;
+ u8 plen, cmd_len = 4;
+
+ /* Wait max 10 ms */
+ timeout = 10;
+
+ if (sdhci_checkl(host, SDHCI_PRESENT_STATE, SDHCI_CMD_INHIBIT, 0, 10)) {
+ pr_err("%s: cmd busy...\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ tlp->error = -EIO;
+ tasklet_schedule(&host->finish_tasklet);
+ return;
+ }
+
+ mod_timer(&host->timer, jiffies + 10 * HZ);
+
+ host->tlp = tlp;
+
+ plen = (u8)((tlp->tlp_send->argument & UHSII_ARG_PLEN_MASK) >>
+ UHSII_ARG_PLEN_SHIFT);
+
+ sdhci_writew(host, cpu_to_be16(tlp->tlp_send->header),
+ SDHCI_UHSII_CMD_HEADER);
+ sdhci_writew(host, cpu_to_be16(tlp->tlp_send->argument),
+ SDHCI_UHSII_CMD_ARGUMENT);
+ if (tlp->tlp_send->argument & UHSII_ARG_DIR_WRITE) {
+ for (i = 0; i < UHSII_PLEN_DWORDS(plen); i++)
+ sdhci_writel(host,
+ cpu_to_be32(tlp->tlp_send->payload[i]),
+ SDHCI_UHSII_CMD_PAYLOAD + 4 * i);
+
+ cmd_len = UHSII_PLEN_BYTES(plen) + 4;
+ }
+
+ cmdreg = (cmd_len & SDHCI_UHSII_COMMAND_LEN_MASK) <<
+ SDHCI_UHSII_COMMAND_LEN_SHIFT;
+ if (tlp->cmd_type == UHSII_COMMAND_GO_DORMANT)
+ cmdreg |= SDHCI_UHSII_GO_DORMANT;
+ sdhci_writew(host, cmdreg, SDHCI_UHSII_COMMAND);
+}
+
void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
{
int flags;
@@ -1077,6 +1148,35 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)

sdhci_prepare_data(host, cmd);

+ if (cmd->use_tlp) {
+ int i;
+ u16 cmdreg;
+ u8 plen = 1, cmd_len = 8;
+
+ if (!UHSII_CHK_CCMD(cmd->tlp_send.header)) {
+ plen = 2;
+ cmd_len = 12;
+ }
+
+ sdhci_writew(host, cpu_to_be16(cmd->tlp_send.header),
+ SDHCI_UHSII_CMD_HEADER);
+ sdhci_writew(host, cpu_to_be16(cmd->tlp_send.argument),
+ SDHCI_UHSII_CMD_ARGUMENT);
+ for (i = 0; i < plen; i++)
+ sdhci_writel(host,
+ cpu_to_be32(cmd->tlp_send.payload[i]),
+ SDHCI_UHSII_CMD_PAYLOAD + (4 * i));
+
+ sdhci_uhsii_set_transfer_mode(host, cmd);
+
+ cmdreg = (cmd_len & SDHCI_UHSII_COMMAND_LEN_MASK) <<
+ SDHCI_UHSII_COMMAND_LEN_SHIFT;
+ if (cmd->data)
+ cmdreg |= SDHCI_UHSII_DATA_PRESENT;
+ sdhci_writew(host, cmdreg, SDHCI_UHSII_COMMAND);
+ return;
+ }
+
sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);

sdhci_set_transfer_mode(host, cmd);
@@ -1112,28 +1212,64 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
}
EXPORT_SYMBOL_GPL(sdhci_send_command);

-static void sdhci_finish_command(struct sdhci_host *host)
+static void sdhci_read_rsp_136(struct sdhci_host *host)
{
int i;

+ if (host->cmd->use_tlp) {
+ for (i = 0; i < 4; i++) {
+ u32 resp = sdhci_raw_readl(host,
+ SDHCI_UHSII_RESP_PAYLOAD + i * 4);
+ host->cmd->resp[i] = be32_to_cpu(resp);
+ }
+ } else {
+ /* CRC is stripped so we need to do some shifting. */
+ for (i = 0; i < 4; i++) {
+ host->cmd->resp[i] = sdhci_readl(host,
+ SDHCI_RESPONSE + (3 - i) * 4) << 8;
+ if (i != 3)
+ host->cmd->resp[i] |= sdhci_readb(host,
+ SDHCI_RESPONSE + (3 - i) * 4 - 1);
+ }
+ }
+
+}
+
+static void sdhci_read_rsp_48(struct sdhci_host *host)
+{
+ if (host->cmd->use_tlp)
+ host->cmd->resp[0] = be32_to_cpu(
+ sdhci_raw_readl(host, SDHCI_UHSII_RESP_PAYLOAD));
+ else
+ host->cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE);
+}
+
+static void sdhci_finish_command(struct sdhci_host *host)
+{
BUG_ON(host->cmd == NULL);

- if (host->cmd->flags & MMC_RSP_PRESENT) {
- if (host->cmd->flags & MMC_RSP_136) {
- /* CRC is stripped so we need to do some shifting. */
- for (i = 0;i < 4;i++) {
- host->cmd->resp[i] = sdhci_readl(host,
- SDHCI_RESPONSE + (3-i)*4) << 8;
- if (i != 3)
- host->cmd->resp[i] |=
- sdhci_readb(host,
- SDHCI_RESPONSE + (3-i)*4-1);
- }
- } else {
- host->cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE);
+ if (host->cmd->use_tlp) {
+ u16 arg = sdhci_readw(host, SDHCI_UHSII_RESP_ARGUMENT);
+
+ arg = be16_to_cpu(arg);
+ if (arg & UHSII_ARG_RES_NACK) {
+ host->cmd->error = -EIO;
+ DBG("Response NACK!");
+
+ tasklet_schedule(&host->finish_tasklet);
+ host->cmd = NULL;
+
+ return;
}
}

+ if (host->cmd->flags & MMC_RSP_PRESENT) {
+ if (host->cmd->flags & MMC_RSP_136)
+ sdhci_read_rsp_136(host);
+ else
+ sdhci_read_rsp_48(host);
+ }
+
host->cmd->error = 0;

/* Finished CMD23, now send actual command. */
@@ -1153,6 +1289,21 @@ static void sdhci_finish_command(struct sdhci_host *host)
}
}

+static void sdhci_finish_native_tlp(struct sdhci_host *host)
+{
+ int i;
+
+ host->tlp->tlp_back->header = be16_to_cpu(
+ sdhci_readw(host, SDHCI_UHSII_RESP_HEADER));
+ host->tlp->tlp_back->argument = be16_to_cpu(
+ sdhci_readw(host, SDHCI_UHSII_RESP_ARGUMENT));
+ for (i = 0; i < 4; i++)
+ host->tlp->tlp_back->payload[i] = be32_to_cpu(
+ sdhci_readl(host, SDHCI_UHSII_RESP_PAYLOAD + 4 * i));
+
+ tasklet_schedule(&host->finish_tasklet);
+}
+
static u16 sdhci_get_preset_value(struct sdhci_host *host)
{
u16 preset = 0;
@@ -1410,7 +1561,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq = mrq;

if (!present || host->flags & SDHCI_DEVICE_DEAD) {
- host->mrq->cmd->error = -ENOMEDIUM;
+ mmc_set_mrq_error_code(host->mrq, -ENOMEDIUM);
tasklet_schedule(&host->finish_tasklet);
} else {
u32 present_state;
@@ -1446,10 +1597,13 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
}
}

- if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
+ if (!host->uhsii_if_enabled && mrq->sbc &&
+ !(host->flags & SDHCI_AUTO_CMD23))
sdhci_send_command(host, mrq->sbc);
- else
+ else if (mrq->cmd)
sdhci_send_command(host, mrq->cmd);
+ else if (mrq->tlp)
+ sdhci_send_native_tlp(host, mrq->tlp);
}

mmiowb();
@@ -2243,7 +2397,7 @@ static void sdhci_card_event(struct mmc_host *mmc)
sdhci_do_reset(host, SDHCI_RESET_CMD);
sdhci_do_reset(host, SDHCI_RESET_DATA);

- host->mrq->cmd->error = -ENOMEDIUM;
+ mmc_set_mrq_error_code(host->mrq, -ENOMEDIUM);
tasklet_schedule(&host->finish_tasklet);
}

@@ -2450,6 +2604,19 @@ static void sdhci_tasklet_finish(unsigned long param)

mrq = host->mrq;

+ if (mrq->tlp) {
+ if (mrq->tlp->cmd_type == UHSII_COMMAND_GO_DORMANT) {
+ /* Wait In Dormant State */
+ if (sdhci_checkl(host, SDHCI_PRESENT_STATE,
+ SDHCI_IN_DORMANT_STATE,
+ SDHCI_IN_DORMANT_STATE, 100) < 0) {
+ pr_err("%s: Not in dormant state.\n",
+ mmc_hostname(host->mmc));
+ mrq->tlp->error = -ETIMEDOUT;
+ }
+ }
+ }
+
/*
* The controller needs a reset of internal state machines
* upon error conditions.
@@ -2457,6 +2624,7 @@ static void sdhci_tasklet_finish(unsigned long param)
if (!(host->flags & SDHCI_DEVICE_DEAD) &&
((mrq->cmd && mrq->cmd->error) ||
(mrq->sbc && mrq->sbc->error) ||
+ (mrq->tlp && mrq->tlp->error) ||
(mrq->data && ((mrq->data->error && !mrq->data->stop) ||
(mrq->data->stop && mrq->data->stop->error))) ||
(host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) {
@@ -2475,6 +2643,7 @@ static void sdhci_tasklet_finish(unsigned long param)
host->mrq = NULL;
host->cmd = NULL;
host->data = NULL;
+ host->tlp = NULL;

#ifndef SDHCI_USE_LEDS_CLASS
sdhci_deactivate_led(host);
@@ -2505,10 +2674,18 @@ static void sdhci_timeout_timer(unsigned long data)
host->data->error = -ETIMEDOUT;
sdhci_finish_data(host);
} else {
- if (host->cmd)
- host->cmd->error = -ETIMEDOUT;
- else
- host->mrq->cmd->error = -ETIMEDOUT;
+ if (host->cmd || host->mrq->cmd) {
+ if (host->cmd)
+ host->cmd->error = -ETIMEDOUT;
+ else
+ host->mrq->cmd->error = -ETIMEDOUT;
+ }
+ if (host->tlp || host->mrq->tlp) {
+ if (host->tlp)
+ host->tlp->error = -ETIMEDOUT;
+ else
+ host->mrq->tlp->error = -ETIMEDOUT;
+ }

tasklet_schedule(&host->finish_tasklet);
}
@@ -2550,6 +2727,25 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
return;
}

+ if (host->tlp) {
+ if (uhsii_intmask & SDHCI_UHSII_INT_TIMEOUT)
+ host->tlp->error = -ETIMEDOUT;
+ else if (uhsii_intmask & (SDHCI_UHSII_INT_CRC |
+ SDHCI_UHSII_INT_HEADER |
+ SDHCI_UHSII_INT_RES |
+ SDHCI_UHSII_INT_UNRECOVERABLE))
+ host->tlp->error = -EILSEQ;
+
+ if (host->tlp->error) {
+ tasklet_schedule(&host->finish_tasklet);
+ return;
+ }
+
+ if (intmask & SDHCI_INT_RESPONSE)
+ sdhci_finish_native_tlp(host);
+ return;
+ }
+
if (intmask & SDHCI_INT_TIMEOUT)
host->cmd->error = -ETIMEDOUT;
else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
@@ -3669,7 +3865,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
pr_err("%s: Controller removed during "
" transfer!\n", mmc_hostname(mmc));

- host->mrq->cmd->error = -ENOMEDIUM;
+ mmc_set_mrq_error_code(host->mrq, -ENOMEDIUM);
tasklet_schedule(&host->finish_tasklet);
}

--
1.9.1

2015-04-29 01:35:35

by 敬锐

[permalink] [raw]
Subject: [PATCH 10/12] mmc: sdhci: disable clock control for SD4.0 mode

From: Micky Ching <[email protected]>

Skip clock control settings for UHSII mode. When card is in UHSII mode,
we only allow poweroff.

Signed-off-by: Micky Ching <[email protected]>
Signed-off-by: Wei Wang <[email protected]>
---
drivers/mmc/host/sdhci.c | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 3c56944..03df58c 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1346,7 +1346,8 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)

host->mmc->actual_clock = 0;

- sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+ if (!host->uhsii_if_enabled)
+ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);

if (clock == 0)
return;
@@ -1686,7 +1687,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
sdhci_enable_preset_value(host, false);

- if (!ios->clock || ios->clock != host->clock) {
+ if (!host->uhsii_if_enabled &&
+ (!ios->clock || ios->clock != host->clock)) {
host->ops->set_clock(host, ios->clock);
host->clock = ios->clock;

@@ -1703,7 +1705,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
}
}

- sdhci_set_power(host, ios->power_mode, ios->vdd);
+ if (!(host->uhsii_if_enabled && (ios->power_mode != MMC_POWER_OFF)))
+ sdhci_set_power(host, ios->power_mode, ios->vdd);

if (host->ops->platform_send_init_74_clocks)
host->ops->platform_send_init_74_clocks(host, ios->power_mode);
@@ -1765,10 +1768,12 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
host->ops->set_clock(host, host->clock);
}

- /* Reset SD Clock Enable */
- clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
- clk &= ~SDHCI_CLOCK_CARD_EN;
- sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+ if (!host->uhsii_if_enabled) {
+ /* Reset SD Clock Enable */
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+ }

host->ops->set_uhs_signaling(host, ios->timing);
host->timing = ios->timing;
@@ -1788,7 +1793,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
}

/* Re-enable SD Clock */
- host->ops->set_clock(host, host->clock);
+ if (!host->uhsii_if_enabled)
+ host->ops->set_clock(host, host->clock);
} else
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);

--
1.9.1

2015-04-29 01:35:01

by 敬锐

[permalink] [raw]
Subject: [PATCH 11/12] mmc: sdhci: set DMA configure for SD4.0 mode

From: Micky Ching <[email protected]>

SD4.0 mode not using SDMA any more, and UHSII mode using different register
to specify block size/count.

Signed-off-by: Micky Ching <[email protected]>
Signed-off-by: Wei Wang <[email protected]>
---
drivers/mmc/host/sdhci.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 03df58c..15bd7c8 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -883,8 +883,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
host->flags &= ~SDHCI_REQ_USE_DMA;
} else {
WARN_ON(sg_cnt != 1);
- sdhci_writel(host, sg_dma_address(data->sg),
- SDHCI_DMA_ADDRESS);
+ if (host->uhsii_if_enabled)
+ sdhci_writel(host,
+ sg_dma_address(data->sg),
+ SDHCI_ADMA_ADDRESS);
+ else
+ sdhci_writel(host,
+ sg_dma_address(data->sg),
+ SDHCI_DMA_ADDRESS);
}
}
}
@@ -924,9 +930,15 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
sdhci_set_transfer_irqs(host);

/* Set the DMA boundary value and block size */
- sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG,
- data->blksz), SDHCI_BLOCK_SIZE);
- sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
+ if (host->uhsii_if_enabled) {
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG,
+ data->blksz), SDHCI_UHSII_BLOCK_SIZE);
+ sdhci_writew(host, data->blocks, SDHCI_UHSII_BLOCK_COUNT);
+ } else {
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG,
+ data->blksz), SDHCI_BLOCK_SIZE);
+ sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
+ }
}

static void sdhci_set_transfer_mode(struct sdhci_host *host,
--
1.9.1

2015-04-29 01:35:33

by 敬锐

[permalink] [raw]
Subject: [PATCH 12/12] mmc: sdhci: add SD4.0 support

From: Micky Ching <[email protected]>

Add support for SD4.0 card.

Signed-off-by: Micky Ching <[email protected]>
Signed-off-by: Wei Wang <[email protected]>
---
drivers/mmc/host/sdhci.c | 108 ++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 102 insertions(+), 6 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 15bd7c8..6ba8699 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -256,6 +256,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);

static void sdhci_init(struct sdhci_host *host, int soft)
{
+ if ((host->flags & SDHCI_HOST_V4_ENABLED) && !soft)
+ sdhci_writew(host, SDHCI_UHSII_HOST_FULL_RESET,
+ SDHCI_UHSII_SOFT_RESET);
if (soft)
sdhci_do_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
else
@@ -270,6 +273,17 @@ static void sdhci_init(struct sdhci_host *host, int soft)
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);

+ if (host->flags & SDHCI_HOST_V4_ENABLED) {
+ u32 ier = SDHCI_UHSII_INT_HEADER | SDHCI_UHSII_INT_RES |
+ SDHCI_UHSII_INT_EXPIRED | SDHCI_UHSII_INT_CRC |
+ SDHCI_UHSII_INT_FRAMING | SDHCI_UHSII_INT_TID |
+ SDHCI_UHSII_INT_UNRECOVERABLE | SDHCI_UHSII_INT_EBSY |
+ SDHCI_UHSII_INT_ADMA | SDHCI_UHSII_INT_TIMEOUT;
+
+ sdhci_writel(host, ier, SDHCI_UHSII_INT_ENABLE);
+ sdhci_writel(host, ier, SDHCI_UHSII_SIGNAL_ENABLE);
+ }
+
if (soft) {
/* force clock reconfiguration */
host->clock = 0;
@@ -2733,11 +2747,12 @@ static void sdhci_tuning_timer(unsigned long data)
* *
\*****************************************************************************/

-static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
+static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask,
+ u32 uhsii_intmask, u32 *mask)
{
- BUG_ON(intmask == 0);
+ BUG_ON(!intmask && !uhsii_intmask);

- if (!host->cmd) {
+ if (!host->cmd && !host->tlp) {
pr_err("%s: Got command interrupt 0x%08x even "
"though no command operation was in progress.\n",
mmc_hostname(host->mmc), (unsigned)intmask);
@@ -2960,6 +2975,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
irqreturn_t result = IRQ_NONE;
struct sdhci_host *host = dev_id;
u32 intmask, mask, unexpected = 0;
+ u32 uhsii_intmask = 0;
int max_loops = 16;

spin_lock(&host->lock);
@@ -2970,7 +2986,12 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
}

intmask = sdhci_readl(host, SDHCI_INT_STATUS);
- if (!intmask || intmask == 0xffffffff) {
+
+ if (host->flags & SDHCI_HOST_V4_ENABLED)
+ uhsii_intmask = sdhci_readl(host, SDHCI_UHSII_INT_STATUS);
+
+ if ((!intmask || intmask == 0xffffffff) &&
+ (!uhsii_intmask || uhsii_intmask == 0xffffffff)) {
result = IRQ_NONE;
goto out;
}
@@ -3014,9 +3035,13 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
result = IRQ_WAKE_THREAD;
}

+ if (uhsii_intmask)
+ sdhci_writel(host, uhsii_intmask,
+ SDHCI_UHSII_INT_STATUS);
+
if (intmask & SDHCI_INT_CMD_MASK)
sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK,
- &intmask);
+ uhsii_intmask, &intmask);

if (intmask & SDHCI_INT_DATA_MASK)
sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
@@ -3340,7 +3365,7 @@ int sdhci_add_host(struct sdhci_host *host)
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
host->version = (host->version & SDHCI_SPEC_VER_MASK)
>> SDHCI_SPEC_VER_SHIFT;
- if (host->version > SDHCI_SPEC_300) {
+ if (host->version > SDHCI_SPEC_400) {
pr_err("%s: Unknown controller version (%d). "
"You may experience problems.\n", mmc_hostname(mmc),
host->version);
@@ -3598,6 +3623,9 @@ int sdhci_add_host(struct sdhci_host *host)
caps[1] &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
SDHCI_SUPPORT_DDR50);

+ if (!(caps[1] & SDHCI_CAN_VDD2_180))
+ caps[1] &= ~SDHCI_SUPPORT_UHSII;
+
/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
if (caps[1] & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
SDHCI_SUPPORT_DDR50))
@@ -3628,6 +3656,64 @@ int sdhci_add_host(struct sdhci_host *host)
!(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50))
mmc->caps |= MMC_CAP_UHS_DDR50;

+ if (caps[1] & SDHCI_SUPPORT_UHSII) {
+ u32 uhsii_caps;
+ u16 ctrl2;
+
+ /* Set Host Version 4.00 Enable */
+ ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ ctrl2 |= SDHCI_CTRL_HOST_V4_ENABLE;
+ sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2);
+ host->flags |= SDHCI_HOST_V4_ENABLED;
+
+ host->uhsii_settings_ptr = sdhci_readw(host,
+ SDHCI_UHSII_SETTINGS_PTR);
+ host->uhsii_caps_ptr = sdhci_readw(host,
+ SDHCI_UHSII_HOST_CAPS_PTR);
+
+ uhsii_caps = sdhci_readl(host,
+ host->uhsii_caps_ptr + SDHCI_UHSII_GENERAL_REG);
+
+ host->lane_mode = (uhsii_caps & SDHCI_UHSII_LANES_MASK) >>
+ SDHCI_UHSII_LANES_SHIFT;
+ host->max_gap = (uhsii_caps & SDHCI_UHSII_GAP_MASK) >>
+ SDHCI_UHSII_GAP_SHIFT;
+ host->max_dap = (uhsii_caps & SDHCI_UHSII_DAP_MASK) >>
+ SDHCI_UHSII_DAP_SHIFT;
+ DBG("lane_mode: 0x%x, max_gap: 0x%x, max_dap: 0x%x\n",
+ host->lane_mode, host->max_gap, host->max_dap);
+
+ uhsii_caps = sdhci_readl(host,
+ host->uhsii_caps_ptr + SDHCI_UHSII_PHY_REG);
+
+ host->n_lss_dir = (uhsii_caps & SDHCI_UHSII_LSS_DIR_MASK) >>
+ SDHCI_UHSII_LSS_DIR_SHIFT;
+ host->n_lss_syn = (uhsii_caps & SDHCI_UHSII_LSS_SYN_MASK) >>
+ SDHCI_UHSII_LSS_SYN_SHIFT;
+ host->speed_range = (uhsii_caps & SDHCI_UHSII_RANGE_MASK) >>
+ SDHCI_UHSII_RANGE_SHIFT;
+ DBG("n_lss_dir: 0x%x, n_lss_syn: 0x%x, speed_range: 0x%x\n",
+ host->n_lss_dir, host->n_lss_syn, host->speed_range);
+
+ uhsii_caps = sdhci_readl(host,
+ host->uhsii_caps_ptr + SDHCI_UHSII_LINK_REG_L);
+
+ host->n_fcu = (uhsii_caps & SDHCI_UHSII_N_FCU_MASK) >>
+ SDHCI_UHSII_N_FCU_SHIFT;
+ DBG("n_fcu: 0x%x\n", host->n_fcu);
+
+ uhsii_caps = sdhci_readl(host,
+ host->uhsii_caps_ptr + SDHCI_UHSII_LINK_REG_H);
+
+ host->n_data_gap = uhsii_caps & SDHCI_UHSII_DATA_GAP_MASK;
+ DBG("n_data_gap: 0x%x\n", host->n_data_gap);
+
+ mmc->caps |= MMC_CAP_UHSII;
+
+ if (host->speed_range == SDHCI_UHSII_RANGE_AB)
+ mmc->caps2 |= MMC_CAP2_UHSII_RANGE_AB;
+ }
+
/* Does the host need tuning for SDR50? */
if (caps[1] & SDHCI_USE_SDR50_TUNING)
host->flags |= SDHCI_SDR50_NEEDS_TUNING;
@@ -3644,6 +3730,16 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps[1] & SDHCI_DRIVER_TYPE_D)
mmc->caps |= MMC_CAP_DRIVER_TYPE_D;

+ if (host->version == SDHCI_SPEC_400) {
+ mmc->lane_mode = host->lane_mode;
+ mmc->max_gap = host->max_gap;
+ mmc->max_dap = host->max_dap;
+ mmc->n_lss_dir = host->n_lss_dir;
+ mmc->n_lss_syn = host->n_lss_syn;
+ mmc->n_data_gap = host->n_data_gap;
+ mmc->n_fcu = host->n_fcu;
+ }
+
/* Initial value for re-tuning timer count */
host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
SDHCI_RETUNING_TIMER_COUNT_SHIFT;
--
1.9.1