cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sdhci-pxav3.c (15071B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2010 Marvell International Ltd.
      4 *		Zhangfei Gao <zhangfei.gao@marvell.com>
      5 *		Kevin Wang <dwang4@marvell.com>
      6 *		Mingwei Wang <mwwang@marvell.com>
      7 *		Philip Rakity <prakity@marvell.com>
      8 *		Mark Brown <markb@marvell.com>
      9 */
     10#include <linux/err.h>
     11#include <linux/init.h>
     12#include <linux/platform_device.h>
     13#include <linux/clk.h>
     14#include <linux/io.h>
     15#include <linux/mmc/card.h>
     16#include <linux/mmc/host.h>
     17#include <linux/platform_data/pxa_sdhci.h>
     18#include <linux/slab.h>
     19#include <linux/delay.h>
     20#include <linux/module.h>
     21#include <linux/of.h>
     22#include <linux/of_device.h>
     23#include <linux/pm.h>
     24#include <linux/pm_runtime.h>
     25#include <linux/mbus.h>
     26
     27#include "sdhci.h"
     28#include "sdhci-pltfm.h"
     29
     30#define PXAV3_RPM_DELAY_MS     50
     31
     32#define SD_CLOCK_BURST_SIZE_SETUP		0x10A
     33#define SDCLK_SEL	0x100
     34#define SDCLK_DELAY_SHIFT	9
     35#define SDCLK_DELAY_MASK	0x1f
     36
     37#define SD_CFG_FIFO_PARAM       0x100
     38#define SDCFG_GEN_PAD_CLK_ON	(1<<6)
     39#define SDCFG_GEN_PAD_CLK_CNT_MASK	0xFF
     40#define SDCFG_GEN_PAD_CLK_CNT_SHIFT	24
     41
     42#define SD_SPI_MODE          0x108
     43#define SD_CE_ATA_1          0x10C
     44
     45#define SD_CE_ATA_2          0x10E
     46#define SDCE_MISC_INT		(1<<2)
     47#define SDCE_MISC_INT_EN	(1<<1)
     48
     49struct sdhci_pxa {
     50	struct clk *clk_core;
     51	struct clk *clk_io;
     52	u8	power_mode;
     53	void __iomem *sdio3_conf_reg;
     54};
     55
     56/*
     57 * These registers are relative to the second register region, for the
     58 * MBus bridge.
     59 */
     60#define SDHCI_WINDOW_CTRL(i)	(0x80 + ((i) << 3))
     61#define SDHCI_WINDOW_BASE(i)	(0x84 + ((i) << 3))
     62#define SDHCI_MAX_WIN_NUM	8
     63
     64/*
     65 * Fields below belong to SDIO3 Configuration Register (third register
     66 * region for the Armada 38x flavor)
     67 */
     68
     69#define SDIO3_CONF_CLK_INV	BIT(0)
     70#define SDIO3_CONF_SD_FB_CLK	BIT(2)
     71
     72static int mv_conf_mbus_windows(struct platform_device *pdev,
     73				const struct mbus_dram_target_info *dram)
     74{
     75	int i;
     76	void __iomem *regs;
     77	struct resource *res;
     78
     79	if (!dram) {
     80		dev_err(&pdev->dev, "no mbus dram info\n");
     81		return -EINVAL;
     82	}
     83
     84	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
     85	if (!res) {
     86		dev_err(&pdev->dev, "cannot get mbus registers\n");
     87		return -EINVAL;
     88	}
     89
     90	regs = ioremap(res->start, resource_size(res));
     91	if (!regs) {
     92		dev_err(&pdev->dev, "cannot map mbus registers\n");
     93		return -ENOMEM;
     94	}
     95
     96	for (i = 0; i < SDHCI_MAX_WIN_NUM; i++) {
     97		writel(0, regs + SDHCI_WINDOW_CTRL(i));
     98		writel(0, regs + SDHCI_WINDOW_BASE(i));
     99	}
    100
    101	for (i = 0; i < dram->num_cs; i++) {
    102		const struct mbus_dram_window *cs = dram->cs + i;
    103
    104		/* Write size, attributes and target id to control register */
    105		writel(((cs->size - 1) & 0xffff0000) |
    106			(cs->mbus_attr << 8) |
    107			(dram->mbus_dram_target_id << 4) | 1,
    108			regs + SDHCI_WINDOW_CTRL(i));
    109		/* Write base address to base register */
    110		writel(cs->base, regs + SDHCI_WINDOW_BASE(i));
    111	}
    112
    113	iounmap(regs);
    114
    115	return 0;
    116}
    117
    118static int armada_38x_quirks(struct platform_device *pdev,
    119			     struct sdhci_host *host)
    120{
    121	struct device_node *np = pdev->dev.of_node;
    122	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
    123	struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host);
    124	struct resource *res;
    125
    126	host->quirks &= ~SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
    127	host->quirks |= SDHCI_QUIRK_MISSING_CAPS;
    128
    129	host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
    130	host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
    131
    132	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
    133					   "conf-sdio3");
    134	if (res) {
    135		pxa->sdio3_conf_reg = devm_ioremap_resource(&pdev->dev, res);
    136		if (IS_ERR(pxa->sdio3_conf_reg))
    137			return PTR_ERR(pxa->sdio3_conf_reg);
    138	} else {
    139		/*
    140		 * According to erratum 'FE-2946959' both SDR50 and DDR50
    141		 * modes require specific clock adjustments in SDIO3
    142		 * Configuration register, if the adjustment is not done,
    143		 * remove them from the capabilities.
    144		 */
    145		host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
    146
    147		dev_warn(&pdev->dev, "conf-sdio3 register not found: disabling SDR50 and DDR50 modes.\nConsider updating your dtb\n");
    148	}
    149
    150	/*
    151	 * According to erratum 'ERR-7878951' Armada 38x SDHCI
    152	 * controller has different capabilities than the ones shown
    153	 * in its registers
    154	 */
    155	if (of_property_read_bool(np, "no-1-8-v")) {
    156		host->caps &= ~SDHCI_CAN_VDD_180;
    157		host->mmc->caps &= ~MMC_CAP_1_8V_DDR;
    158	} else {
    159		host->caps &= ~SDHCI_CAN_VDD_330;
    160	}
    161	host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_USE_SDR50_TUNING);
    162
    163	return 0;
    164}
    165
    166static void pxav3_reset(struct sdhci_host *host, u8 mask)
    167{
    168	struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
    169	struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
    170
    171	sdhci_reset(host, mask);
    172
    173	if (mask == SDHCI_RESET_ALL) {
    174		/*
    175		 * tune timing of read data/command when crc error happen
    176		 * no performance impact
    177		 */
    178		if (pdata && 0 != pdata->clk_delay_cycles) {
    179			u16 tmp;
    180
    181			tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP);
    182			tmp |= (pdata->clk_delay_cycles & SDCLK_DELAY_MASK)
    183				<< SDCLK_DELAY_SHIFT;
    184			tmp |= SDCLK_SEL;
    185			writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP);
    186		}
    187	}
    188}
    189
    190#define MAX_WAIT_COUNT 5
    191static void pxav3_gen_init_74_clocks(struct sdhci_host *host, u8 power_mode)
    192{
    193	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
    194	struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host);
    195	u16 tmp;
    196	int count;
    197
    198	if (pxa->power_mode == MMC_POWER_UP
    199			&& power_mode == MMC_POWER_ON) {
    200
    201		dev_dbg(mmc_dev(host->mmc),
    202				"%s: slot->power_mode = %d,"
    203				"ios->power_mode = %d\n",
    204				__func__,
    205				pxa->power_mode,
    206				power_mode);
    207
    208		/* set we want notice of when 74 clocks are sent */
    209		tmp = readw(host->ioaddr + SD_CE_ATA_2);
    210		tmp |= SDCE_MISC_INT_EN;
    211		writew(tmp, host->ioaddr + SD_CE_ATA_2);
    212
    213		/* start sending the 74 clocks */
    214		tmp = readw(host->ioaddr + SD_CFG_FIFO_PARAM);
    215		tmp |= SDCFG_GEN_PAD_CLK_ON;
    216		writew(tmp, host->ioaddr + SD_CFG_FIFO_PARAM);
    217
    218		/* slowest speed is about 100KHz or 10usec per clock */
    219		udelay(740);
    220		count = 0;
    221
    222		while (count++ < MAX_WAIT_COUNT) {
    223			if ((readw(host->ioaddr + SD_CE_ATA_2)
    224						& SDCE_MISC_INT) == 0)
    225				break;
    226			udelay(10);
    227		}
    228
    229		if (count == MAX_WAIT_COUNT)
    230			dev_warn(mmc_dev(host->mmc), "74 clock interrupt not cleared\n");
    231
    232		/* clear the interrupt bit if posted */
    233		tmp = readw(host->ioaddr + SD_CE_ATA_2);
    234		tmp |= SDCE_MISC_INT;
    235		writew(tmp, host->ioaddr + SD_CE_ATA_2);
    236	}
    237	pxa->power_mode = power_mode;
    238}
    239
    240static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
    241{
    242	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
    243	struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host);
    244	u16 ctrl_2;
    245
    246	/*
    247	 * Set V18_EN -- UHS modes do not work without this.
    248	 * does not change signaling voltage
    249	 */
    250	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
    251
    252	/* Select Bus Speed Mode for host */
    253	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
    254	switch (uhs) {
    255	case MMC_TIMING_UHS_SDR12:
    256		ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
    257		break;
    258	case MMC_TIMING_UHS_SDR25:
    259		ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
    260		break;
    261	case MMC_TIMING_UHS_SDR50:
    262		ctrl_2 |= SDHCI_CTRL_UHS_SDR50 | SDHCI_CTRL_VDD_180;
    263		break;
    264	case MMC_TIMING_UHS_SDR104:
    265		ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180;
    266		break;
    267	case MMC_TIMING_MMC_DDR52:
    268	case MMC_TIMING_UHS_DDR50:
    269		ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180;
    270		break;
    271	}
    272
    273	/*
    274	 * Update SDIO3 Configuration register according to erratum
    275	 * FE-2946959
    276	 */
    277	if (pxa->sdio3_conf_reg) {
    278		u8 reg_val  = readb(pxa->sdio3_conf_reg);
    279
    280		if (uhs == MMC_TIMING_UHS_SDR50 ||
    281		    uhs == MMC_TIMING_UHS_DDR50) {
    282			reg_val &= ~SDIO3_CONF_CLK_INV;
    283			reg_val |= SDIO3_CONF_SD_FB_CLK;
    284		} else if (uhs == MMC_TIMING_MMC_HS) {
    285			reg_val &= ~SDIO3_CONF_CLK_INV;
    286			reg_val &= ~SDIO3_CONF_SD_FB_CLK;
    287		} else {
    288			reg_val |= SDIO3_CONF_CLK_INV;
    289			reg_val &= ~SDIO3_CONF_SD_FB_CLK;
    290		}
    291		writeb(reg_val, pxa->sdio3_conf_reg);
    292	}
    293
    294	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
    295	dev_dbg(mmc_dev(host->mmc),
    296		"%s uhs = %d, ctrl_2 = %04X\n",
    297		__func__, uhs, ctrl_2);
    298}
    299
    300static void pxav3_set_power(struct sdhci_host *host, unsigned char mode,
    301			    unsigned short vdd)
    302{
    303	struct mmc_host *mmc = host->mmc;
    304	u8 pwr = host->pwr;
    305
    306	sdhci_set_power_noreg(host, mode, vdd);
    307
    308	if (host->pwr == pwr)
    309		return;
    310
    311	if (host->pwr == 0)
    312		vdd = 0;
    313
    314	if (!IS_ERR(mmc->supply.vmmc))
    315		mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
    316}
    317
    318static const struct sdhci_ops pxav3_sdhci_ops = {
    319	.set_clock = sdhci_set_clock,
    320	.set_power = pxav3_set_power,
    321	.platform_send_init_74_clocks = pxav3_gen_init_74_clocks,
    322	.get_max_clock = sdhci_pltfm_clk_get_max_clock,
    323	.set_bus_width = sdhci_set_bus_width,
    324	.reset = pxav3_reset,
    325	.set_uhs_signaling = pxav3_set_uhs_signaling,
    326};
    327
    328static const struct sdhci_pltfm_data sdhci_pxav3_pdata = {
    329	.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK
    330		| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
    331		| SDHCI_QUIRK_32BIT_ADMA_SIZE
    332		| SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
    333	.ops = &pxav3_sdhci_ops,
    334};
    335
    336#ifdef CONFIG_OF
    337static const struct of_device_id sdhci_pxav3_of_match[] = {
    338	{
    339		.compatible = "mrvl,pxav3-mmc",
    340	},
    341	{
    342		.compatible = "marvell,armada-380-sdhci",
    343	},
    344	{},
    345};
    346MODULE_DEVICE_TABLE(of, sdhci_pxav3_of_match);
    347
    348static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev)
    349{
    350	struct sdhci_pxa_platdata *pdata;
    351	struct device_node *np = dev->of_node;
    352	u32 clk_delay_cycles;
    353
    354	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
    355	if (!pdata)
    356		return NULL;
    357
    358	if (!of_property_read_u32(np, "mrvl,clk-delay-cycles",
    359				  &clk_delay_cycles))
    360		pdata->clk_delay_cycles = clk_delay_cycles;
    361
    362	return pdata;
    363}
    364#else
    365static inline struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev)
    366{
    367	return NULL;
    368}
    369#endif
    370
    371static int sdhci_pxav3_probe(struct platform_device *pdev)
    372{
    373	struct sdhci_pltfm_host *pltfm_host;
    374	struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
    375	struct device *dev = &pdev->dev;
    376	struct device_node *np = pdev->dev.of_node;
    377	struct sdhci_host *host = NULL;
    378	struct sdhci_pxa *pxa = NULL;
    379	const struct of_device_id *match;
    380	int ret;
    381
    382	host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata, sizeof(*pxa));
    383	if (IS_ERR(host))
    384		return PTR_ERR(host);
    385
    386	pltfm_host = sdhci_priv(host);
    387	pxa = sdhci_pltfm_priv(pltfm_host);
    388
    389	pxa->clk_io = devm_clk_get(dev, "io");
    390	if (IS_ERR(pxa->clk_io))
    391		pxa->clk_io = devm_clk_get(dev, NULL);
    392	if (IS_ERR(pxa->clk_io)) {
    393		dev_err(dev, "failed to get io clock\n");
    394		ret = PTR_ERR(pxa->clk_io);
    395		goto err_clk_get;
    396	}
    397	pltfm_host->clk = pxa->clk_io;
    398	clk_prepare_enable(pxa->clk_io);
    399
    400	pxa->clk_core = devm_clk_get(dev, "core");
    401	if (!IS_ERR(pxa->clk_core))
    402		clk_prepare_enable(pxa->clk_core);
    403
    404	/* enable 1/8V DDR capable */
    405	host->mmc->caps |= MMC_CAP_1_8V_DDR;
    406
    407	if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) {
    408		ret = armada_38x_quirks(pdev, host);
    409		if (ret < 0)
    410			goto err_mbus_win;
    411		ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info());
    412		if (ret < 0)
    413			goto err_mbus_win;
    414	}
    415
    416	match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev);
    417	if (match) {
    418		ret = mmc_of_parse(host->mmc);
    419		if (ret)
    420			goto err_of_parse;
    421		sdhci_get_of_property(pdev);
    422		pdata = pxav3_get_mmc_pdata(dev);
    423		pdev->dev.platform_data = pdata;
    424	} else if (pdata) {
    425		/* on-chip device */
    426		if (pdata->flags & PXA_FLAG_CARD_PERMANENT)
    427			host->mmc->caps |= MMC_CAP_NONREMOVABLE;
    428
    429		/* If slot design supports 8 bit data, indicate this to MMC. */
    430		if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
    431			host->mmc->caps |= MMC_CAP_8_BIT_DATA;
    432
    433		if (pdata->quirks)
    434			host->quirks |= pdata->quirks;
    435		if (pdata->quirks2)
    436			host->quirks2 |= pdata->quirks2;
    437		if (pdata->host_caps)
    438			host->mmc->caps |= pdata->host_caps;
    439		if (pdata->host_caps2)
    440			host->mmc->caps2 |= pdata->host_caps2;
    441		if (pdata->pm_caps)
    442			host->mmc->pm_caps |= pdata->pm_caps;
    443	}
    444
    445	pm_runtime_get_noresume(&pdev->dev);
    446	pm_runtime_set_active(&pdev->dev);
    447	pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS);
    448	pm_runtime_use_autosuspend(&pdev->dev);
    449	pm_runtime_enable(&pdev->dev);
    450	pm_suspend_ignore_children(&pdev->dev, 1);
    451
    452	ret = sdhci_add_host(host);
    453	if (ret)
    454		goto err_add_host;
    455
    456	if (host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ)
    457		device_init_wakeup(&pdev->dev, 1);
    458
    459	pm_runtime_put_autosuspend(&pdev->dev);
    460
    461	return 0;
    462
    463err_add_host:
    464	pm_runtime_disable(&pdev->dev);
    465	pm_runtime_put_noidle(&pdev->dev);
    466err_of_parse:
    467err_mbus_win:
    468	clk_disable_unprepare(pxa->clk_io);
    469	clk_disable_unprepare(pxa->clk_core);
    470err_clk_get:
    471	sdhci_pltfm_free(pdev);
    472	return ret;
    473}
    474
    475static int sdhci_pxav3_remove(struct platform_device *pdev)
    476{
    477	struct sdhci_host *host = platform_get_drvdata(pdev);
    478	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
    479	struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host);
    480
    481	pm_runtime_get_sync(&pdev->dev);
    482	pm_runtime_disable(&pdev->dev);
    483	pm_runtime_put_noidle(&pdev->dev);
    484
    485	sdhci_remove_host(host, 1);
    486
    487	clk_disable_unprepare(pxa->clk_io);
    488	clk_disable_unprepare(pxa->clk_core);
    489
    490	sdhci_pltfm_free(pdev);
    491
    492	return 0;
    493}
    494
    495#ifdef CONFIG_PM_SLEEP
    496static int sdhci_pxav3_suspend(struct device *dev)
    497{
    498	int ret;
    499	struct sdhci_host *host = dev_get_drvdata(dev);
    500
    501	pm_runtime_get_sync(dev);
    502	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
    503		mmc_retune_needed(host->mmc);
    504	ret = sdhci_suspend_host(host);
    505	pm_runtime_mark_last_busy(dev);
    506	pm_runtime_put_autosuspend(dev);
    507
    508	return ret;
    509}
    510
    511static int sdhci_pxav3_resume(struct device *dev)
    512{
    513	int ret;
    514	struct sdhci_host *host = dev_get_drvdata(dev);
    515
    516	pm_runtime_get_sync(dev);
    517	ret = sdhci_resume_host(host);
    518	pm_runtime_mark_last_busy(dev);
    519	pm_runtime_put_autosuspend(dev);
    520
    521	return ret;
    522}
    523#endif
    524
    525#ifdef CONFIG_PM
    526static int sdhci_pxav3_runtime_suspend(struct device *dev)
    527{
    528	struct sdhci_host *host = dev_get_drvdata(dev);
    529	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
    530	struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host);
    531	int ret;
    532
    533	ret = sdhci_runtime_suspend_host(host);
    534	if (ret)
    535		return ret;
    536
    537	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
    538		mmc_retune_needed(host->mmc);
    539
    540	clk_disable_unprepare(pxa->clk_io);
    541	if (!IS_ERR(pxa->clk_core))
    542		clk_disable_unprepare(pxa->clk_core);
    543
    544	return 0;
    545}
    546
    547static int sdhci_pxav3_runtime_resume(struct device *dev)
    548{
    549	struct sdhci_host *host = dev_get_drvdata(dev);
    550	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
    551	struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host);
    552
    553	clk_prepare_enable(pxa->clk_io);
    554	if (!IS_ERR(pxa->clk_core))
    555		clk_prepare_enable(pxa->clk_core);
    556
    557	return sdhci_runtime_resume_host(host, 0);
    558}
    559#endif
    560
    561static const struct dev_pm_ops sdhci_pxav3_pmops = {
    562	SET_SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume)
    563	SET_RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend,
    564		sdhci_pxav3_runtime_resume, NULL)
    565};
    566
    567static struct platform_driver sdhci_pxav3_driver = {
    568	.driver		= {
    569		.name	= "sdhci-pxav3",
    570		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
    571		.of_match_table = of_match_ptr(sdhci_pxav3_of_match),
    572		.pm	= &sdhci_pxav3_pmops,
    573	},
    574	.probe		= sdhci_pxav3_probe,
    575	.remove		= sdhci_pxav3_remove,
    576};
    577
    578module_platform_driver(sdhci_pxav3_driver);
    579
    580MODULE_DESCRIPTION("SDHCI driver for pxav3");
    581MODULE_AUTHOR("Marvell International Ltd.");
    582MODULE_LICENSE("GPL v2");
    583