Linux Headquarters
[ Register ]
[ About us ] [ Home Page ]

Advertisement
[ Kernel ] [ Documentation ] [ Links ] [ Books ]

Advertisement

Kernel v2.6.19-git2 /drivers/mmc/mmc.c

Filename:/drivers/mmc/mmc.c
Lines Added:274
Lines Deleted:20
Also changed in: (Previous) 2.6.19  2.6.19-rc6  2.6.19-rc5-git7  2.6.19-rc5-git6  2.6.19-rc5-git5  2.6.19-rc5-git4 
(Following) 2.6.19-git3  2.6.19-git4  2.6.19-git5  2.6.19-git6  2.6.19-git7  2.6.19-git8 

Location
[  2.6.19-git2
  [  drivers
    [  mmc
       o  mmc.c

Patch

diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 766bc54..9d19002 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -4,6 +4,7 @@
  *  Copyright (C) 2003-2004 Russell King, All Rights Reserved.
  *  SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
  *  SD support Copyright (C) 2005 Pierre Ossman, All Rights Reserved.
+ *  MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -396,23 +397,23 @@ static int mmc_select_card(struct mmc_ho
       return err;
 
    /*
-    * Default bus width is 1 bit.
-    */
-   host->ios.bus_width = MMC_BUS_WIDTH_1;
-
-   /*
-    * We can only change the bus width of the selected
-    * card so therefore we have to put the handling
+    * We can only change the bus width of SD cards when
+    * they are selected so we have to put the handling
     * here.
+    *
+    * The card is in 1 bit mode by default so
+    * we only need to change if it supports the
+    * wider version.
     */
-   if (host->caps & MMC_CAP_4_BIT_DATA) {
+   if (mmc_card_sd(card) &&
+      (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+
       /*
-       * The card is in 1 bit mode by default so
-       * we only need to change if it supports the
-       * wider version.
-       */
-      if (mmc_card_sd(card) &&
-         (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+      * Default bus width is 1 bit.
+      */
+      host->ios.bus_width = MMC_BUS_WIDTH_1;
+
+      if (host->caps & MMC_CAP_4_BIT_DATA) {
          struct mmc_command cmd;
          cmd.opcode = SD_APP_SET_BUS_WIDTH;
          cmd.arg = SD_BUS_WIDTH_4;
@@ -453,11 +454,11 @@ static void mmc_deselect_cards(struct mm
 
 static inline void mmc_delay(unsigned int ms)
 {
-   if (ms < HZ / 1000) {
-      yield();
+   if (ms < 1000 / HZ) {
+      cond_resched();
       mdelay(ms);
    } else {
-      msleep_interruptible (ms);
+      msleep(ms);
    }
 }
 
@@ -953,6 +954,137 @@ static void mmc_read_csds(struct mmc_hos
    }
 }
 
+static void mmc_process_ext_csds(struct mmc_host *host)
+{
+   int err;
+   struct mmc_card *card;
+
+   struct mmc_request mrq;
+   struct mmc_command cmd;
+   struct mmc_data data;
+
+   struct scatterlist sg;
+
+   /*
+    * As the ext_csd is so large and mostly unused, we don't store the
+    * raw block in mmc_card.
+    */
+   u8 *ext_csd;
+   ext_csd = kmalloc(512, GFP_KERNEL);
+   if (!ext_csd) {
+      printk("%s: could not allocate a buffer to receive the ext_csd."
+             "mmc v4 cards will be treated as v3.\n",
+         mmc_hostname(host));
+      return;
+   }
+
+   list_for_each_entry(card, &host->cards, node) {
+      if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
+         continue;
+      if (mmc_card_sd(card))
+         continue;
+      if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
+         continue;
+
+      err = mmc_select_card(host, card);
+      if (err != MMC_ERR_NONE) {
+         mmc_card_set_dead(card);
+         continue;
+      }
+
+      memset(&cmd, 0, sizeof(struct mmc_command));
+
+      cmd.opcode = MMC_SEND_EXT_CSD;
+      cmd.arg = 0;
+      cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+      memset(&data, 0, sizeof(struct mmc_data));
+
+      mmc_set_data_timeout(&data, card, 0);
+
+      data.blksz = 512;
+      data.blocks = 1;
+      data.flags = MMC_DATA_READ;
+      data.sg = &sg;
+      data.sg_len = 1;
+
+      memset(&mrq, 0, sizeof(struct mmc_request));
+
+      mrq.cmd = &cmd;
+      mrq.data = &data;
+
+      sg_init_one(&sg, ext_csd, 512);
+
+      mmc_wait_for_req(host, &mrq);
+
+      if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
+         mmc_card_set_dead(card);
+         continue;
+      }
+
+      switch (ext_csd[EXT_CSD_CARD_TYPE]) {
+      case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+         card->ext_csd.hs_max_dtr = 52000000;
+         break;
+      case EXT_CSD_CARD_TYPE_26:
+         card->ext_csd.hs_max_dtr = 26000000;
+         break;
+      default:
+         /* MMC v4 spec says this cannot happen */
+         printk("%s: card is mmc v4 but doesn't support "
+                "any high-speed modes.\n",
+            mmc_hostname(card->host));
+         mmc_card_set_bad(card);
+         continue;
+      }
+
+      /* Activate highspeed support. */
+      cmd.opcode = MMC_SWITCH;
+      cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+           (EXT_CSD_HS_TIMING << 16) |
+           (1 << 8) |
+           EXT_CSD_CMD_SET_NORMAL;
+      cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+
+      err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
+      if (err != MMC_ERR_NONE) {
+         printk("%s: failed to switch card to mmc v4 "
+                "high-speed mode.\n",
+                mmc_hostname(card->host));
+         continue;
+      }
+
+      mmc_card_set_highspeed(card);
+
+      /* Check for host support for wide-bus modes. */
+      if (!(host->caps & MMC_CAP_4_BIT_DATA)) {
+         continue;
+      }
+
+      /* Activate 4-bit support. */
+      cmd.opcode = MMC_SWITCH;
+      cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+           (EXT_CSD_BUS_WIDTH << 16) |
+           (EXT_CSD_BUS_WIDTH_4 << 8) |
+           EXT_CSD_CMD_SET_NORMAL;
+      cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+
+      err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
+      if (err != MMC_ERR_NONE) {
+         printk("%s: failed to switch card to "
+                "mmc v4 4-bit bus mode.\n",
+                mmc_hostname(card->host));
+         continue;
+      }
+
+      host->ios.bus_width = MMC_BUS_WIDTH_4;
+   }
+
+   kfree(ext_csd);
+
+   mmc_deselect_cards(host);
+}
+
 static void mmc_read_scrs(struct mmc_host *host)
 {
    int err;
@@ -1025,14 +1157,133 @@ static void mmc_read_scrs(struct mmc_hos
    mmc_deselect_cards(host);
 }
 
+static void mmc_read_switch_caps(struct mmc_host *host)
+{
+   int err;
+   struct mmc_card *card;
+   struct mmc_request mrq;
+   struct mmc_command cmd;
+   struct mmc_data data;
+   unsigned char *status;
+   struct scatterlist sg;
+
+   status = kmalloc(64, GFP_KERNEL);
+   if (!status) {
+      printk(KERN_WARNING "%s: Unable to allocate buffer for "
+         "reading switch capabilities.\n",
+         mmc_hostname(host));
+      return;
+   }
+
+   list_for_each_entry(card, &host->cards, node) {
+      if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
+         continue;
+      if (!mmc_card_sd(card))
+         continue;
+      if (card->scr.sda_vsn < SCR_SPEC_VER_1)
+         continue;
+
+      err = mmc_select_card(host, card);
+      if (err != MMC_ERR_NONE) {
+         mmc_card_set_dead(card);
+         continue;
+      }
+
+      memset(&cmd, 0, sizeof(struct mmc_command));
+
+      cmd.opcode = SD_SWITCH;
+      cmd.arg = 0x00FFFFF1;
+      cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+      memset(&data, 0, sizeof(struct mmc_data));
+
+      mmc_set_data_timeout(&data, card, 0);
+
+      data.blksz = 64;
+      data.blocks = 1;
+      data.flags = MMC_DATA_READ;
+      data.sg = &sg;
+      data.sg_len = 1;
+
+      memset(&mrq, 0, sizeof(struct mmc_request));
+
+      mrq.cmd = &cmd;
+      mrq.data = &data;
+
+      sg_init_one(&sg, status, 64);
+
+      mmc_wait_for_req(host, &mrq);
+
+      if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
+         mmc_card_set_dead(card);
+         continue;
+      }
+
+      if (status[13] & 0x02)
+         card->sw_caps.hs_max_dtr = 50000000;
+
+      memset(&cmd, 0, sizeof(struct mmc_command));
+
+      cmd.opcode = SD_SWITCH;
+      cmd.arg = 0x80FFFFF1;
+      cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+      memset(&data, 0, sizeof(struct mmc_data));
+
+      mmc_set_data_timeout(&data, card, 0);
+
+      data.blksz = 64;
+      data.blocks = 1;
+      data.flags = MMC_DATA_READ;
+      data.sg = &sg;
+      data.sg_len = 1;
+
+      memset(&mrq, 0, sizeof(struct mmc_request));
+
+      mrq.cmd = &cmd;
+      mrq.data = &data;
+
+      sg_init_one(&sg, status, 64);
+
+      mmc_wait_for_req(host, &mrq);
+
+      if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
+         mmc_card_set_dead(card);
+         continue;
+      }
+
+      if ((status[16] & 0xF) != 1) {
+         printk(KERN_WARNING "%s: Problem switching card "
+            "into high-speed mode!\n",
+            mmc_hostname(host));
+         continue;
+      }
+
+      mmc_card_set_highspeed(card);
+   }
+
+   kfree(status);
+
+   mmc_deselect_cards(host);
+}
+
 static unsigned int mmc_calculate_clock(struct mmc_host *host)
 {
    struct mmc_card *card;
    unsigned int max_dtr = host->f_max;
 
    list_for_each_entry(card, &host->cards, node)
-      if (!mmc_card_dead(card) && max_dtr > card->csd.max_dtr)
-         max_dtr = card->csd.max_dtr;
+      if (!mmc_card_dead(card)) {
+         if (mmc_card_highspeed(card) && mmc_card_sd(card)) {
+            if (max_dtr > card->sw_caps.hs_max_dtr)
+               max_dtr = card->sw_caps.hs_max_dtr;
+         } else if (mmc_card_highspeed(card) && !mmc_card_sd(card)) {
+            if (max_dtr > card->ext_csd.hs_max_dtr)
+               max_dtr = card->ext_csd.hs_max_dtr;
+         } else if (max_dtr > card->csd.max_dtr) {
+            max_dtr = card->csd.max_dtr;
+         }
+      }
 
    pr_debug("%s: selected %d.%03dMHz transfer rate\n",
        mmc_hostname(host),
@@ -1150,8 +1401,11 @@ static void mmc_setup(struct mmc_host *h
 
    mmc_read_csds(host);
 
-   if (host->mode == MMC_MODE_SD)
+   if (host->mode == MMC_MODE_SD) {
       mmc_read_scrs(host);
+      mmc_read_switch_caps(host);
+   } else
+      mmc_process_ext_csds(host);
 }
 
 


Comments: webmaster (at) linuxhq.com.
Advertising: banners (at) linuxhq.com.
Compilation ©1998-2008 Linux Headquarters, Inc.