Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm
* 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm: (291 commits) ARM: AMBA: Add pclk support to AMBA bus infrastructure ARM: 6278/2: fix regression in RealView after the introduction of pclk ARM: 6277/1: mach-shmobile: Allow users to select HZ, default to 128 ARM: 6276/1: mach-shmobile: remove duplicate NR_IRQS_LEGACY ARM: 6246/1: mmci: support larger MMCIDATALENGTH register ARM: 6245/1: mmci: enable hardware flow control on Ux500 variants ARM: 6244/1: mmci: add variant data and default MCICLOCK support ARM: 6243/1: mmci: pass power_mode to the translate_vdd callback ARM: 6274/1: add global control registers definition header file for nuc900 mx2_camera: fix type of dma buffer virtual address pointer mx2_camera: Add soc_camera support for i.MX25/i.MX27 arm/imx/gpio: add spinlock protection ARM: Add support for the LPC32XX arch ARM: LPC32XX: Arch config menu supoport and makefiles ARM: LPC32XX: Phytec 3250 platform support ARM: LPC32XX: Misc support functions ARM: LPC32XX: Serial support code ARM: LPC32XX: System suspend support ARM: LPC32XX: GPIO, timer, and IRQ drivers ARM: LPC32XX: Clock driver ...
This commit is contained in:
@@ -26,7 +26,6 @@
|
||||
#include <linux/amba/mmci.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/div64.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/sizes.h>
|
||||
@@ -37,12 +36,39 @@
|
||||
|
||||
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
|
||||
*/
|
||||
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) {
|
||||
@@ -54,8 +80,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; */
|
||||
@@ -98,6 +124,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;
|
||||
@@ -109,7 +147,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);
|
||||
@@ -210,8 +248,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 +361,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 +386,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 +398,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.
|
||||
@@ -477,16 +520,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;
|
||||
@@ -555,21 +591,10 @@ 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;
|
||||
struct variant_data *variant = id->data;
|
||||
struct mmci_host *host;
|
||||
struct mmc_host *mmc;
|
||||
int ret;
|
||||
@@ -613,6 +638,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,
|
||||
@@ -673,6 +699,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
|
||||
@@ -681,10 +708,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
|
||||
@@ -738,7 +766,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);
|
||||
|
||||
@@ -746,12 +773,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:
|
||||
@@ -785,8 +806,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);
|
||||
@@ -860,19 +879,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 },
|
||||
};
|
||||
|
@@ -28,8 +28,6 @@
|
||||
#define MCI_4BIT_BUS (1 << 11)
|
||||
/* 8bit wide buses supported in ST Micro versions */
|
||||
#define MCI_ST_8BIT_BUS (1 << 12)
|
||||
/* HW flow control on the ST Micro version */
|
||||
#define MCI_ST_FCEN (1 << 13)
|
||||
|
||||
#define MMCIARGUMENT 0x008
|
||||
#define MMCICOMMAND 0x00c
|
||||
@@ -145,6 +143,7 @@
|
||||
#define NR_SG 16
|
||||
|
||||
struct clk;
|
||||
struct variant_data;
|
||||
|
||||
struct mmci_host {
|
||||
void __iomem *base;
|
||||
@@ -164,6 +163,7 @@ struct mmci_host {
|
||||
unsigned int cclk;
|
||||
u32 pwr;
|
||||
struct mmci_platform_data *plat;
|
||||
struct variant_data *variant;
|
||||
|
||||
u8 hw_designer;
|
||||
u8 hw_revision:4;
|
||||
@@ -171,42 +171,9 @@ struct mmci_host {
|
||||
struct timer_list timer;
|
||||
unsigned int oldstat;
|
||||
|
||||
unsigned int sg_len;
|
||||
|
||||
/* pio stuff */
|
||||
struct scatterlist *sg_ptr;
|
||||
unsigned int sg_off;
|
||||
struct sg_mapping_iter sg_miter;
|
||||
unsigned int size;
|
||||
struct regulator *vcc;
|
||||
};
|
||||
|
||||
static inline void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
|
||||
{
|
||||
/*
|
||||
* Ideally, we want the higher levels to pass us a scatter list.
|
||||
*/
|
||||
host->sg_len = data->sg_len;
|
||||
host->sg_ptr = data->sg;
|
||||
host->sg_off = 0;
|
||||
}
|
||||
|
||||
static inline int mmci_next_sg(struct mmci_host *host)
|
||||
{
|
||||
host->sg_ptr++;
|
||||
host->sg_off = 0;
|
||||
return --host->sg_len;
|
||||
}
|
||||
|
||||
static inline char *mmci_kmap_atomic(struct mmci_host *host, unsigned long *flags)
|
||||
{
|
||||
struct scatterlist *sg = host->sg_ptr;
|
||||
|
||||
local_irq_save(*flags);
|
||||
return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
|
||||
}
|
||||
|
||||
static inline void mmci_kunmap_atomic(struct mmci_host *host, void *buffer, unsigned long *flags)
|
||||
{
|
||||
kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
|
||||
local_irq_restore(*flags);
|
||||
}
|
||||
|
@@ -119,6 +119,7 @@ struct mxcmci_host {
|
||||
int detect_irq;
|
||||
int dma;
|
||||
int do_dma;
|
||||
int default_irq_mask;
|
||||
int use_sdio;
|
||||
unsigned int power_mode;
|
||||
struct imxmmc_platform_data *pdata;
|
||||
@@ -228,7 +229,7 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
||||
static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
|
||||
unsigned int cmdat)
|
||||
{
|
||||
u32 int_cntr;
|
||||
u32 int_cntr = host->default_irq_mask;
|
||||
unsigned long flags;
|
||||
|
||||
WARN_ON(host->cmd != NULL);
|
||||
@@ -275,7 +276,7 @@ static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
|
||||
static void mxcmci_finish_request(struct mxcmci_host *host,
|
||||
struct mmc_request *req)
|
||||
{
|
||||
u32 int_cntr = 0;
|
||||
u32 int_cntr = host->default_irq_mask;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
@@ -585,6 +586,9 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
|
||||
(stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE)))
|
||||
mxcmci_data_done(host, stat);
|
||||
#endif
|
||||
if (host->default_irq_mask &&
|
||||
(stat & (STATUS_CARD_INSERTION | STATUS_CARD_REMOVAL)))
|
||||
mmc_detect_change(host->mmc, msecs_to_jiffies(200));
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@@ -809,6 +813,12 @@ static int mxcmci_probe(struct platform_device *pdev)
|
||||
else
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
|
||||
if (host->pdata && host->pdata->dat3_card_detect)
|
||||
host->default_irq_mask =
|
||||
INT_CARD_INSERTION_EN | INT_CARD_REMOVAL_EN;
|
||||
else
|
||||
host->default_irq_mask = 0;
|
||||
|
||||
host->res = r;
|
||||
host->irq = irq;
|
||||
|
||||
@@ -835,7 +845,7 @@ static int mxcmci_probe(struct platform_device *pdev)
|
||||
/* recommended in data sheet */
|
||||
writew(0x2db4, host->base + MMC_REG_READ_TO);
|
||||
|
||||
writel(0, host->base + MMC_REG_INT_CNTR);
|
||||
writel(host->default_irq_mask, host->base + MMC_REG_INT_CNTR);
|
||||
|
||||
#ifdef HAS_DMA
|
||||
host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW);
|
||||
@@ -926,43 +936,47 @@ static int mxcmci_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mxcmci_suspend(struct platform_device *dev, pm_message_t state)
|
||||
static int mxcmci_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = platform_get_drvdata(dev);
|
||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct mxcmci_host *host = mmc_priv(mmc);
|
||||
int ret = 0;
|
||||
|
||||
if (mmc)
|
||||
ret = mmc_suspend_host(mmc);
|
||||
clk_disable(host->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxcmci_resume(struct platform_device *dev)
|
||||
static int mxcmci_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = platform_get_drvdata(dev);
|
||||
struct mxcmci_host *host;
|
||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct mxcmci_host *host = mmc_priv(mmc);
|
||||
int ret = 0;
|
||||
|
||||
if (mmc) {
|
||||
host = mmc_priv(mmc);
|
||||
clk_enable(host->clk);
|
||||
if (mmc)
|
||||
ret = mmc_resume_host(mmc);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#define mxcmci_suspend NULL
|
||||
#define mxcmci_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops mxcmci_pm_ops = {
|
||||
.suspend = mxcmci_suspend,
|
||||
.resume = mxcmci_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct platform_driver mxcmci_driver = {
|
||||
.probe = mxcmci_probe,
|
||||
.remove = mxcmci_remove,
|
||||
.suspend = mxcmci_suspend,
|
||||
.resume = mxcmci_resume,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &mxcmci_pm_ops,
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user