Merge branch 'intx' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/misc-2.6
[pandora-kernel.git] / drivers / mmc / mmc.c
index 1593a6a..6f2a282 100644 (file)
@@ -454,11 +454,11 @@ static void mmc_deselect_cards(struct mmc_host *host)
 
 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);
        }
 }
 
@@ -1157,6 +1157,116 @@ static void mmc_read_scrs(struct mmc_host *host)
        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;
@@ -1164,9 +1274,12 @@ static unsigned int mmc_calculate_clock(struct mmc_host *host)
 
        list_for_each_entry(card, &host->cards, node)
                if (!mmc_card_dead(card)) {
-                       if (mmc_card_highspeed(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;
+                                       max_dtr = card->ext_csd.hs_max_dtr;
                        } else if (max_dtr > card->csd.max_dtr) {
                                max_dtr = card->csd.max_dtr;
                        }
@@ -1288,9 +1401,10 @@ static void mmc_setup(struct mmc_host *host)
 
        mmc_read_csds(host);
 
-       if (host->mode == MMC_MODE_SD)
+       if (host->mode == MMC_MODE_SD) {
                mmc_read_scrs(host);
-       else
+               mmc_read_switch_caps(host);
+       } else
                mmc_process_ext_csds(host);
 }
 
@@ -1305,18 +1419,16 @@ static void mmc_setup(struct mmc_host *host)
  */
 void mmc_detect_change(struct mmc_host *host, unsigned long delay)
 {
-       if (delay)
-               mmc_schedule_delayed_work(&host->detect, delay);
-       else
-               mmc_schedule_work(&host->detect);
+       mmc_schedule_delayed_work(&host->detect, delay);
 }
 
 EXPORT_SYMBOL(mmc_detect_change);
 
 
-static void mmc_rescan(void *data)
+static void mmc_rescan(struct work_struct *work)
 {
-       struct mmc_host *host = data;
+       struct mmc_host *host =
+               container_of(work, struct mmc_host, detect.work);
        struct list_head *l, *n;
        unsigned char power_mode;
 
@@ -1399,7 +1511,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
                spin_lock_init(&host->lock);
                init_waitqueue_head(&host->wq);
                INIT_LIST_HEAD(&host->cards);
-               INIT_WORK(&host->detect, mmc_rescan, host);
+               INIT_DELAYED_WORK(&host->detect, mmc_rescan);
 
                /*
                 * By default, hosts do not support SGIO or large requests.
@@ -1497,7 +1609,7 @@ EXPORT_SYMBOL(mmc_suspend_host);
  */
 int mmc_resume_host(struct mmc_host *host)
 {
-       mmc_rescan(host);
+       mmc_rescan(&host->detect.work);
 
        return 0;
 }