From: Micky Ching <micky_ch...@realsil.com.cn>

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 <micky_ch...@realsil.com.cn>
Signed-off-by: Wei Wang <wei_w...@realsil.com.cn>
---
 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

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to