Skip to content

Commit

Permalink
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/git/drzeus/mmc

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc:
  mmc: warn about voltage mismatches
  mmc_spi: Add support for OpenFirmware bindings
  pxamci: fix dma_unmap_sg length
  mmc_block: ensure all sectors that do not have errors are read
  drivers/mmc: Move a dereference below a NULL test
  sdhci: handle built-in sdhci with modular leds class
  mmc: balanc pci_iomap with pci_iounmap
  mmc_block: print better error messages
  mmc: Add mmc_vddrange_to_ocrmask() helper function
  ricoh_mmc: Handle newer models of Ricoh controllers
  mmc: Add 8-bit bus width support
  sdhci: activate led support also when module
  mmc: trivial annotation of 'blocks'
  pci: use pci_ioremap_bar() in drivers/mmc
  sdricoh_cs: Add support for Bay Controller devices
  mmc: at91_mci: reorder timer setup and mmc_add_host() call
  • Loading branch information
Linus Torvalds committed Jan 3, 2009
2 parents ad6b646 + 418f19e commit e9e67a8
Show file tree
Hide file tree
Showing 17 changed files with 392 additions and 51 deletions.
122 changes: 96 additions & 26 deletions drivers/mmc/card/block.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ struct mmc_blk_request {
static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
{
int err;
u32 blocks;
__be32 blocks;

struct mmc_request mrq;
struct mmc_command cmd;
Expand Down Expand Up @@ -204,23 +204,38 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
if (cmd.error || data.error)
return (u32)-1;

blocks = ntohl(blocks);
return ntohl(blocks);
}

static u32 get_card_status(struct mmc_card *card, struct request *req)
{
struct mmc_command cmd;
int err;

return blocks;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_STATUS;
if (!mmc_host_is_spi(card->host))
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err)
printk(KERN_ERR "%s: error %d sending status comand",
req->rq_disk->disk_name, err);
return cmd.resp[0];
}

static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
struct mmc_blk_request brq;
int ret = 1;
int ret = 1, disable_multi = 0;

mmc_claim_host(card->host);

do {
struct mmc_command cmd;
u32 readcmd, writecmd;
u32 readcmd, writecmd, status = 0;

memset(&brq, 0, sizeof(struct mmc_blk_request));
brq.mrq.cmd = &brq.cmd;
Expand All @@ -236,6 +251,14 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
brq.data.blocks = req->nr_sectors;

/*
* After a read error, we redo the request one sector at a time
* in order to accurately determine which sectors can be read
* successfully.
*/
if (disable_multi && brq.data.blocks > 1)
brq.data.blocks = 1;

if (brq.data.blocks > 1) {
/* SPI multiblock writes terminate using a special
* token, not a STOP_TRANSMISSION request.
Expand Down Expand Up @@ -264,6 +287,25 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
brq.data.sg = mq->sg;
brq.data.sg_len = mmc_queue_map_sg(mq);

/*
* Adjust the sg list so it is the same size as the
* request.
*/
if (brq.data.blocks != req->nr_sectors) {
int i, data_size = brq.data.blocks << 9;
struct scatterlist *sg;

for_each_sg(brq.data.sg, sg, brq.data.sg_len, i) {
data_size -= sg->length;
if (data_size <= 0) {
sg->length += data_size;
i++;
break;
}
}
brq.data.sg_len = i;
}

mmc_queue_bounce_pre(mq);

mmc_wait_for_req(card->host, &brq.mrq);
Expand All @@ -275,19 +317,40 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
* until later as we need to wait for the card to leave
* programming mode even when things go wrong.
*/
if (brq.cmd.error || brq.data.error || brq.stop.error) {
if (brq.data.blocks > 1 && rq_data_dir(req) == READ) {
/* Redo read one sector at a time */
printk(KERN_WARNING "%s: retrying using single "
"block read\n", req->rq_disk->disk_name);
disable_multi = 1;
continue;
}
status = get_card_status(card, req);
}

if (brq.cmd.error) {
printk(KERN_ERR "%s: error %d sending read/write command\n",
req->rq_disk->disk_name, brq.cmd.error);
printk(KERN_ERR "%s: error %d sending read/write "
"command, response %#x, card status %#x\n",
req->rq_disk->disk_name, brq.cmd.error,
brq.cmd.resp[0], status);
}

if (brq.data.error) {
printk(KERN_ERR "%s: error %d transferring data\n",
req->rq_disk->disk_name, brq.data.error);
if (brq.data.error == -ETIMEDOUT && brq.mrq.stop)
/* 'Stop' response contains card status */
status = brq.mrq.stop->resp[0];
printk(KERN_ERR "%s: error %d transferring data,"
" sector %u, nr %u, card status %#x\n",
req->rq_disk->disk_name, brq.data.error,
(unsigned)req->sector,
(unsigned)req->nr_sectors, status);
}

if (brq.stop.error) {
printk(KERN_ERR "%s: error %d sending stop command\n",
req->rq_disk->disk_name, brq.stop.error);
printk(KERN_ERR "%s: error %d sending stop command, "
"response %#x, card status %#x\n",
req->rq_disk->disk_name, brq.stop.error,
brq.stop.resp[0], status);
}

if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
Expand Down Expand Up @@ -320,8 +383,20 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
#endif
}

if (brq.cmd.error || brq.data.error || brq.stop.error)
if (brq.cmd.error || brq.stop.error || brq.data.error) {
if (rq_data_dir(req) == READ) {
/*
* After an error, we redo I/O one sector at a
* time, so we only reach here after trying to
* read a single sector.
*/
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, -EIO, brq.data.blksz);
spin_unlock_irq(&md->lock);
continue;
}
goto cmd_err;
}

