Blame view

drivers/mmc/s5p_sdhci.c 5.63 KB
83d290c56   Tom Rini   SPDX: Convert all...
1
  // SPDX-License-Identifier: GPL-2.0+
442d55685   Jaehoon Chung   mmc: support the ...
2
3
4
  /*
   * (C) Copyright 2012 SAMSUNG Electronics
   * Jaehoon Chung <jh80.chung@samsung.com>
442d55685   Jaehoon Chung   mmc: support the ...
5
6
7
   */
  
  #include <common.h>
7aedafd6b   Jaehoon Chung   mmc: s5p_sdhci: s...
8
  #include <dm.h>
442d55685   Jaehoon Chung   mmc: support the ...
9
10
  #include <malloc.h>
  #include <sdhci.h>
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
11
  #include <fdtdec.h>
b08c8c487   Masahiro Yamada   libfdt: move head...
12
  #include <linux/libfdt.h>
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
13
  #include <asm/gpio.h>
442d55685   Jaehoon Chung   mmc: support the ...
14
  #include <asm/arch/mmc.h>
b09ed6e4f   Jaehoon Chung   mmc: s5p_sdhci: a...
15
  #include <asm/arch/clk.h>
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
16
  #include <errno.h>
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
17
  #include <asm/arch/pinmux.h>
442d55685   Jaehoon Chung   mmc: support the ...
18

7aedafd6b   Jaehoon Chung   mmc: s5p_sdhci: s...
19
20
21
22
23
24
25
26
  #ifdef CONFIG_DM_MMC
  struct s5p_sdhci_plat {
  	struct mmc_config cfg;
  	struct mmc mmc;
  };
  
  DECLARE_GLOBAL_DATA_PTR;
  #endif
442d55685   Jaehoon Chung   mmc: support the ...
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  static char *S5P_NAME = "SAMSUNG SDHCI";
  static void s5p_sdhci_set_control_reg(struct sdhci_host *host)
  {
  	unsigned long val, ctrl;
  	/*
  	 * SELCLKPADDS[17:16]
  	 * 00 = 2mA
  	 * 01 = 4mA
  	 * 10 = 7mA
  	 * 11 = 9mA
  	 */
  	sdhci_writel(host, SDHCI_CTRL4_DRIVE_MASK(0x3), SDHCI_CONTROL4);
  
  	val = sdhci_readl(host, SDHCI_CONTROL2);
8ebde4f0b   Matt Reimer   mmc: s5p: properl...
41
  	val &= SDHCI_CTRL2_SELBASECLK_MASK(3);
442d55685   Jaehoon Chung   mmc: support the ...
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
  
  	val |=	SDHCI_CTRL2_ENSTAASYNCCLR |
  		SDHCI_CTRL2_ENCMDCNFMSK |
  		SDHCI_CTRL2_ENFBCLKRX |
  		SDHCI_CTRL2_ENCLKOUTHOLD;
  
  	sdhci_writel(host, val, SDHCI_CONTROL2);
  
  	/*
  	 * FCSEL3[31] FCSEL2[23] FCSEL1[15] FCSEL0[7]
  	 * FCSel[1:0] : Rx Feedback Clock Delay Control
  	 *	Inverter delay means10ns delay if SDCLK 50MHz setting
  	 *	01 = Delay1 (basic delay)
  	 *	11 = Delay2 (basic delay + 2ns)
  	 *	00 = Delay3 (inverter delay)
  	 *	10 = Delay4 (inverter delay + 2ns)
  	 */
b268660ce   Jaehoon Chung   mmc: s5p_sdhci: s...
59
  	val = SDHCI_CTRL3_FCSEL0 | SDHCI_CTRL3_FCSEL1;
442d55685   Jaehoon Chung   mmc: support the ...
60
61
62
63
64
65
66
67
68
69
70
71
72
  	sdhci_writel(host, val, SDHCI_CONTROL3);
  
  	/*
  	 * SELBASECLK[5:4]
  	 * 00/01 = HCLK
  	 * 10 = EPLL
  	 * 11 = XTI or XEXTCLK
  	 */
  	ctrl = sdhci_readl(host, SDHCI_CONTROL2);
  	ctrl &= ~SDHCI_CTRL2_SELBASECLK_MASK(0x3);
  	ctrl |= SDHCI_CTRL2_SELBASECLK_MASK(0x2);
  	sdhci_writel(host, ctrl, SDHCI_CONTROL2);
  }
f73b33ff9   Jaehoon Chung   mmc: s5p_sdhci: a...
73
74
75
76
77
  static void s5p_set_clock(struct sdhci_host *host, u32 div)
  {
  	/* ToDo : Use the Clock Framework */
  	set_mmc_clk(host->index, div);
  }
