From 4cd83bd05e6c817a4a72ababd82bf5ae700c021d Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Sun, 4 Oct 2009 17:16:36 +0100 Subject: MMC: hardware abstraction for CNF area This patch abstracts out the CNF area code from tmio_mmc which is not present in all hardware that can use this driver. This is required so that we can support non-toshiba based hardware. ASIC3 support by Philipp Zabel Signed-off-by: Ian Molton --- drivers/mfd/Makefile | 6 +-- drivers/mfd/asic3.c | 43 ++++++++++++++++---- drivers/mfd/t7l66xb.c | 31 ++++++++++++--- drivers/mfd/tc6387xb.c | 97 ++++++++++++++++++++++++++++++++++++--------- drivers/mfd/tc6393xb.c | 56 +++++++++++++++++++++----- drivers/mfd/tmio_core.c | 62 +++++++++++++++++++++++++++++ drivers/mmc/host/tmio_mmc.c | 63 ++++++++++------------------- drivers/mmc/host/tmio_mmc.h | 46 +++------------------ include/linux/mfd/tmio.h | 40 +++++++++++++++++++ 9 files changed, 318 insertions(+), 126 deletions(-) create mode 100644 drivers/mfd/tmio_core.c diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 6f8a9a1af20b..9be344ade746 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -10,9 +10,9 @@ obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o -obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o -obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o -obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o +obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o +obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o +obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o obj-$(CONFIG_MFD_WM8400) += wm8400-core.o wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 63a2a6632106..53432d2363bb 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -80,6 +80,7 @@ struct asic3 { u16 irq_bothedge[4]; struct gpio_chip gpio; struct device *dev; + void __iomem *tmio_cnf; struct asic3_clk clocks[ARRAY_SIZE(asic3_clk_init)]; }; @@ -685,8 +686,24 @@ static struct mfd_cell asic3_cell_ds1wm = { .resources = ds1wm_resources, }; +static void asic3_mmc_pwr(struct platform_device *pdev, int state) +{ + struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); + + tmio_core_mmc_pwr(asic->tmio_cnf, state); +} + +static void asic3_mmc_clk_div(struct platform_device *pdev, int state) +{ + struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); + + tmio_core_mmc_clk_div(asic->tmio_cnf, state); +} + static struct tmio_mmc_data asic3_mmc_data = { - .hclk = 24576000, + .hclk = 24576000, + .set_pwr = asic3_mmc_pwr, + .set_no_clk_div = asic3_mmc_clk_div, }; static struct resource asic3_mmc_resources[] = { @@ -695,11 +712,6 @@ static struct resource asic3_mmc_resources[] = { .end = ASIC3_SD_CTRL_BASE + 0x3ff, .flags = IORESOURCE_MEM, }, - { - .start = ASIC3_SD_CONFIG_BASE, - .end = ASIC3_SD_CONFIG_BASE + 0x1ff, - .flags = IORESOURCE_MEM, - }, { .start = 0, .end = 0, @@ -743,6 +755,9 @@ static int asic3_mmc_enable(struct platform_device *pdev) asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF), ASIC3_SDHWCTRL_SDPWR, 1); + /* ASIC3_SD_CTRL_BASE assumes 32-bit addressing, TMIO is 16-bit */ + tmio_core_mmc_enable(asic->tmio_cnf, ASIC3_SD_CTRL_BASE >> 1); + return 0; } @@ -766,6 +781,8 @@ static struct mfd_cell asic3_cell_mmc = { .name = "tmio-mmc", .enable = asic3_mmc_enable, .disable = asic3_mmc_disable, + .suspend = asic3_mmc_disable, + .resume = asic3_mmc_enable, .driver_data = &asic3_mmc_data, .num_resources = ARRAY_SIZE(asic3_mmc_resources), .resources = asic3_mmc_resources, @@ -797,14 +814,21 @@ static int __init asic3_mfd_probe(struct platform_device *pdev, asic3_cell_ds1wm.data_size = sizeof(asic3_cell_ds1wm); /* MMC */ + asic->tmio_cnf = ioremap((ASIC3_SD_CONFIG_BASE >> asic->bus_shift) + + mem_sdio->start, 0x400 >> asic->bus_shift); + if (!asic->tmio_cnf) { + ret = -ENOMEM; + dev_dbg(asic->dev, "Couldn't ioremap SD_CONFIG\n"); + goto out; + } asic3_mmc_resources[0].start >>= asic->bus_shift; asic3_mmc_resources[0].end >>= asic->bus_shift; - asic3_mmc_resources[1].start >>= asic->bus_shift; - asic3_mmc_resources[1].end >>= asic->bus_shift; asic3_cell_mmc.platform_data = &asic3_cell_mmc; asic3_cell_mmc.data_size = sizeof(asic3_cell_mmc); + tmio_core_set_bus_shift(1 - asic->bus_shift); + ret = mfd_add_devices(&pdev->dev, pdev->id, &asic3_cell_ds1wm, 1, mem, asic->irq_base); if (ret < 0) @@ -820,7 +844,10 @@ static int __init asic3_mfd_probe(struct platform_device *pdev, static void asic3_mfd_remove(struct platform_device *pdev) { + struct asic3 *asic = platform_get_drvdata(pdev); + mfd_remove_devices(&pdev->dev); + iounmap(asic->tmio_cnf); } /* Core */ diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c index 0a255c1f1ce7..2fa070361f05 100644 --- a/drivers/mfd/t7l66xb.c +++ b/drivers/mfd/t7l66xb.c @@ -38,6 +38,8 @@ enum { T7L66XB_CELL_MMC, }; +static const struct resource t7l66xb_mmc_resources[]; + #define SCR_REVID 0x08 /* b Revision ID */ #define SCR_IMR 0x42 /* b Interrupt Mask */ #define SCR_DEV_CTL 0xe0 /* b Device control */ @@ -83,6 +85,9 @@ static int t7l66xb_mmc_enable(struct platform_device *mmc) spin_unlock_irqrestore(&t7l66xb->lock, flags); + tmio_core_mmc_enable(t7l66xb->scr + 0x200, + t7l66xb_mmc_resources[0].start & 0xfffe); + return 0; } @@ -106,10 +111,28 @@ static int t7l66xb_mmc_disable(struct platform_device *mmc) return 0; } +static void t7l66xb_mmc_pwr(struct platform_device *mmc, int state) +{ + struct platform_device *dev = to_platform_device(mmc->dev.parent); + struct t7l66xb *t7l66xb = platform_get_drvdata(dev); + + tmio_core_mmc_pwr(t7l66xb->scr + 0x200, state); +} + +static void t7l66xb_mmc_clk_div(struct platform_device *mmc, int state) +{ + struct platform_device *dev = to_platform_device(mmc->dev.parent); + struct t7l66xb *t7l66xb = platform_get_drvdata(dev); + + tmio_core_mmc_clk_div(t7l66xb->scr + 0x200, state); +} + /*--------------------------------------------------------------------------*/ static struct tmio_mmc_data t7166xb_mmc_data = { .hclk = 24000000, + .set_pwr = t7l66xb_mmc_pwr, + .set_no_clk_div = t7l66xb_mmc_clk_div, }; static const struct resource t7l66xb_mmc_resources[] = { @@ -118,11 +141,6 @@ static const struct resource t7l66xb_mmc_resources[] = { .end = 0x9ff, .flags = IORESOURCE_MEM, }, - { - .start = 0x200, - .end = 0x2ff, - .flags = IORESOURCE_MEM, - }, { .start = IRQ_T7L66XB_MMC, .end = IRQ_T7L66XB_MMC, @@ -282,6 +300,9 @@ static int t7l66xb_resume(struct platform_device *dev) if (pdata && pdata->resume) pdata->resume(dev); + tmio_core_mmc_enable(t7l66xb->scr + 0x200, + t7l66xb_mmc_resources[0].start & 0xfffe); + return 0; } #else diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c index 3280ab33f88a..fa8da0264a9f 100644 --- a/drivers/mfd/tc6387xb.c +++ b/drivers/mfd/tc6387xb.c @@ -22,28 +22,41 @@ enum { TC6387XB_CELL_MMC, }; +struct tc6387xb { + void __iomem *scr; + struct clk *clk32k; + struct resource rscr; +}; + +static struct resource tc6387xb_mmc_resources[]; + +/*--------------------------------------------------------------------------*/ + #ifdef CONFIG_PM static int tc6387xb_suspend(struct platform_device *dev, pm_message_t state) { - struct clk *clk32k = platform_get_drvdata(dev); + struct tc6387xb *tc6387xb = platform_get_drvdata(dev); struct tc6387xb_platform_data *pdata = dev->dev.platform_data; if (pdata && pdata->suspend) pdata->suspend(dev); - clk_disable(clk32k); + clk_disable(tc6387xb->clk32k); return 0; } static int tc6387xb_resume(struct platform_device *dev) { - struct clk *clk32k = platform_get_drvdata(dev); + struct tc6387xb *tc6387xb = platform_get_drvdata(dev); struct tc6387xb_platform_data *pdata = dev->dev.platform_data; - clk_enable(clk32k); + clk_enable(tc6387xb->clk32k); if (pdata && pdata->resume) pdata->resume(dev); + tmio_core_mmc_resume(tc6387xb->scr + 0x200, + tc6387xb_mmc_resources[0].start & 0xfffe); + return 0; } #else @@ -53,12 +66,32 @@ static int tc6387xb_resume(struct platform_device *dev) /*--------------------------------------------------------------------------*/ +static void tc6387xb_mmc_pwr(struct platform_device *mmc, int state) +{ + struct platform_device *dev = to_platform_device(mmc->dev.parent); + struct tc6387xb *tc6387xb = platform_get_drvdata(dev); + + tmio_core_mmc_pwr(tc6387xb->scr + 0x200, state); +} + +static void tc6387xb_mmc_clk_div(struct platform_device *mmc, int state) +{ + struct platform_device *dev = to_platform_device(mmc->dev.parent); + struct tc6387xb *tc6387xb = platform_get_drvdata(dev); + + tmio_core_mmc_clk_div(tc6387xb->scr + 0x200, state); +} + + static int tc6387xb_mmc_enable(struct platform_device *mmc) { struct platform_device *dev = to_platform_device(mmc->dev.parent); - struct clk *clk32k = platform_get_drvdata(dev); + struct tc6387xb *tc6387xb = platform_get_drvdata(dev); - clk_enable(clk32k); + clk_enable(tc6387xb->clk32k); + + tmio_core_mmc_enable(tc6387xb->scr + 0x200, + tc6387xb_mmc_resources[0].start & 0xfffe); return 0; } @@ -66,30 +99,27 @@ static int tc6387xb_mmc_enable(struct platform_device *mmc) static int tc6387xb_mmc_disable(struct platform_device *mmc) { struct platform_device *dev = to_platform_device(mmc->dev.parent); - struct clk *clk32k = platform_get_drvdata(dev); + struct tc6387xb *tc6387xb = platform_get_drvdata(dev); - clk_disable(clk32k); + clk_disable(tc6387xb->clk32k); return 0; } -/*--------------------------------------------------------------------------*/ - static struct tmio_mmc_data tc6387xb_mmc_data = { .hclk = 24000000, + .set_pwr = tc6387xb_mmc_pwr, + .set_no_clk_div = tc6387xb_mmc_clk_div, }; +/*--------------------------------------------------------------------------*/ + static struct resource tc6387xb_mmc_resources[] = { { .start = 0x800, .end = 0x9ff, .flags = IORESOURCE_MEM, }, - { - .start = 0x200, - .end = 0x2ff, - .flags = IORESOURCE_MEM, - }, { .start = 0, .end = 0, @@ -111,8 +141,9 @@ static struct mfd_cell tc6387xb_cells[] = { static int tc6387xb_probe(struct platform_device *dev) { struct tc6387xb_platform_data *pdata = dev->dev.platform_data; - struct resource *iomem; + struct resource *iomem, *rscr; struct clk *clk32k; + struct tc6387xb *tc6387xb; int irq, ret; iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); @@ -120,18 +151,40 @@ static int tc6387xb_probe(struct platform_device *dev) return -EINVAL; } + tc6387xb = kzalloc(sizeof *tc6387xb, GFP_KERNEL); + if (!tc6387xb) + return -ENOMEM; + ret = platform_get_irq(dev, 0); if (ret >= 0) irq = ret; else - goto err_resource; + goto err_no_irq; clk32k = clk_get(&dev->dev, "CLK_CK32K"); if (IS_ERR(clk32k)) { ret = PTR_ERR(clk32k); + goto err_no_clk; + } + + rscr = &tc6387xb->rscr; + rscr->name = "tc6387xb-core"; + rscr->start = iomem->start; + rscr->end = iomem->start + 0xff; + rscr->flags = IORESOURCE_MEM; + + ret = request_resource(iomem, rscr); + if (ret) goto err_resource; + + tc6387xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1); + if (!tc6387xb->scr) { + ret = -ENOMEM; + goto err_ioremap; } - platform_set_drvdata(dev, clk32k); + + tc6387xb->clk32k = clk32k; + platform_set_drvdata(dev, tc6387xb); if (pdata && pdata->enable) pdata->enable(dev); @@ -149,8 +202,13 @@ static int tc6387xb_probe(struct platform_device *dev) if (!ret) return 0; - clk_put(clk32k); +err_ioremap: + release_resource(&tc6387xb->rscr); err_resource: + clk_put(clk32k); +err_no_clk: +err_no_irq: + kfree(tc6387xb); return ret; } @@ -195,3 +253,4 @@ MODULE_DESCRIPTION("Toshiba TC6387XB core driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Ian Molton"); MODULE_ALIAS("platform:tc6387xb"); + diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index 1429a7341a9a..a44b6b02f193 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -136,10 +136,6 @@ static int tc6393xb_nand_enable(struct platform_device *nand) return 0; } -static struct tmio_mmc_data tc6393xb_mmc_data = { - .hclk = 24000000, -}; - static struct resource __devinitdata tc6393xb_nand_resources[] = { { .start = 0x1000, @@ -164,11 +160,6 @@ static struct resource __devinitdata tc6393xb_mmc_resources[] = { .end = 0x9ff, .flags = IORESOURCE_MEM, }, - { - .start = 0x200, - .end = 0x2ff, - .flags = IORESOURCE_MEM, - }, { .start = IRQ_TC6393_MMC, .end = IRQ_TC6393_MMC, @@ -346,6 +337,50 @@ int tc6393xb_lcd_mode(struct platform_device *fb, } EXPORT_SYMBOL(tc6393xb_lcd_mode); +static int tc6393xb_mmc_enable(struct platform_device *mmc) +{ + struct platform_device *dev = to_platform_device(mmc->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + + tmio_core_mmc_enable(tc6393xb->scr + 0x200, + tc6393xb_mmc_resources[0].start & 0xfffe); + + return 0; +} + +static int tc6393xb_mmc_resume(struct platform_device *mmc) +{ + struct platform_device *dev = to_platform_device(mmc->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + + tmio_core_mmc_resume(tc6393xb->scr + 0x200, + tc6393xb_mmc_resources[0].start & 0xfffe); + + return 0; +} + +static void tc6393xb_mmc_pwr(struct platform_device *mmc, int state) +{ + struct platform_device *dev = to_platform_device(mmc->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + + tmio_core_mmc_pwr(tc6393xb->scr + 0x200, state); +} + +static void tc6393xb_mmc_clk_div(struct platform_device *mmc, int state) +{ + struct platform_device *dev = to_platform_device(mmc->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + + tmio_core_mmc_clk_div(tc6393xb->scr + 0x200, state); +} + +static struct tmio_mmc_data tc6393xb_mmc_data = { + .hclk = 24000000, + .set_pwr = tc6393xb_mmc_pwr, + .set_no_clk_div = tc6393xb_mmc_clk_div, +}; + static struct mfd_cell __devinitdata tc6393xb_cells[] = { [TC6393XB_CELL_NAND] = { .name = "tmio-nand", @@ -355,6 +390,8 @@ static struct mfd_cell __devinitdata tc6393xb_cells[] = { }, [TC6393XB_CELL_MMC] = { .name = "tmio-mmc", + .enable = tc6393xb_mmc_enable, + .resume = tc6393xb_mmc_resume, .driver_data = &tc6393xb_mmc_data, .num_resources = ARRAY_SIZE(tc6393xb_mmc_resources), .resources = tc6393xb_mmc_resources, @@ -836,3 +873,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov and Dirk Opfer"); MODULE_DESCRIPTION("tc6393xb Toshiba Mobile IO Controller"); MODULE_ALIAS("platform:tc6393xb"); + diff --git a/drivers/mfd/tmio_core.c b/drivers/mfd/tmio_core.c new file mode 100644 index 000000000000..f88b06393540 --- /dev/null +++ b/drivers/mfd/tmio_core.c @@ -0,0 +1,62 @@ +/* + * Toshiba TC6393XB SoC support + * + * Copyright(c) 2009 Ian Molton + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +static int shift; + +int tmio_core_mmc_enable(void __iomem *cnf, unsigned long base) +{ + /* Enable the MMC/SD Control registers */ + sd_config_write16(cnf, shift, CNF_CMD, SDCREN); + sd_config_write32(cnf, shift, CNF_CTL_BASE, base & 0xfffe); + + /* Disable SD power during suspend */ + sd_config_write8(cnf, shift, CNF_PWR_CTL_3, 0x01); + + /* The below is required but why? FIXME */ + sd_config_write8(cnf, shift, CNF_STOP_CLK_CTL, 0x1f); + + /* Power down SD bus*/ + sd_config_write8(cnf, shift, CNF_PWR_CTL_2, 0x00); + + return 0; +} +EXPORT_SYMBOL(tmio_core_mmc_enable); + +int tmio_core_mmc_resume(void __iomem *cnf, unsigned long base) +{ + + /* Enable the MMC/SD Control registers */ + sd_config_write16(cnf, shift, CNF_CMD, SDCREN); + sd_config_write32(cnf, shift, CNF_CTL_BASE, base & 0xfffe); + + return 0; +} +EXPORT_SYMBOL(tmio_core_mmc_resume); + +void tmio_core_mmc_pwr(void __iomem *cnf, int state) +{ + sd_config_write8(cnf, shift, CNF_PWR_CTL_2, state ? 0x02 : 0x00); +} +EXPORT_SYMBOL(tmio_core_mmc_pwr); + +void tmio_core_mmc_clk_div(void __iomem *cnf, int state) +{ + sd_config_write8(cnf, shift, CNF_SD_CLK_MODE, state ? 1 : 0); +} +EXPORT_SYMBOL(tmio_core_mmc_clk_div); + +void tmio_core_set_bus_shift(int bus_shift) +{ + shift = bus_shift; +} +EXPORT_SYMBOL(tmio_core_set_bus_shift); + diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 91991b460c45..e70173293262 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -46,7 +46,9 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock) clk |= 0x100; } - sd_config_write8(host, CNF_SD_CLK_MODE, clk >> 22); + if (host->set_no_clk_div) + host->set_no_clk_div(host->pdev, (clk>>22) & 1); + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & 0x1ff); } @@ -427,12 +429,13 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) /* Power sequence - OFF -> ON -> UP */ switch (ios->power_mode) { case MMC_POWER_OFF: /* power down SD bus */ - sd_config_write8(host, CNF_PWR_CTL_2, 0x00); + if (host->set_pwr) + host->set_pwr(host->pdev, 0); tmio_mmc_clk_stop(host); break; case MMC_POWER_ON: /* power up SD bus */ - - sd_config_write8(host, CNF_PWR_CTL_2, 0x02); + if (host->set_pwr) + host->set_pwr(host->pdev, 1); break; case MMC_POWER_UP: /* start bus clock */ tmio_mmc_clk_start(host); @@ -475,8 +478,8 @@ static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state) ret = mmc_suspend_host(mmc, state); /* Tell MFD core it can disable us now.*/ - if (!ret && cell->disable) - cell->disable(dev); + if (!ret && cell->suspend) + cell->suspend(dev); return ret; } @@ -485,21 +488,15 @@ static int tmio_mmc_resume(struct platform_device *dev) { struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; struct mmc_host *mmc = platform_get_drvdata(dev); - struct tmio_mmc_host *host = mmc_priv(mmc); int ret = 0; /* Tell the MFD core we are ready to be enabled */ - if (cell->enable) { - ret = cell->enable(dev); + if (cell->resume) { + ret = cell->resume(dev); if (ret) goto out; } - /* Enable the MMC/SD Control registers */ - sd_config_write16(host, CNF_CMD, SDCREN); - sd_config_write32(host, CNF_CTL_BASE, - (dev->resource[0].start >> host->bus_shift) & 0xfffe); - mmc_resume_host(mmc); out: @@ -514,17 +511,16 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) { struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; struct tmio_mmc_data *pdata; - struct resource *res_ctl, *res_cnf; + struct resource *res_ctl; struct tmio_mmc_host *host; struct mmc_host *mmc; int ret = -EINVAL; - if (dev->num_resources != 3) + if (dev->num_resources != 2) goto out; res_ctl = platform_get_resource(dev, IORESOURCE_MEM, 0); - res_cnf = platform_get_resource(dev, IORESOURCE_MEM, 1); - if (!res_ctl || !res_cnf) + if (!res_ctl) goto out; pdata = cell->driver_data; @@ -539,8 +535,12 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) host = mmc_priv(mmc); host->mmc = mmc; + host->pdev = dev; platform_set_drvdata(dev, mmc); + host->set_pwr = pdata->set_pwr; + host->set_no_clk_div = pdata->set_no_clk_div; + /* SD control register space size is 0x200, 0x400 for bus_shift=1 */ host->bus_shift = resource_size(res_ctl) >> 10; @@ -548,10 +548,6 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) if (!host->ctl) goto host_free; - host->cnf = ioremap(res_cnf->start, resource_size(res_cnf)); - if (!host->cnf) - goto unmap_ctl; - mmc->ops = &tmio_mmc_ops; mmc->caps = MMC_CAP_4_BIT_DATA; mmc->f_max = pdata->hclk; @@ -562,23 +558,9 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) if (cell->enable) { ret = cell->enable(dev); if (ret) - goto unmap_cnf; + goto unmap_ctl; } - /* Enable the MMC/SD Control registers */ - sd_config_write16(host, CNF_CMD, SDCREN); - sd_config_write32(host, CNF_CTL_BASE, - (dev->resource[0].start >> host->bus_shift) & 0xfffe); - - /* Disable SD power during suspend */ - sd_config_write8(host, CNF_PWR_CTL_3, 0x01); - - /* The below is required but why? FIXME */ - sd_config_write8(host, CNF_STOP_CLK_CTL, 0x1f); - - /* Power down SD bus*/ - sd_config_write8(host, CNF_PWR_CTL_2, 0x00); - tmio_mmc_clk_stop(host); reset(host); @@ -586,14 +568,14 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) if (ret >= 0) host->irq = ret; else - goto unmap_cnf; + goto unmap_ctl; disable_mmc_irqs(host, TMIO_MASK_ALL); ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "tmio-mmc", host); if (ret) - goto unmap_cnf; + goto unmap_ctl; mmc_add_host(mmc); @@ -605,8 +587,6 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) return 0; -unmap_cnf: - iounmap(host->cnf); unmap_ctl: iounmap(host->ctl); host_free: @@ -626,7 +606,6 @@ static int __devexit tmio_mmc_remove(struct platform_device *dev) mmc_remove_host(mmc); free_irq(host->irq, host); iounmap(host->ctl); - iounmap(host->cnf); mmc_free_host(mmc); } diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 9fa998594974..c676767cce8d 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -11,26 +11,6 @@ #include -#define CNF_CMD 0x04 -#define CNF_CTL_BASE 0x10 -#define CNF_INT_PIN 0x3d -#define CNF_STOP_CLK_CTL 0x40 -#define CNF_GCLK_CTL 0x41 -#define CNF_SD_CLK_MODE 0x42 -#define CNF_PIN_STATUS 0x44 -#define CNF_PWR_CTL_1 0x48 -#define CNF_PWR_CTL_2 0x49 -#define CNF_PWR_CTL_3 0x4a -#define CNF_CARD_DETECT_MODE 0x4c -#define CNF_SD_SLOT 0x50 -#define CNF_EXT_GCLK_CTL_1 0xf0 -#define CNF_EXT_GCLK_CTL_2 0xf1 -#define CNF_EXT_GCLK_CTL_3 0xf9 -#define CNF_SD_LED_EN_1 0xfa -#define CNF_SD_LED_EN_2 0xfe - -#define SDCREN 0x2 /* Enable access to MMC CTL regs. (flag in COMMAND_REG)*/ - #define CTL_SD_CMD 0x00 #define CTL_ARG_REG 0x04 #define CTL_STOP_INTERNAL_ACTION 0x08 @@ -110,7 +90,6 @@ struct tmio_mmc_host { - void __iomem *cnf; void __iomem *ctl; unsigned long bus_shift; struct mmc_command *cmd; @@ -119,10 +98,16 @@ struct tmio_mmc_host { struct mmc_host *mmc; int irq; + /* Callbacks for clock / power control */ + void (*set_pwr)(struct platform_device *host, int state); + void (*set_no_clk_div)(struct platform_device *host, int state); + /* pio related stuff */ struct scatterlist *sg_ptr; unsigned int sg_len; unsigned int sg_off; + + struct platform_device *pdev; }; #include @@ -163,25 +148,6 @@ static inline void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift)); } -static inline void sd_config_write8(struct tmio_mmc_host *host, int addr, - u8 val) -{ - writeb(val, host->cnf + (addr << host->bus_shift)); -} - -static inline void sd_config_write16(struct tmio_mmc_host *host, int addr, - u16 val) -{ - writew(val, host->cnf + (addr << host->bus_shift)); -} - -static inline void sd_config_write32(struct tmio_mmc_host *host, int addr, - u32 val) -{ - writew(val, host->cnf + (addr << host->bus_shift)); - writew(val >> 16, host->cnf + ((addr + 2) << host->bus_shift)); -} - #include #include diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 6b9c5d06690c..0992e7112d3f 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -2,6 +2,8 @@ #define MFD_TMIO_H #include +#include +#include #define tmio_ioread8(addr) readb(addr) #define tmio_ioread16(addr) readw(addr) @@ -18,11 +20,49 @@ writew((val) >> 16, (addr) + 2); \ } while (0) +#define CNF_CMD 0x04 +#define CNF_CTL_BASE 0x10 +#define CNF_INT_PIN 0x3d +#define CNF_STOP_CLK_CTL 0x40 +#define CNF_GCLK_CTL 0x41 +#define CNF_SD_CLK_MODE 0x42 +#define CNF_PIN_STATUS 0x44 +#define CNF_PWR_CTL_1 0x48 +#define CNF_PWR_CTL_2 0x49 +#define CNF_PWR_CTL_3 0x4a +#define CNF_CARD_DETECT_MODE 0x4c +#define CNF_SD_SLOT 0x50 +#define CNF_EXT_GCLK_CTL_1 0xf0 +#define CNF_EXT_GCLK_CTL_2 0xf1 +#define CNF_EXT_GCLK_CTL_3 0xf9 +#define CNF_SD_LED_EN_1 0xfa +#define CNF_SD_LED_EN_2 0xfe + +#define SDCREN 0x2 /* Enable access to MMC CTL regs. (flag in COMMAND_REG)*/ + +#define sd_config_write8(base, shift, reg, val) \ + tmio_iowrite8((val), (base) + ((reg) << (shift))) +#define sd_config_write16(base, shift, reg, val) \ + tmio_iowrite16((val), (base) + ((reg) << (shift))) +#define sd_config_write32(base, shift, reg, val) \ + do { \ + tmio_iowrite16((val), (base) + ((reg) << (shift))); \ + tmio_iowrite16((val) >> 16, (base) + ((reg + 2) << (shift))); \ + } while (0) + +int tmio_core_mmc_enable(void __iomem *cnf, unsigned long base); +int tmio_core_mmc_resume(void __iomem *cnf, unsigned long base); +void tmio_core_mmc_pwr(void __iomem *cnf, int state); +void tmio_core_mmc_clk_div(void __iomem *cnf, int state); +void tmio_core_set_bus_shift(int bus_shift); + /* * data for the MMC controller */ struct tmio_mmc_data { const unsigned int hclk; + void (*set_pwr)(struct platform_device *host, int state); + void (*set_no_clk_div)(struct platform_device *host, int state); }; /* -- cgit v1.2.3 From 283895abf4240989e16489555b4ec32fa4c2ef05 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Sat, 26 Sep 2009 00:44:33 +0100 Subject: MFD: tc6393xb: fix clock frequency This patch corrects the HCLK frequency in the tc6393xb driver, I believe that it uses 33MHz, based on the comments in the (poor) documentation I have. Signed-off-by: Ian Molton --- drivers/mfd/tc6393xb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index a44b6b02f193..40bb375c605e 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -376,7 +376,7 @@ static void tc6393xb_mmc_clk_div(struct platform_device *mmc, int state) } static struct tmio_mmc_data tc6393xb_mmc_data = { - .hclk = 24000000, + .hclk = 33000000, .set_pwr = tc6393xb_mmc_pwr, .set_no_clk_div = tc6393xb_mmc_clk_div, }; -- cgit v1.2.3 From ffa594842f9108e951a96877472639b54f494886 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Tue, 29 Sep 2009 12:52:03 +0100 Subject: MMC: tmio-mmc: Fix clocking This patch alters the clock selection on tmio-mmc based hosts such that it will select the clock closest in frequency to the desired frequency. This should improve the accuracy of clock selection, particularly during card detection. Signed-off-by: Ian Molton --- drivers/mmc/host/tmio_mmc.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index e70173293262..a4cdc0b74cae 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -43,6 +43,15 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock) for (clock = host->mmc->f_min, clk = 0x80000080; new_clock >= (clock<<1); clk >>= 1) clock <<= 1; + + /* Round the clock to the closest available. This is required + * for some fussy cards that dont like to initialise below 400kHz + */ + if (new_clock - clock >= (clock << 1) - new_clock) { + clk >>= 1; clock <<= 1; + } + + /* Clock enable */ clk |= 0x100; } -- cgit v1.2.3 From ac34a9ad8288a0cd2fd7b846622e509fbad694f7 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Tue, 29 Sep 2009 12:55:27 +0100 Subject: MMC: tmio-mmc: Fix power sequencing This patch fixes power sequencing on tmio-mmc based hosts so that the clock is enabled only once the bus has been powered up. Signed-off-by: Ian Molton --- drivers/mmc/host/tmio_mmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index a4cdc0b74cae..f233867f54bb 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -435,18 +435,18 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (ios->clock) tmio_mmc_set_clock(host, ios->clock); - /* Power sequence - OFF -> ON -> UP */ + /* Power sequence - OFF -> UP -> ON */ switch (ios->power_mode) { case MMC_POWER_OFF: /* power down SD bus */ if (host->set_pwr) host->set_pwr(host->pdev, 0); tmio_mmc_clk_stop(host); break; - case MMC_POWER_ON: /* power up SD bus */ + case MMC_POWER_UP: /* power up SD bus */ if (host->set_pwr) host->set_pwr(host->pdev, 1); break; - case MMC_POWER_UP: /* start bus clock */ + case MMC_POWER_ON: /* enable bus clock */ tmio_mmc_clk_start(host); break; } -- cgit v1.2.3 From 4b52cd752af330463abf2527a8b0df9a10916613 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Wed, 30 Sep 2009 21:41:52 +0100 Subject: MMC: core: Lower MMC/SD init frequency. This patch lowers the previously imposed limit for MMC / SD card initialisation. Some cards are a little 'out of spec' in that they wont initialise over about 200kHz (one such card works fine at 20MHz once initialised). Spec says init must occur below 400kHz. This patch lowers this to 64kHz and fixes the regression caused by the previous limit, whilst not breaking the specs. Signed-off-by: Ian Molton --- drivers/mmc/core/core.c | 4 ++-- drivers/mmc/core/core.h | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d84c880fac84..5cb0df45bd24 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -708,12 +708,12 @@ static void mmc_power_up(struct mmc_host *host) */ mmc_delay(10); - if (host->f_min > 400000) { + if (host->f_min > MMC_INIT_FREQ) { pr_warning("%s: Minimum clock frequency too high for " "identification mode\n", mmc_hostname(host)); host->ios.clock = host->f_min; } else - host->ios.clock = 400000; + host->ios.clock = MMC_INIT_FREQ; host->ios.power_mode = MMC_POWER_ON; mmc_set_ios(host); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index c819effa1032..96505f0c82e2 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -15,6 +15,16 @@ #define MMC_CMD_RETRIES 3 +/* Spec says initialisation must happen at or below 400kHz. + * Some MMC cards fail to initialise at 400kHz (even as low as 200kHz) and + * Some host controllers (eg. tmio_mmc) cannot garantee to set the clock below + * 400kHz (it rounds to the closest clock freq available). + * + * 64 kHz seems like a good speed/reliability compromise. + */ + +#define MMC_INIT_FREQ 64000 + struct mmc_bus_ops { void (*remove)(struct mmc_host *); void (*detect)(struct mmc_host *); -- cgit v1.2.3