/*
* A block was successfully transferred.
Expand All @@ -343,25 +418,20 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
* If the card is not SD, we can still ok written sectors
* as reported by the controller (which might be less than
* the real number of written sectors, but never more).
*
* For reads we just fail the entire chunk as that should
* be safe in all cases.
*/
if (rq_data_dir(req) != READ) {
if (mmc_card_sd(card)) {
u32 blocks;
if (mmc_card_sd(card)) {
u32 blocks;

blocks = mmc_sd_num_wr_blocks(card);
if (blocks != (u32)-1) {
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0, blocks << 9);
spin_unlock_irq(&md->lock);
}
} else {
blocks = mmc_sd_num_wr_blocks(card);
if (blocks != (u32)-1) {
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0, brq.data.bytes_xfered);
ret = __blk_end_request(req, 0, blocks << 9);
spin_unlock_irq(&md->lock);
}
} else {
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0, brq.data.bytes_xfered);
spin_unlock_irq(&md->lock);
}

mmc_release_host(card->host);
Expand Down
77 changes: 77 additions & 0 deletions drivers/mmc/core/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <linux/err.h>
#include <linux/leds.h>
#include <linux/scatterlist.h>
#include <linux/log2.h>

#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
Expand Down Expand Up @@ -448,6 +449,80 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
mmc_set_ios(host);
}

/**
* mmc_vdd_to_ocrbitnum - Convert a voltage to the OCR bit number
* @vdd: voltage (mV)
* @low_bits: prefer low bits in boundary cases
*
* This function returns the OCR bit number according to the provided @vdd
* value. If conversion is not possible a negative errno value returned.
*
* Depending on the @low_bits flag the function prefers low or high OCR bits
* on boundary voltages. For example,
* with @low_bits = true, 3300 mV translates to ilog2(MMC_VDD_32_33);
* with @low_bits = false, 3300 mV translates to ilog2(MMC_VDD_33_34);
*
* Any value in the [1951:1999] range translates to the ilog2(MMC_VDD_20_21).
*/
static int mmc_vdd_to_ocrbitnum(int vdd, bool low_bits)
{
const int max_bit = ilog2(MMC_VDD_35_36);
int bit;

if (vdd < 1650 || vdd > 3600)
return -EINVAL;

if (vdd >= 1650 && vdd <= 1950)
return ilog2(MMC_VDD_165_195);

if (low_bits)
vdd -= 1;

/* Base 2000 mV, step 100 mV, bit's base 8. */
bit = (vdd - 2000) / 100 + 8;
if (bit > max_bit)
return max_bit;
return bit;
}

