From 642064ae9a94b47054ea7fb39804776ae11fbc4e Mon Sep 17 00:00:00 2001 From: kaloz Date: Wed, 21 Jul 2010 11:20:53 +0000 Subject: add support for the Gateworks Laguna family (Cavium Networks Econa CNS3xxx) git-svn-id: svn://svn.openwrt.org/openwrt/trunk@22323 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../patches-2.6.31/204-cns3xxx_mmc_support.patch | 2663 ++++++++++++++++++++ 1 file changed, 2663 insertions(+) create mode 100644 target/linux/cns3xxx/patches-2.6.31/204-cns3xxx_mmc_support.patch (limited to 'target/linux/cns3xxx/patches-2.6.31/204-cns3xxx_mmc_support.patch') diff --git a/target/linux/cns3xxx/patches-2.6.31/204-cns3xxx_mmc_support.patch b/target/linux/cns3xxx/patches-2.6.31/204-cns3xxx_mmc_support.patch new file mode 100644 index 000000000..3f81ad81e --- /dev/null +++ b/target/linux/cns3xxx/patches-2.6.31/204-cns3xxx_mmc_support.patch @@ -0,0 +1,2663 @@ +--- a/drivers/mmc/card/block.c ++++ b/drivers/mmc/card/block.c +@@ -130,7 +130,7 @@ mmc_blk_getgeo(struct block_device *bdev + return 0; + } + +-static struct block_device_operations mmc_bdops = { ++static const struct block_device_operations mmc_bdops = { + .open = mmc_blk_open, + .release = mmc_blk_release, + .getgeo = mmc_blk_getgeo, +@@ -392,13 +392,9 @@ static int mmc_blk_issue_rq(struct mmc_q + } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || + (R1_CURRENT_STATE(cmd.resp[0]) == 7)); + +-#if 0 + if (cmd.resp[0] & ~0x00000900) + printk(KERN_ERR "%s: status = %08x\n", + req->rq_disk->disk_name, cmd.resp[0]); +- if (mmc_decode_status(cmd.resp)) +- goto cmd_err; +-#endif + } + + if (brq.cmd.error || brq.stop.error || brq.data.error) { +--- a/drivers/mmc/core/core.c ++++ b/drivers/mmc/core/core.c +@@ -37,6 +37,9 @@ + #include "sd_ops.h" + #include "sdio_ops.h" + ++/* scott.trace */ ++//#define MMC_DEBUG ++ + static struct workqueue_struct *workqueue; + + /* +@@ -90,17 +93,30 @@ void mmc_request_done(struct mmc_host *h + cmd->error = 0; + host->ops->request(host, mrq); + } else { ++#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) + led_trigger_event(host->led, LED_OFF); ++#endif + + pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n", + mmc_hostname(host), cmd->opcode, err, + cmd->resp[0], cmd->resp[1], + cmd->resp[2], cmd->resp[3]); ++#ifdef MMC_DEBUG ++ printk("[MMC_DEBUG] %s: req done (CMD%u): %d: %08x %08x %08x %08x\n", ++ mmc_hostname(host), cmd->opcode, err, ++ cmd->resp[0], cmd->resp[1], ++ cmd->resp[2], cmd->resp[3]); ++#endif + + if (mrq->data) { + pr_debug("%s: %d bytes transferred: %d\n", + mmc_hostname(host), + mrq->data->bytes_xfered, mrq->data->error); ++#ifdef MMC_DEBUG ++ printk("[MMC_DEBUG] %s: %d bytes transferred: %d\n", ++ mmc_hostname(host), ++ mrq->data->bytes_xfered, mrq->data->error); ++#endif + } + + if (mrq->stop) { +@@ -109,6 +125,13 @@ void mmc_request_done(struct mmc_host *h + mrq->stop->error, + mrq->stop->resp[0], mrq->stop->resp[1], + mrq->stop->resp[2], mrq->stop->resp[3]); ++#ifdef MMC_DEBUG ++ printk("[MMC_DEBUG] %s: (CMD%u): %d: %08x %08x %08x %08x\n", ++ mmc_hostname(host), mrq->stop->opcode, ++ mrq->stop->error, ++ mrq->stop->resp[0], mrq->stop->resp[1], ++ mrq->stop->resp[2], mrq->stop->resp[3]); ++#endif + } + + if (mrq->done) +@@ -129,6 +152,11 @@ mmc_start_request(struct mmc_host *host, + pr_debug("%s: starting CMD%u arg %08x flags %08x\n", + mmc_hostname(host), mrq->cmd->opcode, + mrq->cmd->arg, mrq->cmd->flags); ++#ifdef MMC_DEBUG ++ printk("[MMC_DEBUG] %s: starting CMD%u arg %08x flags %08x\n", ++ mmc_hostname(host), mrq->cmd->opcode, ++ mrq->cmd->arg, mrq->cmd->flags); ++#endif + + if (mrq->data) { + pr_debug("%s: blksz %d blocks %d flags %08x " +@@ -137,17 +165,32 @@ mmc_start_request(struct mmc_host *host, + mrq->data->blocks, mrq->data->flags, + mrq->data->timeout_ns / 1000000, + mrq->data->timeout_clks); ++#ifdef MMC_DEBUG ++ printk("[MMC_DEBUG] %s: blksz %d blocks %d flags %08x " ++ "tsac %d ms nsac %d\n", ++ mmc_hostname(host), mrq->data->blksz, ++ mrq->data->blocks, mrq->data->flags, ++ mrq->data->timeout_ns / 1000000, ++ mrq->data->timeout_clks); ++#endif + } + + if (mrq->stop) { + pr_debug("%s: CMD%u arg %08x flags %08x\n", + mmc_hostname(host), mrq->stop->opcode, + mrq->stop->arg, mrq->stop->flags); ++#ifdef MMC_DEBUG ++ printk("[MMC_DEBUG] %s: CMD%u arg %08x flags %08x\n", ++ mmc_hostname(host), mrq->stop->opcode, ++ mrq->stop->arg, mrq->stop->flags); ++#endif + } + + WARN_ON(!host->claimed); + ++#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) + led_trigger_event(host->led, LED_FULL); ++#endif + + mrq->cmd->error = 0; + mrq->cmd->mrq = mrq; +@@ -286,9 +329,9 @@ void mmc_set_data_timeout(struct mmc_dat + * The limit is really 250 ms, but that is + * insufficient for some crappy cards. + */ +- limit_us = 300000; ++ limit_us = 500000; + else +- limit_us = 100000; ++ limit_us = 200000; + + /* + * SDHC cards always use these fixed values. +@@ -344,6 +387,101 @@ unsigned int mmc_align_data_size(struct + EXPORT_SYMBOL(mmc_align_data_size); + + /** ++ * mmc_host_enable - enable a host. ++ * @host: mmc host to enable ++ * ++ * Hosts that support power saving can use the 'enable' and 'disable' ++ * methods to exit and enter power saving states. For more information ++ * see comments for struct mmc_host_ops. ++ */ ++int mmc_host_enable(struct mmc_host *host) ++{ ++ if (!(host->caps & MMC_CAP_DISABLE)) ++ return 0; ++ ++ if (host->en_dis_recurs) ++ return 0; ++ ++ if (host->nesting_cnt++) ++ return 0; ++ ++ cancel_delayed_work_sync(&host->disable); ++ ++ if (host->enabled) ++ return 0; ++ ++ if (host->ops->enable) { ++ int err; ++ ++ host->en_dis_recurs = 1; ++ err = host->ops->enable(host); ++ host->en_dis_recurs = 0; ++ ++ if (err) { ++ pr_debug("%s: enable error %d\n", ++ mmc_hostname(host), err); ++ return err; ++ } ++ } ++ host->enabled = 1; ++ return 0; ++} ++EXPORT_SYMBOL(mmc_host_enable); ++ ++static int mmc_host_do_disable(struct mmc_host *host, int lazy) ++{ ++ if (host->ops->disable) { ++ int err; ++ ++ host->en_dis_recurs = 1; ++ err = host->ops->disable(host, lazy); ++ host->en_dis_recurs = 0; ++ ++ if (err < 0) { ++ pr_debug("%s: disable error %d\n", ++ mmc_hostname(host), err); ++ return err; ++ } ++ if (err > 0) { ++ unsigned long delay = msecs_to_jiffies(err); ++ ++ mmc_schedule_delayed_work(&host->disable, delay); ++ } ++ } ++ host->enabled = 0; ++ return 0; ++} ++ ++/** ++ * mmc_host_disable - disable a host. ++ * @host: mmc host to disable ++ * ++ * Hosts that support power saving can use the 'enable' and 'disable' ++ * methods to exit and enter power saving states. For more information ++ * see comments for struct mmc_host_ops. ++ */ ++int mmc_host_disable(struct mmc_host *host) ++{ ++ int err; ++ ++ if (!(host->caps & MMC_CAP_DISABLE)) ++ return 0; ++ ++ if (host->en_dis_recurs) ++ return 0; ++ ++ if (--host->nesting_cnt) ++ return 0; ++ ++ if (!host->enabled) ++ return 0; ++ ++ err = mmc_host_do_disable(host, 0); ++ return err; ++} ++EXPORT_SYMBOL(mmc_host_disable); ++ ++/** + * __mmc_claim_host - exclusively claim a host + * @host: mmc host to claim + * @abort: whether or not the operation should be aborted +@@ -366,25 +504,111 @@ int __mmc_claim_host(struct mmc_host *ho + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); + stop = abort ? atomic_read(abort) : 0; +- if (stop || !host->claimed) ++ if (stop || !host->claimed || host->claimer == current) + break; + spin_unlock_irqrestore(&host->lock, flags); + schedule(); + spin_lock_irqsave(&host->lock, flags); + } + set_current_state(TASK_RUNNING); +- if (!stop) ++ if (!stop) { + host->claimed = 1; +- else ++ host->claimer = current; ++ host->claim_cnt += 1; ++ } else + wake_up(&host->wq); + spin_unlock_irqrestore(&host->lock, flags); + remove_wait_queue(&host->wq, &wait); ++ if (!stop) ++ mmc_host_enable(host); + return stop; + } + + EXPORT_SYMBOL(__mmc_claim_host); + + /** ++ * mmc_try_claim_host - try exclusively to claim a host ++ * @host: mmc host to claim ++ * ++ * Returns %1 if the host is claimed, %0 otherwise. ++ */ ++int mmc_try_claim_host(struct mmc_host *host) ++{ ++ int claimed_host = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ if (!host->claimed || host->claimer == current) { ++ host->claimed = 1; ++ host->claimer = current; ++ host->claim_cnt += 1; ++ claimed_host = 1; ++ } ++ spin_unlock_irqrestore(&host->lock, flags); ++ return claimed_host; ++} ++EXPORT_SYMBOL(mmc_try_claim_host); ++ ++static void mmc_do_release_host(struct mmc_host *host) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ if (--host->claim_cnt) { ++ /* Release for nested claim */ ++ spin_unlock_irqrestore(&host->lock, flags); ++ } else { ++ host->claimed = 0; ++ host->claimer = NULL; ++ spin_unlock_irqrestore(&host->lock, flags); ++ wake_up(&host->wq); ++ } ++} ++ ++void mmc_host_deeper_disable(struct work_struct *work) ++{ ++ struct mmc_host *host = ++ container_of(work, struct mmc_host, disable.work); ++ ++ /* If the host is claimed then we do not want to disable it anymore */ ++ if (!mmc_try_claim_host(host)) ++ return; ++ mmc_host_do_disable(host, 1); ++ mmc_do_release_host(host); ++} ++ ++/** ++ * mmc_host_lazy_disable - lazily disable a host. ++ * @host: mmc host to disable ++ * ++ * Hosts that support power saving can use the 'enable' and 'disable' ++ * methods to exit and enter power saving states. For more information ++ * see comments for struct mmc_host_ops. ++ */ ++int mmc_host_lazy_disable(struct mmc_host *host) ++{ ++ if (!(host->caps & MMC_CAP_DISABLE)) ++ return 0; ++ ++ if (host->en_dis_recurs) ++ return 0; ++ ++ if (--host->nesting_cnt) ++ return 0; ++ ++ if (!host->enabled) ++ return 0; ++ ++ if (host->disable_delay) { ++ mmc_schedule_delayed_work(&host->disable, ++ msecs_to_jiffies(host->disable_delay)); ++ return 0; ++ } else ++ return mmc_host_do_disable(host, 1); ++} ++EXPORT_SYMBOL(mmc_host_lazy_disable); ++ ++/** + * mmc_release_host - release a host + * @host: mmc host to release + * +@@ -393,15 +617,11 @@ EXPORT_SYMBOL(__mmc_claim_host); + */ + void mmc_release_host(struct mmc_host *host) + { +- unsigned long flags; +- + WARN_ON(!host->claimed); + +- spin_lock_irqsave(&host->lock, flags); +- host->claimed = 0; +- spin_unlock_irqrestore(&host->lock, flags); ++ mmc_host_lazy_disable(host); + +- wake_up(&host->wq); ++ mmc_do_release_host(host); + } + + EXPORT_SYMBOL(mmc_release_host); +@@ -687,7 +907,13 @@ void mmc_set_timing(struct mmc_host *hos + */ + static void mmc_power_up(struct mmc_host *host) + { +- int bit = fls(host->ocr_avail) - 1; ++ int bit; ++ ++ /* If ocr is set, we use it */ ++ if (host->ocr) ++ bit = ffs(host->ocr) - 1; ++ else ++ bit = fls(host->ocr_avail) - 1; + + host->ios.vdd = bit; + if (mmc_host_is_spi(host)) { +@@ -947,6 +1173,8 @@ void mmc_stop_host(struct mmc_host *host + spin_unlock_irqrestore(&host->lock, flags); + #endif + ++ if (host->caps & MMC_CAP_DISABLE) ++ cancel_delayed_work(&host->disable); + cancel_delayed_work(&host->detect); + mmc_flush_scheduled_work(); + +@@ -958,6 +1186,8 @@ void mmc_stop_host(struct mmc_host *host + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_release_host(host); ++ mmc_bus_put(host); ++ return; + } + mmc_bus_put(host); + +@@ -966,6 +1196,80 @@ void mmc_stop_host(struct mmc_host *host + mmc_power_off(host); + } + ++void mmc_power_save_host(struct mmc_host *host) ++{ ++ mmc_bus_get(host); ++ ++ if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) { ++ mmc_bus_put(host); ++ return; ++ } ++ ++ if (host->bus_ops->power_save) ++ host->bus_ops->power_save(host); ++ ++ mmc_bus_put(host); ++ ++ mmc_power_off(host); ++} ++EXPORT_SYMBOL(mmc_power_save_host); ++ ++void mmc_power_restore_host(struct mmc_host *host) ++{ ++ mmc_bus_get(host); ++ ++ if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) { ++ mmc_bus_put(host); ++ return; ++ } ++ ++ mmc_power_up(host); ++ host->bus_ops->power_restore(host); ++ ++ mmc_bus_put(host); ++} ++EXPORT_SYMBOL(mmc_power_restore_host); ++ ++int mmc_card_awake(struct mmc_host *host) ++{ ++ int err = -ENOSYS; ++ ++ mmc_bus_get(host); ++ ++ if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) ++ err = host->bus_ops->awake(host); ++ ++ mmc_bus_put(host); ++ ++ return err; ++} ++EXPORT_SYMBOL(mmc_card_awake); ++ ++int mmc_card_sleep(struct mmc_host *host) ++{ ++ int err = -ENOSYS; ++ ++ mmc_bus_get(host); ++ ++ if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) ++ err = host->bus_ops->sleep(host); ++ ++ mmc_bus_put(host); ++ ++ return err; ++} ++EXPORT_SYMBOL(mmc_card_sleep); ++ ++int mmc_card_can_sleep(struct mmc_host *host) ++{ ++ struct mmc_card *card = host->card; ++ ++ if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3) ++ return 1; ++ return 0; ++} ++EXPORT_SYMBOL(mmc_card_can_sleep); ++ + #ifdef CONFIG_PM + + /** +@@ -975,27 +1279,36 @@ void mmc_stop_host(struct mmc_host *host + */ + int mmc_suspend_host(struct mmc_host *host, pm_message_t state) + { ++ int err = 0; ++ ++ if (host->caps & MMC_CAP_DISABLE) ++ cancel_delayed_work(&host->disable); + cancel_delayed_work(&host->detect); + mmc_flush_scheduled_work(); + + mmc_bus_get(host); + if (host->bus_ops && !host->bus_dead) { + if (host->bus_ops->suspend) +- host->bus_ops->suspend(host); +- if (!host->bus_ops->resume) { ++ err = host->bus_ops->suspend(host); ++ if (err == -ENOSYS || !host->bus_ops->resume) { ++ /* ++ * We simply "remove" the card in this case. ++ * It will be redetected on resume. ++ */ + if (host->bus_ops->remove) + host->bus_ops->remove(host); +- + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_release_host(host); ++ err = 0; + } + } + mmc_bus_put(host); + +- mmc_power_off(host); ++ if (!err) ++ mmc_power_off(host); + +- return 0; ++ return err; + } + + EXPORT_SYMBOL(mmc_suspend_host); +@@ -1006,12 +1319,26 @@ EXPORT_SYMBOL(mmc_suspend_host); + */ + int mmc_resume_host(struct mmc_host *host) + { ++ int err = 0; ++ + mmc_bus_get(host); + if (host->bus_ops && !host->bus_dead) { + mmc_power_up(host); + mmc_select_voltage(host, host->ocr); + BUG_ON(!host->bus_ops->resume); +- host->bus_ops->resume(host); ++ err = host->bus_ops->resume(host); ++ if (err) { ++ printk(KERN_WARNING "%s: error %d during resume " ++ "(card was removed?)\n", ++ mmc_hostname(host), err); ++ if (host->bus_ops->remove) ++ host->bus_ops->remove(host); ++ mmc_claim_host(host); ++ mmc_detach_bus(host); ++ mmc_release_host(host); ++ /* no need to bother upper layers */ ++ err = 0; ++ } + } + mmc_bus_put(host); + +@@ -1021,7 +1348,7 @@ int mmc_resume_host(struct mmc_host *hos + */ + mmc_detect_change(host, 1); + +- return 0; ++ return err; + } + + EXPORT_SYMBOL(mmc_resume_host); +--- a/drivers/mmc/core/core.h ++++ b/drivers/mmc/core/core.h +@@ -16,10 +16,14 @@ + #define MMC_CMD_RETRIES 3 + + struct mmc_bus_ops { ++ int (*awake)(struct mmc_host *); ++ int (*sleep)(struct mmc_host *); + void (*remove)(struct mmc_host *); + void (*detect)(struct mmc_host *); +- void (*suspend)(struct mmc_host *); +- void (*resume)(struct mmc_host *); ++ int (*suspend)(struct mmc_host *); ++ int (*resume)(struct mmc_host *); ++ void (*power_save)(struct mmc_host *); ++ void (*power_restore)(struct mmc_host *); + }; + + void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); +--- a/drivers/mmc/core/debugfs.c ++++ b/drivers/mmc/core/debugfs.c +@@ -240,7 +240,7 @@ static int mmc_ext_csd_release(struct in + return 0; + } + +-static struct file_operations mmc_dbg_ext_csd_fops = { ++static const struct file_operations mmc_dbg_ext_csd_fops = { + .open = mmc_ext_csd_open, + .read = mmc_ext_csd_read, + .release = mmc_ext_csd_release, +--- a/drivers/mmc/core/host.c ++++ b/drivers/mmc/core/host.c +@@ -83,6 +83,7 @@ struct mmc_host *mmc_alloc_host(int extr + spin_lock_init(&host->lock); + init_waitqueue_head(&host->wq); + INIT_DELAYED_WORK(&host->detect, mmc_rescan); ++ INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable); + + /* + * By default, hosts do not support SGIO or large requests. +--- a/drivers/mmc/core/host.h ++++ b/drivers/mmc/core/host.h +@@ -14,5 +14,7 @@ + int mmc_register_host_class(void); + void mmc_unregister_host_class(void); + ++void mmc_host_deeper_disable(struct work_struct *work); ++ + #endif + +--- a/drivers/mmc/core/mmc.c ++++ b/drivers/mmc/core/mmc.c +@@ -160,7 +160,6 @@ static int mmc_read_ext_csd(struct mmc_c + { + int err; + u8 *ext_csd; +- unsigned int ext_csd_struct; + + BUG_ON(!card); + +@@ -207,16 +206,16 @@ static int mmc_read_ext_csd(struct mmc_c + goto out; + } + +- ext_csd_struct = ext_csd[EXT_CSD_REV]; +- if (ext_csd_struct > 3) { ++ card->ext_csd.rev = ext_csd[EXT_CSD_REV]; ++ if (card->ext_csd.rev > 3) { + printk(KERN_ERR "%s: unrecognised EXT_CSD structure " + "version %d\n", mmc_hostname(card->host), +- ext_csd_struct); ++ card->ext_csd.rev); + err = -EINVAL; + goto out; + } + +- if (ext_csd_struct >= 2) { ++ if (card->ext_csd.rev >= 2) { + card->ext_csd.sectors = + ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | + ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | +@@ -241,6 +240,15 @@ static int mmc_read_ext_csd(struct mmc_c + goto out; + } + ++ if (card->ext_csd.rev >= 3) { ++ u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT]; ++ ++ /* Sleep / awake timeout in 100ns units */ ++ if (sa_shift > 0 && sa_shift <= 0x17) ++ card->ext_csd.sa_timeout = ++ 1 << ext_csd[EXT_CSD_S_A_TIMEOUT]; ++ } ++ + out: + kfree(ext_csd); + +@@ -276,7 +284,7 @@ static struct attribute_group mmc_std_at + .attrs = mmc_std_attrs, + }; + +-static struct attribute_group *mmc_attr_groups[] = { ++static const struct attribute_group *mmc_attr_groups[] = { + &mmc_std_attr_group, + NULL, + }; +@@ -408,12 +416,17 @@ static int mmc_init_card(struct mmc_host + (host->caps & MMC_CAP_MMC_HIGHSPEED)) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, 1); +- if (err) ++ if (err && err != -EBADMSG) + goto free_card; + +- mmc_card_set_highspeed(card); +- +- mmc_set_timing(card->host, MMC_TIMING_MMC_HS); ++ if (err) { ++ printk(KERN_WARNING "%s: switch to highspeed failed\n", ++ mmc_hostname(card->host)); ++ err = 0; ++ } else { ++ mmc_card_set_highspeed(card); ++ mmc_set_timing(card->host, MMC_TIMING_MMC_HS); ++ } + } + + /* +@@ -448,10 +461,17 @@ static int mmc_init_card(struct mmc_host + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, ext_csd_bit); + +- if (err) ++ if (err && err != -EBADMSG) + goto free_card; + +- mmc_set_bus_width(card->host, bus_width); ++ if (err) { ++ printk(KERN_WARNING "%s: switch to bus width %d " ++ "failed\n", mmc_hostname(card->host), ++ 1 << bus_width); ++ err = 0; ++ } else { ++ mmc_set_bus_width(card->host, bus_width); ++ } + } + + if (!oldcard) +@@ -507,12 +527,10 @@ static void mmc_detect(struct mmc_host * + } + } + +-#ifdef CONFIG_MMC_UNSAFE_RESUME +- + /* + * Suspend callback from host. + */ +-static void mmc_suspend(struct mmc_host *host) ++static int mmc_suspend(struct mmc_host *host) + { + BUG_ON(!host); + BUG_ON(!host->card); +@@ -522,6 +540,8 @@ static void mmc_suspend(struct mmc_host + mmc_deselect_cards(host); + host->card->state &= ~MMC_STATE_HIGHSPEED; + mmc_release_host(host); ++ ++ return 0; + } + + /* +@@ -530,7 +550,7 @@ static void mmc_suspend(struct mmc_host + * This function tries to determine if the same card is still present + * and, if so, restore all state to it. + */ +-static void mmc_resume(struct mmc_host *host) ++static int mmc_resume(struct mmc_host *host) + { + int err; + +@@ -541,30 +561,99 @@ static void mmc_resume(struct mmc_host * + err = mmc_init_card(host, host->ocr, host->card); + mmc_release_host(host); + +- if (err) { +- mmc_remove(host); ++ return err; ++} + +- mmc_claim_host(host); +- mmc_detach_bus(host); +- mmc_release_host(host); ++static void mmc_power_restore(struct mmc_host *host) ++{ ++ host->card->state &= ~MMC_STATE_HIGHSPEED; ++ mmc_claim_host(host); ++ mmc_init_card(host, host->ocr, host->card); ++ mmc_release_host(host); ++} ++ ++static int mmc_sleep(struct mmc_host *host) ++{ ++ struct mmc_card *card = host->card; ++ int err = -ENOSYS; ++ ++ if (card && card->ext_csd.rev >= 3) { ++ err = mmc_card_sleepawake(host, 1); ++ if (err < 0) ++ pr_debug("%s: Error %d while putting card into sleep", ++ mmc_hostname(host), err); + } + ++ return err; + } + +-#else ++static int mmc_awake(struct mmc_host *host) ++{ ++ struct mmc_card *card = host->card; ++ int err = -ENOSYS; + +-#define mmc_suspend NULL +-#define mmc_resume NULL ++ if (card && card->ext_csd.rev >= 3) { ++ err = mmc_card_sleepawake(host, 0); ++ if (err < 0) ++ pr_debug("%s: Error %d while awaking sleeping card", ++ mmc_hostname(host), err); ++ } + +-#endif ++ return err; ++} ++ ++#ifdef CONFIG_MMC_UNSAFE_RESUME ++ ++static const struct mmc_bus_ops mmc_ops = { ++ .awake = mmc_awake, ++ .sleep = mmc_sleep, ++ .remove = mmc_remove, ++ .detect = mmc_detect, ++ .suspend = mmc_suspend, ++ .resume = mmc_resume, ++ .power_restore = mmc_power_restore, ++}; ++ ++static void mmc_attach_bus_ops(struct mmc_host *host) ++{ ++ mmc_attach_bus(host, &mmc_ops); ++} ++ ++#else + + static const struct mmc_bus_ops mmc_ops = { ++ .awake = mmc_awake, ++ .sleep = mmc_sleep, ++ .remove = mmc_remove, ++ .detect = mmc_detect, ++ .suspend = NULL, ++ .resume = NULL, ++ .power_restore = mmc_power_restore, ++}; ++ ++static const struct mmc_bus_ops mmc_ops_unsafe = { ++ .awake = mmc_awake, ++ .sleep = mmc_sleep, + .remove = mmc_remove, + .detect = mmc_detect, + .suspend = mmc_suspend, + .resume = mmc_resume, ++ .power_restore = mmc_power_restore, + }; + ++static void mmc_attach_bus_ops(struct mmc_host *host) ++{ ++ const struct mmc_bus_ops *bus_ops; ++ ++ if (host->caps & MMC_CAP_NONREMOVABLE) ++ bus_ops = &mmc_ops_unsafe; ++ else ++ bus_ops = &mmc_ops; ++ mmc_attach_bus(host, bus_ops); ++} ++ ++#endif ++ + /* + * Starting point for MMC card init. + */ +@@ -575,7 +664,7 @@ int mmc_attach_mmc(struct mmc_host *host + BUG_ON(!host); + WARN_ON(!host->claimed); + +- mmc_attach_bus(host, &mmc_ops); ++ mmc_attach_bus_ops(host); + + /* + * We need to get OCR a different way for SPI. +--- a/drivers/mmc/core/mmc_ops.c ++++ b/drivers/mmc/core/mmc_ops.c +@@ -57,6 +57,42 @@ int mmc_deselect_cards(struct mmc_host * + return _mmc_select_card(host, NULL); + } + ++int mmc_card_sleepawake(struct mmc_host *host, int sleep) ++{ ++ struct mmc_command cmd; ++ struct mmc_card *card = host->card; ++ int err; ++ ++ if (sleep) ++ mmc_deselect_cards(host); ++ ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ ++ cmd.opcode = MMC_SLEEP_AWAKE; ++ cmd.arg = card->rca << 16; ++ if (sleep) ++ cmd.arg |= 1 << 15; ++ ++ cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; ++ err = mmc_wait_for_cmd(host, &cmd, 0); ++ if (err) ++ return err; ++ ++ /* ++ * If the host does not wait while the card signals busy, then we will ++ * will have to wait the sleep/awake timeout. Note, we cannot use the ++ * SEND_STATUS command to poll the status because that command (and most ++ * others) is invalid while the card sleeps. ++ */ ++ if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) ++ mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000)); ++ ++ if (!sleep) ++ err = mmc_select_card(card); ++ ++ return err; ++} ++ + int mmc_go_idle(struct mmc_host *host) + { + int err; +@@ -354,6 +390,7 @@ int mmc_switch(struct mmc_card *card, u8 + { + int err; + struct mmc_command cmd; ++ u32 status; + + BUG_ON(!card); + BUG_ON(!card->host); +@@ -371,6 +408,28 @@ int mmc_switch(struct mmc_card *card, u8 + if (err) + return err; + ++ /* Must check status to be sure of no errors */ ++ do { ++ err = mmc_send_status(card, &status); ++ if (err) ++ return err; ++ if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) ++ break; ++ if (mmc_host_is_spi(card->host)) ++ break; ++ } while (R1_CURRENT_STATE(status) == 7); ++ ++ if (mmc_host_is_spi(card->host)) { ++ if (status & R1_SPI_ILLEGAL_COMMAND) ++ return -EBADMSG; ++ } else { ++ if (status & 0xFDFFA000) ++ printk(KERN_WARNING "%s: unexpected status %#x after " ++ "switch", mmc_hostname(card->host), status); ++ if (status & R1_SWITCH_ERROR) ++ return -EBADMSG; ++ } ++ + return 0; + } + +--- a/drivers/mmc/core/mmc_ops.h ++++ b/drivers/mmc/core/mmc_ops.h +@@ -25,6 +25,7 @@ int mmc_send_status(struct mmc_card *car + int mmc_send_cid(struct mmc_host *host, u32 *cid); + int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); + int mmc_spi_set_crc(struct mmc_host *host, int use_crc); ++int mmc_card_sleepawake(struct mmc_host *host, int sleep); + + #endif + +--- a/drivers/mmc/core/sd.c ++++ b/drivers/mmc/core/sd.c +@@ -314,7 +314,7 @@ static struct attribute_group sd_std_att + .attrs = sd_std_attrs, + }; + +-static struct attribute_group *sd_attr_groups[] = { ++static const struct attribute_group *sd_attr_groups[] = { + &sd_std_attr_group, + NULL, + }; +@@ -561,12 +561,10 @@ static void mmc_sd_detect(struct mmc_hos + } + } + +-#ifdef CONFIG_MMC_UNSAFE_RESUME +- + /* + * Suspend callback from host. + */ +-static void mmc_sd_suspend(struct mmc_host *host) ++static int mmc_sd_suspend(struct mmc_host *host) + { + BUG_ON(!host); + BUG_ON(!host->card); +@@ -576,6 +574,8 @@ static void mmc_sd_suspend(struct mmc_ho + mmc_deselect_cards(host); + host->card->state &= ~MMC_STATE_HIGHSPEED; + mmc_release_host(host); ++ ++ return 0; + } + + /* +@@ -584,7 +584,7 @@ static void mmc_sd_suspend(struct mmc_ho + * This function tries to determine if the same card is still present + * and, if so, restore all state to it. + */ +-static void mmc_sd_resume(struct mmc_host *host) ++static int mmc_sd_resume(struct mmc_host *host) + { + int err; + +@@ -595,30 +595,63 @@ static void mmc_sd_resume(struct mmc_hos + err = mmc_sd_init_card(host, host->ocr, host->card); + mmc_release_host(host); + +- if (err) { +- mmc_sd_remove(host); +- +- mmc_claim_host(host); +- mmc_detach_bus(host); +- mmc_release_host(host); +- } ++ return err; ++} + ++static void mmc_sd_power_restore(struct mmc_host *host) ++{ ++ host->card->state &= ~MMC_STATE_HIGHSPEED; ++ mmc_claim_host(host); ++ mmc_sd_init_card(host, host->ocr, host->card); ++ mmc_release_host(host); + } + +-#else ++#ifdef CONFIG_MMC_UNSAFE_RESUME + +-#define mmc_sd_suspend NULL +-#define mmc_sd_resume NULL ++static const struct mmc_bus_ops mmc_sd_ops = { ++ .remove = mmc_sd_remove, ++ .detect = mmc_sd_detect, ++ .suspend = mmc_sd_suspend, ++ .resume = mmc_sd_resume, ++ .power_restore = mmc_sd_power_restore, ++}; + +-#endif ++static void mmc_sd_attach_bus_ops(struct mmc_host *host) ++{ ++ mmc_attach_bus(host, &mmc_sd_ops); ++} ++ ++#else + + static const struct mmc_bus_ops mmc_sd_ops = { + .remove = mmc_sd_remove, + .detect = mmc_sd_detect, ++ .suspend = NULL, ++ .resume = NULL, ++ .power_restore = mmc_sd_power_restore, ++}; ++ ++static const struct mmc_bus_ops mmc_sd_ops_unsafe = { ++ .remove = mmc_sd_remove, ++ .detect = mmc_sd_detect, + .suspend = mmc_sd_suspend, + .resume = mmc_sd_resume, ++ .power_restore = mmc_sd_power_restore, + }; + ++static void mmc_sd_attach_bus_ops(struct mmc_host *host) ++{ ++ const struct mmc_bus_ops *bus_ops; ++ ++ if (host->caps & MMC_CAP_NONREMOVABLE) ++ bus_ops = &mmc_sd_ops_unsafe; ++ else ++ bus_ops = &mmc_sd_ops; ++ mmc_attach_bus(host, bus_ops); ++} ++ ++#endif ++ + /* + * Starting point for SD card init. + */ +@@ -629,7 +662,7 @@ int mmc_attach_sd(struct mmc_host *host, + BUG_ON(!host); + WARN_ON(!host->claimed); + +- mmc_attach_bus(host, &mmc_sd_ops); ++ mmc_sd_attach_bus_ops(host); + + /* + * We need to get OCR a different way for SPI. +--- a/drivers/mmc/core/sdio_bus.c ++++ b/drivers/mmc/core/sdio_bus.c +@@ -20,9 +20,6 @@ + #include "sdio_cis.h" + #include "sdio_bus.h" + +-#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) +-#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) +- + /* show configuration fields */ + #define sdio_config_attr(field, format_string) \ + static ssize_t \ +@@ -251,12 +248,15 @@ int sdio_add_func(struct sdio_func *func + /* + * Unregister a SDIO function with the driver model, and + * (eventually) free it. ++ * This function can be called through error paths where sdio_add_func() was ++ * never executed (because a failure occurred at an earlier point). + */ + void sdio_remove_func(struct sdio_func *func) + { +- if (sdio_func_present(func)) +- device_del(&func->dev); ++ if (!sdio_func_present(func)) ++ return; + ++ device_del(&func->dev); + put_device(&func->dev); + } + +--- a/drivers/mmc/core/sdio.c ++++ b/drivers/mmc/core/sdio.c +@@ -165,6 +165,29 @@ static int sdio_enable_wide(struct mmc_c + } + + /* ++ * If desired, disconnect the pull-up resistor on CD/DAT[3] (pin 1) ++ * of the card. This may be required on certain setups of boards, ++ * controllers and embedded sdio device which do not need the card's ++ * pull-up. As a result, card detection is disabled and power is saved. ++ */ ++static int sdio_disable_cd(struct mmc_card *card) ++{ ++ int ret; ++ u8 ctrl; ++ ++ if (!card->cccr.disable_cd) ++ return 0; ++ ++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl); ++ if (ret) ++ return ret; ++ ++ ctrl |= SDIO_BUS_CD_DISABLE; ++ ++ return mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL); ++} ++ ++/* + * Test if the card supports high-speed mode and, if so, switch to it. + */ + static int sdio_enable_hs(struct mmc_card *card) +@@ -195,6 +218,135 @@ static int sdio_enable_hs(struct mmc_car + } + + /* ++ * 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) ++{ ++ struct mmc_card *card; ++ int err; ++ ++ BUG_ON(!host); ++ WARN_ON(!host->claimed); ++ ++ /* ++ * Inform the card of the voltage ++ */ ++ err = mmc_send_io_op_cond(host, host->ocr, &ocr); ++ if (err) ++ goto err; ++ ++ /* ++ * For SPI, enable CRC as appropriate. ++ */ ++ if (mmc_host_is_spi(host)) { ++ err = mmc_spi_set_crc(host, use_spi_crc); ++ if (err) ++ goto err; ++ } ++ ++ /* ++ * Allocate card structure. ++ */ ++ card = mmc_alloc_card(host, NULL); ++ if (IS_ERR(card)) { ++ err = PTR_ERR(card); ++ goto err; ++ } ++ ++ card->type = MMC_TYPE_SDIO; ++ ++ /* ++ * For native busses: set card RCA and quit open drain mode. ++ */ ++ if (!mmc_host_is_spi(host)) { ++ err = mmc_send_relative_addr(host, &card->rca); ++ if (err) ++ goto remove; ++ ++ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); ++ } ++ ++ /* ++ * Select card, as all following commands rely on that. ++ */ ++ if (!mmc_host_is_spi(host)) { ++ err = mmc_select_card(card); ++ if (err) ++ goto remove; ++ } ++ ++ /* ++ * Read the common registers. ++ */ ++ err = sdio_read_cccr(card); ++ if (err) ++ goto remove; ++ ++ /* ++ * Read the common CIS tuples. ++ */ ++ err = sdio_read_common_cis(card); ++ 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 (!same) { ++ err = -ENOENT; ++ goto err; ++ } ++ card = oldcard; ++ return 0; ++ } ++ ++ /* ++ * Switch to high-speed (if supported). ++ */ ++ err = sdio_enable_hs(card); ++ if (err) ++ goto remove; ++ ++ /* ++ * Change to the card's maximum speed. ++ */ ++ if (mmc_card_highspeed(card)) { ++ /* ++ * The SDIO specification doesn't mention how ++ * the CIS transfer speed register relates to ++ * high-speed, but it seems that 50 MHz is ++ * mandatory. ++ */ ++ mmc_set_clock(host, 50000000); ++ } else { ++ mmc_set_clock(host, card->cis.max_dtr); ++ } ++ ++ /* ++ * Switch to wider bus (if supported). ++ */ ++ err = sdio_enable_wide(card); ++ if (err) ++ goto remove; ++ ++ if (!oldcard) ++ host->card = card; ++ return 0; ++ ++remove: ++ if (!oldcard) ++ mmc_remove_card(card); ++ ++err: ++ return err; ++} ++ ++/* + * Host is being removed. Free up the current card. + */ + static void mmc_sdio_remove(struct mmc_host *host) +@@ -243,10 +395,77 @@ static void mmc_sdio_detect(struct mmc_h + } + } + ++/* ++ * SDIO suspend. We need to suspend all functions separately. ++ * Therefore all registered functions must have drivers with suspend ++ * and resume methods. Failing that we simply remove the whole card. ++ */ ++static int mmc_sdio_suspend(struct mmc_host *host) ++{ ++ int i, err = 0; ++ ++ for (i = 0; i < host->card->sdio_funcs; i++) { ++ struct sdio_func *func = host->card->sdio_func[i]; ++ if (func && sdio_func_present(func) && func->dev.driver) { ++ const struct dev_pm_ops *pmops = func->dev.driver->pm; ++ if (!pmops || !pmops->suspend || !pmops->resume) { ++ /* force removal of entire card in that case */ ++ err = -ENOSYS; ++ } else ++ err = pmops->suspend(&func->dev); ++ if (err) ++ break; ++ } ++ } ++ while (err && --i >= 0) { ++ struct sdio_func *func = host->card->sdio_func[i]; ++ if (func && sdio_func_present(func) && func->dev.driver) { ++ const struct dev_pm_ops *pmops = func->dev.driver->pm; ++ pmops->resume(&func->dev); ++ } ++ } ++ ++ return err; ++} ++ ++static int mmc_sdio_resume(struct mmc_host *host) ++{ ++ int i, err; ++ ++ BUG_ON(!host); ++ BUG_ON(!host->card); ++ ++ /* Basic card reinitialization. */ ++ mmc_claim_host(host); ++ err = mmc_sdio_init_card(host, host->ocr, host->card); ++ mmc_release_host(host); ++ ++ /* ++ * If the card looked to be the same as before suspending, then ++ * we proceed to resume all card functions. If one of them returns ++ * an error then we simply return that error to the core and the ++ * card will be redetected as new. It is the responsibility of ++ * the function driver to perform further tests with the extra ++ * knowledge it has of the card to confirm the card is indeed the ++ * same as before suspending (same MAC address for network cards, ++ * etc.) and return an error otherwise. ++ */ ++ for (i = 0; !err && i < host->card->sdio_funcs; i++) { ++ struct sdio_func *func = host->card->sdio_func[i]; ++ if (func && sdio_func_present(func) && func->dev.driver) { ++ const struct dev_pm_ops *pmops = func->dev.driver->pm; ++ err = pmops->resume(&func->dev); ++ } ++ } ++ ++ return err; ++} + + static const struct mmc_bus_ops mmc_sdio_ops = { + .remove = mmc_sdio_remove, + .detect = mmc_sdio_detect, ++ .suspend = mmc_sdio_suspend, ++ .resume = mmc_sdio_resume, + }; + + +@@ -275,13 +494,6 @@ int mmc_attach_sdio(struct mmc_host *hos + ocr &= ~0x7F; + } + +- if (ocr & MMC_VDD_165_195) { +- printk(KERN_WARNING "%s: SDIO card claims to support the " +- "incompletely defined 'low voltage range'. This " +- "will be ignored.\n", mmc_hostname(host)); +- ocr &= ~MMC_VDD_165_195; +- } +- + host->ocr = mmc_select_voltage(host, ocr); + + /* +@@ -293,108 +505,31 @@ int mmc_attach_sdio(struct mmc_host *hos + } + + /* +- * Inform the card of the voltage ++ * Detect and init the card. + */ +- err = mmc_send_io_op_cond(host, host->ocr, &ocr); ++ err = mmc_sdio_init_card(host, host->ocr, NULL); + if (err) + goto err; +- +- /* +- * For SPI, enable CRC as appropriate. +- */ +- if (mmc_host_is_spi(host)) { +- err = mmc_spi_set_crc(host, use_spi_crc); +- if (err) +- goto err; +- } ++ card = host->card; + + /* + * The number of functions on the card is encoded inside + * the ocr. + */ + funcs = (ocr & 0x70000000) >> 28; ++ card->sdio_funcs = 0; + + /* +- * Allocate card structure. +- */ +- card = mmc_alloc_card(host, NULL); +- if (IS_ERR(card)) { +- err = PTR_ERR(card); +- goto err; +- } +- +- card->type = MMC_TYPE_SDIO; +- card->sdio_funcs = funcs; +- +- host->card = card; +- +- /* +- * For native busses: set card RCA and quit open drain mode. ++ * If needed, disconnect card detection pull-up resistor. + */ +- if (!mmc_host_is_spi(host)) { +- err = mmc_send_relative_addr(host, &card->rca); +- if (err) +- goto remove; +- +- mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); +- } +- +- /* +- * Select card, as all following commands rely on that. +- */ +- if (!mmc_host_is_spi(host)) { +- err = mmc_select_card(card); +- if (err) +- goto remove; +- } +- +- /* +- * Read the common registers. +- */ +- err = sdio_read_cccr(card); +- if (err) +- goto remove; +- +- /* +- * Read the common CIS tuples. +- */ +- err = sdio_read_common_cis(card); +- if (err) +- goto remove; +- +- /* +- * Switch to high-speed (if supported). +- */ +- err = sdio_enable_hs(card); +- if (err) +- goto remove; +- +- /* +- * Change to the card's maximum speed. +- */ +- if (mmc_card_highspeed(card)) { +- /* +- * The SDIO specification doesn't mention how +- * the CIS transfer speed register relates to +- * high-speed, but it seems that 50 MHz is +- * mandatory. +- */ +- mmc_set_clock(host, 50000000); +- } else { +- mmc_set_clock(host, card->cis.max_dtr); +- } +- +- /* +- * Switch to wider bus (if supported). +- */ +- err = sdio_enable_wide(card); ++ err = sdio_disable_cd(card); + if (err) + goto remove; + + /* + * Initialize (but don't add) all present functions. + */ +- for (i = 0;i < funcs;i++) { ++ for (i = 0; i < funcs; i++, card->sdio_funcs++) { + err = sdio_init_func(host->card, i + 1); + if (err) + goto remove; +--- a/drivers/mmc/core/sdio_cis.c ++++ b/drivers/mmc/core/sdio_cis.c +@@ -29,6 +29,8 @@ static int cistpl_vers_1(struct mmc_card + unsigned i, nr_strings; + char **buffer, *string; + ++ /* Find all null-terminated (including zero length) strings in ++ the TPLLV1_INFO field. Trailing garbage is ignored. */ + buf += 2; + size -= 2; + +@@ -39,11 +41,8 @@ static int cistpl_vers_1(struct mmc_card + if (buf[i] == 0) + nr_strings++; + } +- +- if (buf[i-1] != '\0') { +- printk(KERN_WARNING "SDIO: ignoring broken CISTPL_VERS_1\n"); ++ if (nr_strings == 0) + return 0; +- } + + size = i; + +@@ -98,6 +97,22 @@ static const unsigned char speed_val[16] + static const unsigned int speed_unit[8] = + { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; + ++/* FUNCE tuples with these types get passed to SDIO drivers */ ++static const unsigned char funce_type_whitelist[] = { ++ 4 /* CISTPL_FUNCE_LAN_NODE_ID used in Broadcom cards */ ++}; ++ ++static int cistpl_funce_whitelisted(unsigned char type) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(funce_type_whitelist); i++) { ++ if (funce_type_whitelist[i] == type) ++ return 1; ++ } ++ return 0; ++} ++ + static int cistpl_funce_common(struct mmc_card *card, + const unsigned char *buf, unsigned size) + { +@@ -120,6 +135,10 @@ static int cistpl_funce_func(struct sdio + unsigned vsn; + unsigned min_size; + ++ /* let SDIO drivers take care of whitelisted FUNCE tuples */ ++ if (cistpl_funce_whitelisted(buf[0])) ++ return -EILSEQ; ++ + vsn = func->card->cccr.sdio_vsn; + min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42; + +@@ -154,13 +173,12 @@ static int cistpl_funce(struct mmc_card + else + ret = cistpl_funce_common(card, buf, size); + +- if (ret) { ++ if (ret && ret != -EILSEQ) { + printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u " + "type %u\n", mmc_hostname(card->host), size, buf[0]); +- return ret; + } + +- return 0; ++ return ret; + } + + typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *, +@@ -253,21 +271,12 @@ static int sdio_read_cis(struct mmc_card + for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++) + if (cis_tpl_list[i].code == tpl_code) + break; +- if (i >= ARRAY_SIZE(cis_tpl_list)) { +- /* this tuple is unknown to the core */ +- this->next = NULL; +- this->code = tpl_code; +- this->size = tpl_link; +- *prev = this; +- prev = &this->next; +- printk(KERN_DEBUG +- "%s: queuing CIS tuple 0x%02x length %u\n", +- mmc_hostname(card->host), tpl_code, tpl_link); +- } else { ++ if (i < ARRAY_SIZE(cis_tpl_list)) { + const struct cis_tpl *tpl = cis_tpl_list + i; + if (tpl_link < tpl->min_size) { + printk(KERN_ERR +- "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u)\n", ++ "%s: bad CIS tuple 0x%02x" ++ " (length = %u, expected >= %u)\n", + mmc_hostname(card->host), + tpl_code, tpl_link, tpl->min_size); + ret = -EINVAL; +@@ -275,7 +284,30 @@ static int sdio_read_cis(struct mmc_card + ret = tpl->parse(card, func, + this->data, tpl_link); + } +- kfree(this); ++ /* ++ * We don't need the tuple anymore if it was ++ * successfully parsed by the SDIO core or if it is ++ * not going to be parsed by SDIO drivers. ++ */ ++ if (!ret || ret != -EILSEQ) ++ kfree(this); ++ } else { ++ /* unknown tuple */ ++ ret = -EILSEQ; ++ } ++ ++ if (ret == -EILSEQ) { ++ /* this tuple is unknown to the core or whitelisted */ ++ this->next = NULL; ++ this->code = tpl_code; ++ this->size = tpl_link; ++ *prev = this; ++ prev = &this->next; ++ printk(KERN_DEBUG ++ "%s: queuing CIS tuple 0x%02x length %u\n", ++ mmc_hostname(card->host), tpl_code, tpl_link); ++ /* keep on analyzing tuples */ ++ ret = 0; + } + + ptr += tpl_link; +--- a/drivers/mmc/core/sdio_io.c ++++ b/drivers/mmc/core/sdio_io.c +@@ -624,7 +624,7 @@ void sdio_f0_writeb(struct sdio_func *fu + + BUG_ON(!func); + +- if (addr < 0xF0 || addr > 0xFF) { ++ if ((addr < 0xF0 || addr > 0xFF) && (!mmc_card_lenient_fn0(func->card))) { + if (err_ret) + *err_ret = -EINVAL; + return; +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -55,6 +55,17 @@ config MMC_SDHCI_PCI + + If unsure, say N. + ++config MMC_SDHCI_CNS3XXX ++ tristate "SDHCI support on CNS3XXX" ++ depends on MMC_SDHCI && ARCH_CNS3XXX ++ help ++ This selects the Secure Digital Host Controller Interface (SDHCI) ++ in Cavium Networks CNS3XXX SOCs. ++ ++ If you have a controller with this interface, say Y or M here. ++ ++ If unsure, say N. ++ + config MMC_RICOH_MMC + tristate "Ricoh MMC Controller Disabler (EXPERIMENTAL)" + depends on MMC_SDHCI_PCI +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -12,6 +12,7 @@ obj-$(CONFIG_MMC_IMX) += imxmmc.o + obj-$(CONFIG_MMC_MXC) += mxcmmc.o + obj-$(CONFIG_MMC_SDHCI) += sdhci.o + obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o ++obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o + obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o + obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o + obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o +--- a/drivers/mmc/host/sdhci.c ++++ b/drivers/mmc/host/sdhci.c +@@ -27,6 +27,15 @@ + + #define DRIVER_NAME "sdhci" + ++#define SDHCI_DEBUG ++#undef SDHCI_DEBUG ++ ++#ifdef SDHCI_DEBUG ++#define sd_printk(x...) printk(x) ++#else ++#define sd_printk(x...) do { } while(0) ++#endif ++ + #define DBG(f, x...) \ + pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) + +@@ -43,6 +52,39 @@ static void sdhci_finish_data(struct sdh + static void sdhci_send_command(struct sdhci_host *, struct mmc_command *); + static void sdhci_finish_command(struct sdhci_host *); + ++static void sdhci_dumpallregs(struct sdhci_host *host) ++{ ++#ifdef SDHCI_DEBUG ++ printk(" _______________________________________________\n"); ++ ++ printk(" 0x00: 0x%08x | 0x04: 0x%08x\n", sdhci_readl(host, 0x00), sdhci_readl(host, 0x04)); ++ printk(" 0x08: 0x%08x | 0x0C: 0x%08x\n", sdhci_readl(host, 0x08), sdhci_readl(host, 0x0C)); ++ printk(" 0x10: 0x%08x | 0x14: 0x%08x\n", sdhci_readl(host, 0x10), sdhci_readl(host, 0x14)); ++ printk(" 0x18: 0x%08x | 0x1C: 0x%08x\n", sdhci_readl(host, 0x18), sdhci_readl(host, 0x1C)); ++ printk(" -----------------| 0x24: 0x%08x\n", sdhci_readl(host, 0x24)); ++ printk(" 0x28: 0x%08x | 0x2C: 0x%08x\n", sdhci_readl(host, 0x28), sdhci_readl(host, 0x2C)); ++ printk(" 0x30: 0x%08x | 0x34: 0x%08x\n", sdhci_readl(host, 0x30), sdhci_readl(host, 0x34)); ++ printk(" 0x38: 0x%08x | 0x3C: 0x%08x\n", sdhci_readl(host, 0x38), sdhci_readl(host, 0x3C)); ++ printk(" 0x40: 0x%08x | 0x44: 0x%08x\n", sdhci_readl(host, 0x40), sdhci_readl(host, 0x44)); ++ printk(" 0x48: 0x%08x | 0x4C: 0x%08x\n", sdhci_readl(host, 0x48), sdhci_readl(host, 0x4C)); ++ printk(" 0x50: 0x%08x | 0xFC: 0x%08x\n", sdhci_readl(host, 0x50), sdhci_readl(host, 0xFC)); ++//#else ++ printk(KERN_DEBUG " _______________________________________________\n"); ++ ++ printk(KERN_DEBUG " 0x00: 0x%08x | 0x04: 0x%08x\n", sdhci_readl(host, 0x00), sdhci_readl(host, 0x04)); ++ printk(KERN_DEBUG " 0x08: 0x%08x | 0x0C: 0x%08x\n", sdhci_readl(host, 0x08), sdhci_readl(host, 0x0C)); ++ printk(KERN_DEBUG " 0x10: 0x%08x | 0x14: 0x%08x\n", sdhci_readl(host, 0x10), sdhci_readl(host, 0x14)); ++ printk(KERN_DEBUG " 0x18: 0x%08x | 0x1C: 0x%08x\n", sdhci_readl(host, 0x18), sdhci_readl(host, 0x1C)); ++ printk(KERN_DEBUG " -----------------| 0x24: 0x%08x\n", sdhci_readl(host, 0x24)); ++ printk(KERN_DEBUG " 0x28: 0x%08x | 0x2C: 0x%08x\n", sdhci_readl(host, 0x28), sdhci_readl(host, 0x2C)); ++ printk(KERN_DEBUG " 0x30: 0x%08x | 0x34: 0x%08x\n", sdhci_readl(host, 0x30), sdhci_readl(host, 0x34)); ++ printk(KERN_DEBUG " 0x38: 0x%08x | 0x3C: 0x%08x\n", sdhci_readl(host, 0x38), sdhci_readl(host, 0x3C)); ++ printk(KERN_DEBUG " 0x40: 0x%08x | 0x44: 0x%08x\n", sdhci_readl(host, 0x40), sdhci_readl(host, 0x44)); ++ printk(KERN_DEBUG " 0x48: 0x%08x | 0x4C: 0x%08x\n", sdhci_readl(host, 0x48), sdhci_readl(host, 0x4C)); ++ printk(KERN_DEBUG " 0x50: 0x%08x | 0xFC: 0x%08x\n", sdhci_readl(host, 0x50), sdhci_readl(host, 0xFC)); ++#endif ++} ++ + static void sdhci_dumpregs(struct sdhci_host *host) + { + printk(KERN_DEBUG DRIVER_NAME ": ============== REGISTER DUMP ==============\n"); +@@ -591,6 +633,9 @@ static u8 sdhci_calc_timeout(struct sdhc + target_timeout = data->timeout_ns / 1000 + + data->timeout_clks / host->clock; + ++ if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) ++ host->timeout_clk = host->clock / 1000; ++ + /* + * Figure out needed cycles. + * We do this in steps in order to fit inside a 32 bit int. +@@ -622,7 +667,7 @@ static u8 sdhci_calc_timeout(struct sdhc + static void sdhci_set_transfer_irqs(struct sdhci_host *host) + { + u32 pio_irqs = SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL; +- u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR; ++ u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ACMD12ERR | SDHCI_INT_ADMA_ERROR; + + if (host->flags & SDHCI_REQ_USE_DMA) + sdhci_clear_set_irqs(host, pio_irqs, dma_irqs); +@@ -652,7 +697,7 @@ static void sdhci_prepare_data(struct sd + count = sdhci_calc_timeout(host, data); + sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL); + +- if (host->flags & SDHCI_USE_DMA) ++ if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) + host->flags |= SDHCI_REQ_USE_DMA; + + /* +@@ -736,11 +781,21 @@ static void sdhci_prepare_data(struct sd + } else { + int sg_cnt; + ++ sd_printk("[SDHCI_DEBUG] dma_map_sg(), mmc_dev(host->mmc) = %p \n", mmc_dev(host->mmc)); ++ sd_printk("[SDHCI_DEBUG] dma_map_sg(), data->sg = %p \n", data->sg); ++ sd_printk("[SDHCI_DEBUG] dma_map_sg(), data->sg_len = %d \n", data->sg_len); + sg_cnt = dma_map_sg(mmc_dev(host->mmc), + data->sg, data->sg_len, + (data->flags & MMC_DATA_READ) ? + DMA_FROM_DEVICE : + DMA_TO_DEVICE); ++ if (data->sg == NULL) { ++ sd_printk("[SDHCI_DEBUG] dma_map_sg(), data->sg = (NULL) \n"); ++ return; ++ } ++ sd_printk("[SDHCI_DEBUG] dma_map_sg(), data->sg = %p \n", data->sg); ++ sd_printk("[SDHCI_DEBUG] dma_map_sg(), sg_cnt = %d \n", sg_cnt); ++ + if (sg_cnt == 0) { + /* + * This only happens when someone fed +@@ -750,6 +805,7 @@ static void sdhci_prepare_data(struct sd + host->flags &= ~SDHCI_REQ_USE_DMA; + } else { + WARN_ON(sg_cnt != 1); ++ sd_printk("[SDHCI_DEBUG] sg_dma_address() => %08x \n", sg_dma_address(data->sg)); + sdhci_writel(host, sg_dma_address(data->sg), + SDHCI_DMA_ADDRESS); + } +@@ -763,14 +819,32 @@ static void sdhci_prepare_data(struct sd + */ + if (host->version >= SDHCI_SPEC_200) { + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ++#ifdef SDHCI_USE_LEDS_CLASS ++ ctrl |= SDHCI_CTRL_LED; ++#else ++ ctrl &= ~SDHCI_CTRL_LED; ++#endif + ctrl &= ~SDHCI_CTRL_DMA_MASK; + if ((host->flags & SDHCI_REQ_USE_DMA) && + (host->flags & SDHCI_USE_ADMA)) + ctrl |= SDHCI_CTRL_ADMA32; + else + ctrl |= SDHCI_CTRL_SDMA; ++ + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); +- } ++ } else if (host->version == SDHCI_SPEC_100) { ++ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ++#ifdef SDHCI_USE_LEDS_CLASS ++ ctrl |= SDHCI_CTRL_LED; ++#else ++ ctrl &= ~SDHCI_CTRL_LED; ++#endif ++ ctrl &= ~SDHCI_CTRL_DMA_MASK; ++ if (host->flags & SDHCI_REQ_USE_DMA) ++ ctrl |= SDHCI_CTRL_SDMA; ++ ++ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); ++ } + + if (!(host->flags & SDHCI_REQ_USE_DMA)) { + int flags; +@@ -795,15 +869,26 @@ static void sdhci_set_transfer_mode(stru + struct mmc_data *data) + { + u16 mode; ++ u8 bgctrl; + + if (data == NULL) + return; + + WARN_ON(!host->data); + ++ bgctrl = sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL); ++ if (host->quirks & SDHCI_QUIRK_READ_WAIT_CTRL) ++ bgctrl |= SDHCI_READ_WAIT_CTRL; ++ sdhci_writeb(host, bgctrl, SDHCI_BLOCK_GAP_CONTROL); ++ + mode = SDHCI_TRNS_BLK_CNT_EN; +- if (data->blocks > 1) ++ ++ if (data->blocks > 1) { + mode |= SDHCI_TRNS_MULTI; ++ ++ if (host->quirks & SDHCI_QUIRK_AUTO_CMD12) ++ mode |= SDHCI_TRNS_ACMD12; ++ } + if (data->flags & MMC_DATA_READ) + mode |= SDHCI_TRNS_READ; + if (host->flags & SDHCI_REQ_USE_DMA) +@@ -812,6 +897,20 @@ static void sdhci_set_transfer_mode(stru + sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); + } + ++static void shdci_check_dma_overrun(struct sdhci_host *host, struct mmc_data *data) ++{ ++ u32 dma_pos = sdhci_readl(host, SDHCI_DMA_ADDRESS); ++ u32 dma_start = sg_dma_address(data->sg); ++ u32 dma_end = dma_start + data->sg->length; ++ ++ /* Test whether we ended up moving more data than was originally requested. */ ++ if (dma_pos <= dma_end) ++ return; ++ ++ printk(KERN_ERR "%s: dma overrun, dma %08x, req %08x..%08x\n", ++ mmc_hostname(host->mmc), dma_pos, dma_start, dma_end); ++} ++ + static void sdhci_finish_data(struct sdhci_host *host) + { + struct mmc_data *data; +@@ -825,6 +924,9 @@ static void sdhci_finish_data(struct sdh + if (host->flags & SDHCI_USE_ADMA) + sdhci_adma_table_post(host, data); + else { ++ shdci_check_dma_overrun(host, data); ++ ++ sd_printk("[SDHCI_DEBUG] dma_unmap_sg(), data->sg_len = %d \n", data->sg_len); + dma_unmap_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, (data->flags & MMC_DATA_READ) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); +@@ -866,12 +968,16 @@ static void sdhci_send_command(struct sd + + WARN_ON(host->cmd); + ++ sd_printk("[SDHCI_DEBUG] sdhci_send_command() \n"); ++ + /* Wait max 10 ms */ + timeout = 10; + + mask = SDHCI_CMD_INHIBIT; + if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY)) + mask |= SDHCI_DATA_INHIBIT; ++ if ((cmd->data != NULL)) ++ mask |= SDHCI_DATA_INHIBIT; + + /* We shouldn't wait for data inihibit for stop commands, even + though they might use busy signaling */ +@@ -925,7 +1031,11 @@ static void sdhci_send_command(struct sd + if (cmd->data) + flags |= SDHCI_CMD_DATA; + ++ sd_printk("[SDHCI_DEBUG] sdhci_send_command() => %08x \n", SDHCI_MAKE_CMD(cmd->opcode, flags)); ++ sdhci_dumpallregs(host); + sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); ++ sd_printk("[SDHCI_DEBUG] sdhci_send_command(): After issue command \n"); ++ sdhci_dumpallregs(host); + } + + static void sdhci_finish_command(struct sdhci_host *host) +@@ -934,6 +1044,8 @@ static void sdhci_finish_command(struct + + BUG_ON(host->cmd == NULL); + ++ sd_printk("[SDHCI_DEBUG] sdhci_finish_command() \n"); ++ + if (host->cmd->flags & MMC_RSP_PRESENT) { + if (host->cmd->flags & MMC_RSP_136) { + /* CRC is stripped so we need to do some shifting. */ +@@ -991,8 +1103,8 @@ static void sdhci_set_clock(struct sdhci + clk |= SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + +- /* Wait max 10 ms */ +- timeout = 10; ++ /* Wait max 20 ms */ ++ timeout = 20; + while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) + & SDHCI_CLOCK_INT_STABLE)) { + if (timeout == 0) { +@@ -1154,6 +1266,12 @@ static void sdhci_set_ios(struct mmc_hos + else + ctrl &= ~SDHCI_CTRL_HISPD; + ++#ifdef SDHCI_USE_LEDS_CLASS ++ ctrl |= SDHCI_CTRL_LED; ++#else ++ ctrl &= ~SDHCI_CTRL_LED; ++#endif ++ + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + + /* +@@ -1321,7 +1439,11 @@ static void sdhci_timeout_timer(unsigned + if (host->mrq) { + printk(KERN_ERR "%s: Timeout waiting for hardware " + "interrupt.\n", mmc_hostname(host->mmc)); ++#ifdef SDHCI_DEBUG ++ sdhci_dumpallregs(host); ++#else + sdhci_dumpregs(host); ++#endif + + if (host->data) { + host->data->error = -ETIMEDOUT; +@@ -1508,6 +1630,10 @@ static irqreturn_t sdhci_irq(int irq, vo + DBG("*** %s got interrupt: 0x%08x\n", + mmc_hostname(host->mmc), intmask); + ++#ifdef SDHCI_DEBUG ++ printk("*** %s got interrupt: 0x%08x\n", mmc_hostname(host->mmc), intmask); ++#endif ++ + if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { + sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | + SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); +@@ -1597,7 +1723,7 @@ int sdhci_resume_host(struct sdhci_host + { + int ret; + +- if (host->flags & SDHCI_USE_DMA) { ++ if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { + if (host->ops->enable_dma) + host->ops->enable_dma(host); + } +@@ -1678,23 +1804,20 @@ int sdhci_add_host(struct sdhci_host *ho + caps = sdhci_readl(host, SDHCI_CAPABILITIES); + + if (host->quirks & SDHCI_QUIRK_FORCE_DMA) +- host->flags |= SDHCI_USE_DMA; +- else if (!(caps & SDHCI_CAN_DO_DMA)) +- DBG("Controller doesn't have DMA capability\n"); ++ host->flags |= SDHCI_USE_SDMA; ++ else if (!(caps & SDHCI_CAN_DO_SDMA)) ++ DBG("Controller doesn't have SDMA capability\n"); + else +- host->flags |= SDHCI_USE_DMA; ++ host->flags |= SDHCI_USE_SDMA; + + if ((host->quirks & SDHCI_QUIRK_BROKEN_DMA) && +- (host->flags & SDHCI_USE_DMA)) { ++ (host->flags & SDHCI_USE_SDMA)) { + DBG("Disabling DMA as it is marked broken\n"); +- host->flags &= ~SDHCI_USE_DMA; ++ host->flags &= ~SDHCI_USE_SDMA; + } + +- if (host->flags & SDHCI_USE_DMA) { +- if ((host->version >= SDHCI_SPEC_200) && +- (caps & SDHCI_CAN_DO_ADMA2)) +- host->flags |= SDHCI_USE_ADMA; +- } ++ if ((host->version >= SDHCI_SPEC_200) && (caps & SDHCI_CAN_DO_ADMA2)) ++ host->flags |= SDHCI_USE_ADMA; + + if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) && + (host->flags & SDHCI_USE_ADMA)) { +@@ -1702,13 +1825,14 @@ int sdhci_add_host(struct sdhci_host *ho + host->flags &= ~SDHCI_USE_ADMA; + } + +- if (host->flags & SDHCI_USE_DMA) { ++ if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { + if (host->ops->enable_dma) { + if (host->ops->enable_dma(host)) { + printk(KERN_WARNING "%s: No suitable DMA " + "available. Falling back to PIO.\n", + mmc_hostname(mmc)); +- host->flags &= ~(SDHCI_USE_DMA | SDHCI_USE_ADMA); ++ host->flags &= ++ ~(SDHCI_USE_SDMA | SDHCI_USE_ADMA); + } + } + } +@@ -1736,7 +1860,7 @@ int sdhci_add_host(struct sdhci_host *ho + * mask, but PIO does not need the hw shim so we set a new + * mask here in that case. + */ +- if (!(host->flags & SDHCI_USE_DMA)) { ++ if (!(host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA))) { + host->dma_mask = DMA_BIT_MASK(64); + mmc_dev(host->mmc)->dma_mask = &host->dma_mask; + } +@@ -1757,13 +1881,15 @@ int sdhci_add_host(struct sdhci_host *ho + host->timeout_clk = + (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; + if (host->timeout_clk == 0) { +- if (!host->ops->get_timeout_clock) { ++ if (host->ops->get_timeout_clock) { ++ host->timeout_clk = host->ops->get_timeout_clock(host); ++ } else if (!(host->quirks & ++ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) { + printk(KERN_ERR + "%s: Hardware doesn't specify timeout clock " + "frequency.\n", mmc_hostname(mmc)); + return -ENODEV; + } +- host->timeout_clk = host->ops->get_timeout_clock(host); + } + if (caps & SDHCI_TIMEOUT_CLK_UNIT) + host->timeout_clk *= 1000; +@@ -1772,7 +1898,8 @@ int sdhci_add_host(struct sdhci_host *ho + * Set host parameters. + */ + mmc->ops = &sdhci_ops; +- if (host->ops->get_min_clock) ++ if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK && ++ host->ops->set_clock && host->ops->get_min_clock) + mmc->f_min = host->ops->get_min_clock(host); + else + mmc->f_min = host->max_clk / 256; +@@ -1810,7 +1937,7 @@ int sdhci_add_host(struct sdhci_host *ho + */ + if (host->flags & SDHCI_USE_ADMA) + mmc->max_hw_segs = 128; +- else if (host->flags & SDHCI_USE_DMA) ++ else if (host->flags & SDHCI_USE_SDMA) + mmc->max_hw_segs = 1; + else /* PIO */ + mmc->max_hw_segs = 128; +@@ -1893,10 +2020,10 @@ int sdhci_add_host(struct sdhci_host *ho + + mmc_add_host(mmc); + +- printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s%s\n", ++ printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s\n", + mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), +- (host->flags & SDHCI_USE_ADMA)?"A":"", +- (host->flags & SDHCI_USE_DMA)?"DMA":"PIO"); ++ (host->flags & SDHCI_USE_ADMA) ? "ADMA" : ++ (host->flags & SDHCI_USE_SDMA) ? "SDMA" : "PIO"); + + sdhci_enable_card_detection(host); + +--- /dev/null ++++ b/drivers/mmc/host/sdhci-cns3xxx.c +@@ -0,0 +1,313 @@ ++/******************************************************************************* ++ * ++ * drivers/mmc/host/sdhci-cns3xxx.c ++ * ++ * SDHCI support for the CNS3XXX SOCs ++ * ++ * Author: Scott Shu ++ * ++ * Copyright (c) 2008 Cavium Networks ++ * ++ * This file is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License, Version 2, as ++ * published by the Free Software Foundation. ++ * ++ * This file is distributed in the hope that it will be useful, ++ * but AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or ++ * NONINFRINGEMENT. See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this file; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA or ++ * visit http://www.gnu.org/licenses/. ++ * ++ * This file may also be available under a different license from Cavium. ++ * Contact Cavium Networks for more information ++ * ++ ******************************************************************************/ ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "sdhci.h" ++ ++//#define DEBUG ++ ++#define MAX_BUS_CLK (4) ++ ++static unsigned __initdata use_dma = 0; ++ ++struct sdhci_cns3xxx { ++ struct sdhci_host *host; ++ struct platform_device *pdev; ++ struct resource *ioarea; ++ struct cns3xxx_sdhci_platdata *pdata; ++ struct clk *clk_io; ++ struct clk *clk_bus[MAX_BUS_CLK]; ++}; ++ ++static unsigned int sdhci_cns3xxx_get_max_clk(struct sdhci_host *host) ++{ ++ int clk = 50000000; ++ ++ return clk; ++} ++ ++static unsigned int sdhci_cns3xxx_get_timeout_clk(struct sdhci_host *host) ++{ ++ return sdhci_cns3xxx_get_max_clk(host) / 100000; ++} ++ ++/* ++ * sdhci_cns3xxx_set_clock - callback on clock change ++ * ++ * When the card's clock is going to be changed, look at the new frequency ++ * and find the best clock source to go with it. ++ */ ++static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) ++{ ++ u16 clk; ++ unsigned long timeout; ++ ++ if (clock == host->clock) ++ return; ++ ++ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); ++ ++ if (clock == 0) ++ goto out; ++#if 1 ++ clk = 0x03 << SDHCI_DIVIDER_SHIFT; /* base clock divided by 3 */ ++#else ++ /* high speed mode or normal speed mode */ ++ if (0x4 & sdhci_readw(host, 0x28)) { ++ clk = 0x03 << SDHCI_DIVIDER_SHIFT; /* base clock divided by 3 */ ++ } else { ++ clk = 0x02 << SDHCI_DIVIDER_SHIFT; /* base clock divided by 4 */ ++ } ++#endif ++ clk |= SDHCI_CLOCK_INT_EN; ++ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); ++ ++ timeout = 10; ++ while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) ++ & SDHCI_CLOCK_INT_STABLE)) { ++ if (timeout == 0) { ++ return; ++ } ++ timeout--; ++ mdelay(1); ++ } ++ ++ clk |= SDHCI_CLOCK_CARD_EN; ++ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); ++ ++ host->timeout_clk = sdhci_cns3xxx_get_timeout_clk(host); ++out: ++ host->clock = clock; ++} ++ ++static struct sdhci_ops sdhci_cns3xxx_ops = { ++ .get_max_clock = sdhci_cns3xxx_get_max_clk, ++ .get_timeout_clock = sdhci_cns3xxx_get_timeout_clk, ++ .set_clock = sdhci_cns3xxx_set_clock, ++}; ++ ++static int __devinit sdhci_cns3xxx_probe(struct platform_device *pdev) ++{ ++ struct cns3xxx_sdhci_platdata *pdata = pdev->dev.platform_data; ++ struct device *dev = &pdev->dev; ++ struct sdhci_host *host; ++ struct sdhci_cns3xxx *sc; ++ struct resource *res; ++ int ret, irq; ++ ++ if (!pdata) { ++ dev_err(dev, "no device data specified\n"); ++ return -ENOENT; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(dev, "no irq specified\n"); ++ return irq; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(dev, "no memory specified\n"); ++ return -ENOENT; ++ } ++ ++ host = sdhci_alloc_host(dev, sizeof(*sc)); ++ if (IS_ERR(host)) { ++ dev_err(dev, "sdhci_alloc_host() failed\n"); ++ return PTR_ERR(host); ++ } ++ ++ sc = sdhci_priv(host); ++ ++ sc->host = host; ++ sc->pdev = pdev; ++ sc->pdata = pdata; ++ ++ platform_set_drvdata(pdev, host); ++ ++ sc->ioarea = request_mem_region(res->start, resource_size(res), mmc_hostname(host->mmc)); ++ if (!sc->ioarea) { ++ dev_err(dev, "failed to reserve register area\n"); ++ ret = -ENXIO; ++ goto err_req_regs; ++ } ++ ++ host->ioaddr = ioremap_nocache(res->start, resource_size(res)); ++ if (!host->ioaddr) { ++ dev_err(dev, "failed to map registers\n"); ++ ret = -ENXIO; ++ goto err_req_regs; ++ } ++ ++ host->hw_name = "cns3xxx"; ++ host->ops = &sdhci_cns3xxx_ops; ++ host->quirks = 0; ++ host->irq = irq; ++ ++ if (use_dma != 1) { ++ host->quirks |= SDHCI_QUIRK_BROKEN_DMA; ++ host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; ++ } else { ++ host->quirks |= SDHCI_QUIRK_FORCE_DMA; ++ host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; ++ host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE); ++ host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ; ++ //host->quirks |= SDHCI_QUIRK_FORCE_BLK_SZ_2048; ++ //host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK; ++ host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; ++ host->quirks |= SDHCI_QUIRK_AUTO_CMD12; ++ host->quirks |= SDHCI_QUIRK_READ_WAIT_CTRL; ++ } ++ ++ //host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT; ++ ++ host->quirks |= SDHCI_QUIRK_NONSTANDARD_CLOCK; ++ ++ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; ++ ++ ret = sdhci_add_host(host); ++ if (ret) { ++ dev_err(dev, "sdhci_add_host() failed (%d)\n", ret); ++ goto err_add_host; ++ } ++ ++ return 0; ++ ++err_add_host: ++ free_irq(host->irq, host); ++ iounmap(host->ioaddr); ++ release_resource(sc->ioarea); ++ kfree(sc->ioarea); ++ ++err_req_regs: ++ sdhci_free_host(host); ++ ++ return ret; ++} ++ ++static int __devexit sdhci_cns3xxx_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct sdhci_host *host = dev_get_drvdata(dev); ++ struct resource *res; ++ ++ pr_debug("%s: remove=%p\n", __func__, pdev); ++ ++ sdhci_remove_host(host, 0); ++ sdhci_free_host(host); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ release_mem_region(res->start, resource_size(res)); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++static int sdhci_cns3xxx_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ ++ return 0; ++} ++ ++static int sdhci_cns3xxx_resume(struct platform_device *dev) ++{ ++ ++ return 0; ++} ++ ++#else ++#define sdhci_cns3xxx_suspend NULL ++#define sdhci_cns3xxx_resume NULL ++#endif /* CONFIG_PM */ ++ ++static struct platform_driver sdhci_cns3xxx_driver = { ++ .probe = sdhci_cns3xxx_probe, ++ .remove = __devexit_p(sdhci_cns3xxx_remove), ++ .suspend = sdhci_cns3xxx_suspend, ++ .resume = sdhci_cns3xxx_resume, ++ .driver = { ++ .name = "cns3xxx-sdhci", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static char banner[] __initdata = KERN_INFO "cns3xxx-sdhci, (c) 2009 Cavium Networks\n"; ++ ++static int __init sdhci_cns3xxx_init(void) ++{ ++#ifdef CONFIG_SILICON ++ unsigned long gpioapin = __raw_readl((void __iomem *)(CNS3XXX_MISC_BASE_VIRT + 0x0014));; ++#else ++ unsigned long status = __raw_readl((void __iomem *)(CNS3XXX_MISC_BASE_VIRT + 0x0700)); ++#endif ++ ++ printk(banner); ++ ++#ifdef CONFIG_SILICON ++ /* MMC/SD pins share with GPIOA */ ++ __raw_writel(gpioapin | (0x1fff0004), (void __iomem *)(CNS3XXX_MISC_BASE_VIRT + 0x0014)); ++ cns3xxx_pwr_clk_en(CNS3XXX_PWR_CLK_EN(SDIO)); ++ cns3xxx_pwr_soft_rst(CNS3XXX_PWR_SOFTWARE_RST(SDIO)); ++#else ++ /* insert a delay on SDIO output interface (only for FPGA mode & high-speed mode) */ ++ __raw_writel(status | (1 << 4), (void __iomem *)(CNS3XXX_MISC_BASE_VIRT + 0x0700)); ++#endif ++ return platform_driver_register(&sdhci_cns3xxx_driver); ++} ++ ++static void __exit sdhci_cns3xxx_exit(void) ++{ ++ platform_driver_unregister(&sdhci_cns3xxx_driver); ++} ++ ++module_init(sdhci_cns3xxx_init); ++module_exit(sdhci_cns3xxx_exit); ++ ++module_param(use_dma, uint, 0); ++ ++MODULE_AUTHOR("Scott Shu"); ++MODULE_DESCRIPTION("Cavium Networks CNS3XXX SDHCI glue"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:cns3xxx-sdhci"); ++ ++MODULE_PARM_DESC(use_dma, "Whether to use DMA or not. Default = 0"); +--- a/drivers/mmc/host/sdhci.h ++++ b/drivers/mmc/host/sdhci.h +@@ -8,6 +8,8 @@ + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ ++#ifndef __SDHCI_H ++#define __SDHCI_H + + #include + #include +@@ -78,6 +80,7 @@ + #define SDHCI_POWER_330 0x0E + + #define SDHCI_BLOCK_GAP_CONTROL 0x2A ++#define SDHCI_READ_WAIT_CTRL 0x04 + + #define SDHCI_WAKE_UP_CONTROL 0x2B + +@@ -143,7 +146,7 @@ + #define SDHCI_CAN_DO_ADMA2 0x00080000 + #define SDHCI_CAN_DO_ADMA1 0x00100000 + #define SDHCI_CAN_DO_HISPD 0x00200000 +-#define SDHCI_CAN_DO_DMA 0x00400000 ++#define SDHCI_CAN_DO_SDMA 0x00400000 + #define SDHCI_CAN_VDD_330 0x01000000 + #define SDHCI_CAN_VDD_300 0x02000000 + #define SDHCI_CAN_VDD_180 0x04000000 +@@ -232,6 +235,12 @@ struct sdhci_host { + #define SDHCI_QUIRK_FORCE_1_BIT_DATA (1<<22) + /* Controller needs 10ms delay between applying power and clock */ + #define SDHCI_QUIRK_DELAY_AFTER_POWER (1<<23) ++/* Controller uses SDCLK instead of TMCLK for data timeouts */ ++#define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24) ++/* Controller uses Auto CMD12 */ ++#define SDHCI_QUIRK_AUTO_CMD12 (1<<25) ++/* Controller uses read wait control protocol */ ++#define SDHCI_QUIRK_READ_WAIT_CTRL (1<<26) + + int irq; /* Device IRQ */ + void __iomem * ioaddr; /* Mapped address */ +@@ -250,7 +259,7 @@ struct sdhci_host { + spinlock_t lock; /* Mutex */ + + int flags; /* Host attributes */ +-#define SDHCI_USE_DMA (1<<0) /* Host is DMA capable */ ++#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */ + #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */ + #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ + #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ +@@ -406,3 +415,5 @@ extern void sdhci_remove_host(struct sdh + extern int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state); + extern int sdhci_resume_host(struct sdhci_host *host); + #endif ++ ++#endif /* __SDHCI_H */ +--- a/include/linux/mmc/card.h ++++ b/include/linux/mmc/card.h +@@ -40,6 +40,8 @@ struct mmc_csd { + }; + + struct mmc_ext_csd { ++ u8 rev; ++ unsigned int sa_timeout; /* Units: 100ns */ + unsigned int hs_max_dtr; + unsigned int sectors; + }; +@@ -62,7 +64,8 @@ struct sdio_cccr { + low_speed:1, + wide_bus:1, + high_power:1, +- high_speed:1; ++ high_speed:1, ++ disable_cd:1; + }; + + struct sdio_cis { +@@ -94,6 +97,8 @@ struct mmc_card { + #define MMC_STATE_READONLY (1<<1) /* card is read-only */ + #define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */ + #define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */ ++ unsigned int quirks; /* card quirks */ ++#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ + + u32 raw_cid[4]; /* raw card CID */ + u32 raw_csd[4]; /* raw card CSD */ +@@ -129,6 +134,11 @@ struct mmc_card { + #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED) + #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) + ++static inline int mmc_card_lenient_fn0(const struct mmc_card *c) ++{ ++ return c->quirks & MMC_QUIRK_LENIENT_FN0; ++} ++ + #define mmc_card_name(c) ((c)->cid.prod_name) + #define mmc_card_id(c) (dev_name(&(c)->dev)) + +--- a/include/linux/mmc/core.h ++++ b/include/linux/mmc/core.h +@@ -139,6 +139,7 @@ extern unsigned int mmc_align_data_size( + + extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort); + extern void mmc_release_host(struct mmc_host *host); ++extern int mmc_try_claim_host(struct mmc_host *host); + + /** + * mmc_claim_host - exclusively claim a host +--- a/include/linux/mmc/host.h ++++ b/include/linux/mmc/host.h +@@ -11,6 +11,7 @@ + #define LINUX_MMC_HOST_H + + #include ++#include + + #include + +@@ -51,6 +52,35 @@ struct mmc_ios { + }; + + struct mmc_host_ops { ++ /* ++ * Hosts that support power saving can use the 'enable' and 'disable' ++ * methods to exit and enter power saving states. 'enable' is called ++ * when the host is claimed and 'disable' is called (or scheduled with ++ * a delay) when the host is released. The 'disable' is scheduled if ++ * the disable delay set by 'mmc_set_disable_delay()' is non-zero, ++ * otherwise 'disable' is called immediately. 'disable' may be ++ * scheduled repeatedly, to permit ever greater power saving at the ++ * expense of ever greater latency to re-enable. Rescheduling is ++ * determined by the return value of the 'disable' method. A positive ++ * value gives the delay in milliseconds. ++ * ++ * In the case where a host function (like set_ios) may be called ++ * with or without the host claimed, enabling and disabling can be ++ * done directly and will nest correctly. Call 'mmc_host_enable()' and ++ * 'mmc_host_lazy_disable()' for this purpose, but note that these ++ * functions must be paired. ++ * ++ * Alternatively, 'mmc_host_enable()' may be paired with ++ * 'mmc_host_disable()' which calls 'disable' immediately. In this ++ * case the 'disable' method will be called with 'lazy' set to 0. ++ * This is mainly useful for error paths. ++ * ++ * Because lazy disable may be called from a work queue, the 'disable' ++ * method must claim the host when 'lazy' != 0, which will work ++ * correctly because recursion is detected and handled. ++ */ ++ int (*enable)(struct mmc_host *host); ++ int (*disable)(struct mmc_host *host, int lazy); + void (*request)(struct mmc_host *host, struct mmc_request *req); + /* + * Avoid calling these three functions too often or in a "fast path", +@@ -118,6 +148,9 @@ struct mmc_host { + #define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */ + #define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */ + #define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */ ++#define MMC_CAP_DISABLE (1 << 7) /* Can the host be disabled */ ++#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */ ++#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */ + + /* host specific block data */ + unsigned int max_seg_size; /* see blk_queue_max_segment_size */ +@@ -142,9 +175,18 @@ struct mmc_host { + unsigned int removed:1; /* host is being removed */ + #endif + ++ /* Only used with MMC_CAP_DISABLE */ ++ int enabled; /* host is enabled */ ++ int nesting_cnt; /* "enable" nesting count */ ++ int en_dis_recurs; /* detect recursion */ ++ unsigned int disable_delay; /* disable delay in msecs */ ++ struct delayed_work disable; /* disabling work */ ++ + struct mmc_card *card; /* device attached to this host */ + + wait_queue_head_t wq; ++ struct task_struct *claimer; /* task that has host claimed */ ++ int claim_cnt; /* "claim" nesting count */ + + struct delayed_work detect; + +@@ -183,6 +225,9 @@ static inline void *mmc_priv(struct mmc_ + extern int mmc_suspend_host(struct mmc_host *, pm_message_t); + extern int mmc_resume_host(struct mmc_host *); + ++extern void mmc_power_save_host(struct mmc_host *host); ++extern void mmc_power_restore_host(struct mmc_host *host); ++ + extern void mmc_detect_change(struct mmc_host *, unsigned long delay); + extern void mmc_request_done(struct mmc_host *, struct mmc_request *); + +@@ -197,5 +242,19 @@ struct regulator; + int mmc_regulator_get_ocrmask(struct regulator *supply); + int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit); + ++int mmc_card_awake(struct mmc_host *host); ++int mmc_card_sleep(struct mmc_host *host); ++int mmc_card_can_sleep(struct mmc_host *host); ++ ++int mmc_host_enable(struct mmc_host *host); ++int mmc_host_disable(struct mmc_host *host); ++int mmc_host_lazy_disable(struct mmc_host *host); ++ ++static inline void mmc_set_disable_delay(struct mmc_host *host, ++ unsigned int disable_delay) ++{ ++ host->disable_delay = disable_delay; ++} ++ + #endif + +--- a/include/linux/mmc/mmc.h ++++ b/include/linux/mmc/mmc.h +@@ -31,6 +31,7 @@ + #define MMC_ALL_SEND_CID 2 /* bcr R2 */ + #define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ + #define MMC_SET_DSR 4 /* bc [31:16] RCA */ ++#define MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */ + #define MMC_SWITCH 6 /* ac [31:0] See below R1b */ + #define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */ + #define MMC_SEND_EXT_CSD 8 /* adtc R1 */ +@@ -127,6 +128,7 @@ + #define R1_STATUS(x) (x & 0xFFFFE000) + #define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ + #define R1_READY_FOR_DATA (1 << 8) /* sx, a */ ++#define R1_SWITCH_ERROR (1 << 7) /* sx, c */ + #define R1_APP_CMD (1 << 5) /* sr, c */ + + /* +@@ -254,6 +256,7 @@ struct _mmc_csd { + #define EXT_CSD_CARD_TYPE 196 /* RO */ + #define EXT_CSD_REV 192 /* RO */ + #define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ ++#define EXT_CSD_S_A_TIMEOUT 217 + + /* + * EXT_CSD field definitions +--- a/include/linux/mmc/sdio_func.h ++++ b/include/linux/mmc/sdio_func.h +@@ -67,6 +67,7 @@ struct sdio_func { + + #define sdio_get_drvdata(f) dev_get_drvdata(&(f)->dev) + #define sdio_set_drvdata(f,d) dev_set_drvdata(&(f)->dev, d) ++#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) + + /* + * SDIO function device driver +@@ -81,6 +82,8 @@ struct sdio_driver { + struct device_driver drv; + }; + ++#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) ++ + /** + * SDIO_DEVICE - macro used to describe a specific SDIO device + * @vend: the 16 bit manufacturer code +--- a/include/linux/mmc/sdio_ids.h ++++ b/include/linux/mmc/sdio_ids.h +@@ -22,6 +22,12 @@ + /* + * Vendors and devices. Sort key: vendor first, device next. + */ ++#define SDIO_VENDOR_ID_INTEL 0x0089 ++#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402 ++#define SDIO_DEVICE_ID_INTEL_IWMC3200WIFI 0x1403 ++#define SDIO_DEVICE_ID_INTEL_IWMC3200TOP 0x1404 ++#define SDIO_DEVICE_ID_INTEL_IWMC3200GPS 0x1405 ++#define SDIO_DEVICE_ID_INTEL_IWMC3200BT 0x1406 + + #define SDIO_VENDOR_ID_MARVELL 0x02df + #define SDIO_DEVICE_ID_MARVELL_LIBERTAS 0x9103 -- cgit v1.2.3