diff options
Diffstat (limited to 'drivers/mmc/core/sdio.c')
-rw-r--r-- | drivers/mmc/core/sdio.c | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c new file mode 100644 index 000000000000..ac0dd68df8e7 --- /dev/null +++ b/drivers/mmc/core/sdio.c @@ -0,0 +1,176 @@ +/* + * linux/drivers/mmc/sdio.c + * + * Copyright 2006-2007 Pierre Ossman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include <linux/err.h> + +#include <linux/mmc/host.h> +#include <linux/mmc/card.h> + +#include "core.h" +#include "bus.h" +#include "mmc_ops.h" +#include "sd_ops.h" +#include "sdio_ops.h" + +/* + * Host is being removed. Free up the current card. + */ +static void mmc_sdio_remove(struct mmc_host *host) +{ + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_remove_card(host->card); + host->card = NULL; +} + +/* + * Card detection callback from host. + */ +static void mmc_sdio_detect(struct mmc_host *host) +{ + int err; + + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_claim_host(host); + + /* + * Just check if our card has been removed. + */ + err = mmc_select_card(host->card); + + mmc_release_host(host); + + if (err) { + mmc_sdio_remove(host); + + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_release_host(host); + } +} + + +static const struct mmc_bus_ops mmc_sdio_ops = { + .remove = mmc_sdio_remove, + .detect = mmc_sdio_detect, +}; + + +/* + * Starting point for SDIO card init. + */ +int mmc_attach_sdio(struct mmc_host *host, u32 ocr) +{ + int err; + int funcs; + struct mmc_card *card; + + BUG_ON(!host); + BUG_ON(!host->claimed); + + mmc_attach_bus(host, &mmc_sdio_ops); + + /* + * Sanity check the voltages that the card claims to + * support. + */ + if (ocr & 0x7F) { + printk(KERN_WARNING "%s: card claims to support voltages " + "below the defined range. These will be ignored.\n", + mmc_hostname(host)); + 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); + + /* + * Can we support the voltage(s) of the card(s)? + */ + if (!host->ocr) { + err = -EINVAL; + goto err; + } + + /* + * Inform the card of the voltage + */ + err = mmc_send_io_op_cond(host, host->ocr, &ocr); + if (err) + goto err; + + /* + * The number of functions on the card is encoded inside + * the ocr. + */ + funcs = (ocr & 0x70000000) >> 28; + + /* + * Allocate card structure. + */ + card = mmc_alloc_card(host); + if (IS_ERR(card)) { + err = PTR_ERR(card); + goto err; + } + + card->type = MMC_TYPE_SDIO; + + /* + * Set card RCA. + */ + err = mmc_send_relative_addr(host, &card->rca); + if (err) + goto free_card; + + mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); + + /* + * Select card, as all following commands rely on that. + */ + err = mmc_select_card(card); + if (err) + goto free_card; + + host->card = card; + + mmc_release_host(host); + + err = mmc_add_card(host->card); + if (err) + goto reclaim_host; + + return 0; + +reclaim_host: + mmc_claim_host(host); +free_card: + mmc_remove_card(card); + host->card = NULL; +err: + mmc_detach_bus(host); + mmc_release_host(host); + + printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n", + mmc_hostname(host), err); + + return err; +} + |