From 4ce1d6cbf07271ab8f7cc47c3e27edeac08b58a7 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 21 Jul 2010 12:44:58 +0100 Subject: ARM: 6237/1: mmci: use sg_miter API to fix multi-page sg handling The mmci driver's SG list iteration logic assumes that each SG entry spans only one page, and only maps and flushes one page of the sg. This is not a valid assumption. Fix it by converting the driver to the sg_miter API, which correctly handles sgs which span multiple pages. Acked-by: Linus Walleij Signed-off-by: Rabin Vincent Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 60 +++++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 22 deletions(-) (limited to 'drivers/mmc/host/mmci.c') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 4917af96bae1..d63d7565f896 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -98,6 +97,18 @@ static void mmci_stop_data(struct mmci_host *host) host->data = NULL; } +static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) +{ + unsigned int flags = SG_MITER_ATOMIC; + + if (data->flags & MMC_DATA_READ) + flags |= SG_MITER_TO_SG; + else + flags |= SG_MITER_FROM_SG; + + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); +} + static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) { unsigned int datactrl, timeout, irqmask; @@ -210,8 +221,17 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, * We hit an error condition. Ensure that any data * partially written to a page is properly coherent. */ - if (host->sg_len && data->flags & MMC_DATA_READ) - flush_dcache_page(sg_page(host->sg_ptr)); + if (data->flags & MMC_DATA_READ) { + struct sg_mapping_iter *sg_miter = &host->sg_miter; + unsigned long flags; + + local_irq_save(flags); + if (sg_miter_next(sg_miter)) { + flush_dcache_page(sg_miter->page); + sg_miter_stop(sg_miter); + } + local_irq_restore(flags); + } } if (status & MCI_DATAEND) { mmci_stop_data(host); @@ -314,15 +334,18 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem static irqreturn_t mmci_pio_irq(int irq, void *dev_id) { struct mmci_host *host = dev_id; + struct sg_mapping_iter *sg_miter = &host->sg_miter; void __iomem *base = host->base; + unsigned long flags; u32 status; status = readl(base + MMCISTATUS); dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status); + local_irq_save(flags); + do { - unsigned long flags; unsigned int remain, len; char *buffer; @@ -336,11 +359,11 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id) if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL))) break; - /* - * Map the current scatter buffer. - */ - buffer = mmci_kmap_atomic(host, &flags) + host->sg_off; - remain = host->sg_ptr->length - host->sg_off; + if (!sg_miter_next(sg_miter)) + break; + + buffer = sg_miter->addr; + remain = sg_miter->length; len = 0; if (status & MCI_RXACTIVE) @@ -348,31 +371,24 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id) if (status & MCI_TXACTIVE) len = mmci_pio_write(host, buffer, remain, status); - /* - * Unmap the buffer. - */ - mmci_kunmap_atomic(host, buffer, &flags); + sg_miter->consumed = len; - host->sg_off += len; host->size -= len; remain -= len; if (remain) break; - /* - * If we were reading, and we have completed this - * page, ensure that the data cache is coherent. - */ if (status & MCI_RXACTIVE) - flush_dcache_page(sg_page(host->sg_ptr)); - - if (!mmci_next_sg(host)) - break; + flush_dcache_page(sg_miter->page); status = readl(base + MMCISTATUS); } while (1); + sg_miter_stop(sg_miter); + + local_irq_restore(flags); + /* * If we're nearing the end of the read, switch to * "any data available" mode. -- cgit v1.2.1 From 528320db013b687c5f0150fd77eb4dc02ca328d1 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 21 Jul 2010 12:49:49 +0100 Subject: ARM: 6238/1: mmci: fix multi block transfers Fix the data transfer size to allow multi block transfers to work. Acked-by: Linus Walleij Signed-off-by: Rabin Vincent Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host/mmci.c') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index d63d7565f896..ddcfc4c7e6ac 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -120,7 +120,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) data->blksz, data->blocks, data->flags); host->data = data; - host->size = data->blksz; + host->size = data->blksz * data->blocks; host->data_xfered = 0; mmci_init_sg(host, data); -- cgit v1.2.1 From f5e2574e734650bbeb801a31cc99e628f9a027af Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 21 Jul 2010 12:50:31 +0100 Subject: ARM: 6239/1: mmci: let core poll for card detection Use the MMC core's ability to poll for card detection. This also has the advantage of doing the gpio_get_value from a workqueue instead of timer, allowing the gpio to be on a sleeping gpiochip. Acked-by: Linus Walleij Signed-off-by: Rabin Vincent Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) (limited to 'drivers/mmc/host/mmci.c') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index ddcfc4c7e6ac..3eaa0e9373cd 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -567,18 +567,6 @@ static const struct mmc_host_ops mmci_ops = { .get_cd = mmci_get_cd, }; -static void mmci_check_status(unsigned long data) -{ - struct mmci_host *host = (struct mmci_host *)data; - unsigned int status = mmci_get_cd(host->mmc); - - if (status ^ host->oldstat) - mmc_detect_change(host->mmc, 0); - - host->oldstat = status; - mod_timer(&host->timer, jiffies + HZ); -} - static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) { struct mmci_platform_data *plat = dev->dev.platform_data; @@ -685,6 +673,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) if (host->vcc == NULL) mmc->ocr_avail = plat->ocr_mask; mmc->caps = plat->capabilities; + mmc->caps |= MMC_CAP_NEEDS_POLL; /* * We can do SGIO @@ -750,7 +739,6 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) writel(MCI_IRQENABLE, host->base + MMCIMASK0); amba_set_drvdata(dev, mmc); - host->oldstat = mmci_get_cd(host->mmc); mmc_add_host(mmc); @@ -758,12 +746,6 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) mmc_hostname(mmc), amba_rev(dev), amba_config(dev), (unsigned long long)dev->res.start, dev->irq[0], dev->irq[1]); - init_timer(&host->timer); - host->timer.data = (unsigned long)host; - host->timer.function = mmci_check_status; - host->timer.expires = jiffies + HZ; - add_timer(&host->timer); - return 0; irq0_free: @@ -797,8 +779,6 @@ static int __devexit mmci_remove(struct amba_device *dev) if (mmc) { struct mmci_host *host = mmc_priv(mmc); - del_timer_sync(&host->timer); - mmc_remove_host(mmc); writel(0, host->base + MMCIMASK0); -- cgit v1.2.1 From bb8f563c848faa113059973f68c24a3bb6a9585e Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 21 Jul 2010 12:53:57 +0100 Subject: ARM: 6243/1: mmci: pass power_mode to the translate_vdd callback Platforms may have some external power control which need to be controlled from board specific code. Rename the translate_vdd() callback to vdd_handler() and pass it the power mode. Acked-by: Linus Walleij Signed-off-by: Rabin Vincent Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'drivers/mmc/host/mmci.c') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 3eaa0e9373cd..7ae3eeeefc29 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -493,16 +493,9 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) /* This implicitly enables the regulator */ mmc_regulator_set_ocr(host->vcc, ios->vdd); #endif - /* - * The translate_vdd function is not used if you have - * an external regulator, or your design is really weird. - * Using it would mean sending in power control BOTH using - * a regulator AND the 4 MMCIPWR bits. If we don't have - * a regulator, we might have some other platform specific - * power control behind this translate function. - */ - if (!host->vcc && host->plat->translate_vdd) - pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd); + if (host->plat->vdd_handler) + pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd, + ios->power_mode); /* The ST version does not have this, fall through to POWER_ON */ if (host->hw_designer != AMBA_VENDOR_ST) { pwr |= MCI_PWR_UP; -- cgit v1.2.1 From 4956e10903fd3459306dd9438c1e714ba3068a2a Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 21 Jul 2010 12:54:40 +0100 Subject: ARM: 6244/1: mmci: add variant data and default MCICLOCK support Add a variant_data structure to handle the differences between the various variants of this peripheral. Add a first quirk for a default MCICLOCK value, required on the Ux500 variant where the enable bit needs to be always set, since it controls access to some registers. Acked-by: Linus Walleij Signed-off-by: Rabin Vincent Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/host/mmci.c') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 7ae3eeeefc29..fd2e7acbed39 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -36,12 +36,30 @@ static unsigned int fmax = 515633; +/** + * struct variant_data - MMCI variant-specific quirks + * @clkreg: default value for MCICLOCK register + */ +struct variant_data { + unsigned int clkreg; +}; + +static struct variant_data variant_arm = { +}; + +static struct variant_data variant_u300 = { +}; + +static struct variant_data variant_ux500 = { + .clkreg = MCI_CLK_ENABLE, +}; /* * This must be called with host->lock held */ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) { - u32 clk = 0; + struct variant_data *variant = host->variant; + u32 clk = variant->clkreg; if (desired) { if (desired >= host->mclk) { @@ -563,6 +581,7 @@ static const struct mmc_host_ops mmci_ops = { static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) { struct mmci_platform_data *plat = dev->dev.platform_data; + struct variant_data *variant = id->data; struct mmci_host *host; struct mmc_host *mmc; int ret; @@ -606,6 +625,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) goto clk_free; host->plat = plat; + host->variant = variant; host->mclk = clk_get_rate(host->clk); /* * According to the spec, mclk is max 100 MHz, @@ -845,19 +865,28 @@ static struct amba_id mmci_ids[] = { { .id = 0x00041180, .mask = 0x000fffff, + .data = &variant_arm, }, { .id = 0x00041181, .mask = 0x000fffff, + .data = &variant_arm, }, /* ST Micro variants */ { .id = 0x00180180, .mask = 0x00ffffff, + .data = &variant_u300, }, { .id = 0x00280180, .mask = 0x00ffffff, + .data = &variant_u300, + }, + { + .id = 0x00480180, + .mask = 0x00ffffff, + .data = &variant_ux500, }, { 0, 0 }, }; -- cgit v1.2.1 From 4380c14fd77338bac9d1da4dc5dd9f6eb4966c82 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 21 Jul 2010 12:55:18 +0100 Subject: ARM: 6245/1: mmci: enable hardware flow control on Ux500 variants Although both the U300 and Ux500 use ST variants, the HWFCEN bits are at different positions, so use the variant_data to store the information. Acked-by: Linus Walleij Signed-off-by: Rabin Vincent Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/host/mmci.c') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index fd2e7acbed39..379af904bf6c 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -39,19 +39,23 @@ static unsigned int fmax = 515633; /** * struct variant_data - MMCI variant-specific quirks * @clkreg: default value for MCICLOCK register + * @clkreg_enable: enable value for MMCICLOCK register */ struct variant_data { unsigned int clkreg; + unsigned int clkreg_enable; }; static struct variant_data variant_arm = { }; static struct variant_data variant_u300 = { + .clkreg_enable = 1 << 13, /* HWFCEN */ }; static struct variant_data variant_ux500 = { .clkreg = MCI_CLK_ENABLE, + .clkreg_enable = 1 << 14, /* HWFCEN */ }; /* * This must be called with host->lock held @@ -71,8 +75,8 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) clk = 255; host->cclk = host->mclk / (2 * (clk + 1)); } - if (host->hw_designer == AMBA_VENDOR_ST) - clk |= MCI_ST_FCEN; /* Bug fix in ST IP block */ + + clk |= variant->clkreg_enable; clk |= MCI_CLK_ENABLE; /* This hasn't proven to be worthwhile */ /* clk |= MCI_CLK_PWRSAVE; */ -- cgit v1.2.1 From 08458ef6eede6cf7d5a33c3a7c8bcdc3943012c2 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 21 Jul 2010 12:55:59 +0100 Subject: ARM: 6246/1: mmci: support larger MMCIDATALENGTH register The Ux500 variant has a 24-bit MMCIDATALENGTH register, as opposed to the 16-bit one on the ARM version. Acked-by: Linus Walleij Signed-off-by: Rabin Vincent Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/host/mmci.c') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 379af904bf6c..7edae83603dd 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -40,22 +40,27 @@ static unsigned int fmax = 515633; * struct variant_data - MMCI variant-specific quirks * @clkreg: default value for MCICLOCK register * @clkreg_enable: enable value for MMCICLOCK register + * @datalength_bits: number of bits in the MMCIDATALENGTH register */ struct variant_data { unsigned int clkreg; unsigned int clkreg_enable; + unsigned int datalength_bits; }; static struct variant_data variant_arm = { + .datalength_bits = 16, }; static struct variant_data variant_u300 = { .clkreg_enable = 1 << 13, /* HWFCEN */ + .datalength_bits = 16, }; static struct variant_data variant_ux500 = { .clkreg = MCI_CLK_ENABLE, .clkreg_enable = 1 << 14, /* HWFCEN */ + .datalength_bits = 24, }; /* * This must be called with host->lock held @@ -699,10 +704,11 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) mmc->max_phys_segs = NR_SG; /* - * Since we only have a 16-bit data length register, we must - * ensure that we don't exceed 2^16-1 bytes in a single request. + * Since only a certain number of bits are valid in the data length + * register, we must ensure that we don't exceed 2^num-1 bytes in a + * single request. */ - mmc->max_req_size = 65535; + mmc->max_req_size = (1 << variant->datalength_bits) - 1; /* * Set the maximum segment size. Since we aren't doing DMA -- cgit v1.2.1