62226b686   Jaehoon Chung   mmc: sdhci: move ...
78
79
80
81
  static const struct sdhci_ops s5p_sdhci_ops = {
  	.set_clock	= &s5p_set_clock,
  	.set_control_reg = &s5p_sdhci_set_control_reg,
  };
9b8c9a3c0   Jaehoon Chung   mmc: s5p_sdhci: a...
82
  static int s5p_sdhci_core_init(struct sdhci_host *host)
442d55685   Jaehoon Chung   mmc: support the ...
83
  {
442d55685   Jaehoon Chung   mmc: support the ...
84
  	host->name = S5P_NAME;
442d55685   Jaehoon Chung   mmc: support the ...
85

b268660ce   Jaehoon Chung   mmc: s5p_sdhci: s...
86
  	host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE |
a034ec06f   Jaehoon Chung   mmc: s5p_sdhci: u...
87
  		SDHCI_QUIRK_32BIT_DMA_ADDR |
113e5dfcd   Jaehoon Chung   mmc: sdhci: use t...
88
  		SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_USE_WIDE8;
6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
89
  	host->max_clk = 52000000;
442d55685   Jaehoon Chung   mmc: support the ...
90
  	host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
62226b686   Jaehoon Chung   mmc: sdhci: move ...
91
  	host->ops = &s5p_sdhci_ops;
442d55685   Jaehoon Chung   mmc: support the ...
92

9b8c9a3c0   Jaehoon Chung   mmc: s5p_sdhci: a...
93
  	if (host->bus_width == 8)
113e5dfcd   Jaehoon Chung   mmc: sdhci: use t...
94
  		host->host_caps |= MMC_MODE_8BIT;
442d55685   Jaehoon Chung   mmc: support the ...
95

7aedafd6b   Jaehoon Chung   mmc: s5p_sdhci: s...
96
  #ifndef CONFIG_BLK
6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
97
  	return add_sdhci(host, 0, 400000);
7aedafd6b   Jaehoon Chung   mmc: s5p_sdhci: s...
98
99
100
  #else
  	return 0;
  #endif
442d55685   Jaehoon Chung   mmc: support the ...
101
  }
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
102