/**
* mmc_vddrange_to_ocrmask - Convert a voltage range to the OCR mask
* @vdd_min: minimum voltage value (mV)
* @vdd_max: maximum voltage value (mV)
*
* This function returns the OCR mask bits according to the provided @vdd_min
* and @vdd_max values. If conversion is not possible the function returns 0.
*
* Notes wrt boundary cases:
* This function sets the OCR bits for all boundary voltages, for example
* [3300:3400] range is translated to MMC_VDD_32_33 | MMC_VDD_33_34 |
* MMC_VDD_34_35 mask.
*/
u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max)
{
u32 mask = 0;

if (vdd_max < vdd_min)
return 0;

/* Prefer high bits for the boundary vdd_max values. */
vdd_max = mmc_vdd_to_ocrbitnum(vdd_max, false);
if (vdd_max < 0)
return 0;

/* Prefer low bits for the boundary vdd_min values. */
vdd_min = mmc_vdd_to_ocrbitnum(vdd_min, true);
if (vdd_min < 0)
return 0;

/* Fill the mask, from max bit to min bit. */
while (vdd_max >= vdd_min)
mask |= 1 << vdd_max--;

return mask;
}
EXPORT_SYMBOL(mmc_vddrange_to_ocrmask);

/*
* Mask off any voltages we don't support and select
* the lowest voltage
Expand All @@ -467,6 +542,8 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
host->ios.vdd = bit;
mmc_set_ios(host);
} else {
pr_warning("%s: host doesn't support card's voltages\n",
mmc_hostname(host));
ocr = 0;
}

Expand Down
18 changes: 14 additions & 4 deletions drivers/mmc/core/mmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -434,13 +434,24 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* Activate wide bus (if supported).
*/
if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
(host->caps & MMC_CAP_4_BIT_DATA)) {
(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
unsigned ext_csd_bit, bus_width;

if (host->caps & MMC_CAP_8_BIT_DATA) {
ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
bus_width = MMC_BUS_WIDTH_8;
} else {
ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
bus_width = MMC_BUS_WIDTH_4;
}

err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4);
EXT_CSD_BUS_WIDTH, ext_csd_bit);

if (err)
goto free_card;

mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
mmc_set_bus_width(card->host, bus_width);
}

if (!oldcard)
Expand Down Expand Up @@ -624,4 +635,3 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)

return err;
}

3 changes: 3 additions & 0 deletions drivers/mmc/host/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ obj-$(CONFIG_MMC_AT91) += at91_mci.o
obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o
endif
obj-$(CONFIG_MMC_S3C) += s3cmci.o
obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o
obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
Expand Down
4 changes: 2 additions & 2 deletions drivers/mmc/host/at91_mci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,8 @@ static int __init at91_mci_probe(struct platform_device *pdev)
goto fail0;
}

setup_timer(&host->timer, at91_timeout_timer, (unsigned long)host);

platform_set_drvdata(pdev, mmc);

/*
Expand All @@ -1101,8 +1103,6 @@ static int __init at91_mci_probe(struct platform_device *pdev)

mmc_add_host(mmc);

setup_timer(&host->timer, at91_timeout_timer, (unsigned long)host);

/*
* monitor card insertion/removal if we can
*/
Expand Down
4 changes: 3 additions & 1 deletion drivers/mmc/host/mmc_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1285,7 +1285,7 @@ static int mmc_spi_probe(struct spi_device *spi)
/* Platform data is used to hook up things like card sensing
* and power switching gpios.
*/
host->pdata = spi->dev.platform_data;
host->pdata = mmc_spi_get_pdata(spi);
if (host->pdata)
mmc->ocr_avail = host->pdata->ocr_mask;
if (!mmc->ocr_avail) {
Expand Down Expand Up @@ -1368,6 +1368,7 @@ static int mmc_spi_probe(struct spi_device *spi)

fail_nobuf1:
mmc_free_host(mmc);
mmc_spi_put_pdata(spi);
dev_set_drvdata(&spi->dev, NULL);

nomem:
Expand Down Expand Up @@ -1402,6 +1403,7 @@ static int __devexit mmc_spi_remove(struct spi_device *spi)

spi->max_speed_hz = mmc->f_max;
mmc_free_host(mmc);
mmc_spi_put_pdata(spi);
dev_set_drvdata(&spi->dev, NULL);
}
return 0;
Expand Down
Loading

0 comments on commit e9e67a8

Please sign in to comment.