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-pxav2.c (6208B)


      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 *		Jun Nie <njun@marvell.com>
      7 *		Qiming Wu <wuqm@marvell.com>
      8 *		Philip Rakity <prakity@marvell.com>
      9 */
     10
     11#include <linux/err.h>
     12#include <linux/init.h>
     13#include <linux/platform_device.h>
     14#include <linux/clk.h>
     15#include <linux/module.h>
     16#include <linux/io.h>
     17#include <linux/mmc/card.h>
     18#include <linux/mmc/host.h>
     19#include <linux/platform_data/pxa_sdhci.h>
     20#include <linux/slab.h>
     21#include <linux/of.h>
     22#include <linux/of_device.h>
     23
     24#include "sdhci.h"
     25#include "sdhci-pltfm.h"
     26
     27#define SD_FIFO_PARAM		0xe0
     28#define DIS_PAD_SD_CLK_GATE	0x0400 /* Turn on/off Dynamic SD Clock Gating */
     29#define CLK_GATE_ON		0x0200 /* Disable/enable Clock Gate */
     30#define CLK_GATE_CTL		0x0100 /* Clock Gate Control */
     31#define CLK_GATE_SETTING_BITS	(DIS_PAD_SD_CLK_GATE | \
     32		CLK_GATE_ON | CLK_GATE_CTL)
     33
     34#define SD_CLOCK_BURST_SIZE_SETUP	0xe6
     35#define SDCLK_SEL_SHIFT		8
     36#define SDCLK_SEL_MASK		0x3
     37#define SDCLK_DELAY_SHIFT	10
     38#define SDCLK_DELAY_MASK	0x3c
     39
     40#define SD_CE_ATA_2		0xea
     41#define MMC_CARD		0x1000
     42#define MMC_WIDTH		0x0100
     43
     44static void pxav2_reset(struct sdhci_host *host, u8 mask)
     45{
     46	struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
     47	struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
     48
     49	sdhci_reset(host, mask);
     50
     51	if (mask == SDHCI_RESET_ALL) {
     52		u16 tmp = 0;
     53
     54		/*
     55		 * tune timing of read data/command when crc error happen
     56		 * no performance impact
     57		 */
     58		if (pdata && pdata->clk_delay_sel == 1) {
     59			tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP);
     60
     61			tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT);
     62			tmp |= (pdata->clk_delay_cycles & SDCLK_DELAY_MASK)
     63				<< SDCLK_DELAY_SHIFT;
     64			tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT);
     65			tmp |= (1 & SDCLK_SEL_MASK) << SDCLK_SEL_SHIFT;
     66
     67			writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP);
     68		}
     69
     70		if (pdata && (pdata->flags & PXA_FLAG_ENABLE_CLOCK_GATING)) {
     71			tmp = readw(host->ioaddr + SD_FIFO_PARAM);
     72			tmp &= ~CLK_GATE_SETTING_BITS;
     73			writew(tmp, host->ioaddr + SD_FIFO_PARAM);
     74		} else {
     75			tmp = readw(host->ioaddr + SD_FIFO_PARAM);
     76			tmp &= ~CLK_GATE_SETTING_BITS;
     77			tmp |= CLK_GATE_SETTING_BITS;
     78			writew(tmp, host->ioaddr + SD_FIFO_PARAM);
     79		}
     80	}
     81}
     82
     83static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width)
     84{
     85	u8 ctrl;
     86	u16 tmp;
     87
     88	ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
     89	tmp = readw(host->ioaddr + SD_CE_ATA_2);
     90	if (width == MMC_BUS_WIDTH_8) {
     91		ctrl &= ~SDHCI_CTRL_4BITBUS;
     92		tmp |= MMC_CARD | MMC_WIDTH;
     93	} else {
     94		tmp &= ~(MMC_CARD | MMC_WIDTH);
     95		if (width == MMC_BUS_WIDTH_4)
     96			ctrl |= SDHCI_CTRL_4BITBUS;
     97		else
     98			ctrl &= ~SDHCI_CTRL_4BITBUS;
     99	}
    100	writew(tmp, host->ioaddr + SD_CE_ATA_2);
    101	writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
    102}
    103
    104static const struct sdhci_ops pxav2_sdhci_ops = {
    105	.set_clock     = sdhci_set_clock,
    106	.get_max_clock = sdhci_pltfm_clk_get_max_clock,
    107	.set_bus_width = pxav2_mmc_set_bus_width,
    108	.reset         = pxav2_reset,
    109	.set_uhs_signaling = sdhci_set_uhs_signaling,
    110};
    111
    112#ifdef CONFIG_OF
    113static const struct of_device_id sdhci_pxav2_of_match[] = {
    114	{
    115		.compatible = "mrvl,pxav2-mmc",
    116	},
    117	{},
    118};
    119MODULE_DEVICE_TABLE(of, sdhci_pxav2_of_match);
    120
    121static struct sdhci_pxa_platdata *pxav2_get_mmc_pdata(struct device *dev)
    122{
    123	struct sdhci_pxa_platdata *pdata;
    124	struct device_node *np = dev->of_node;
    125	u32 bus_width;
    126	u32 clk_delay_cycles;
    127
    128	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
    129	if (!pdata)
    130		return NULL;
    131
    132	if (of_find_property(np, "non-removable", NULL))
    133		pdata->flags |= PXA_FLAG_CARD_PERMANENT;
    134
    135	of_property_read_u32(np, "bus-width", &bus_width);
    136	if (bus_width == 8)
    137		pdata->flags |= PXA_FLAG_SD_8_BIT_CAPABLE_SLOT;
    138
    139	of_property_read_u32(np, "mrvl,clk-delay-cycles", &clk_delay_cycles);
    140	if (clk_delay_cycles > 0) {
    141		pdata->clk_delay_sel = 1;
    142		pdata->clk_delay_cycles = clk_delay_cycles;
    143	}
    144
    145	return pdata;
    146}
    147#else
    148static inline struct sdhci_pxa_platdata *pxav2_get_mmc_pdata(struct device *dev)
    149{
    150	return NULL;
    151}
    152#endif
    153
    154static int sdhci_pxav2_probe(struct platform_device *pdev)
    155{
    156	struct sdhci_pltfm_host *pltfm_host;
    157	struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
    158	struct device *dev = &pdev->dev;
    159	struct sdhci_host *host = NULL;
    160	const struct of_device_id *match;
    161
    162	int ret;
    163	struct clk *clk;
    164
    165	host = sdhci_pltfm_init(pdev, NULL, 0);
    166	if (IS_ERR(host))
    167		return PTR_ERR(host);
    168
    169	pltfm_host = sdhci_priv(host);
    170
    171	clk = devm_clk_get(dev, "PXA-SDHCLK");
    172	if (IS_ERR(clk)) {
    173		dev_err(dev, "failed to get io clock\n");
    174		ret = PTR_ERR(clk);
    175		goto free;
    176	}
    177	pltfm_host->clk = clk;
    178	ret = clk_prepare_enable(clk);
    179	if (ret) {
    180		dev_err(&pdev->dev, "failed to enable io clock\n");
    181		goto free;
    182	}
    183
    184	host->quirks = SDHCI_QUIRK_BROKEN_ADMA
    185		| SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
    186		| SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
    187
    188	match = of_match_device(of_match_ptr(sdhci_pxav2_of_match), &pdev->dev);
    189	if (match) {
    190		pdata = pxav2_get_mmc_pdata(dev);
    191	}
    192	if (pdata) {
    193		if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
    194			/* on-chip device */
    195			host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
    196			host->mmc->caps |= MMC_CAP_NONREMOVABLE;
    197		}
    198
    199		/* If slot design supports 8 bit data, indicate this to MMC. */
    200		if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
    201			host->mmc->caps |= MMC_CAP_8_BIT_DATA;
    202
    203		if (pdata->quirks)
    204			host->quirks |= pdata->quirks;
    205		if (pdata->host_caps)
    206			host->mmc->caps |= pdata->host_caps;
    207		if (pdata->pm_caps)
    208			host->mmc->pm_caps |= pdata->pm_caps;
    209	}
    210
    211	host->ops = &pxav2_sdhci_ops;
    212
    213	ret = sdhci_add_host(host);
    214	if (ret)
    215		goto disable_clk;
    216
    217	return 0;
    218
    219disable_clk:
    220	clk_disable_unprepare(clk);
    221free:
    222	sdhci_pltfm_free(pdev);
    223	return ret;
    224}
    225
    226static struct platform_driver sdhci_pxav2_driver = {
    227	.driver		= {
    228		.name	= "sdhci-pxav2",
    229		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
    230		.of_match_table = of_match_ptr(sdhci_pxav2_of_match),
    231		.pm	= &sdhci_pltfm_pmops,
    232	},
    233	.probe		= sdhci_pxav2_probe,
    234	.remove		= sdhci_pltfm_unregister,
    235};
    236
    237module_platform_driver(sdhci_pxav2_driver);
    238
    239MODULE_DESCRIPTION("SDHCI driver for pxav2");
    240MODULE_AUTHOR("Marvell International Ltd.");
    241MODULE_LICENSE("GPL v2");
    242