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