Message ID | 1307526121-12059-3-git-send-email-zhangfei.gao@marvell.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Jun 8, 2011, at 2:41 AM, Zhangfei Gao wrote: > SDHCI driver for PXAV2 SoCs, such as pxa910, the driver based on sdhci-pltfm to handle resource etc. > > Signed-off-by: Zhangfei Gao <zhangfei.gao@marvell.com> > Signed-off-by: Jun Nie <njun@marvell.com> > Signed-off-by: Qiming Wu <wuqm@marvell.com> > --- > +++ b/drivers/mmc/host/sdhci-pxav2.c > + > +#define SD_FIFO_PARAM 0xe0 > +#define DIS_PAD_SD_CLK_GATE 0x0400 /* Turn on/off Dynamic SD Clock Gating */ > +#define CLK_GATE_ON 0x0200 /* Disable/enable Clock Gate */ > +#define CLK_GATE_CTL 0x0100 /* Clock Gate Control */ > +#define CLK_GATE_SETTING_BITS (DIS_PAD_SD_CLK_GATE | \ > + CLK_GATE_ON | CLK_GATE_CTL) > + > +#define SD_CLOCK_BURST_SIZE_SETUP 0xe6 > +#define SDCLK_SEL_SHIFT 8 > +#define SDCLK_SEL_MASK 0x3 > +#define SDCLK_DELAY_SHIFT 10 > +#define SDCLK_DELAY_MASK 0x3c > + > +#define SD_CE_ATA_2 0xea > +#define MMC_CARD 0x1000 > +#define MMC_WIDTH 0x0100 > + > +static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask) > +{ > + struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); > + struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; > + > + if (mask == SDHCI_RESET_ALL) { > + u16 tmp = 0; > + > + /* > + * tune timing of read data/command when crc error happen > + * no performance impact > + */ > + if (pdata->clk_delay_sel == 1) { > + tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); > + > + tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT); > + tmp |= (pdata->clk_delay_cycles & SDCLK_DELAY_MASK) > + << SDCLK_DELAY_SHIFT; > + tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT); > + tmp |= (1 & SDCLK_SEL_MASK) << SDCLK_SEL_SHIFT; > + > + writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); > + } There are 3 possible value for clk_delay_sel. 0 == do not use clk_delay_cycles 1 == use programmed clk_delay_cycles value (the code above) all other values use the sd_clk which was used to drive the output The code about only handles 2 of the 3 cases suggest (if (pdata->clk_delay_sel) { > + if (pdata->clk_delay_sel == 1) { > + tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); > + > + tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT); > + tmp |= (pdata->clk_delay_cycles & SDCLK_DELAY_MASK) > + << SDCLK_DELAY_SHIFT; > + tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT); > + tmp |= 1 << SDCLK_SEL_SHIFT; > + > + writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); > + } else { > + tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); > + tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT); > + tmp |= 2 << SDCLK_SEL_SHIFT; > + writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); } } Philip and Mark >
>> +static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask) >> +{ >> + struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); >> + struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; >> + >> + if (mask == SDHCI_RESET_ALL) { >> + u16 tmp = 0; >> + >> + /* >> + * tune timing of read data/command when crc error happen >> + * no performance impact >> + */ >> + if (pdata->clk_delay_sel == 1) { >> + tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); >> + >> + tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT); >> + tmp |= (pdata->clk_delay_cycles & SDCLK_DELAY_MASK) >> + << SDCLK_DELAY_SHIFT; >> + tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT); >> + tmp |= (1 & SDCLK_SEL_MASK) << SDCLK_SEL_SHIFT; >> + >> + writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); >> + } > > There are 3 possible value for clk_delay_sel. > > 0 == do not use clk_delay_cycles > 1 == use programmed clk_delay_cycles value (the code above) > all other values use the sd_clk which was used to drive the output > > The code about only handles 2 of the 3 cases pxa910/920 only require two cases. 1, clk_delay_sel = 1 with delay value. 2, use default setting and directly use sd_clk. clk_delay_sel = 3. > > suggest > > (if (pdata->clk_delay_sel) { >> + if (pdata->clk_delay_sel == 1) { >> + tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); >> + >> + tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT); >> + tmp |= (pdata->clk_delay_cycles & SDCLK_DELAY_MASK) >> + << SDCLK_DELAY_SHIFT; >> + tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT); >> + tmp |= 1 << SDCLK_SEL_SHIFT; >> + >> + writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); >> + } else { > >> + tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); >> + tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT); >> + tmp |= 2 << SDCLK_SEL_SHIFT; >> + writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); The else is default setting on pxa910 after reset. > } > } > > Philip and Mark >> > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel >
On Jun 13, 2011, at 11:07 PM, zhangfei gao wrote: >>> +static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask) >>> +{ >>> + struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); >>> + struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; >>> + >>> + if (mask == SDHCI_RESET_ALL) { >>> + u16 tmp = 0; >>> + >>> + /* >>> + * tune timing of read data/command when crc error happen >>> + * no performance impact >>> + */ >>> + if (pdata->clk_delay_sel == 1) { >>> + tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); >>> + >>> + tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT); >>> + tmp |= (pdata->clk_delay_cycles & SDCLK_DELAY_MASK) >>> + << SDCLK_DELAY_SHIFT; >>> + tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT); >>> + tmp |= (1 & SDCLK_SEL_MASK) << SDCLK_SEL_SHIFT; >>> + >>> + writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); >>> + } >> >> There are 3 possible value for clk_delay_sel. >> >> 0 == do not use clk_delay_cycles >> 1 == use programmed clk_delay_cycles value (the code above) >> all other values use the sd_clk which was used to drive the output >> >> The code about only handles 2 of the 3 cases > > pxa910/920 only require two cases. > 1, clk_delay_sel = 1 with delay value. > 2, use default setting and directly use sd_clk. clk_delay_sel = 3. > >> >> suggest >> >> (if (pdata->clk_delay_sel) { >>> + if (pdata->clk_delay_sel == 1) { >>> + tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); >>> + >>> + tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT); >>> + tmp |= (pdata->clk_delay_cycles & SDCLK_DELAY_MASK) >>> + << SDCLK_DELAY_SHIFT; >>> + tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT); >>> + tmp |= 1 << SDCLK_SEL_SHIFT; >>> + >>> + writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); >>> + } else { >> >>> + tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); >>> + tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT); >>> + tmp |= 2 << SDCLK_SEL_SHIFT; >>> + writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); > The else is default setting on pxa910 after reset. >> } >> } >> >> Philip and Mark If you are comfortable not support mode 0 then I am okay with this patch set. Acked-by: Philip Rakity <prakity@marvell.com> Acked-by: Mark F. Brown <mark.brown314@gmail.com> >>> >> >> _______________________________________________ >> linux-arm-kernel mailing list >> linux-arm-kernel@lists.infradead.org >> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel >>
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 6525a04..97b8322 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -205,6 +205,18 @@ config MMC_SDHCI_PXAV3 If unsure, say N. +config MMC_SDHCI_PXAV2 + tristate "Marvell PXA9XX SD Host Controller support (PXAV2)" + select MMC_SDHCI + select MMC_SDHCI_PLTFM + default CPU_PXA910 + help + This selects the Marvell(R) PXAV2 SD Host Controller. + If you have a PXA9XX platform with SD Host Controller + and a card slot, say Y or M here. + + If unsure, say N. + config MMC_SDHCI_SPEAR tristate "SDHCI support on ST SPEAr platform" depends on MMC_SDHCI && PLAT_SPEAR diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 688cc52..99f7d79 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o obj-$(CONFIG_MMC_SDHCI_PXA) += sdhci-pxa.o obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o +obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o obj-$(CONFIG_MMC_WBSD) += wbsd.o diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c new file mode 100644 index 0000000..7a6fa8c --- /dev/null +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2010 Marvell International Ltd. + * Zhangfei Gao <zhangfei.gao@marvell.com> + * Kevin Wang <dwang4@marvell.com> + * Jun Nie <njun@marvell.com> + * Qiming Wu <wuqm@marvell.com> + * Philip Rakity <prakity@marvell.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/gpio.h> +#include <linux/mmc/card.h> +#include <linux/mmc/host.h> +#include <plat/sdhci.h> +#include <linux/slab.h> +#include "sdhci.h" +#include "sdhci-pltfm.h" + +#define SD_FIFO_PARAM 0xe0 +#define DIS_PAD_SD_CLK_GATE 0x0400 /* Turn on/off Dynamic SD Clock Gating */ +#define CLK_GATE_ON 0x0200 /* Disable/enable Clock Gate */ +#define CLK_GATE_CTL 0x0100 /* Clock Gate Control */ +#define CLK_GATE_SETTING_BITS (DIS_PAD_SD_CLK_GATE | \ + CLK_GATE_ON | CLK_GATE_CTL) + +#define SD_CLOCK_BURST_SIZE_SETUP 0xe6 +#define SDCLK_SEL_SHIFT 8 +#define SDCLK_SEL_MASK 0x3 +#define SDCLK_DELAY_SHIFT 10 +#define SDCLK_DELAY_MASK 0x3c + +#define SD_CE_ATA_2 0xea +#define MMC_CARD 0x1000 +#define MMC_WIDTH 0x0100 + +static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask) +{ + struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); + struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; + + if (mask == SDHCI_RESET_ALL) { + u16 tmp = 0; + + /* + * tune timing of read data/command when crc error happen + * no performance impact + */ + if (pdata->clk_delay_sel == 1) { + tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); + + tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT); + tmp |= (pdata->clk_delay_cycles & SDCLK_DELAY_MASK) + << SDCLK_DELAY_SHIFT; + tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT); + tmp |= (1 & SDCLK_SEL_MASK) << SDCLK_SEL_SHIFT; + + writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); + } + + if (pdata->flags & PXA_FLAG_ENABLE_CLOCK_GATING) { + tmp = readw(host->ioaddr + SD_FIFO_PARAM); + tmp &= ~CLK_GATE_SETTING_BITS; + writew(tmp, host->ioaddr + SD_FIFO_PARAM); + } else { + tmp = readw(host->ioaddr + SD_FIFO_PARAM); + tmp &= ~CLK_GATE_SETTING_BITS; + tmp |= CLK_GATE_SETTING_BITS; + writew(tmp, host->ioaddr + SD_FIFO_PARAM); + } + } +} + +static int pxav2_mmc_set_width(struct sdhci_host *host, int width) +{ + u8 ctrl; + u16 tmp; + + ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); + tmp = readw(host->ioaddr + SD_CE_ATA_2); + if (width == MMC_BUS_WIDTH_8) { + ctrl &= ~SDHCI_CTRL_4BITBUS; + tmp |= MMC_CARD | MMC_WIDTH; + } else { + tmp &= ~(MMC_CARD | MMC_WIDTH); + if (width == MMC_BUS_WIDTH_4) + ctrl |= SDHCI_CTRL_4BITBUS; + else + ctrl &= ~SDHCI_CTRL_4BITBUS; + } + writew(tmp, host->ioaddr + SD_CE_ATA_2); + writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); + + return 0; +} + +static u32 pxav2_get_max_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + return clk_get_rate(pltfm_host->clk); +} + +static struct sdhci_ops pxav2_sdhci_ops = { + .get_max_clock = pxav2_get_max_clock, + .platform_reset_exit = pxav2_set_private_registers, + .platform_8bit_width = pxav2_mmc_set_width, +}; + +static int __devinit sdhci_pxav2_probe(struct platform_device *pdev) +{ + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct sdhci_host *host = NULL; + struct sdhci_pxa *pxa = NULL; + int ret; + struct clk *clk; + + pxa = kzalloc(sizeof(struct sdhci_pxa), GFP_KERNEL); + if (!pxa) + return -ENOMEM; + + host = sdhci_pltfm_init(pdev, NULL); + if (IS_ERR(host)) { + kfree(pxa); + return PTR_ERR(host); + } + pltfm_host = sdhci_priv(host); + pltfm_host->priv = pxa; + + clk = clk_get(dev, "PXA-SDHCLK"); + if (IS_ERR(clk)) { + dev_err(dev, "failed to get io clock\n"); + ret = PTR_ERR(clk); + goto err_clk_get; + } + pltfm_host->clk = clk; + clk_enable(clk); + + host->quirks = SDHCI_QUIRK_BROKEN_ADMA + | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL + | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN; + + if (pdata) { + if (pdata->flags & PXA_FLAG_CARD_PERMANENT) { + /* on-chip device */ + host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; + host->mmc->caps |= MMC_CAP_NONREMOVABLE; + } + + /* If slot design supports 8 bit data, indicate this to MMC. */ + if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT) + host->mmc->caps |= MMC_CAP_8_BIT_DATA; + + if (pdata->quirks) + host->quirks |= pdata->quirks; + if (pdata->host_caps) + host->mmc->caps |= pdata->host_caps; + if (pdata->pm_caps) + host->mmc->pm_caps |= pdata->pm_caps; + } + + host->ops = &pxav2_sdhci_ops; + + ret = sdhci_add_host(host); + if (ret) { + dev_err(&pdev->dev, "failed to add host\n"); + goto err_add_host; + } + + platform_set_drvdata(pdev, host); + + return 0; + +err_add_host: + clk_disable(clk); + clk_put(clk); +err_clk_get: + sdhci_pltfm_free(pdev); + kfree(pxa); + return ret; +} + +static int __devexit sdhci_pxav2_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_pxa *pxa = pltfm_host->priv; + + sdhci_remove_host(host, 1); + + clk_disable(pltfm_host->clk); + clk_put(pltfm_host->clk); + sdhci_pltfm_free(pdev); + kfree(pxa); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver sdhci_pxav2_driver = { + .driver = { + .name = "sdhci-pxav2", + .owner = THIS_MODULE, + }, + .probe = sdhci_pxav2_probe, + .remove = __devexit_p(sdhci_pxav2_remove), +#ifdef CONFIG_PM + .suspend = sdhci_pltfm_suspend, + .resume = sdhci_pltfm_resume, +#endif +}; +static int __init sdhci_pxav2_init(void) +{ + return platform_driver_register(&sdhci_pxav2_driver); +} + +static void __exit sdhci_pxav2_exit(void) +{ + platform_driver_unregister(&sdhci_pxav2_driver); +} + +module_init(sdhci_pxav2_init); +module_exit(sdhci_pxav2_exit); + +MODULE_DESCRIPTION("SDHCI driver for pxav2"); +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_LICENSE("GPL v2"); +