From 5c4e6f1301649d5b29dd0f70e6da83e728ab5ca5 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 21 May 2007 20:23:20 +0200 Subject: [PATCH] mmc: detect SDIO cards Really basic init sequence for SDIO cards. Signed-off-by: Pierre Ossman --- MAINTAINERS | 2 +- drivers/mmc/core/Makefile | 3 +- drivers/mmc/core/bus.c | 8 ++ drivers/mmc/core/core.c | 40 +++++--- drivers/mmc/core/sdio.c | 176 ++++++++++++++++++++++++++++++++++++ drivers/mmc/core/sdio_ops.c | 49 ++++++++++ drivers/mmc/core/sdio_ops.h | 18 ++++ include/linux/mmc/card.h | 2 + include/linux/mmc/core.h | 1 + include/linux/mmc/sdio.h | 19 ++++ 10 files changed, 304 insertions(+), 14 deletions(-) create mode 100644 drivers/mmc/core/sdio.c create mode 100644 drivers/mmc/core/sdio_ops.c create mode 100644 drivers/mmc/core/sdio_ops.h create mode 100644 include/linux/mmc/sdio.h diff --git a/MAINTAINERS b/MAINTAINERS index 9a91d9e3f1f2..c22d34b11f64 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2561,7 +2561,7 @@ L: linux-kernel@vger.kernel.org W: http://www.atnf.csiro.au/~rgooch/linux/kernel-patches.html S: Maintained -MULTIMEDIA CARD (MMC) AND SECURE DIGITAL (SD) SUBSYSTEM +MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM P: Pierre Ossman M: drzeus-mmc@drzeus.cx L: linux-kernel@vger.kernel.org diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 3fdd08c7f143..2fa5ebbc170d 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -8,5 +8,6 @@ endif obj-$(CONFIG_MMC) += mmc_core.o mmc_core-y := core.o sysfs.o bus.o host.o \ - mmc.o mmc_ops.o sd.o sd_ops.o + mmc.o mmc_ops.o sd.o sd_ops.o \ + sdio.o sdio_ops.o diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 817a79462b3d..87a6070522f8 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -34,6 +34,8 @@ static ssize_t mmc_type_show(struct device *dev, return sprintf(buf, "MMC\n"); case MMC_TYPE_SD: return sprintf(buf, "SD\n"); + case MMC_TYPE_SDIO: + return sprintf(buf, "SDIO\n"); default: return -EFAULT; } @@ -76,6 +78,9 @@ mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, case MMC_TYPE_SD: add_env("MMC_TYPE=%s", "SD"); break; + case MMC_TYPE_SDIO: + add_env("MMC_TYPE=%s", "SDIO"); + break; } add_env("MMC_NAME=%s", mmc_card_name(card)); @@ -221,6 +226,9 @@ int mmc_add_card(struct mmc_card *card) if (mmc_card_blockaddr(card)) type = "SDHC"; break; + case MMC_TYPE_SDIO: + type = "SDIO"; + break; default: type = "?"; break; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 51e611f2f33d..092fa906ab86 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -32,9 +32,11 @@ #include "mmc_ops.h" #include "sd_ops.h" +#include "sdio_ops.h" extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr); extern int mmc_attach_sd(struct mmc_host *host, u32 ocr); +extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr); static struct workqueue_struct *workqueue; @@ -595,24 +597,38 @@ void mmc_rescan(struct work_struct *work) mmc_send_if_cond(host, host->ocr_avail); + /* + * First we search for SDIO... + */ + err = mmc_send_io_op_cond(host, 0, &ocr); + if (!err) { + if (mmc_attach_sdio(host, ocr)) + mmc_power_off(host); + return; + } + + /* + * ...then normal SD... + */ err = mmc_send_app_op_cond(host, 0, &ocr); if (!err) { if (mmc_attach_sd(host, ocr)) mmc_power_off(host); - } else { - /* - * If we fail to detect any SD cards then try - * searching for MMC cards. - */ - err = mmc_send_op_cond(host, 0, &ocr); - if (!err) { - if (mmc_attach_mmc(host, ocr)) - mmc_power_off(host); - } else { + return; + } + + /* + * ...and finally MMC. + */ + err = mmc_send_op_cond(host, 0, &ocr); + if (!err) { + if (mmc_attach_mmc(host, ocr)) mmc_power_off(host); - mmc_release_host(host); - } + return; } + + mmc_release_host(host); + mmc_power_off(host); } else { if (host->bus_ops->detect && !host->bus_dead) host->bus_ops->detect(host); 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 + +#include +#include + +#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; +} + diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c new file mode 100644 index 000000000000..d6f9f9d85178 --- /dev/null +++ b/drivers/mmc/core/sdio_ops.c @@ -0,0 +1,49 @@ +/* + * linux/drivers/mmc/sdio_ops.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 +#include +#include + +#include "core.h" + +int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) +{ + struct mmc_command cmd; + int i, err = 0; + + BUG_ON(!host); + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_IO_SEND_OP_COND; + cmd.arg = ocr; + cmd.flags = MMC_RSP_R4 | MMC_CMD_BCR; + + for (i = 100; i; i--) { + err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); + if (err) + break; + + if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) + break; + + err = -ETIMEDOUT; + + mmc_delay(10); + } + + if (rocr) + *rocr = cmd.resp[0]; + + return err; +} + diff --git a/drivers/mmc/core/sdio_ops.h b/drivers/mmc/core/sdio_ops.h new file mode 100644 index 000000000000..d8c982976f19 --- /dev/null +++ b/drivers/mmc/core/sdio_ops.h @@ -0,0 +1,18 @@ +/* + * linux/drivers/mmc/sdio_ops.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. + */ + +#ifndef _MMC_SDIO_OPS_H +#define _MMC_SDIO_OPS_H + +int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); + +#endif + diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index badf702fcff4..43480ebebf9a 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -67,6 +67,7 @@ struct mmc_card { unsigned int type; /* card type */ #define MMC_TYPE_MMC 0 /* MMC card */ #define MMC_TYPE_SD 1 /* SD card */ +#define MMC_TYPE_SDIO 2 /* SDIO card */ unsigned int state; /* (our) card state */ #define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ #define MMC_STATE_READONLY (1<<1) /* card is read-only */ @@ -84,6 +85,7 @@ struct mmc_card { #define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) #define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD) +#define mmc_card_sdio(c) ((c)->type == MMC_TYPE_SDIO) #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT) #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 29c98ae10aff..8faa436c5571 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -41,6 +41,7 @@ struct mmc_command { #define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY) #define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC) #define MMC_RSP_R3 (MMC_RSP_PRESENT) +#define MMC_RSP_R4 (MMC_RSP_PRESENT) #define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) #define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) diff --git a/include/linux/mmc/sdio.h b/include/linux/mmc/sdio.h new file mode 100644 index 000000000000..d1a0b15cdfdf --- /dev/null +++ b/include/linux/mmc/sdio.h @@ -0,0 +1,19 @@ +/* + * include/linux/mmc/sdio.h + * + * 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. + */ + +#ifndef MMC_SDIO_H +#define MMC_SDIO_H + +/* SDIO commands type argument response */ +#define SD_IO_SEND_OP_COND 5 /* bcr [23:0] OCR R4 */ + +#endif + -- 2.20.1