9b8c9a3c0   Jaehoon Chung   mmc: s5p_sdhci: a...
103
104
  int s5p_sdhci_init(u32 regbase, int index, int bus_width)
  {
1a9d1731f   Tobias Jakobi   exynos: Properly ...
105
  	struct sdhci_host *host = calloc(1, sizeof(struct sdhci_host));
9b8c9a3c0   Jaehoon Chung   mmc: s5p_sdhci: a...
106
  	if (!host) {
1a9d1731f   Tobias Jakobi   exynos: Properly ...
107
108
  		printf("sdhci__host allocation fail!
  ");
2cb5d67c1   Jaehoon Chung   mmc: sdhci: use t...
109
  		return -ENOMEM;
9b8c9a3c0   Jaehoon Chung   mmc: s5p_sdhci: a...
110
111
112
113
114
115
116
  	}
  	host->ioaddr = (void *)regbase;
  	host->index = index;
  	host->bus_width = bus_width;
  
  	return s5p_sdhci_core_init(host);
  }
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
117
118
  static int do_sdhci_init(struct sdhci_host *host)
  {
2308ea7c6   Tobias Jakobi   exynos: more debu...
119
  	int dev_id, flag, ret;
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
120
121
122
  
  	flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE;
  	dev_id = host->index + PERIPH_ID_SDMMC0;
96094d4c4   Przemyslaw Marczak   s5p sdhci: call p...
123
124
125
126
127
128
  	ret = exynos_pinmux_config(dev_id, flag);
  	if (ret) {
  		printf("external SD not configured
  ");
  		return ret;
  	}
0347960b8   Simon Glass   dm: mmc: Remove u...
129
130
  	if (dm_gpio_is_valid(&host->pwr_gpio)) {
  		dm_gpio_set_value(&host->pwr_gpio, 1);
2308ea7c6   Tobias Jakobi   exynos: more debu...
131
132
  		ret = exynos_pinmux_config(dev_id, flag);
  		if (ret) {
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
133
134
  			debug("MMC not configured
  ");
2308ea7c6   Tobias Jakobi   exynos: more debu...
135
  			return ret;
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
136
137
  		}
  	}
0347960b8   Simon Glass   dm: mmc: Remove u...
138
  	if (dm_gpio_is_valid(&host->cd_gpio)) {
2308ea7c6   Tobias Jakobi   exynos: more debu...
139
140
141
142
  		ret = dm_gpio_get_value(&host->cd_gpio);
  		if (ret) {
  			debug("no SD card detected (%d)
  ", ret);
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
143
  			return -ENODEV;
2308ea7c6   Tobias Jakobi   exynos: more debu...
144
  		}
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
145
  	}
9b8c9a3c0   Jaehoon Chung   mmc: s5p_sdhci: a...
146
  	return s5p_sdhci_core_init(host);
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
147
148
149
150
151
152
153
154
155
  }
  
  static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host)
  {
  	int bus_width, dev_id;
  	unsigned int base;
  
  	/* Get device id */
  	dev_id = pinmux_decode_periph_id(blob, node);
f0ecfc5e7   Seung-Woo Kim   mmc: s5p_sdhci: f...
156
  	if (dev_id < PERIPH_ID_SDMMC0 || dev_id > PERIPH_ID_SDMMC3) {
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
157
158
  		debug("MMC: Can't get device id
  ");
2cb5d67c1   Jaehoon Chung   mmc: sdhci: use t...
159
  		return -EINVAL;
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
160
161
162
163
164
165
166
167
  	}
  	host->index = dev_id - PERIPH_ID_SDMMC0;
  
  	/* Get bus width */
  	bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0);
  	if (bus_width <= 0) {
  		debug("MMC: Can't get bus-width
  ");
2cb5d67c1   Jaehoon Chung   mmc: sdhci: use t...
168
  		return -EINVAL;
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
169
170
171
172
173
174
175
176
  	}
  	host->bus_width = bus_width;
  
  	/* Get the base address from the device node */
  	base = fdtdec_get_addr(blob, node, "reg");
  	if (!base) {
  		debug("MMC: Can't get base address
  ");
2cb5d67c1   Jaehoon Chung   mmc: sdhci: use t...
177
  		return -EINVAL;
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
178
179
  	}
  	host->ioaddr = (void *)base;
150c5afe5   Simon Glass   dm: gpio: Add liv...
180
181
182
183
  	gpio_request_by_name_nodev(offset_to_ofnode(node), "pwr-gpios", 0,
  				   &host->pwr_gpio, GPIOD_IS_OUT);
  	gpio_request_by_name_nodev(offset_to_ofnode(node), "cd-gpios", 0,
  				   &host->cd_gpio, GPIOD_IS_IN);
3577fe8be   Piotr Wilczek   drivers:mmc:sdhci...
184
185
186
  
  	return 0;
  }
7aedafd6b   Jaehoon Chung   mmc: s5p_sdhci: s...
187
188
189
190
191
192
193
  #ifdef CONFIG_DM_MMC
  static int s5p_sdhci_probe(struct udevice *dev)
  {
  	struct s5p_sdhci_plat *plat = dev_get_platdata(dev);
  	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
  	struct sdhci_host *host = dev_get_priv(dev);
  	int ret;
e160f7d43   Simon Glass   dm: core: Replace...
194
  	ret = sdhci_get_config(gd->fdt_blob, dev_of_offset(dev), host);
7aedafd6b   Jaehoon Chung   mmc: s5p_sdhci: s...
195
196
197
198
199
200
  	if (ret)
  		return ret;
  
  	ret = do_sdhci_init(host);
  	if (ret)
  		return ret;
e27108c43   Marek Szyprowski   mmc: s5p_sdhci: R...
201
202
203
  	ret = mmc_of_parse(dev, &plat->cfg);
  	if (ret)
  		return ret;
6f16cbe55   Peng Fan   mmc: s5p: fix uni...
204
205
  	host->mmc = &plat->mmc;
  	host->mmc->dev = dev;
e27108c43   Marek Szyprowski   mmc: s5p_sdhci: R...
206

6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
207
  	ret = sdhci_setup_cfg(&plat->cfg, host, 0, 400000);
7aedafd6b   Jaehoon Chung   mmc: s5p_sdhci: s...
208
209
  	if (ret)
  		return ret;
7aedafd6b   Jaehoon Chung   mmc: s5p_sdhci: s...
210
  	host->mmc->priv = host;
7aedafd6b   Jaehoon Chung   mmc: s5p_sdhci: s...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  	upriv->mmc = host->mmc;
  
  	return sdhci_probe(dev);
  }
  
  static int s5p_sdhci_bind(struct udevice *dev)
  {
  	struct s5p_sdhci_plat *plat = dev_get_platdata(dev);
  	int ret;
  
  	ret = sdhci_bind(dev, &plat->mmc, &plat->cfg);
  	if (ret)
  		return ret;
  
  	return 0;
  }
  
  static const struct udevice_id s5p_sdhci_ids[] = {
  	{ .compatible = "samsung,exynos4412-sdhci"},
  	{ }
  };
  
  U_BOOT_DRIVER(s5p_sdhci_drv) = {
  	.name		= "s5p_sdhci",
  	.id		= UCLASS_MMC,
  	.of_match	= s5p_sdhci_ids,
  	.bind		= s5p_sdhci_bind,
  	.ops		= &sdhci_ops,
  	.probe		= s5p_sdhci_probe,
  	.priv_auto_alloc_size = sizeof(struct sdhci_host),
  	.platdata_auto_alloc_size = sizeof(struct s5p_sdhci_plat),
  };
  #endif /* CONFIG_DM_MMC */