blob: f44e49014a440e1a4790d1d13b3ea7f62037db5f [file] [log] [blame]
Fabio Estevama6e7e402018-07-06 14:40:07 -03001// SPDX-License-Identifier: GPL-2.0
Wolfram Sang95f25ef2010-10-15 12:21:04 +02002/*
3 * Freescale eSDHC i.MX controller driver for the platform bus.
4 *
5 * derived from the OF-version.
6 *
7 * Copyright (c) 2010 Pengutronix e.K.
Wolfram Sang035ff832015-04-20 15:51:42 +02008 * Author: Wolfram Sang <kernel@pengutronix.de>
Wolfram Sang95f25ef2010-10-15 12:21:04 +02009 */
10
11#include <linux/io.h>
12#include <linux/delay.h>
13#include <linux/err.h>
14#include <linux/clk.h>
Wolfram Sang0c6d49c2011-02-26 14:44:39 +010015#include <linux/gpio.h>
Shawn Guo66506f72011-08-15 10:28:18 +080016#include <linux/module.h>
Richard Zhue1498602011-03-25 09:18:27 -040017#include <linux/slab.h>
Wolfram Sang95f25ef2010-10-15 12:21:04 +020018#include <linux/mmc/host.h>
Richard Zhu58ac8172011-03-21 13:22:16 +080019#include <linux/mmc/mmc.h>
20#include <linux/mmc/sdio.h>
Shawn Guofbe5fdd2012-12-11 22:32:20 +080021#include <linux/mmc/slot-gpio.h>
Shawn Guoabfafc22011-06-30 15:44:44 +080022#include <linux/of.h>
23#include <linux/of_device.h>
24#include <linux/of_gpio.h>
Dong Aishenge62d8b82012-05-11 14:56:01 +080025#include <linux/pinctrl/consumer.h>
Arnd Bergmann82906b12012-08-24 15:14:29 +020026#include <linux/platform_data/mmc-esdhc-imx.h>
Dong Aisheng89d7e5c2013-11-04 16:38:29 +080027#include <linux/pm_runtime.h>
Wolfram Sang95f25ef2010-10-15 12:21:04 +020028#include "sdhci-pltfm.h"
29#include "sdhci-esdhc.h"
30
Haibo Chena2151862016-08-15 16:19:38 +080031#define ESDHC_SYS_CTRL_DTOCV_MASK 0x0f
Shawn Guo60bf6392013-01-15 23:36:53 +080032#define ESDHC_CTRL_D3CD 0x08
Haibo Chenfd449542015-08-11 19:38:30 +080033#define ESDHC_BURST_LEN_EN_INCR (1 << 27)
Richard Zhu58ac8172011-03-21 13:22:16 +080034/* VENDOR SPEC register */
Shawn Guo60bf6392013-01-15 23:36:53 +080035#define ESDHC_VENDOR_SPEC 0xc0
36#define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1)
Dong Aisheng03221912013-09-13 19:11:34 +080037#define ESDHC_VENDOR_SPEC_VSELECT (1 << 1)
Dong Aishengfed2f6e2013-09-13 19:11:33 +080038#define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8)
Shawn Guo60bf6392013-01-15 23:36:53 +080039#define ESDHC_WTMK_LVL 0x44
Dong Aishengcc17e122016-07-12 15:46:13 +080040#define ESDHC_WTMK_DEFAULT_VAL 0x10401040
Andrew Gabbasov3fbd4322018-04-17 18:45:12 +053041#define ESDHC_WTMK_LVL_RD_WML_MASK 0x000000FF
42#define ESDHC_WTMK_LVL_RD_WML_SHIFT 0
43#define ESDHC_WTMK_LVL_WR_WML_MASK 0x00FF0000
44#define ESDHC_WTMK_LVL_WR_WML_SHIFT 16
45#define ESDHC_WTMK_LVL_WML_VAL_DEF 64
46#define ESDHC_WTMK_LVL_WML_VAL_MAX 128
Shawn Guo60bf6392013-01-15 23:36:53 +080047#define ESDHC_MIX_CTRL 0x48
Dong Aishengde5bdbf2013-10-18 19:48:46 +080048#define ESDHC_MIX_CTRL_DDREN (1 << 3)
Shawn Guo2a15f982013-01-21 19:02:26 +080049#define ESDHC_MIX_CTRL_AC23EN (1 << 7)
Dong Aisheng03221912013-09-13 19:11:34 +080050#define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22)
51#define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23)
Dong Aisheng0b330e32016-07-12 15:46:18 +080052#define ESDHC_MIX_CTRL_AUTO_TUNE_EN (1 << 24)
Dong Aisheng03221912013-09-13 19:11:34 +080053#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
Haibo Chen28b07672015-08-11 19:38:26 +080054#define ESDHC_MIX_CTRL_HS400_EN (1 << 26)
Shawn Guo2a15f982013-01-21 19:02:26 +080055/* Bits 3 and 6 are not SDHCI standard definitions */
56#define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
Dong Aishengd131a712013-11-04 16:38:26 +080057/* Tuning bits */
58#define ESDHC_MIX_CTRL_TUNING_MASK 0x03c00000
Richard Zhu58ac8172011-03-21 13:22:16 +080059
Dong Aisheng602519b2013-10-18 19:48:47 +080060/* dll control register */
61#define ESDHC_DLL_CTRL 0x60
62#define ESDHC_DLL_OVERRIDE_VAL_SHIFT 9
63#define ESDHC_DLL_OVERRIDE_EN_SHIFT 8
64
Dong Aisheng03221912013-09-13 19:11:34 +080065/* tune control register */
66#define ESDHC_TUNE_CTRL_STATUS 0x68
67#define ESDHC_TUNE_CTRL_STEP 1
68#define ESDHC_TUNE_CTRL_MIN 0
69#define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1)
70
Haibo Chen28b07672015-08-11 19:38:26 +080071/* strobe dll register */
72#define ESDHC_STROBE_DLL_CTRL 0x70
73#define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0)
74#define ESDHC_STROBE_DLL_CTRL_RESET (1 << 1)
75#define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT 3
76
77#define ESDHC_STROBE_DLL_STATUS 0x74
78#define ESDHC_STROBE_DLL_STS_REF_LOCK (1 << 1)
79#define ESDHC_STROBE_DLL_STS_SLV_LOCK 0x1
80
Dong Aisheng6e9fd282013-10-18 19:48:43 +080081#define ESDHC_TUNING_CTRL 0xcc
82#define ESDHC_STD_TUNING_EN (1 << 24)
83/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
Dong Aishengd87fc962016-07-12 15:46:15 +080084#define ESDHC_TUNING_START_TAP_DEFAULT 0x1
85#define ESDHC_TUNING_START_TAP_MASK 0xff
Haibo Chen260ecb32015-11-10 17:43:30 +080086#define ESDHC_TUNING_STEP_MASK 0x00070000
Haibo Chend407e30ba2015-08-11 19:38:27 +080087#define ESDHC_TUNING_STEP_SHIFT 16
Dong Aisheng6e9fd282013-10-18 19:48:43 +080088
Dong Aishengad932202013-09-13 19:11:35 +080089/* pinctrl state */
90#define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz"
91#define ESDHC_PINCTRL_STATE_200MHZ "state_200mhz"
92
Richard Zhu58ac8172011-03-21 13:22:16 +080093/*
Sascha Haueraf510792013-01-21 19:02:28 +080094 * Our interpretation of the SDHCI_HOST_CONTROL register
95 */
96#define ESDHC_CTRL_4BITBUS (0x1 << 1)
97#define ESDHC_CTRL_8BITBUS (0x2 << 1)
98#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1)
99
100/*
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200101 * There is an INT DMA ERR mismatch between eSDHC and STD SDHC SPEC:
Richard Zhu97e4ba62011-08-11 16:51:46 -0400102 * Bit25 is used in STD SPEC, and is reserved in fsl eSDHC design,
103 * but bit28 is used as the INT DMA ERR in fsl eSDHC design.
104 * Define this macro DMA error INT for fsl eSDHC
105 */
Shawn Guo60bf6392013-01-15 23:36:53 +0800106#define ESDHC_INT_VENDOR_SPEC_DMA_ERR (1 << 28)
Richard Zhu97e4ba62011-08-11 16:51:46 -0400107
108/*
Richard Zhu58ac8172011-03-21 13:22:16 +0800109 * The CMDTYPE of the CMD register (offset 0xE) should be set to
110 * "11" when the STOP CMD12 is issued on imx53 to abort one
111 * open ended multi-blk IO. Otherwise the TC INT wouldn't
112 * be generated.
113 * In exact block transfer, the controller doesn't complete the
114 * operations automatically as required at the end of the
115 * transfer and remains on hold if the abort command is not sent.
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200116 * As a result, the TC flag is not asserted and SW received timeout
117 * exception. Bit1 of Vendor Spec register is used to fix it.
Richard Zhu58ac8172011-03-21 13:22:16 +0800118 */
Shawn Guo31fbb302013-10-17 15:19:44 +0800119#define ESDHC_FLAG_MULTIBLK_NO_INT BIT(1)
120/*
Shawn Guo9d61c002013-10-17 15:19:45 +0800121 * The flag tells that the ESDHC controller is an USDHC block that is
122 * integrated on the i.MX6 series.
123 */
124#define ESDHC_FLAG_USDHC BIT(3)
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800125/* The IP supports manual tuning process */
126#define ESDHC_FLAG_MAN_TUNING BIT(4)
127/* The IP supports standard tuning process */
128#define ESDHC_FLAG_STD_TUNING BIT(5)
129/* The IP has SDHCI_CAPABILITIES_1 register */
130#define ESDHC_FLAG_HAVE_CAP1 BIT(6)
Dong Aisheng18094432015-05-27 18:13:28 +0800131/*
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200132 * The IP has erratum ERR004536
Dong Aisheng18094432015-05-27 18:13:28 +0800133 * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow,
134 * when reading data from the card
Benoît Thébaudeau667123f2017-05-30 11:14:11 +0200135 * This flag is also set for i.MX25 and i.MX35 in order to get
136 * SDHCI_QUIRK_BROKEN_ADMA, but for different reasons (ADMA capability bits).
Dong Aisheng18094432015-05-27 18:13:28 +0800137 */
138#define ESDHC_FLAG_ERR004536 BIT(7)
Dong Aisheng4245aff2015-05-27 18:13:31 +0800139/* The IP supports HS200 mode */
140#define ESDHC_FLAG_HS200 BIT(8)
Haibo Chen28b07672015-08-11 19:38:26 +0800141/* The IP supports HS400 mode */
142#define ESDHC_FLAG_HS400 BIT(9)
143
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200144/* A clock frequency higher than this rate requires strobe dll control */
Haibo Chen28b07672015-08-11 19:38:26 +0800145#define ESDHC_STROBE_DLL_CLK_FREQ 100000000
Richard Zhue1498602011-03-25 09:18:27 -0400146
Shawn Guof47c4bb2013-10-17 15:19:47 +0800147struct esdhc_soc_data {
148 u32 flags;
149};
150
151static struct esdhc_soc_data esdhc_imx25_data = {
Benoît Thébaudeau667123f2017-05-30 11:14:11 +0200152 .flags = ESDHC_FLAG_ERR004536,
Shawn Guof47c4bb2013-10-17 15:19:47 +0800153};
154
155static struct esdhc_soc_data esdhc_imx35_data = {
Benoît Thébaudeau667123f2017-05-30 11:14:11 +0200156 .flags = ESDHC_FLAG_ERR004536,
Shawn Guof47c4bb2013-10-17 15:19:47 +0800157};
158
159static struct esdhc_soc_data esdhc_imx51_data = {
160 .flags = 0,
161};
162
163static struct esdhc_soc_data esdhc_imx53_data = {
164 .flags = ESDHC_FLAG_MULTIBLK_NO_INT,
165};
166
167static struct esdhc_soc_data usdhc_imx6q_data = {
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800168 .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING,
169};
170
171static struct esdhc_soc_data usdhc_imx6sl_data = {
172 .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
Dong Aisheng4245aff2015-05-27 18:13:31 +0800173 | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
174 | ESDHC_FLAG_HS200,
Shawn Guo57ed3312011-06-30 09:24:26 +0800175};
176
Dong Aisheng913d4952015-05-27 18:13:30 +0800177static struct esdhc_soc_data usdhc_imx6sx_data = {
178 .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
Dong Aisheng4245aff2015-05-27 18:13:31 +0800179 | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200,
Dong Aisheng913d4952015-05-27 18:13:30 +0800180};
181
Haibo Chen28b07672015-08-11 19:38:26 +0800182static struct esdhc_soc_data usdhc_imx7d_data = {
183 .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
184 | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
185 | ESDHC_FLAG_HS400,
186};
187
Richard Zhue1498602011-03-25 09:18:27 -0400188struct pltfm_imx_data {
Richard Zhue1498602011-03-25 09:18:27 -0400189 u32 scratchpad;
Dong Aishenge62d8b82012-05-11 14:56:01 +0800190 struct pinctrl *pinctrl;
Dong Aishengad932202013-09-13 19:11:35 +0800191 struct pinctrl_state *pins_default;
192 struct pinctrl_state *pins_100mhz;
193 struct pinctrl_state *pins_200mhz;
Shawn Guof47c4bb2013-10-17 15:19:47 +0800194 const struct esdhc_soc_data *socdata;
Shawn Guo842afc02011-07-06 22:57:48 +0800195 struct esdhc_platform_data boarddata;
Sascha Hauer52dac612012-03-07 09:31:34 +0100196 struct clk *clk_ipg;
197 struct clk *clk_ahb;
198 struct clk *clk_per;
Michael Trimarchi36027852018-01-04 16:30:59 +0100199 unsigned int actual_clock;
Lucas Stach361b8482013-03-15 09:49:26 +0100200 enum {
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200201 NO_CMD_PENDING, /* no multiblock command pending */
Lucas Stach361b8482013-03-15 09:49:26 +0100202 MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
203 WAIT_FOR_INT, /* sent CMD12, waiting for response INT */
204 } multiblock_status;
Dong Aishengde5bdbf2013-10-18 19:48:46 +0800205 u32 is_ddr;
Richard Zhue1498602011-03-25 09:18:27 -0400206};
207
Krzysztof Kozlowskif8cbf462015-05-02 00:49:21 +0900208static const struct platform_device_id imx_esdhc_devtype[] = {
Shawn Guo57ed3312011-06-30 09:24:26 +0800209 {
210 .name = "sdhci-esdhc-imx25",
Shawn Guof47c4bb2013-10-17 15:19:47 +0800211 .driver_data = (kernel_ulong_t) &esdhc_imx25_data,
Shawn Guo57ed3312011-06-30 09:24:26 +0800212 }, {
213 .name = "sdhci-esdhc-imx35",
Shawn Guof47c4bb2013-10-17 15:19:47 +0800214 .driver_data = (kernel_ulong_t) &esdhc_imx35_data,
Shawn Guo57ed3312011-06-30 09:24:26 +0800215 }, {
216 .name = "sdhci-esdhc-imx51",
Shawn Guof47c4bb2013-10-17 15:19:47 +0800217 .driver_data = (kernel_ulong_t) &esdhc_imx51_data,
Shawn Guo57ed3312011-06-30 09:24:26 +0800218 }, {
Shawn Guo57ed3312011-06-30 09:24:26 +0800219 /* sentinel */
220 }
221};
222MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype);
223
Shawn Guoabfafc22011-06-30 15:44:44 +0800224static const struct of_device_id imx_esdhc_dt_ids[] = {
Shawn Guof47c4bb2013-10-17 15:19:47 +0800225 { .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, },
226 { .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
227 { .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
228 { .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
Dong Aisheng913d4952015-05-27 18:13:30 +0800229 { .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800230 { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
Shawn Guof47c4bb2013-10-17 15:19:47 +0800231 { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
Haibo Chen28b07672015-08-11 19:38:26 +0800232 { .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, },
Shawn Guoabfafc22011-06-30 15:44:44 +0800233 { /* sentinel */ }
234};
235MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
236
Shawn Guo57ed3312011-06-30 09:24:26 +0800237static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
238{
Shawn Guof47c4bb2013-10-17 15:19:47 +0800239 return data->socdata == &esdhc_imx25_data;
Shawn Guo57ed3312011-06-30 09:24:26 +0800240}
241
242static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
243{
Shawn Guof47c4bb2013-10-17 15:19:47 +0800244 return data->socdata == &esdhc_imx53_data;
Shawn Guo57ed3312011-06-30 09:24:26 +0800245}
246
Shawn Guo95a24822011-09-19 17:32:21 +0800247static inline int is_imx6q_usdhc(struct pltfm_imx_data *data)
248{
Shawn Guof47c4bb2013-10-17 15:19:47 +0800249 return data->socdata == &usdhc_imx6q_data;
Shawn Guo95a24822011-09-19 17:32:21 +0800250}
251
Shawn Guo9d61c002013-10-17 15:19:45 +0800252static inline int esdhc_is_usdhc(struct pltfm_imx_data *data)
253{
Shawn Guof47c4bb2013-10-17 15:19:47 +0800254 return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
Shawn Guo9d61c002013-10-17 15:19:45 +0800255}
256
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200257static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
258{
259 void __iomem *base = host->ioaddr + (reg & ~0x3);
260 u32 shift = (reg & 0x3) * 8;
261
262 writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
263}
264
Wolfram Sang7e29c302011-02-26 14:44:41 +0100265static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
266{
Lucas Stach361b8482013-03-15 09:49:26 +0100267 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Jisheng Zhang070e6d32016-02-16 21:08:20 +0800268 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Wolfram Sang7e29c302011-02-26 14:44:41 +0100269 u32 val = readl(host->ioaddr + reg);
270
Dong Aisheng03221912013-09-13 19:11:34 +0800271 if (unlikely(reg == SDHCI_PRESENT_STATE)) {
272 u32 fsl_prss = val;
273 /* save the least 20 bits */
274 val = fsl_prss & 0x000FFFFF;
275 /* move dat[0-3] bits */
276 val |= (fsl_prss & 0x0F000000) >> 4;
277 /* move cmd line bit */
278 val |= (fsl_prss & 0x00800000) << 1;
279 }
280
Richard Zhu97e4ba62011-08-11 16:51:46 -0400281 if (unlikely(reg == SDHCI_CAPABILITIES)) {
Dong Aisheng6b4fb6712a2013-10-18 19:48:44 +0800282 /* ignore bit[0-15] as it stores cap_1 register val for mx6sl */
283 if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
284 val &= 0xffff0000;
285
Richard Zhu97e4ba62011-08-11 16:51:46 -0400286 /* In FSL esdhc IC module, only bit20 is used to indicate the
287 * ADMA2 capability of esdhc, but this bit is messed up on
288 * some SOCs (e.g. on MX25, MX35 this bit is set, but they
289 * don't actually support ADMA2). So set the BROKEN_ADMA
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200290 * quirk on MX25/35 platforms.
Richard Zhu97e4ba62011-08-11 16:51:46 -0400291 */
292
293 if (val & SDHCI_CAN_DO_ADMA1) {
294 val &= ~SDHCI_CAN_DO_ADMA1;
295 val |= SDHCI_CAN_DO_ADMA2;
296 }
297 }
298
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800299 if (unlikely(reg == SDHCI_CAPABILITIES_1)) {
300 if (esdhc_is_usdhc(imx_data)) {
301 if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
302 val = readl(host->ioaddr + SDHCI_CAPABILITIES) & 0xFFFF;
303 else
304 /* imx6q/dl does not have cap_1 register, fake one */
305 val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
Dong Aisheng888824b2013-10-18 19:48:48 +0800306 | SDHCI_SUPPORT_SDR50
Dong Aishengda0295f2016-07-12 15:46:19 +0800307 | SDHCI_USE_SDR50_TUNING
308 | (SDHCI_TUNING_MODE_3 << SDHCI_RETUNING_MODE_SHIFT);
Haibo Chen28b07672015-08-11 19:38:26 +0800309
310 if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
311 val |= SDHCI_SUPPORT_HS400;
Stefan Agner92748be2018-07-04 17:07:45 +0200312
313 /*
314 * Do not advertise faster UHS modes if there are no
315 * pinctrl states for 100MHz/200MHz.
316 */
317 if (IS_ERR_OR_NULL(imx_data->pins_100mhz) ||
318 IS_ERR_OR_NULL(imx_data->pins_200mhz))
319 val &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50
320 | SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_HS400);
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800321 }
322 }
Dong Aisheng03221912013-09-13 19:11:34 +0800323
Shawn Guo9d61c002013-10-17 15:19:45 +0800324 if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
Dong Aisheng03221912013-09-13 19:11:34 +0800325 val = 0;
326 val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT;
327 val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT;
328 val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT;
329 }
330
Richard Zhu97e4ba62011-08-11 16:51:46 -0400331 if (unlikely(reg == SDHCI_INT_STATUS)) {
Shawn Guo60bf6392013-01-15 23:36:53 +0800332 if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
333 val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
Richard Zhu97e4ba62011-08-11 16:51:46 -0400334 val |= SDHCI_INT_ADMA_ERROR;
335 }
Lucas Stach361b8482013-03-15 09:49:26 +0100336
337 /*
338 * mask off the interrupt we get in response to the manually
339 * sent CMD12
340 */
341 if ((imx_data->multiblock_status == WAIT_FOR_INT) &&
342 ((val & SDHCI_INT_RESPONSE) == SDHCI_INT_RESPONSE)) {
343 val &= ~SDHCI_INT_RESPONSE;
344 writel(SDHCI_INT_RESPONSE, host->ioaddr +
345 SDHCI_INT_STATUS);
346 imx_data->multiblock_status = NO_CMD_PENDING;
347 }
Richard Zhu97e4ba62011-08-11 16:51:46 -0400348 }
349
Wolfram Sang7e29c302011-02-26 14:44:41 +0100350 return val;
351}
352
353static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
354{
Richard Zhue1498602011-03-25 09:18:27 -0400355 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Jisheng Zhang070e6d32016-02-16 21:08:20 +0800356 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Tony Lin0d588642011-08-11 16:45:59 -0400357 u32 data;
Richard Zhue1498602011-03-25 09:18:27 -0400358
Aaron Brice77da3da2016-10-10 11:39:52 -0700359 if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE ||
360 reg == SDHCI_INT_STATUS)) {
Dong Aishengb7321042015-05-27 18:13:27 +0800361 if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) {
Tony Lin0d588642011-08-11 16:45:59 -0400362 /*
363 * Clear and then set D3CD bit to avoid missing the
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200364 * card interrupt. This is an eSDHC controller problem
Tony Lin0d588642011-08-11 16:45:59 -0400365 * so we need to apply the following workaround: clear
366 * and set D3CD bit will make eSDHC re-sample the card
367 * interrupt. In case a card interrupt was lost,
368 * re-sample it by the following steps.
369 */
370 data = readl(host->ioaddr + SDHCI_HOST_CONTROL);
Shawn Guo60bf6392013-01-15 23:36:53 +0800371 data &= ~ESDHC_CTRL_D3CD;
Tony Lin0d588642011-08-11 16:45:59 -0400372 writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
Shawn Guo60bf6392013-01-15 23:36:53 +0800373 data |= ESDHC_CTRL_D3CD;
Tony Lin0d588642011-08-11 16:45:59 -0400374 writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
375 }
Dong Aisheng915be4852015-05-27 18:13:26 +0800376
377 if (val & SDHCI_INT_ADMA_ERROR) {
378 val &= ~SDHCI_INT_ADMA_ERROR;
379 val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
380 }
Tony Lin0d588642011-08-11 16:45:59 -0400381 }
Wolfram Sang7e29c302011-02-26 14:44:41 +0100382
Shawn Guof47c4bb2013-10-17 15:19:47 +0800383 if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
Richard Zhu58ac8172011-03-21 13:22:16 +0800384 && (reg == SDHCI_INT_STATUS)
385 && (val & SDHCI_INT_DATA_END))) {
386 u32 v;
Shawn Guo60bf6392013-01-15 23:36:53 +0800387 v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
388 v &= ~ESDHC_VENDOR_SPEC_SDIO_QUIRK;
389 writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
Lucas Stach361b8482013-03-15 09:49:26 +0100390
391 if (imx_data->multiblock_status == MULTIBLK_IN_PROCESS)
392 {
393 /* send a manual CMD12 with RESPTYP=none */
394 data = MMC_STOP_TRANSMISSION << 24 |
395 SDHCI_CMD_ABORTCMD << 16;
396 writel(data, host->ioaddr + SDHCI_TRANSFER_MODE);
397 imx_data->multiblock_status = WAIT_FOR_INT;
398 }
Richard Zhu58ac8172011-03-21 13:22:16 +0800399 }
400
Wolfram Sang7e29c302011-02-26 14:44:41 +0100401 writel(val, host->ioaddr + reg);
402}
403
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200404static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
405{
Shawn Guoef4d0882013-01-15 23:30:27 +0800406 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Jisheng Zhang070e6d32016-02-16 21:08:20 +0800407 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Dong Aisheng03221912013-09-13 19:11:34 +0800408 u16 ret = 0;
409 u32 val;
Shawn Guoef4d0882013-01-15 23:30:27 +0800410
Shawn Guo95a24822011-09-19 17:32:21 +0800411 if (unlikely(reg == SDHCI_HOST_VERSION)) {
Shawn Guoef4d0882013-01-15 23:30:27 +0800412 reg ^= 2;
Shawn Guo9d61c002013-10-17 15:19:45 +0800413 if (esdhc_is_usdhc(imx_data)) {
Shawn Guoef4d0882013-01-15 23:30:27 +0800414 /*
415 * The usdhc register returns a wrong host version.
416 * Correct it here.
417 */
418 return SDHCI_SPEC_300;
419 }
Shawn Guo95a24822011-09-19 17:32:21 +0800420 }
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200421
Dong Aisheng03221912013-09-13 19:11:34 +0800422 if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
423 val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
424 if (val & ESDHC_VENDOR_SPEC_VSELECT)
425 ret |= SDHCI_CTRL_VDD_180;
426
Shawn Guo9d61c002013-10-17 15:19:45 +0800427 if (esdhc_is_usdhc(imx_data)) {
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800428 if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
429 val = readl(host->ioaddr + ESDHC_MIX_CTRL);
430 else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
431 /* the std tuning bits is in ACMD12_ERR for imx6sl */
432 val = readl(host->ioaddr + SDHCI_ACMD12_ERR);
Dong Aisheng03221912013-09-13 19:11:34 +0800433 }
434
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800435 if (val & ESDHC_MIX_CTRL_EXE_TUNE)
436 ret |= SDHCI_CTRL_EXEC_TUNING;
437 if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
438 ret |= SDHCI_CTRL_TUNED_CLK;
439
Dong Aisheng03221912013-09-13 19:11:34 +0800440 ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
441
442 return ret;
443 }
444
Dong Aisheng7dd109e2013-10-30 22:09:49 +0800445 if (unlikely(reg == SDHCI_TRANSFER_MODE)) {
446 if (esdhc_is_usdhc(imx_data)) {
447 u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
448 ret = m & ESDHC_MIX_CTRL_SDHCI_MASK;
449 /* Swap AC23 bit */
450 if (m & ESDHC_MIX_CTRL_AC23EN) {
451 ret &= ~ESDHC_MIX_CTRL_AC23EN;
452 ret |= SDHCI_TRNS_AUTO_CMD23;
453 }
454 } else {
455 ret = readw(host->ioaddr + SDHCI_TRANSFER_MODE);
456 }
457
458 return ret;
459 }
460
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200461 return readw(host->ioaddr + reg);
462}
463
464static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
465{
466 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Jisheng Zhang070e6d32016-02-16 21:08:20 +0800467 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Dong Aisheng03221912013-09-13 19:11:34 +0800468 u32 new_val = 0;
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200469
470 switch (reg) {
Dong Aisheng03221912013-09-13 19:11:34 +0800471 case SDHCI_CLOCK_CONTROL:
472 new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
473 if (val & SDHCI_CLOCK_CARD_EN)
474 new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
475 else
476 new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
Dan Carpentereeed7022015-02-26 23:37:55 +0300477 writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
Dong Aisheng03221912013-09-13 19:11:34 +0800478 return;
479 case SDHCI_HOST_CONTROL2:
480 new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
481 if (val & SDHCI_CTRL_VDD_180)
482 new_val |= ESDHC_VENDOR_SPEC_VSELECT;
483 else
484 new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
485 writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800486 if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
487 new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
Dong Aishengda0295f2016-07-12 15:46:19 +0800488 if (val & SDHCI_CTRL_TUNED_CLK) {
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800489 new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL;
Dong Aishengda0295f2016-07-12 15:46:19 +0800490 new_val |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
491 } else {
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800492 new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
Dong Aishengda0295f2016-07-12 15:46:19 +0800493 new_val &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
494 }
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800495 writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
496 } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
497 u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
498 u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
Dong Aisheng8b2bb0ad2013-11-04 16:38:27 +0800499 if (val & SDHCI_CTRL_TUNED_CLK) {
500 v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800501 } else {
Dong Aisheng8b2bb0ad2013-11-04 16:38:27 +0800502 v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800503 m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
Dong Aisheng0b330e32016-07-12 15:46:18 +0800504 m &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800505 }
506
Dong Aisheng8b2bb0ad2013-11-04 16:38:27 +0800507 if (val & SDHCI_CTRL_EXEC_TUNING) {
508 v |= ESDHC_MIX_CTRL_EXE_TUNE;
509 m |= ESDHC_MIX_CTRL_FBCLK_SEL;
Dong Aisheng0b330e32016-07-12 15:46:18 +0800510 m |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
Dong Aisheng8b2bb0ad2013-11-04 16:38:27 +0800511 } else {
512 v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
513 }
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800514
Dong Aisheng6e9fd282013-10-18 19:48:43 +0800515 writel(v, host->ioaddr + SDHCI_ACMD12_ERR);
516 writel(m, host->ioaddr + ESDHC_MIX_CTRL);
517 }
Dong Aisheng03221912013-09-13 19:11:34 +0800518 return;
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200519 case SDHCI_TRANSFER_MODE:
Shawn Guof47c4bb2013-10-17 15:19:47 +0800520 if ((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
Richard Zhu58ac8172011-03-21 13:22:16 +0800521 && (host->cmd->opcode == SD_IO_RW_EXTENDED)
522 && (host->cmd->data->blocks > 1)
523 && (host->cmd->data->flags & MMC_DATA_READ)) {
524 u32 v;
Shawn Guo60bf6392013-01-15 23:36:53 +0800525 v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
526 v |= ESDHC_VENDOR_SPEC_SDIO_QUIRK;
527 writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
Richard Zhu58ac8172011-03-21 13:22:16 +0800528 }
Shawn Guo69f54692013-01-21 19:02:24 +0800529
Shawn Guo9d61c002013-10-17 15:19:45 +0800530 if (esdhc_is_usdhc(imx_data)) {
Andrew Gabbasov3fbd4322018-04-17 18:45:12 +0530531 u32 wml;
Shawn Guo69f54692013-01-21 19:02:24 +0800532 u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
Shawn Guo2a15f982013-01-21 19:02:26 +0800533 /* Swap AC23 bit */
534 if (val & SDHCI_TRNS_AUTO_CMD23) {
535 val &= ~SDHCI_TRNS_AUTO_CMD23;
536 val |= ESDHC_MIX_CTRL_AC23EN;
537 }
538 m = val | (m & ~ESDHC_MIX_CTRL_SDHCI_MASK);
Shawn Guo69f54692013-01-21 19:02:24 +0800539 writel(m, host->ioaddr + ESDHC_MIX_CTRL);
Andrew Gabbasov3fbd4322018-04-17 18:45:12 +0530540
541 /* Set watermark levels for PIO access to maximum value
542 * (128 words) to accommodate full 512 bytes buffer.
543 * For DMA access restore the levels to default value.
544 */
545 m = readl(host->ioaddr + ESDHC_WTMK_LVL);
546 if (val & SDHCI_TRNS_DMA)
547 wml = ESDHC_WTMK_LVL_WML_VAL_DEF;
548 else
549 wml = ESDHC_WTMK_LVL_WML_VAL_MAX;
550 m &= ~(ESDHC_WTMK_LVL_RD_WML_MASK |
551 ESDHC_WTMK_LVL_WR_WML_MASK);
552 m |= (wml << ESDHC_WTMK_LVL_RD_WML_SHIFT) |
553 (wml << ESDHC_WTMK_LVL_WR_WML_SHIFT);
554 writel(m, host->ioaddr + ESDHC_WTMK_LVL);
Shawn Guo69f54692013-01-21 19:02:24 +0800555 } else {
556 /*
557 * Postpone this write, we must do it together with a
558 * command write that is down below.
559 */
560 imx_data->scratchpad = val;
561 }
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200562 return;
563 case SDHCI_COMMAND:
Lucas Stach361b8482013-03-15 09:49:26 +0100564 if (host->cmd->opcode == MMC_STOP_TRANSMISSION)
Richard Zhu58ac8172011-03-21 13:22:16 +0800565 val |= SDHCI_CMD_ABORTCMD;
Shawn Guo95a24822011-09-19 17:32:21 +0800566
Lucas Stach361b8482013-03-15 09:49:26 +0100567 if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) &&
Shawn Guof47c4bb2013-10-17 15:19:47 +0800568 (imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
Lucas Stach361b8482013-03-15 09:49:26 +0100569 imx_data->multiblock_status = MULTIBLK_IN_PROCESS;
570
Shawn Guo9d61c002013-10-17 15:19:45 +0800571 if (esdhc_is_usdhc(imx_data))
Shawn Guo95a24822011-09-19 17:32:21 +0800572 writel(val << 16,
573 host->ioaddr + SDHCI_TRANSFER_MODE);
Shawn Guo69f54692013-01-21 19:02:24 +0800574 else
Shawn Guo95a24822011-09-19 17:32:21 +0800575 writel(val << 16 | imx_data->scratchpad,
576 host->ioaddr + SDHCI_TRANSFER_MODE);
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200577 return;
578 case SDHCI_BLOCK_SIZE:
579 val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
580 break;
581 }
582 esdhc_clrset_le(host, 0xffff, val, reg);
583}
584
Aaron Brice77da3da2016-10-10 11:39:52 -0700585static u8 esdhc_readb_le(struct sdhci_host *host, int reg)
586{
587 u8 ret;
588 u32 val;
589
590 switch (reg) {
591 case SDHCI_HOST_CONTROL:
592 val = readl(host->ioaddr + reg);
593
594 ret = val & SDHCI_CTRL_LED;
595 ret |= (val >> 5) & SDHCI_CTRL_DMA_MASK;
596 ret |= (val & ESDHC_CTRL_4BITBUS);
597 ret |= (val & ESDHC_CTRL_8BITBUS) << 3;
598 return ret;
599 }
600
601 return readb(host->ioaddr + reg);
602}
603
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200604static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
605{
Wilson Callan9a0985b2012-07-19 02:49:16 -0400606 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Jisheng Zhang070e6d32016-02-16 21:08:20 +0800607 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Benoît Thébaudeau81a0a8b2017-05-30 11:14:09 +0200608 u32 new_val = 0;
Sascha Haueraf510792013-01-21 19:02:28 +0800609 u32 mask;
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200610
611 switch (reg) {
612 case SDHCI_POWER_CONTROL:
613 /*
614 * FSL put some DMA bits here
615 * If your board has a regulator, code should be here
616 */
617 return;
618 case SDHCI_HOST_CONTROL:
Shawn Guo6b40d182013-01-15 23:36:52 +0800619 /* FSL messed up here, so we need to manually compose it. */
Sascha Haueraf510792013-01-21 19:02:28 +0800620 new_val = val & SDHCI_CTRL_LED;
Masanari Iida7122bbb2012-08-05 23:25:40 +0900621 /* ensure the endianness */
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200622 new_val |= ESDHC_HOST_CONTROL_LE;
Wilson Callan9a0985b2012-07-19 02:49:16 -0400623 /* bits 8&9 are reserved on mx25 */
624 if (!is_imx25_esdhc(imx_data)) {
625 /* DMA mode bits are shifted */
626 new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;
627 }
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200628
Sascha Haueraf510792013-01-21 19:02:28 +0800629 /*
630 * Do not touch buswidth bits here. This is done in
631 * esdhc_pltfm_bus_width.
Martin Fuzzeyf6825742013-04-15 17:08:35 +0200632 * Do not touch the D3CD bit either which is used for the
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200633 * SDIO interrupt erratum workaround.
Sascha Haueraf510792013-01-21 19:02:28 +0800634 */
Martin Fuzzeyf6825742013-04-15 17:08:35 +0200635 mask = 0xffff & ~(ESDHC_CTRL_BUSWIDTH_MASK | ESDHC_CTRL_D3CD);
Sascha Haueraf510792013-01-21 19:02:28 +0800636
637 esdhc_clrset_le(host, mask, new_val, reg);
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200638 return;
Benoît Thébaudeau81a0a8b2017-05-30 11:14:09 +0200639 case SDHCI_SOFTWARE_RESET:
640 if (val & SDHCI_RESET_DATA)
641 new_val = readl(host->ioaddr + SDHCI_HOST_CONTROL);
642 break;
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200643 }
644 esdhc_clrset_le(host, 0xff, val, reg);
Shawn Guo913413c2011-06-21 22:41:51 +0800645
Benoît Thébaudeau81a0a8b2017-05-30 11:14:09 +0200646 if (reg == SDHCI_SOFTWARE_RESET) {
647 if (val & SDHCI_RESET_ALL) {
648 /*
649 * The esdhc has a design violation to SDHC spec which
650 * tells that software reset should not affect card
651 * detection circuit. But esdhc clears its SYSCTL
652 * register bits [0..2] during the software reset. This
653 * will stop those clocks that card detection circuit
654 * relies on. To work around it, we turn the clocks on
655 * back to keep card detection circuit functional.
656 */
657 esdhc_clrset_le(host, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
658 /*
659 * The reset on usdhc fails to clear MIX_CTRL register.
660 * Do it manually here.
661 */
662 if (esdhc_is_usdhc(imx_data)) {
663 /*
664 * the tuning bits should be kept during reset
665 */
666 new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
667 writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK,
668 host->ioaddr + ESDHC_MIX_CTRL);
669 imx_data->is_ddr = 0;
670 }
671 } else if (val & SDHCI_RESET_DATA) {
672 /*
673 * The eSDHC DAT line software reset clears at least the
674 * data transfer width on i.MX25, so make sure that the
675 * Host Control register is unaffected.
676 */
677 esdhc_clrset_le(host, 0xff, new_val,
678 SDHCI_HOST_CONTROL);
Dong Aishengde5bdbf2013-10-18 19:48:46 +0800679 }
Shawn Guo58c8c4f2013-01-21 19:02:25 +0800680 }
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200681}
682
Lucas Stach0ddf03c2013-06-05 15:13:26 +0200683static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
684{
685 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Lucas Stach0ddf03c2013-06-05 15:13:26 +0200686
Dong Aishenga3bd4f92015-07-22 20:53:09 +0800687 return pltfm_host->clock;
Lucas Stach0ddf03c2013-06-05 15:13:26 +0200688}
689
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200690static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
691{
692 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
693
Dong Aishenga9748622013-12-26 15:23:53 +0800694 return pltfm_host->clock / 256 / 16;
Wolfram Sang95f25ef2010-10-15 12:21:04 +0200695}
696
Lucas Stach8ba95802013-06-05 15:13:25 +0200697static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
698 unsigned int clock)
699{
700 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Jisheng Zhang070e6d32016-02-16 21:08:20 +0800701 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Dong Aishenga9748622013-12-26 15:23:53 +0800702 unsigned int host_clock = pltfm_host->clock;
Benoît Thébaudeau5143c952017-05-30 11:14:10 +0200703 int ddr_pre_div = imx_data->is_ddr ? 2 : 1;
704 int pre_div = 1;
Dong Aishengd31fc002013-09-13 19:11:32 +0800705 int div = 1;
Dong Aishengfed2f6e2013-09-13 19:11:33 +0800706 u32 temp, val;
Lucas Stach8ba95802013-06-05 15:13:25 +0200707
Stefan Agner73e736f2018-07-12 10:07:24 +0200708 if (esdhc_is_usdhc(imx_data)) {
709 val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
710 writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
711 host->ioaddr + ESDHC_VENDOR_SPEC);
712 }
713
Dong Aishengfed2f6e2013-09-13 19:11:33 +0800714 if (clock == 0) {
Russell King1650d0c2014-04-25 12:58:50 +0100715 host->mmc->actual_clock = 0;
Russell King373073e2014-04-25 12:58:45 +0100716 return;
Dong Aishengfed2f6e2013-09-13 19:11:33 +0800717 }
Dong Aishengd31fc002013-09-13 19:11:32 +0800718
Benoît Thébaudeau499ed502018-01-14 19:43:05 +0100719 /* For i.MX53 eSDHCv3, SYSCTL.SDCLKFS may not be set to 0. */
720 if (is_imx53_esdhc(imx_data)) {
721 /*
722 * According to the i.MX53 reference manual, if DLLCTRL[10] can
723 * be set, then the controller is eSDHCv3, else it is eSDHCv2.
724 */
725 val = readl(host->ioaddr + ESDHC_DLL_CTRL);
726 writel(val | BIT(10), host->ioaddr + ESDHC_DLL_CTRL);
727 temp = readl(host->ioaddr + ESDHC_DLL_CTRL);
728 writel(val, host->ioaddr + ESDHC_DLL_CTRL);
729 if (temp & BIT(10))
730 pre_div = 2;
731 }
732
Dong Aishengd31fc002013-09-13 19:11:32 +0800733 temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
734 temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
735 | ESDHC_CLOCK_MASK);
736 sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
737
Benoît Thébaudeau5143c952017-05-30 11:14:10 +0200738 while (host_clock / (16 * pre_div * ddr_pre_div) > clock &&
739 pre_div < 256)
Dong Aishengd31fc002013-09-13 19:11:32 +0800740 pre_div *= 2;
741
Benoît Thébaudeau5143c952017-05-30 11:14:10 +0200742 while (host_clock / (div * pre_div * ddr_pre_div) > clock && div < 16)
Dong Aishengd31fc002013-09-13 19:11:32 +0800743 div++;
744
Benoît Thébaudeau5143c952017-05-30 11:14:10 +0200745 host->mmc->actual_clock = host_clock / (div * pre_div * ddr_pre_div);
Dong Aishengd31fc002013-09-13 19:11:32 +0800746 dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
Dong Aishenge76b8552013-09-13 19:11:37 +0800747 clock, host->mmc->actual_clock);
Dong Aishengd31fc002013-09-13 19:11:32 +0800748
Benoît Thébaudeau5143c952017-05-30 11:14:10 +0200749 pre_div >>= 1;
Dong Aishengd31fc002013-09-13 19:11:32 +0800750 div--;
751
752 temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
753 temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
754 | (div << ESDHC_DIVIDER_SHIFT)
755 | (pre_div << ESDHC_PREDIV_SHIFT));
756 sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
Dong Aishengfed2f6e2013-09-13 19:11:33 +0800757
Shawn Guo9d61c002013-10-17 15:19:45 +0800758 if (esdhc_is_usdhc(imx_data)) {
Dong Aishengfed2f6e2013-09-13 19:11:33 +0800759 val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
760 writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
Stefan Agnerd47b0582018-07-12 10:07:25 +0200761 host->ioaddr + ESDHC_VENDOR_SPEC);
Dong Aishengfed2f6e2013-09-13 19:11:33 +0800762 }
763
Dong Aishengd31fc002013-09-13 19:11:32 +0800764 mdelay(1);
Lucas Stach8ba95802013-06-05 15:13:25 +0200765}
766
Shawn Guo913413c2011-06-21 22:41:51 +0800767static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
768{
Shawn Guo842afc02011-07-06 22:57:48 +0800769 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Jisheng Zhang070e6d32016-02-16 21:08:20 +0800770 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Shawn Guo842afc02011-07-06 22:57:48 +0800771 struct esdhc_platform_data *boarddata = &imx_data->boarddata;
Shawn Guo913413c2011-06-21 22:41:51 +0800772
773 switch (boarddata->wp_type) {
774 case ESDHC_WP_GPIO:
Shawn Guofbe5fdd2012-12-11 22:32:20 +0800775 return mmc_gpio_get_ro(host->mmc);
Shawn Guo913413c2011-06-21 22:41:51 +0800776 case ESDHC_WP_CONTROLLER:
777 return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
778 SDHCI_WRITE_PROTECT);
779 case ESDHC_WP_NONE:
780 break;
781 }
782
783 return -ENOSYS;
784}
785
Russell King2317f562014-04-25 12:57:07 +0100786static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
Sascha Haueraf510792013-01-21 19:02:28 +0800787{
788 u32 ctrl;
789
790 switch (width) {
791 case MMC_BUS_WIDTH_8:
792 ctrl = ESDHC_CTRL_8BITBUS;
793 break;
794 case MMC_BUS_WIDTH_4:
795 ctrl = ESDHC_CTRL_4BITBUS;
796 break;
797 default:
798 ctrl = 0;
799 break;
800 }
801
802 esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl,
803 SDHCI_HOST_CONTROL);
Sascha Haueraf510792013-01-21 19:02:28 +0800804}
805
Dong Aisheng03221912013-09-13 19:11:34 +0800806static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
807{
808 u32 reg;
809
810 /* FIXME: delay a bit for card to be ready for next tuning due to errors */
811 mdelay(1);
812
813 reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
814 reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
815 ESDHC_MIX_CTRL_FBCLK_SEL;
816 writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
817 writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
818 dev_dbg(mmc_dev(host->mmc),
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200819 "tuning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
Dong Aisheng03221912013-09-13 19:11:34 +0800820 val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
821}
822
Dong Aisheng03221912013-09-13 19:11:34 +0800823static void esdhc_post_tuning(struct sdhci_host *host)
824{
825 u32 reg;
826
827 reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
828 reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
Dong Aishengda0295f2016-07-12 15:46:19 +0800829 reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
Dong Aisheng03221912013-09-13 19:11:34 +0800830 writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
831}
832
833static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
834{
835 int min, max, avg, ret;
836
837 /* find the mininum delay first which can pass tuning */
838 min = ESDHC_TUNE_CTRL_MIN;
839 while (min < ESDHC_TUNE_CTRL_MAX) {
840 esdhc_prepare_tuning(host, min);
Chaotian Jing9979dbe2015-10-27 14:24:28 +0800841 if (!mmc_send_tuning(host->mmc, opcode, NULL))
Dong Aisheng03221912013-09-13 19:11:34 +0800842 break;
843 min += ESDHC_TUNE_CTRL_STEP;
844 }
845
846 /* find the maxinum delay which can not pass tuning */
847 max = min + ESDHC_TUNE_CTRL_STEP;
848 while (max < ESDHC_TUNE_CTRL_MAX) {
849 esdhc_prepare_tuning(host, max);
Chaotian Jing9979dbe2015-10-27 14:24:28 +0800850 if (mmc_send_tuning(host->mmc, opcode, NULL)) {
Dong Aisheng03221912013-09-13 19:11:34 +0800851 max -= ESDHC_TUNE_CTRL_STEP;
852 break;
853 }
854 max += ESDHC_TUNE_CTRL_STEP;
855 }
856
857 /* use average delay to get the best timing */
858 avg = (min + max) / 2;
859 esdhc_prepare_tuning(host, avg);
Chaotian Jing9979dbe2015-10-27 14:24:28 +0800860 ret = mmc_send_tuning(host->mmc, opcode, NULL);
Dong Aisheng03221912013-09-13 19:11:34 +0800861 esdhc_post_tuning(host);
862
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200863 dev_dbg(mmc_dev(host->mmc), "tuning %s at 0x%x ret %d\n",
Dong Aisheng03221912013-09-13 19:11:34 +0800864 ret ? "failed" : "passed", avg, ret);
865
866 return ret;
867}
868
Dong Aishengad932202013-09-13 19:11:35 +0800869static int esdhc_change_pinstate(struct sdhci_host *host,
870 unsigned int uhs)
871{
872 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Jisheng Zhang070e6d32016-02-16 21:08:20 +0800873 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Dong Aishengad932202013-09-13 19:11:35 +0800874 struct pinctrl_state *pinctrl;
875
876 dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs);
877
878 if (IS_ERR(imx_data->pinctrl) ||
879 IS_ERR(imx_data->pins_default) ||
880 IS_ERR(imx_data->pins_100mhz) ||
881 IS_ERR(imx_data->pins_200mhz))
882 return -EINVAL;
883
884 switch (uhs) {
885 case MMC_TIMING_UHS_SDR50:
Haibo Chen9f327842017-04-19 10:53:51 +0800886 case MMC_TIMING_UHS_DDR50:
Dong Aishengad932202013-09-13 19:11:35 +0800887 pinctrl = imx_data->pins_100mhz;
888 break;
889 case MMC_TIMING_UHS_SDR104:
Dong Aisheng429a5b42013-10-30 22:10:42 +0800890 case MMC_TIMING_MMC_HS200:
Haibo Chen28b07672015-08-11 19:38:26 +0800891 case MMC_TIMING_MMC_HS400:
Dong Aishengad932202013-09-13 19:11:35 +0800892 pinctrl = imx_data->pins_200mhz;
893 break;
894 default:
895 /* back to default state for other legacy timing */
896 pinctrl = imx_data->pins_default;
897 }
898
899 return pinctrl_select_state(imx_data->pinctrl, pinctrl);
900}
901
Haibo Chen28b07672015-08-11 19:38:26 +0800902/*
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200903 * For HS400 eMMC, there is a data_strobe line. This signal is generated
Haibo Chen28b07672015-08-11 19:38:26 +0800904 * by the device and used for data output and CRC status response output
905 * in HS400 mode. The frequency of this signal follows the frequency of
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200906 * CLK generated by host. The host receives the data which is aligned to the
Haibo Chen28b07672015-08-11 19:38:26 +0800907 * edge of data_strobe line. Due to the time delay between CLK line and
908 * data_strobe line, if the delay time is larger than one clock cycle,
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200909 * then CLK and data_strobe line will be misaligned, read error shows up.
Haibo Chen28b07672015-08-11 19:38:26 +0800910 * So when the CLK is higher than 100MHz, each clock cycle is short enough,
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200911 * host should configure the delay target.
Haibo Chen28b07672015-08-11 19:38:26 +0800912 */
913static void esdhc_set_strobe_dll(struct sdhci_host *host)
914{
915 u32 v;
916
917 if (host->mmc->actual_clock > ESDHC_STROBE_DLL_CLK_FREQ) {
Dong Aisheng7ac6da22016-07-12 15:46:20 +0800918 /* disable clock before enabling strobe dll */
919 writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) &
920 ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
921 host->ioaddr + ESDHC_VENDOR_SPEC);
922
Haibo Chen28b07672015-08-11 19:38:26 +0800923 /* force a reset on strobe dll */
924 writel(ESDHC_STROBE_DLL_CTRL_RESET,
925 host->ioaddr + ESDHC_STROBE_DLL_CTRL);
926 /*
927 * enable strobe dll ctrl and adjust the delay target
928 * for the uSDHC loopback read clock
929 */
930 v = ESDHC_STROBE_DLL_CTRL_ENABLE |
931 (7 << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
932 writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
933 /* wait 1us to make sure strobe dll status register stable */
934 udelay(1);
935 v = readl(host->ioaddr + ESDHC_STROBE_DLL_STATUS);
936 if (!(v & ESDHC_STROBE_DLL_STS_REF_LOCK))
937 dev_warn(mmc_dev(host->mmc),
938 "warning! HS400 strobe DLL status REF not lock!\n");
939 if (!(v & ESDHC_STROBE_DLL_STS_SLV_LOCK))
940 dev_warn(mmc_dev(host->mmc),
941 "warning! HS400 strobe DLL status SLV not lock!\n");
942 }
943}
944
Haibo Chend9370422017-04-18 18:05:31 +0800945static void esdhc_reset_tuning(struct sdhci_host *host)
946{
947 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
948 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
949 u32 ctrl;
950
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +0200951 /* Reset the tuning circuit */
Haibo Chend9370422017-04-18 18:05:31 +0800952 if (esdhc_is_usdhc(imx_data)) {
953 if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
954 ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
955 ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
956 ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
957 writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
958 writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
959 } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
960 ctrl = readl(host->ioaddr + SDHCI_ACMD12_ERR);
961 ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
962 writel(ctrl, host->ioaddr + SDHCI_ACMD12_ERR);
963 }
964 }
965}
966
Russell King850a29b2014-04-25 12:59:41 +0100967static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
Dong Aishengad932202013-09-13 19:11:35 +0800968{
Haibo Chen28b07672015-08-11 19:38:26 +0800969 u32 m;
Dong Aishengad932202013-09-13 19:11:35 +0800970 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Jisheng Zhang070e6d32016-02-16 21:08:20 +0800971 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Dong Aisheng602519b2013-10-18 19:48:47 +0800972 struct esdhc_platform_data *boarddata = &imx_data->boarddata;
Dong Aishengad932202013-09-13 19:11:35 +0800973
Haibo Chen28b07672015-08-11 19:38:26 +0800974 /* disable ddr mode and disable HS400 mode */
975 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
976 m &= ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN);
977 imx_data->is_ddr = 0;
978
Russell King850a29b2014-04-25 12:59:41 +0100979 switch (timing) {
Dong Aishengad932202013-09-13 19:11:35 +0800980 case MMC_TIMING_UHS_SDR12:
Dong Aishengad932202013-09-13 19:11:35 +0800981 case MMC_TIMING_UHS_SDR25:
Dong Aishengad932202013-09-13 19:11:35 +0800982 case MMC_TIMING_UHS_SDR50:
Dong Aishengad932202013-09-13 19:11:35 +0800983 case MMC_TIMING_UHS_SDR104:
Dong Aisheng429a5b42013-10-30 22:10:42 +0800984 case MMC_TIMING_MMC_HS200:
Haibo Chen28b07672015-08-11 19:38:26 +0800985 writel(m, host->ioaddr + ESDHC_MIX_CTRL);
Dong Aishengad932202013-09-13 19:11:35 +0800986 break;
987 case MMC_TIMING_UHS_DDR50:
Aisheng Dong69f5bf32014-05-09 14:53:15 +0800988 case MMC_TIMING_MMC_DDR52:
Haibo Chen28b07672015-08-11 19:38:26 +0800989 m |= ESDHC_MIX_CTRL_DDREN;
990 writel(m, host->ioaddr + ESDHC_MIX_CTRL);
Dong Aishengde5bdbf2013-10-18 19:48:46 +0800991 imx_data->is_ddr = 1;
Dong Aisheng602519b2013-10-18 19:48:47 +0800992 if (boarddata->delay_line) {
993 u32 v;
994 v = boarddata->delay_line <<
995 ESDHC_DLL_OVERRIDE_VAL_SHIFT |
996 (1 << ESDHC_DLL_OVERRIDE_EN_SHIFT);
997 if (is_imx53_esdhc(imx_data))
998 v <<= 1;
999 writel(v, host->ioaddr + ESDHC_DLL_CTRL);
1000 }
Dong Aishengad932202013-09-13 19:11:35 +08001001 break;
Haibo Chen28b07672015-08-11 19:38:26 +08001002 case MMC_TIMING_MMC_HS400:
1003 m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN;
1004 writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1005 imx_data->is_ddr = 1;
Dong Aisheng7ac6da22016-07-12 15:46:20 +08001006 /* update clock after enable DDR for strobe DLL lock */
1007 host->ops->set_clock(host, host->clock);
Haibo Chen28b07672015-08-11 19:38:26 +08001008 esdhc_set_strobe_dll(host);
1009 break;
Haibo Chend9370422017-04-18 18:05:31 +08001010 case MMC_TIMING_LEGACY:
1011 default:
1012 esdhc_reset_tuning(host);
1013 break;
Dong Aishengad932202013-09-13 19:11:35 +08001014 }
1015
Russell King850a29b2014-04-25 12:59:41 +01001016 esdhc_change_pinstate(host, timing);
Dong Aishengad932202013-09-13 19:11:35 +08001017}
1018
Russell King0718e592014-04-25 12:57:18 +01001019static void esdhc_reset(struct sdhci_host *host, u8 mask)
1020{
1021 sdhci_reset(host, mask);
1022
1023 sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
1024 sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
1025}
1026
Aisheng Dong10fd0ad2014-08-27 15:26:28 +08001027static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
1028{
1029 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Jisheng Zhang070e6d32016-02-16 21:08:20 +08001030 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Aisheng Dong10fd0ad2014-08-27 15:26:28 +08001031
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +02001032 /* Doc Erratum: the uSDHC actual maximum timeout count is 1 << 29 */
Haibo Chen2fb0b022016-08-15 16:31:33 +08001033 return esdhc_is_usdhc(imx_data) ? 1 << 29 : 1 << 27;
Aisheng Dong10fd0ad2014-08-27 15:26:28 +08001034}
1035
Aisheng Donge33eb8e22014-08-27 15:26:30 +08001036static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
1037{
1038 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Jisheng Zhang070e6d32016-02-16 21:08:20 +08001039 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Aisheng Donge33eb8e22014-08-27 15:26:30 +08001040
1041 /* use maximum timeout counter */
Haibo Chena2151862016-08-15 16:19:38 +08001042 esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
1043 esdhc_is_usdhc(imx_data) ? 0xF : 0xE,
Aisheng Donge33eb8e22014-08-27 15:26:30 +08001044 SDHCI_TIMEOUT_CONTROL);
1045}
1046
Dong Aisheng6e9fd282013-10-18 19:48:43 +08001047static struct sdhci_ops sdhci_esdhc_ops = {
Richard Zhue1498602011-03-25 09:18:27 -04001048 .read_l = esdhc_readl_le,
Wolfram Sang0c6d49c2011-02-26 14:44:39 +01001049 .read_w = esdhc_readw_le,
Aaron Brice77da3da2016-10-10 11:39:52 -07001050 .read_b = esdhc_readb_le,
Richard Zhue1498602011-03-25 09:18:27 -04001051 .write_l = esdhc_writel_le,
Wolfram Sang0c6d49c2011-02-26 14:44:39 +01001052 .write_w = esdhc_writew_le,
1053 .write_b = esdhc_writeb_le,
Lucas Stach8ba95802013-06-05 15:13:25 +02001054 .set_clock = esdhc_pltfm_set_clock,
Lucas Stach0ddf03c2013-06-05 15:13:26 +02001055 .get_max_clock = esdhc_pltfm_get_max_clock,
Wolfram Sang0c6d49c2011-02-26 14:44:39 +01001056 .get_min_clock = esdhc_pltfm_get_min_clock,
Aisheng Dong10fd0ad2014-08-27 15:26:28 +08001057 .get_max_timeout_count = esdhc_get_max_timeout_count,
Shawn Guo913413c2011-06-21 22:41:51 +08001058 .get_ro = esdhc_pltfm_get_ro,
Aisheng Donge33eb8e22014-08-27 15:26:30 +08001059 .set_timeout = esdhc_set_timeout,
Russell King2317f562014-04-25 12:57:07 +01001060 .set_bus_width = esdhc_pltfm_set_bus_width,
Dong Aishengad932202013-09-13 19:11:35 +08001061 .set_uhs_signaling = esdhc_set_uhs_signaling,
Russell King0718e592014-04-25 12:57:18 +01001062 .reset = esdhc_reset,
Wolfram Sang0c6d49c2011-02-26 14:44:39 +01001063};
1064
Lars-Peter Clausen1db5eeb2013-03-13 19:26:03 +01001065static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
Richard Zhu97e4ba62011-08-11 16:51:46 -04001066 .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_HISPD_BIT
1067 | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
1068 | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC
Shawn Guo85d65092011-05-27 23:48:12 +08001069 | SDHCI_QUIRK_BROKEN_CARD_DETECTION,
Shawn Guo85d65092011-05-27 23:48:12 +08001070 .ops = &sdhci_esdhc_ops,
1071};
1072
Dong Aishengf3f5cf32016-07-12 15:46:21 +08001073static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
1074{
1075 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1076 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Dong Aisheng2b16cf32016-07-12 15:46:22 +08001077 int tmp;
Dong Aishengf3f5cf32016-07-12 15:46:21 +08001078
1079 if (esdhc_is_usdhc(imx_data)) {
1080 /*
1081 * The imx6q ROM code will change the default watermark
1082 * level setting to something insane. Change it back here.
1083 */
1084 writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL);
1085
1086 /*
1087 * ROM code will change the bit burst_length_enable setting
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +02001088 * to zero if this usdhc is chosen to boot system. Change
Dong Aishengf3f5cf32016-07-12 15:46:21 +08001089 * it back here, otherwise it will impact the performance a
1090 * lot. This bit is used to enable/disable the burst length
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +02001091 * for the external AHB2AXI bridge. It's useful especially
Dong Aishengf3f5cf32016-07-12 15:46:21 +08001092 * for INCR transfer because without burst length indicator,
1093 * the AHB2AXI bridge does not know the burst length in
1094 * advance. And without burst length indicator, AHB INCR
1095 * transfer can only be converted to singles on the AXI side.
1096 */
1097 writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
1098 | ESDHC_BURST_LEN_EN_INCR,
1099 host->ioaddr + SDHCI_HOST_CONTROL);
1100 /*
Benoît Thébaudeaud04f8d52017-05-30 11:14:07 +02001101 * erratum ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
Dong Aishengf3f5cf32016-07-12 15:46:21 +08001102 * TO1.1, it's harmless for MX6SL
1103 */
1104 writel(readl(host->ioaddr + 0x6c) | BIT(7),
1105 host->ioaddr + 0x6c);
1106
1107 /* disable DLL_CTRL delay line settings */
1108 writel(0x0, host->ioaddr + ESDHC_DLL_CTRL);
Dong Aisheng2b16cf32016-07-12 15:46:22 +08001109
1110 if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
1111 tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
1112 tmp |= ESDHC_STD_TUNING_EN |
1113 ESDHC_TUNING_START_TAP_DEFAULT;
1114 if (imx_data->boarddata.tuning_start_tap) {
1115 tmp &= ~ESDHC_TUNING_START_TAP_MASK;
1116 tmp |= imx_data->boarddata.tuning_start_tap;
1117 }
1118
1119 if (imx_data->boarddata.tuning_step) {
1120 tmp &= ~ESDHC_TUNING_STEP_MASK;
1121 tmp |= imx_data->boarddata.tuning_step
1122 << ESDHC_TUNING_STEP_SHIFT;
1123 }
1124 writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
1125 }
Dong Aishengf3f5cf32016-07-12 15:46:21 +08001126 }
1127}
1128
Shawn Guoabfafc22011-06-30 15:44:44 +08001129#ifdef CONFIG_OF
Bill Pembertonc3be1ef2012-11-19 13:23:06 -05001130static int
Shawn Guoabfafc22011-06-30 15:44:44 +08001131sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
Sascha Hauer07bf2b52015-03-24 14:45:04 +01001132 struct sdhci_host *host,
Dong Aisheng91fa4252015-07-22 20:53:06 +08001133 struct pltfm_imx_data *imx_data)
Shawn Guoabfafc22011-06-30 15:44:44 +08001134{
1135 struct device_node *np = pdev->dev.of_node;
Dong Aisheng91fa4252015-07-22 20:53:06 +08001136 struct esdhc_platform_data *boarddata = &imx_data->boarddata;
Dong Aisheng4800e872015-07-22 20:53:05 +08001137 int ret;
Shawn Guoabfafc22011-06-30 15:44:44 +08001138
Shawn Guoabfafc22011-06-30 15:44:44 +08001139 if (of_get_property(np, "fsl,wp-controller", NULL))
1140 boarddata->wp_type = ESDHC_WP_CONTROLLER;
1141
Shawn Guoabfafc22011-06-30 15:44:44 +08001142 boarddata->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
1143 if (gpio_is_valid(boarddata->wp_gpio))
1144 boarddata->wp_type = ESDHC_WP_GPIO;
1145
Haibo Chend407e30ba2015-08-11 19:38:27 +08001146 of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
Dong Aishengd87fc962016-07-12 15:46:15 +08001147 of_property_read_u32(np, "fsl,tuning-start-tap",
1148 &boarddata->tuning_start_tap);
Haibo Chend407e30ba2015-08-11 19:38:27 +08001149
Dong Aishengad932202013-09-13 19:11:35 +08001150 if (of_find_property(np, "no-1-8-v", NULL))
Stefan Agner86f495c2018-06-28 10:13:29 +02001151 host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
Dong Aishengad932202013-09-13 19:11:35 +08001152
Dong Aisheng602519b2013-10-18 19:48:47 +08001153 if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
1154 boarddata->delay_line = 0;
1155
Sascha Hauer07bf2b52015-03-24 14:45:04 +01001156 mmc_of_parse_voltage(np, &host->ocr_mask);
1157
Stefan Agner86f495c2018-06-28 10:13:29 +02001158 if (esdhc_is_usdhc(imx_data) && !IS_ERR(imx_data->pins_default)) {
Dong Aisheng91fa4252015-07-22 20:53:06 +08001159 imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
1160 ESDHC_PINCTRL_STATE_100MHZ);
1161 imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
1162 ESDHC_PINCTRL_STATE_200MHZ);
Dong Aisheng91fa4252015-07-22 20:53:06 +08001163 }
1164
Fabio Estevam15064112015-05-09 09:57:08 -03001165 /* call to generic mmc_of_parse to support additional capabilities */
Dong Aisheng4800e872015-07-22 20:53:05 +08001166 ret = mmc_of_parse(host->mmc);
1167 if (ret)
1168 return ret;
1169
Arnd Bergmann287980e2016-05-27 23:23:25 +02001170 if (mmc_gpio_get_cd(host->mmc) >= 0)
Dong Aisheng4800e872015-07-22 20:53:05 +08001171 host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
1172
1173 return 0;
Shawn Guoabfafc22011-06-30 15:44:44 +08001174}
1175#else
1176static inline int
1177sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
Sascha Hauer07bf2b52015-03-24 14:45:04 +01001178 struct sdhci_host *host,
Dong Aisheng91fa4252015-07-22 20:53:06 +08001179 struct pltfm_imx_data *imx_data)
Shawn Guoabfafc22011-06-30 15:44:44 +08001180{
1181 return -ENODEV;
1182}
1183#endif
1184
Dong Aisheng91fa4252015-07-22 20:53:06 +08001185static int sdhci_esdhc_imx_probe_nondt(struct platform_device *pdev,
1186 struct sdhci_host *host,
1187 struct pltfm_imx_data *imx_data)
1188{
1189 struct esdhc_platform_data *boarddata = &imx_data->boarddata;
1190 int err;
1191
1192 if (!host->mmc->parent->platform_data) {
1193 dev_err(mmc_dev(host->mmc), "no board data!\n");
1194 return -EINVAL;
1195 }
1196
1197 imx_data->boarddata = *((struct esdhc_platform_data *)
1198 host->mmc->parent->platform_data);
1199 /* write_protect */
1200 if (boarddata->wp_type == ESDHC_WP_GPIO) {
1201 err = mmc_gpio_request_ro(host->mmc, boarddata->wp_gpio);
1202 if (err) {
1203 dev_err(mmc_dev(host->mmc),
1204 "failed to request write-protect gpio!\n");
1205 return err;
1206 }
1207 host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
1208 }
1209
1210 /* card_detect */
1211 switch (boarddata->cd_type) {
1212 case ESDHC_CD_GPIO:
1213 err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio, 0);
1214 if (err) {
1215 dev_err(mmc_dev(host->mmc),
1216 "failed to request card-detect gpio!\n");
1217 return err;
1218 }
1219 /* fall through */
1220
1221 case ESDHC_CD_CONTROLLER:
1222 /* we have a working card_detect back */
1223 host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
1224 break;
1225
1226 case ESDHC_CD_PERMANENT:
1227 host->mmc->caps |= MMC_CAP_NONREMOVABLE;
1228 break;
1229
1230 case ESDHC_CD_NONE:
1231 break;
1232 }
1233
1234 switch (boarddata->max_bus_width) {
1235 case 8:
1236 host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA;
1237 break;
1238 case 4:
1239 host->mmc->caps |= MMC_CAP_4_BIT_DATA;
1240 break;
1241 case 1:
1242 default:
1243 host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
1244 break;
1245 }
1246
1247 return 0;
1248}
1249
Bill Pembertonc3be1ef2012-11-19 13:23:06 -05001250static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
Wolfram Sang95f25ef2010-10-15 12:21:04 +02001251{
Shawn Guoabfafc22011-06-30 15:44:44 +08001252 const struct of_device_id *of_id =
1253 of_match_device(imx_esdhc_dt_ids, &pdev->dev);
Shawn Guo85d65092011-05-27 23:48:12 +08001254 struct sdhci_pltfm_host *pltfm_host;
1255 struct sdhci_host *host;
Wolfram Sang0c6d49c2011-02-26 14:44:39 +01001256 int err;
Richard Zhue1498602011-03-25 09:18:27 -04001257 struct pltfm_imx_data *imx_data;
Wolfram Sang95f25ef2010-10-15 12:21:04 +02001258
Jisheng Zhang070e6d32016-02-16 21:08:20 +08001259 host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata,
1260 sizeof(*imx_data));
Shawn Guo85d65092011-05-27 23:48:12 +08001261 if (IS_ERR(host))
1262 return PTR_ERR(host);
1263
1264 pltfm_host = sdhci_priv(host);
1265
Jisheng Zhang070e6d32016-02-16 21:08:20 +08001266 imx_data = sdhci_pltfm_priv(pltfm_host);
Shawn Guo57ed3312011-06-30 09:24:26 +08001267
Shawn Guof47c4bb2013-10-17 15:19:47 +08001268 imx_data->socdata = of_id ? of_id->data : (struct esdhc_soc_data *)
1269 pdev->id_entry->driver_data;
Shawn Guo85d65092011-05-27 23:48:12 +08001270
Sascha Hauer52dac612012-03-07 09:31:34 +01001271 imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
1272 if (IS_ERR(imx_data->clk_ipg)) {
1273 err = PTR_ERR(imx_data->clk_ipg);
Shawn Guoe3af31c2012-11-26 14:39:43 +08001274 goto free_sdhci;
Wolfram Sang95f25ef2010-10-15 12:21:04 +02001275 }
Sascha Hauer52dac612012-03-07 09:31:34 +01001276
1277 imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
1278 if (IS_ERR(imx_data->clk_ahb)) {
1279 err = PTR_ERR(imx_data->clk_ahb);
Shawn Guoe3af31c2012-11-26 14:39:43 +08001280 goto free_sdhci;
Sascha Hauer52dac612012-03-07 09:31:34 +01001281 }
1282
1283 imx_data->clk_per = devm_clk_get(&pdev->dev, "per");
1284 if (IS_ERR(imx_data->clk_per)) {
1285 err = PTR_ERR(imx_data->clk_per);
Shawn Guoe3af31c2012-11-26 14:39:43 +08001286 goto free_sdhci;
Sascha Hauer52dac612012-03-07 09:31:34 +01001287 }
1288
1289 pltfm_host->clk = imx_data->clk_per;
Dong Aishenga9748622013-12-26 15:23:53 +08001290 pltfm_host->clock = clk_get_rate(pltfm_host->clk);
Fabio Estevam17b1eb72017-05-16 19:08:31 -03001291 err = clk_prepare_enable(imx_data->clk_per);
1292 if (err)
1293 goto free_sdhci;
1294 err = clk_prepare_enable(imx_data->clk_ipg);
1295 if (err)
1296 goto disable_per_clk;
1297 err = clk_prepare_enable(imx_data->clk_ahb);
1298 if (err)
1299 goto disable_ipg_clk;
Wolfram Sang95f25ef2010-10-15 12:21:04 +02001300
Dong Aishengad932202013-09-13 19:11:35 +08001301 imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
Dong Aishenge62d8b82012-05-11 14:56:01 +08001302 if (IS_ERR(imx_data->pinctrl)) {
1303 err = PTR_ERR(imx_data->pinctrl);
Fabio Estevam17b1eb72017-05-16 19:08:31 -03001304 goto disable_ahb_clk;
Dong Aishenge62d8b82012-05-11 14:56:01 +08001305 }
1306
Dong Aishengad932202013-09-13 19:11:35 +08001307 imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl,
1308 PINCTRL_STATE_DEFAULT);
Dirk Behmecd529af2014-10-01 04:25:32 -05001309 if (IS_ERR(imx_data->pins_default))
1310 dev_warn(mmc_dev(host->mmc), "could not get default state\n");
Dong Aishengad932202013-09-13 19:11:35 +08001311
Dong Aisheng69ed60e2013-10-18 19:48:49 +08001312 if (esdhc_is_usdhc(imx_data)) {
Dong Aisheng69ed60e2013-10-18 19:48:49 +08001313 host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
Stefan Agner09c81922018-06-28 09:31:36 +02001314 host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
Dong Aisheng4245aff2015-05-27 18:13:31 +08001315 if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
1316 host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
Dong Aishenga75dcbf2016-07-12 15:46:24 +08001317
1318 /* clear tuning bits in case ROM has set it already */
1319 writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
1320 writel(0x0, host->ioaddr + SDHCI_ACMD12_ERR);
1321 writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
Dong Aisheng69ed60e2013-10-18 19:48:49 +08001322 }
Shawn Guof750ba92011-11-10 16:39:32 +08001323
Dong Aisheng6e9fd282013-10-18 19:48:43 +08001324 if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
1325 sdhci_esdhc_ops.platform_execute_tuning =
1326 esdhc_executing_tuning;
Dong Aisheng8b2bb0ad2013-11-04 16:38:27 +08001327
Dong Aisheng18094432015-05-27 18:13:28 +08001328 if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
1329 host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
1330
Haibo Chen28b07672015-08-11 19:38:26 +08001331 if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
1332 host->quirks2 |= SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400;
1333
Dong Aisheng91fa4252015-07-22 20:53:06 +08001334 if (of_id)
1335 err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
1336 else
1337 err = sdhci_esdhc_imx_probe_nondt(pdev, host, imx_data);
1338 if (err)
Fabio Estevam17b1eb72017-05-16 19:08:31 -03001339 goto disable_ahb_clk;
Dong Aishengad932202013-09-13 19:11:35 +08001340
Dong Aishengf3f5cf32016-07-12 15:46:21 +08001341 sdhci_esdhc_imx_hwinit(host);
1342
Shawn Guo85d65092011-05-27 23:48:12 +08001343 err = sdhci_add_host(host);
1344 if (err)
Fabio Estevam17b1eb72017-05-16 19:08:31 -03001345 goto disable_ahb_clk;
Shawn Guo85d65092011-05-27 23:48:12 +08001346
Dong Aisheng89d7e5c2013-11-04 16:38:29 +08001347 pm_runtime_set_active(&pdev->dev);
Dong Aisheng89d7e5c2013-11-04 16:38:29 +08001348 pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
1349 pm_runtime_use_autosuspend(&pdev->dev);
1350 pm_suspend_ignore_children(&pdev->dev, 1);
Ulf Hansson77903c02014-12-11 15:12:25 +01001351 pm_runtime_enable(&pdev->dev);
Dong Aisheng89d7e5c2013-11-04 16:38:29 +08001352
Wolfram Sang95f25ef2010-10-15 12:21:04 +02001353 return 0;
Wolfram Sang7e29c302011-02-26 14:44:41 +01001354
Fabio Estevam17b1eb72017-05-16 19:08:31 -03001355disable_ahb_clk:
Sascha Hauer52dac612012-03-07 09:31:34 +01001356 clk_disable_unprepare(imx_data->clk_ahb);
Fabio Estevam17b1eb72017-05-16 19:08:31 -03001357disable_ipg_clk:
1358 clk_disable_unprepare(imx_data->clk_ipg);
1359disable_per_clk:
1360 clk_disable_unprepare(imx_data->clk_per);
Shawn Guoe3af31c2012-11-26 14:39:43 +08001361free_sdhci:
Shawn Guo85d65092011-05-27 23:48:12 +08001362 sdhci_pltfm_free(pdev);
1363 return err;
Wolfram Sang95f25ef2010-10-15 12:21:04 +02001364}
1365
Bill Pemberton6e0ee712012-11-19 13:26:03 -05001366static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
Wolfram Sang95f25ef2010-10-15 12:21:04 +02001367{
Shawn Guo85d65092011-05-27 23:48:12 +08001368 struct sdhci_host *host = platform_get_drvdata(pdev);
Wolfram Sang95f25ef2010-10-15 12:21:04 +02001369 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Jisheng Zhang070e6d32016-02-16 21:08:20 +08001370 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Shawn Guo85d65092011-05-27 23:48:12 +08001371 int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
1372
Ulf Hansson0b414362014-12-11 14:56:15 +01001373 pm_runtime_get_sync(&pdev->dev);
1374 pm_runtime_disable(&pdev->dev);
1375 pm_runtime_put_noidle(&pdev->dev);
1376
Shawn Guo85d65092011-05-27 23:48:12 +08001377 sdhci_remove_host(host, dead);
Wolfram Sang0c6d49c2011-02-26 14:44:39 +01001378
Ulf Hansson0b414362014-12-11 14:56:15 +01001379 clk_disable_unprepare(imx_data->clk_per);
1380 clk_disable_unprepare(imx_data->clk_ipg);
1381 clk_disable_unprepare(imx_data->clk_ahb);
Sascha Hauer52dac612012-03-07 09:31:34 +01001382
Shawn Guo85d65092011-05-27 23:48:12 +08001383 sdhci_pltfm_free(pdev);
1384
1385 return 0;
Wolfram Sang95f25ef2010-10-15 12:21:04 +02001386}
1387
Ulf Hansson2788ed42016-07-27 11:46:25 +02001388#ifdef CONFIG_PM_SLEEP
Dong Aisheng04143fb2016-07-12 15:46:12 +08001389static int sdhci_esdhc_suspend(struct device *dev)
1390{
Ulf Hansson3e3274a2016-07-27 12:17:14 +02001391 struct sdhci_host *host = dev_get_drvdata(dev);
1392
Adrian Hunterd38dcad2017-03-20 19:50:32 +02001393 if (host->tuning_mode != SDHCI_TUNING_MODE_3)
1394 mmc_retune_needed(host->mmc);
1395
Ulf Hansson3e3274a2016-07-27 12:17:14 +02001396 return sdhci_suspend_host(host);
Dong Aisheng04143fb2016-07-12 15:46:12 +08001397}
1398
1399static int sdhci_esdhc_resume(struct device *dev)
1400{
Dong Aishengcc17e122016-07-12 15:46:13 +08001401 struct sdhci_host *host = dev_get_drvdata(dev);
Dong Aishengcc17e122016-07-12 15:46:13 +08001402
Dong Aisheng19dbfdd32016-07-12 15:46:23 +08001403 /* re-initialize hw state in case it's lost in low power mode */
1404 sdhci_esdhc_imx_hwinit(host);
Dong Aishengcc17e122016-07-12 15:46:13 +08001405
Ulf Hansson3e3274a2016-07-27 12:17:14 +02001406 return sdhci_resume_host(host);
Dong Aisheng04143fb2016-07-12 15:46:12 +08001407}
Ulf Hansson2788ed42016-07-27 11:46:25 +02001408#endif
Dong Aisheng04143fb2016-07-12 15:46:12 +08001409
Ulf Hansson2788ed42016-07-27 11:46:25 +02001410#ifdef CONFIG_PM
Dong Aisheng89d7e5c2013-11-04 16:38:29 +08001411static int sdhci_esdhc_runtime_suspend(struct device *dev)
1412{
1413 struct sdhci_host *host = dev_get_drvdata(dev);
1414 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Jisheng Zhang070e6d32016-02-16 21:08:20 +08001415 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Dong Aisheng89d7e5c2013-11-04 16:38:29 +08001416 int ret;
1417
1418 ret = sdhci_runtime_suspend_host(host);
Michael Trimarchi371d39f2018-01-04 16:30:57 +01001419 if (ret)
1420 return ret;
Dong Aisheng89d7e5c2013-11-04 16:38:29 +08001421
Adrian Hunterd38dcad2017-03-20 19:50:32 +02001422 if (host->tuning_mode != SDHCI_TUNING_MODE_3)
1423 mmc_retune_needed(host->mmc);
1424
Russell Kingbe138552014-04-25 12:55:56 +01001425 if (!sdhci_sdio_irq_enabled(host)) {
Michael Trimarchi36027852018-01-04 16:30:59 +01001426 imx_data->actual_clock = host->mmc->actual_clock;
1427 esdhc_pltfm_set_clock(host, 0);
Russell Kingbe138552014-04-25 12:55:56 +01001428 clk_disable_unprepare(imx_data->clk_per);
1429 clk_disable_unprepare(imx_data->clk_ipg);
1430 }
Dong Aisheng89d7e5c2013-11-04 16:38:29 +08001431 clk_disable_unprepare(imx_data->clk_ahb);
1432
1433 return ret;
1434}
1435
1436static int sdhci_esdhc_runtime_resume(struct device *dev)
1437{
1438 struct sdhci_host *host = dev_get_drvdata(dev);
1439 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Jisheng Zhang070e6d32016-02-16 21:08:20 +08001440 struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
Fabio Estevam17b1eb72017-05-16 19:08:31 -03001441 int err;
Dong Aisheng89d7e5c2013-11-04 16:38:29 +08001442
Michael Trimarchia0ad3082018-01-04 16:30:58 +01001443 err = clk_prepare_enable(imx_data->clk_ahb);
1444 if (err)
1445 return err;
1446
Russell Kingbe138552014-04-25 12:55:56 +01001447 if (!sdhci_sdio_irq_enabled(host)) {
Fabio Estevam17b1eb72017-05-16 19:08:31 -03001448 err = clk_prepare_enable(imx_data->clk_per);
1449 if (err)
Michael Trimarchia0ad3082018-01-04 16:30:58 +01001450 goto disable_ahb_clk;
Fabio Estevam17b1eb72017-05-16 19:08:31 -03001451 err = clk_prepare_enable(imx_data->clk_ipg);
1452 if (err)
1453 goto disable_per_clk;
Michael Trimarchi36027852018-01-04 16:30:59 +01001454 esdhc_pltfm_set_clock(host, imx_data->actual_clock);
Russell Kingbe138552014-04-25 12:55:56 +01001455 }
Michael Trimarchia0ad3082018-01-04 16:30:58 +01001456
Fabio Estevam17b1eb72017-05-16 19:08:31 -03001457 err = sdhci_runtime_resume_host(host);
1458 if (err)
Michael Trimarchia0ad3082018-01-04 16:30:58 +01001459 goto disable_ipg_clk;
Dong Aisheng89d7e5c2013-11-04 16:38:29 +08001460
Fabio Estevam17b1eb72017-05-16 19:08:31 -03001461 return 0;
1462
Fabio Estevam17b1eb72017-05-16 19:08:31 -03001463disable_ipg_clk:
1464 if (!sdhci_sdio_irq_enabled(host))
1465 clk_disable_unprepare(imx_data->clk_ipg);
1466disable_per_clk:
1467 if (!sdhci_sdio_irq_enabled(host))
1468 clk_disable_unprepare(imx_data->clk_per);
Michael Trimarchia0ad3082018-01-04 16:30:58 +01001469disable_ahb_clk:
1470 clk_disable_unprepare(imx_data->clk_ahb);
Fabio Estevam17b1eb72017-05-16 19:08:31 -03001471 return err;
Dong Aisheng89d7e5c2013-11-04 16:38:29 +08001472}
1473#endif
1474
1475static const struct dev_pm_ops sdhci_esdhc_pmops = {
Dong Aisheng04143fb2016-07-12 15:46:12 +08001476 SET_SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume)
Dong Aisheng89d7e5c2013-11-04 16:38:29 +08001477 SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
1478 sdhci_esdhc_runtime_resume, NULL)
1479};
1480
Shawn Guo85d65092011-05-27 23:48:12 +08001481static struct platform_driver sdhci_esdhc_imx_driver = {
1482 .driver = {
1483 .name = "sdhci-esdhc-imx",
Shawn Guoabfafc22011-06-30 15:44:44 +08001484 .of_match_table = imx_esdhc_dt_ids,
Dong Aisheng89d7e5c2013-11-04 16:38:29 +08001485 .pm = &sdhci_esdhc_pmops,
Shawn Guo85d65092011-05-27 23:48:12 +08001486 },
Shawn Guo57ed3312011-06-30 09:24:26 +08001487 .id_table = imx_esdhc_devtype,
Shawn Guo85d65092011-05-27 23:48:12 +08001488 .probe = sdhci_esdhc_imx_probe,
Bill Pemberton0433c142012-11-19 13:20:26 -05001489 .remove = sdhci_esdhc_imx_remove,
Wolfram Sang95f25ef2010-10-15 12:21:04 +02001490};
Shawn Guo85d65092011-05-27 23:48:12 +08001491
Axel Lind1f81a62011-11-26 12:55:43 +08001492module_platform_driver(sdhci_esdhc_imx_driver);
Shawn Guo85d65092011-05-27 23:48:12 +08001493
1494MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
Wolfram Sang035ff832015-04-20 15:51:42 +02001495MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
Shawn Guo85d65092011-05-27 23:48:12 +08001496MODULE_LICENSE("GPL v2");