brintos

brintos / linux-shallow public Read only

0
0
Text · 9.0 KiB · 83706ed Raw
347 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd4 *              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		0x020024#define  MLB_SOFT_RESET_RSTX		BIT(0)25 26#define MLB_WP_CD_LED_SET	0x021027#define  MLB_WP_CD_LED_SET_LED_INV  BIT(2)28 29#define MLB_CR_SET			0x022030#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  1639#define MLB_TOCLKFREQ_UNIT_THRES    1600000040#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   6343#define MLB_TOCLKFREQ_MIN    144 45#define MLB_SD_BCLK_I_DIV   446#define MLB_CAL_BCLKFREQ(rate)  (rate / MLB_SD_BCLK_I_DIV / 1000000)47#define MLB_BCLKFREQ_MAX        25548#define MLB_BCLKFREQ_MIN          149 50#define MLB_CDR_SET			0x023051#define MLB_CDR_SET_CLK2POW16	352 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	else140		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 void 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 331static struct platform_driver sdhci_milbeaut_driver = {332	.driver = {333		.name = "sdhci-milbeaut",334		.probe_type = PROBE_PREFER_ASYNCHRONOUS,335		.of_match_table = mlb_dt_ids,336	},337	.probe	= sdhci_milbeaut_probe,338	.remove_new = sdhci_milbeaut_remove,339};340 341module_platform_driver(sdhci_milbeaut_driver);342 343MODULE_DESCRIPTION("MILBEAUT SD Card Controller driver");344MODULE_AUTHOR("Takao Orito <orito.takao@socionext.com>");345MODULE_LICENSE("GPL v2");346MODULE_ALIAS("platform:sdhci-milbeaut");347