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-milbeaut.c (9218B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd
      4 *              Vincent Yang <vincent.yang@tw.fujitsu.com>
      5 * Copyright (C) 2015 Linaro Ltd  Andy Green <andy.green@linaro.org>
      6 * Copyright (C) 2019 Socionext Inc.
      7 *              Takao Orito <orito.takao@socionext.com>
      8 */
      9
     10#include <linux/bits.h>
     11#include <linux/clk.h>
     12#include <linux/delay.h>
     13#include <linux/err.h>
     14#include <linux/gpio/consumer.h>
     15#include <linux/module.h>
     16#include <linux/of.h>
     17#include <linux/property.h>
     18
     19#include "sdhci-pltfm.h"
     20#include "sdhci_f_sdh30.h"
     21
     22/* milbeaut bridge controller register */
     23#define MLB_SOFT_RESET		0x0200
     24#define  MLB_SOFT_RESET_RSTX		BIT(0)
     25
     26#define MLB_WP_CD_LED_SET	0x0210
     27#define  MLB_WP_CD_LED_SET_LED_INV  BIT(2)
     28
     29#define MLB_CR_SET			0x0220
     30#define  MLB_CR_SET_CR_TOCLKUNIT       BIT(24)
     31#define  MLB_CR_SET_CR_TOCLKFREQ_SFT   (16)
     32#define  MLB_CR_SET_CR_TOCLKFREQ_MASK  (0x3F << MLB_CR_SET_CR_TOCLKFREQ_SFT)
     33#define  MLB_CR_SET_CR_BCLKFREQ_SFT    (8)
     34#define  MLB_CR_SET_CR_BCLKFREQ_MASK   (0xFF << MLB_CR_SET_CR_BCLKFREQ_SFT)
     35#define  MLB_CR_SET_CR_RTUNTIMER_SFT   (4)
     36#define  MLB_CR_SET_CR_RTUNTIMER_MASK  (0xF << MLB_CR_SET_CR_RTUNTIMER_SFT)
     37
     38#define MLB_SD_TOCLK_I_DIV  16
     39#define MLB_TOCLKFREQ_UNIT_THRES    16000000
     40#define MLB_CAL_TOCLKFREQ_MHZ(rate) (rate / MLB_SD_TOCLK_I_DIV / 1000000)
     41#define MLB_CAL_TOCLKFREQ_KHZ(rate) (rate / MLB_SD_TOCLK_I_DIV / 1000)
     42#define MLB_TOCLKFREQ_MAX   63
     43#define MLB_TOCLKFREQ_MIN    1
     44
     45#define MLB_SD_BCLK_I_DIV   4
     46#define MLB_CAL_BCLKFREQ(rate)  (rate / MLB_SD_BCLK_I_DIV / 1000000)
     47#define MLB_BCLKFREQ_MAX        255
     48#define MLB_BCLKFREQ_MIN          1
     49
     50#define MLB_CDR_SET			0x0230
     51#define MLB_CDR_SET_CLK2POW16	3
     52
     53struct f_sdhost_priv {
     54	struct clk *clk_iface;
     55	struct clk *clk;
     56	struct device *dev;
     57	bool enable_cmd_dat_delay;
     58};
     59
     60static void sdhci_milbeaut_soft_voltage_switch(struct sdhci_host *host)
     61{
     62	u32 ctrl = 0;
     63
     64	usleep_range(2500, 3000);
     65	ctrl = sdhci_readl(host, F_SDH30_IO_CONTROL2);
     66	ctrl |= F_SDH30_CRES_O_DN;
     67	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
     68	ctrl |= F_SDH30_MSEL_O_1_8;
     69	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
     70
     71	ctrl &= ~F_SDH30_CRES_O_DN;
     72	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
     73	usleep_range(2500, 3000);
     74
     75	ctrl = sdhci_readl(host, F_SDH30_TUNING_SETTING);
     76	ctrl |= F_SDH30_CMD_CHK_DIS;
     77	sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
     78}
     79
     80static unsigned int sdhci_milbeaut_get_min_clock(struct sdhci_host *host)
     81{
     82	return F_SDH30_MIN_CLOCK;
     83}
     84
     85static void sdhci_milbeaut_reset(struct sdhci_host *host, u8 mask)
     86{
     87	struct f_sdhost_priv *priv = sdhci_priv(host);
     88	u16 clk;
     89	u32 ctl;
     90	ktime_t timeout;
     91
     92	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
     93	clk = (clk & ~SDHCI_CLOCK_CARD_EN) | SDHCI_CLOCK_INT_EN;
     94	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
     95
     96	sdhci_reset(host, mask);
     97
     98	clk |= SDHCI_CLOCK_CARD_EN;
     99	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
    100
    101	timeout = ktime_add_ms(ktime_get(), 10);
    102	while (1) {
    103		bool timedout = ktime_after(ktime_get(), timeout);
    104
    105		clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
    106		if (clk & SDHCI_CLOCK_INT_STABLE)
    107			break;
    108		if (timedout) {
    109			pr_err("%s: Internal clock never stabilised.\n",
    110				mmc_hostname(host->mmc));
    111			sdhci_dumpregs(host);
    112			return;
    113		}
    114		udelay(10);
    115	}
    116
    117	if (priv->enable_cmd_dat_delay) {
    118		ctl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
    119		ctl |= F_SDH30_CMD_DAT_DELAY;
    120		sdhci_writel(host, ctl, F_SDH30_ESD_CONTROL);
    121	}
    122}
    123
    124static const struct sdhci_ops sdhci_milbeaut_ops = {
    125	.voltage_switch = sdhci_milbeaut_soft_voltage_switch,
    126	.get_min_clock = sdhci_milbeaut_get_min_clock,
    127	.reset = sdhci_milbeaut_reset,
    128	.set_clock = sdhci_set_clock,
    129	.set_bus_width = sdhci_set_bus_width,
    130	.set_uhs_signaling = sdhci_set_uhs_signaling,
    131	.set_power = sdhci_set_power_and_bus_voltage,
    132};
    133
    134static void sdhci_milbeaut_bridge_reset(struct sdhci_host *host,
    135						int reset_flag)
    136{
    137	if (reset_flag)
    138		sdhci_writel(host, 0, MLB_SOFT_RESET);
    139	else
    140		sdhci_writel(host, MLB_SOFT_RESET_RSTX, MLB_SOFT_RESET);
    141}
    142
    143static void sdhci_milbeaut_bridge_init(struct sdhci_host *host,
    144						int rate)
    145{
    146	u32 val, clk;
    147
    148	/* IO_SDIO_CR_SET should be set while reset */
    149	val = sdhci_readl(host, MLB_CR_SET);
    150	val &= ~(MLB_CR_SET_CR_TOCLKFREQ_MASK | MLB_CR_SET_CR_TOCLKUNIT |
    151			MLB_CR_SET_CR_BCLKFREQ_MASK);
    152	if (rate >= MLB_TOCLKFREQ_UNIT_THRES) {
    153		clk = MLB_CAL_TOCLKFREQ_MHZ(rate);
    154		clk = min_t(u32, MLB_TOCLKFREQ_MAX, clk);
    155		val |= MLB_CR_SET_CR_TOCLKUNIT |
    156			(clk << MLB_CR_SET_CR_TOCLKFREQ_SFT);
    157	} else {
    158		clk = MLB_CAL_TOCLKFREQ_KHZ(rate);
    159		clk = min_t(u32, MLB_TOCLKFREQ_MAX, clk);
    160		clk = max_t(u32, MLB_TOCLKFREQ_MIN, clk);
    161		val |= clk << MLB_CR_SET_CR_TOCLKFREQ_SFT;
    162	}
    163
    164	clk = MLB_CAL_BCLKFREQ(rate);
    165	clk = min_t(u32, MLB_BCLKFREQ_MAX, clk);
    166	clk = max_t(u32, MLB_BCLKFREQ_MIN, clk);
    167	val |=  clk << MLB_CR_SET_CR_BCLKFREQ_SFT;
    168	val &= ~MLB_CR_SET_CR_RTUNTIMER_MASK;
    169	sdhci_writel(host, val, MLB_CR_SET);
    170
    171	sdhci_writel(host, MLB_CDR_SET_CLK2POW16, MLB_CDR_SET);
    172
    173	sdhci_writel(host, MLB_WP_CD_LED_SET_LED_INV, MLB_WP_CD_LED_SET);
    174}
    175
    176static void sdhci_milbeaut_vendor_init(struct sdhci_host *host)
    177{
    178	struct f_sdhost_priv *priv = sdhci_priv(host);
    179	u32 ctl;
    180
    181	ctl = sdhci_readl(host, F_SDH30_IO_CONTROL2);
    182	ctl |= F_SDH30_CRES_O_DN;
    183	sdhci_writel(host, ctl, F_SDH30_IO_CONTROL2);
    184	ctl &= ~F_SDH30_MSEL_O_1_8;
    185	sdhci_writel(host, ctl, F_SDH30_IO_CONTROL2);
    186	ctl &= ~F_SDH30_CRES_O_DN;
    187	sdhci_writel(host, ctl, F_SDH30_IO_CONTROL2);
    188
    189	ctl = sdhci_readw(host, F_SDH30_AHB_CONFIG);
    190	ctl |= F_SDH30_SIN | F_SDH30_AHB_INCR_16 | F_SDH30_AHB_INCR_8 |
    191	       F_SDH30_AHB_INCR_4;
    192	ctl &= ~(F_SDH30_AHB_BIGED | F_SDH30_BUSLOCK_EN);
    193	sdhci_writew(host, ctl, F_SDH30_AHB_CONFIG);
    194
    195	if (priv->enable_cmd_dat_delay) {
    196		ctl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
    197		ctl |= F_SDH30_CMD_DAT_DELAY;
    198		sdhci_writel(host, ctl, F_SDH30_ESD_CONTROL);
    199	}
    200}
    201
    202static const struct of_device_id mlb_dt_ids[] = {
    203	{
    204		.compatible = "socionext,milbeaut-m10v-sdhci-3.0",
    205	},
    206	{ /* sentinel */ }
    207};
    208MODULE_DEVICE_TABLE(of, mlb_dt_ids);
    209
    210static void sdhci_milbeaut_init(struct sdhci_host *host)
    211{
    212	struct f_sdhost_priv *priv = sdhci_priv(host);
    213	int rate = clk_get_rate(priv->clk);
    214	u16 ctl;
    215
    216	sdhci_milbeaut_bridge_reset(host, 0);
    217
    218	ctl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
    219	ctl &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN);
    220	sdhci_writew(host, ctl, SDHCI_CLOCK_CONTROL);
    221
    222	sdhci_milbeaut_bridge_reset(host, 1);
    223
    224	sdhci_milbeaut_bridge_init(host, rate);
    225	sdhci_milbeaut_bridge_reset(host, 0);
    226
    227	sdhci_milbeaut_vendor_init(host);
    228}
    229
    230static int sdhci_milbeaut_probe(struct platform_device *pdev)
    231{
    232	struct sdhci_host *host;
    233	struct device *dev = &pdev->dev;
    234	int irq, ret = 0;
    235	struct f_sdhost_priv *priv;
    236
    237	irq = platform_get_irq(pdev, 0);
    238	if (irq < 0)
    239		return irq;
    240
    241	host = sdhci_alloc_host(dev, sizeof(struct f_sdhost_priv));
    242	if (IS_ERR(host))
    243		return PTR_ERR(host);
    244
    245	priv = sdhci_priv(host);
    246	priv->dev = dev;
    247
    248	host->quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
    249			   SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
    250			   SDHCI_QUIRK_CLOCK_BEFORE_RESET |
    251			   SDHCI_QUIRK_DELAY_AFTER_POWER;
    252	host->quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE |
    253			SDHCI_QUIRK2_TUNING_WORK_AROUND |
    254			SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
    255
    256	priv->enable_cmd_dat_delay = device_property_read_bool(dev,
    257						"fujitsu,cmd-dat-delay-select");
    258
    259	ret = mmc_of_parse(host->mmc);
    260	if (ret)
    261		goto err;
    262
    263	platform_set_drvdata(pdev, host);
    264
    265	host->hw_name = "f_sdh30";
    266	host->ops = &sdhci_milbeaut_ops;
    267	host->irq = irq;
    268
    269	host->ioaddr = devm_platform_ioremap_resource(pdev, 0);
    270	if (IS_ERR(host->ioaddr)) {
    271		ret = PTR_ERR(host->ioaddr);
    272		goto err;
    273	}
    274
    275	if (dev_of_node(dev)) {
    276		sdhci_get_of_property(pdev);
    277
    278		priv->clk_iface = devm_clk_get(&pdev->dev, "iface");
    279		if (IS_ERR(priv->clk_iface)) {
    280			ret = PTR_ERR(priv->clk_iface);
    281			goto err;
    282		}
    283
    284		ret = clk_prepare_enable(priv->clk_iface);
    285		if (ret)
    286			goto err;
    287
    288		priv->clk = devm_clk_get(&pdev->dev, "core");
    289		if (IS_ERR(priv->clk)) {
    290			ret = PTR_ERR(priv->clk);
    291			goto err_clk;
    292		}
    293
    294		ret = clk_prepare_enable(priv->clk);
    295		if (ret)
    296			goto err_clk;
    297	}
    298
    299	sdhci_milbeaut_init(host);
    300
    301	ret = sdhci_add_host(host);
    302	if (ret)
    303		goto err_add_host;
    304
    305	return 0;
    306
    307err_add_host:
    308	clk_disable_unprepare(priv->clk);
    309err_clk:
    310	clk_disable_unprepare(priv->clk_iface);
    311err:
    312	sdhci_free_host(host);
    313	return ret;
    314}
    315
    316static int sdhci_milbeaut_remove(struct platform_device *pdev)
    317{
    318	struct sdhci_host *host = platform_get_drvdata(pdev);
    319	struct f_sdhost_priv *priv = sdhci_priv(host);
    320
    321	sdhci_remove_host(host, readl(host->ioaddr + SDHCI_INT_STATUS) ==
    322			  0xffffffff);
    323
    324	clk_disable_unprepare(priv->clk_iface);
    325	clk_disable_unprepare(priv->clk);
    326
    327	sdhci_free_host(host);
    328	platform_set_drvdata(pdev, NULL);
    329
    330	return 0;
    331}
    332
    333static struct platform_driver sdhci_milbeaut_driver = {
    334	.driver = {
    335		.name = "sdhci-milbeaut",
    336		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
    337		.of_match_table = of_match_ptr(mlb_dt_ids),
    338	},
    339	.probe	= sdhci_milbeaut_probe,
    340	.remove	= sdhci_milbeaut_remove,
    341};
    342
    343module_platform_driver(sdhci_milbeaut_driver);
    344
    345MODULE_DESCRIPTION("MILBEAUT SD Card Controller driver");
    346MODULE_AUTHOR("Takao Orito <orito.takao@socionext.com>");
    347MODULE_LICENSE("GPL v2");
    348MODULE_ALIAS("platform:sdhci-milbeaut");