Linux Audio

Check our new training course

Loading...
v4.6
  1/*
  2 * Copyright (C) 2010 Google, Inc.
  3 *
  4 * This software is licensed under the terms of the GNU General Public
  5 * License version 2, as published by the Free Software Foundation, and
  6 * may be copied, distributed, and modified under those terms.
  7 *
  8 * This program is distributed in the hope that it will be useful,
  9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 11 * GNU General Public License for more details.
 12 *
 13 */
 14
 15#include <linux/delay.h>
 16#include <linux/err.h>
 17#include <linux/module.h>
 18#include <linux/init.h>
 19#include <linux/platform_device.h>
 20#include <linux/clk.h>
 21#include <linux/io.h>
 22#include <linux/of.h>
 23#include <linux/of_device.h>
 
 
 24#include <linux/mmc/card.h>
 25#include <linux/mmc/host.h>
 26#include <linux/mmc/mmc.h>
 27#include <linux/mmc/slot-gpio.h>
 28#include <linux/gpio/consumer.h>
 
 
 29
 30#include "sdhci-pltfm.h"
 31
 32/* Tegra SDHOST controller vendor register definitions */
 33#define SDHCI_TEGRA_VENDOR_CLOCK_CTRL			0x100
 34#define SDHCI_CLOCK_CTRL_TAP_MASK			0x00ff0000
 35#define SDHCI_CLOCK_CTRL_TAP_SHIFT			16
 36#define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE		BIT(5)
 37#define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE		BIT(3)
 38#define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE	BIT(2)
 39
 40#define SDHCI_TEGRA_VENDOR_MISC_CTRL		0x120
 41#define SDHCI_MISC_CTRL_ENABLE_SDR104		0x8
 42#define SDHCI_MISC_CTRL_ENABLE_SDR50		0x10
 43#define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300	0x20
 44#define SDHCI_MISC_CTRL_ENABLE_DDR50		0x200
 45
 46#define SDHCI_TEGRA_AUTO_CAL_CONFIG		0x1e4
 47#define SDHCI_AUTO_CAL_START			BIT(31)
 48#define SDHCI_AUTO_CAL_ENABLE			BIT(29)
 49
 50#define NVQUIRK_FORCE_SDHCI_SPEC_200	BIT(0)
 51#define NVQUIRK_ENABLE_BLOCK_GAP_DET	BIT(1)
 52#define NVQUIRK_ENABLE_SDHCI_SPEC_300	BIT(2)
 53#define NVQUIRK_ENABLE_SDR50		BIT(3)
 54#define NVQUIRK_ENABLE_SDR104		BIT(4)
 55#define NVQUIRK_ENABLE_DDR50		BIT(5)
 56#define NVQUIRK_HAS_PADCALIB		BIT(6)
 57
 58struct sdhci_tegra_soc_data {
 59	const struct sdhci_pltfm_data *pdata;
 60	u32 nvquirks;
 61};
 62
 63struct sdhci_tegra {
 
 64	const struct sdhci_tegra_soc_data *soc_data;
 65	struct gpio_desc *power_gpio;
 66	bool ddr_signaling;
 67	bool pad_calib_required;
 68};
 69
 
 
 
 
 
 
 
 
 
 
 
 
 
 70static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
 71{
 72	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 73	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
 74	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
 75
 76	if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
 77			(reg == SDHCI_HOST_VERSION))) {
 78		/* Erratum: Version register is invalid in HW. */
 79		return SDHCI_SPEC_200;
 80	}
 81
 82	return readw(host->ioaddr + reg);
 83}
 84
 85static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
 86{
 87	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 88
 89	switch (reg) {
 90	case SDHCI_TRANSFER_MODE:
 91		/*
 92		 * Postpone this write, we must do it together with a
 93		 * command write that is down below.
 94		 */
 95		pltfm_host->xfer_mode_shadow = val;
 96		return;
 97	case SDHCI_COMMAND:
 98		writel((val << 16) | pltfm_host->xfer_mode_shadow,
 99			host->ioaddr + SDHCI_TRANSFER_MODE);
100		return;
101	}
102
103	writew(val, host->ioaddr + reg);
104}
105
106static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
107{
108	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
109	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
110	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
111
112	/* Seems like we're getting spurious timeout and crc errors, so
113	 * disable signalling of them. In case of real errors software
114	 * timers should take care of eventually detecting them.
115	 */
116	if (unlikely(reg == SDHCI_SIGNAL_ENABLE))
117		val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC);
118
119	writel(val, host->ioaddr + reg);
120
121	if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
122			(reg == SDHCI_INT_ENABLE))) {
123		/* Erratum: Must enable block gap interrupt detection */
124		u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
125		if (val & SDHCI_INT_CARD_INT)
126			gap_ctrl |= 0x8;
127		else
128			gap_ctrl &= ~0x8;
129		writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
130	}
131}
132
133static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
134{
135	return mmc_gpio_get_ro(host->mmc);
 
 
 
 
 
 
 
136}
137
138static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
 
 
 
 
 
 
 
 
139{
140	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
141	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
142	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
143	u32 misc_ctrl, clk_ctrl;
144
145	sdhci_reset(host, mask);
146
147	if (!(mask & SDHCI_RESET_ALL))
148		return;
149
150	misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
151	/* Erratum: Enable SDHCI spec v3.00 support */
152	if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
153		misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
154	/* Advertise UHS modes as supported by host */
155	if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
156		misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
157	else
158		misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50;
159	if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
160		misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
161	else
162		misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50;
163	if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
164		misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
165	else
166		misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104;
167	sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
168
169	clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
170	clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE;
171	if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50)
172		clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
173	sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
174
175	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
176		tegra_host->pad_calib_required = true;
177
178	tegra_host->ddr_signaling = false;
179}
180
181static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
182{
 
 
 
183	u32 ctrl;
184
185	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
186	if ((host->mmc->caps & MMC_CAP_8_BIT_DATA) &&
187	    (bus_width == MMC_BUS_WIDTH_8)) {
188		ctrl &= ~SDHCI_CTRL_4BITBUS;
189		ctrl |= SDHCI_CTRL_8BITBUS;
190	} else {
191		ctrl &= ~SDHCI_CTRL_8BITBUS;
192		if (bus_width == MMC_BUS_WIDTH_4)
193			ctrl |= SDHCI_CTRL_4BITBUS;
194		else
195			ctrl &= ~SDHCI_CTRL_4BITBUS;
196	}
197	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
 
198}
199
200static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
201{
202	u32 val;
203
204	mdelay(1);
205
206	val = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
207	val |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START;
208	sdhci_writel(host,val, SDHCI_TEGRA_AUTO_CAL_CONFIG);
209}
210
211static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
212{
213	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
214	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
215	unsigned long host_clk;
216
217	if (!clock)
218		return sdhci_set_clock(host, clock);
219
220	host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
221	clk_set_rate(pltfm_host->clk, host_clk);
222	host->max_clk = clk_get_rate(pltfm_host->clk);
223
224	sdhci_set_clock(host, clock);
225
226	if (tegra_host->pad_calib_required) {
227		tegra_sdhci_pad_autocalib(host);
228		tegra_host->pad_calib_required = false;
229	}
230}
231
232static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
233					  unsigned timing)
234{
235	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
236	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
237
238	if (timing == MMC_TIMING_UHS_DDR50)
239		tegra_host->ddr_signaling = true;
240
241	return sdhci_set_uhs_signaling(host, timing);
242}
243
244static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
245{
246	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
247
248	/*
249	 * DDR modes require the host to run at double the card frequency, so
250	 * the maximum rate we can support is half of the module input clock.
251	 */
252	return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2;
253}
254
255static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap)
256{
257	u32 reg;
258
259	reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
260	reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK;
261	reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT;
262	sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
263}
264
265static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
266{
267	unsigned int min, max;
268
269	/*
270	 * Start search for minimum tap value at 10, as smaller values are
271	 * may wrongly be reported as working but fail at higher speeds,
272	 * according to the TRM.
273	 */
274	min = 10;
275	while (min < 255) {
276		tegra_sdhci_set_tap(host, min);
277		if (!mmc_send_tuning(host->mmc, opcode, NULL))
278			break;
279		min++;
280	}
281
282	/* Find the maximum tap value that still passes. */
283	max = min + 1;
284	while (max < 255) {
285		tegra_sdhci_set_tap(host, max);
286		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
287			max--;
288			break;
289		}
290		max++;
291	}
292
293	/* The TRM states the ideal tap value is at 75% in the passing range. */
294	tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4));
295
296	return mmc_send_tuning(host->mmc, opcode, NULL);
297}
298
299static void tegra_sdhci_voltage_switch(struct sdhci_host *host)
300{
301	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
302	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
303	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
304
305	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
306		tegra_host->pad_calib_required = true;
307}
308
309static const struct sdhci_ops tegra_sdhci_ops = {
310	.get_ro     = tegra_sdhci_get_ro,
 
311	.read_w     = tegra_sdhci_readw,
312	.write_l    = tegra_sdhci_writel,
313	.set_clock  = tegra_sdhci_set_clock,
314	.set_bus_width = tegra_sdhci_set_bus_width,
315	.reset      = tegra_sdhci_reset,
316	.platform_execute_tuning = tegra_sdhci_execute_tuning,
317	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
318	.voltage_switch = tegra_sdhci_voltage_switch,
319	.get_max_clock = tegra_sdhci_get_max_clock,
320};
321
322static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
 
323	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
324		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
325		  SDHCI_QUIRK_NO_HISPD_BIT |
326		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
327		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
328	.ops  = &tegra_sdhci_ops,
329};
330
331static const struct sdhci_tegra_soc_data soc_data_tegra20 = {
332	.pdata = &sdhci_tegra20_pdata,
333	.nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
334		    NVQUIRK_ENABLE_BLOCK_GAP_DET,
335};
 
336
337static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
 
338	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
339		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
340		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
341		  SDHCI_QUIRK_NO_HISPD_BIT |
342		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
343		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
344	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
345	.ops  = &tegra_sdhci_ops,
346};
347
348static const struct sdhci_tegra_soc_data soc_data_tegra30 = {
349	.pdata = &sdhci_tegra30_pdata,
350	.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 |
351		    NVQUIRK_ENABLE_SDR50 |
352		    NVQUIRK_ENABLE_SDR104 |
353		    NVQUIRK_HAS_PADCALIB,
354};
 
355
356static const struct sdhci_ops tegra114_sdhci_ops = {
357	.get_ro     = tegra_sdhci_get_ro,
358	.read_w     = tegra_sdhci_readw,
359	.write_w    = tegra_sdhci_writew,
360	.write_l    = tegra_sdhci_writel,
361	.set_clock  = tegra_sdhci_set_clock,
362	.set_bus_width = tegra_sdhci_set_bus_width,
363	.reset      = tegra_sdhci_reset,
364	.platform_execute_tuning = tegra_sdhci_execute_tuning,
365	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
366	.voltage_switch = tegra_sdhci_voltage_switch,
367	.get_max_clock = tegra_sdhci_get_max_clock,
368};
 
369
370static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
371	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
372		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
373		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
374		  SDHCI_QUIRK_NO_HISPD_BIT |
375		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
376		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
377	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
378	.ops  = &tegra114_sdhci_ops,
379};
380
381static const struct sdhci_tegra_soc_data soc_data_tegra114 = {
382	.pdata = &sdhci_tegra114_pdata,
383};
384
385static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
386	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
387		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
388		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
389		  SDHCI_QUIRK_NO_HISPD_BIT |
390		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
391		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
392	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
393	.ops  = &tegra114_sdhci_ops,
394};
395
396static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
397	.pdata = &sdhci_tegra210_pdata,
398};
 
 
399
400static const struct of_device_id sdhci_tegra_dt_match[] = {
401	{ .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
402	{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 },
403	{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
404	{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
405	{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
406	{}
407};
408MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
409
410static int sdhci_tegra_probe(struct platform_device *pdev)
411{
412	const struct of_device_id *match;
413	const struct sdhci_tegra_soc_data *soc_data;
414	struct sdhci_host *host;
415	struct sdhci_pltfm_host *pltfm_host;
 
416	struct sdhci_tegra *tegra_host;
417	struct clk *clk;
418	int rc;
419
420	match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
421	if (!match)
422		return -EINVAL;
423	soc_data = match->data;
 
424
425	host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host));
426	if (IS_ERR(host))
427		return PTR_ERR(host);
 
428	pltfm_host = sdhci_priv(host);
429
430	tegra_host = sdhci_pltfm_priv(pltfm_host);
431	tegra_host->ddr_signaling = false;
432	tegra_host->pad_calib_required = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433	tegra_host->soc_data = soc_data;
434
435	rc = mmc_of_parse(host->mmc);
436	if (rc)
437		goto err_parse_dt;
438
439	if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
440		host->mmc->caps |= MMC_CAP_1_8V_DDR;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441
442	tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
443							 GPIOD_OUT_HIGH);
444	if (IS_ERR(tegra_host->power_gpio)) {
445		rc = PTR_ERR(tegra_host->power_gpio);
446		goto err_power_req;
447	}
448
449	clk = devm_clk_get(mmc_dev(host->mmc), NULL);
 
 
 
 
 
 
 
 
 
 
450	if (IS_ERR(clk)) {
451		dev_err(mmc_dev(host->mmc), "clk err\n");
452		rc = PTR_ERR(clk);
453		goto err_clk_get;
454	}
455	clk_prepare_enable(clk);
456	pltfm_host->clk = clk;
457
 
 
 
 
 
458	rc = sdhci_add_host(host);
459	if (rc)
460		goto err_add_host;
461
462	return 0;
463
464err_add_host:
465	clk_disable_unprepare(pltfm_host->clk);
 
466err_clk_get:
 
 
 
 
 
 
 
 
 
 
 
467err_power_req:
468err_parse_dt:
469	sdhci_pltfm_free(pdev);
470	return rc;
471}
472
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
473static struct platform_driver sdhci_tegra_driver = {
474	.driver		= {
475		.name	= "sdhci-tegra",
 
476		.of_match_table = sdhci_tegra_dt_match,
477		.pm	= SDHCI_PLTFM_PMOPS,
478	},
479	.probe		= sdhci_tegra_probe,
480	.remove		= sdhci_pltfm_unregister,
481};
482
483module_platform_driver(sdhci_tegra_driver);
484
485MODULE_DESCRIPTION("SDHCI driver for Tegra");
486MODULE_AUTHOR("Google, Inc.");
487MODULE_LICENSE("GPL v2");
v3.5.6
  1/*
  2 * Copyright (C) 2010 Google, Inc.
  3 *
  4 * This software is licensed under the terms of the GNU General Public
  5 * License version 2, as published by the Free Software Foundation, and
  6 * may be copied, distributed, and modified under those terms.
  7 *
  8 * This program is distributed in the hope that it will be useful,
  9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 11 * GNU General Public License for more details.
 12 *
 13 */
 14
 
 15#include <linux/err.h>
 16#include <linux/module.h>
 17#include <linux/init.h>
 18#include <linux/platform_device.h>
 19#include <linux/clk.h>
 20#include <linux/io.h>
 21#include <linux/of.h>
 22#include <linux/of_device.h>
 23#include <linux/of_gpio.h>
 24#include <linux/gpio.h>
 25#include <linux/mmc/card.h>
 26#include <linux/mmc/host.h>
 27
 28#include <asm/gpio.h>
 29
 30#include <mach/gpio-tegra.h>
 31#include <mach/sdhci.h>
 32
 33#include "sdhci-pltfm.h"
 34
 35/* Tegra SDHOST controller vendor register definitions */
 
 
 
 
 
 
 
 36#define SDHCI_TEGRA_VENDOR_MISC_CTRL		0x120
 
 
 37#define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300	0x20
 
 
 
 
 
 38
 39#define NVQUIRK_FORCE_SDHCI_SPEC_200	BIT(0)
 40#define NVQUIRK_ENABLE_BLOCK_GAP_DET	BIT(1)
 41#define NVQUIRK_ENABLE_SDHCI_SPEC_300	BIT(2)
 
 
 
 
 42
 43struct sdhci_tegra_soc_data {
 44	struct sdhci_pltfm_data *pdata;
 45	u32 nvquirks;
 46};
 47
 48struct sdhci_tegra {
 49	const struct tegra_sdhci_platform_data *plat;
 50	const struct sdhci_tegra_soc_data *soc_data;
 
 
 
 51};
 52
 53static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
 54{
 55	u32 val;
 56
 57	if (unlikely(reg == SDHCI_PRESENT_STATE)) {
 58		/* Use wp_gpio here instead? */
 59		val = readl(host->ioaddr + reg);
 60		return val | SDHCI_WRITE_PROTECT;
 61	}
 62
 63	return readl(host->ioaddr + reg);
 64}
 65
 66static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
 67{
 68	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 69	struct sdhci_tegra *tegra_host = pltfm_host->priv;
 70	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
 71
 72	if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
 73			(reg == SDHCI_HOST_VERSION))) {
 74		/* Erratum: Version register is invalid in HW. */
 75		return SDHCI_SPEC_200;
 76	}
 77
 78	return readw(host->ioaddr + reg);
 79}
 80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 81static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
 82{
 83	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 84	struct sdhci_tegra *tegra_host = pltfm_host->priv;
 85	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
 86
 87	/* Seems like we're getting spurious timeout and crc errors, so
 88	 * disable signalling of them. In case of real errors software
 89	 * timers should take care of eventually detecting them.
 90	 */
 91	if (unlikely(reg == SDHCI_SIGNAL_ENABLE))
 92		val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC);
 93
 94	writel(val, host->ioaddr + reg);
 95
 96	if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
 97			(reg == SDHCI_INT_ENABLE))) {
 98		/* Erratum: Must enable block gap interrupt detection */
 99		u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
100		if (val & SDHCI_INT_CARD_INT)
101			gap_ctrl |= 0x8;
102		else
103			gap_ctrl &= ~0x8;
104		writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
105	}
106}
107
108static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
109{
110	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
111	struct sdhci_tegra *tegra_host = pltfm_host->priv;
112	const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
113
114	if (!gpio_is_valid(plat->wp_gpio))
115		return -1;
116
117	return gpio_get_value(plat->wp_gpio);
118}
119
120static irqreturn_t carddetect_irq(int irq, void *data)
121{
122	struct sdhci_host *sdhost = (struct sdhci_host *)data;
123
124	tasklet_schedule(&sdhost->card_tasklet);
125	return IRQ_HANDLED;
126};
127
128static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask)
129{
130	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
131	struct sdhci_tegra *tegra_host = pltfm_host->priv;
132	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
 
 
 
133
134	if (!(mask & SDHCI_RESET_ALL))
135		return;
136
 
137	/* Erratum: Enable SDHCI spec v3.00 support */
138	if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) {
139		u32 misc_ctrl;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
141		misc_ctrl = sdhci_readb(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
142		misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
143		sdhci_writeb(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
144	}
 
 
 
 
 
 
145}
146
147static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
148{
149	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
150	struct sdhci_tegra *tegra_host = pltfm_host->priv;
151	const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
152	u32 ctrl;
153
154	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
155	if (plat->is_8bit && bus_width == MMC_BUS_WIDTH_8) {
 
156		ctrl &= ~SDHCI_CTRL_4BITBUS;
157		ctrl |= SDHCI_CTRL_8BITBUS;
158	} else {
159		ctrl &= ~SDHCI_CTRL_8BITBUS;
160		if (bus_width == MMC_BUS_WIDTH_4)
161			ctrl |= SDHCI_CTRL_4BITBUS;
162		else
163			ctrl &= ~SDHCI_CTRL_4BITBUS;
164	}
165	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
166	return 0;
167}
168
169static struct sdhci_ops tegra_sdhci_ops = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170	.get_ro     = tegra_sdhci_get_ro,
171	.read_l     = tegra_sdhci_readl,
172	.read_w     = tegra_sdhci_readw,
173	.write_l    = tegra_sdhci_writel,
174	.platform_8bit_width = tegra_sdhci_8bit,
175	.platform_reset_exit = tegra_sdhci_reset_exit,
 
 
 
 
 
176};
177
178#ifdef CONFIG_ARCH_TEGRA_2x_SOC
179static struct sdhci_pltfm_data sdhci_tegra20_pdata = {
180	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
181		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
182		  SDHCI_QUIRK_NO_HISPD_BIT |
183		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
 
184	.ops  = &tegra_sdhci_ops,
185};
186
187static struct sdhci_tegra_soc_data soc_data_tegra20 = {
188	.pdata = &sdhci_tegra20_pdata,
189	.nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
190		    NVQUIRK_ENABLE_BLOCK_GAP_DET,
191};
192#endif
193
194#ifdef CONFIG_ARCH_TEGRA_3x_SOC
195static struct sdhci_pltfm_data sdhci_tegra30_pdata = {
196	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
197		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
198		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
199		  SDHCI_QUIRK_NO_HISPD_BIT |
200		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
 
 
201	.ops  = &tegra_sdhci_ops,
202};
203
204static struct sdhci_tegra_soc_data soc_data_tegra30 = {
205	.pdata = &sdhci_tegra30_pdata,
206	.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300,
 
 
 
207};
208#endif
209
210static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = {
211#ifdef CONFIG_ARCH_TEGRA_3x_SOC
212	{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
213#endif
214#ifdef CONFIG_ARCH_TEGRA_2x_SOC
215	{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
216#endif
217	{}
 
 
 
 
218};
219MODULE_DEVICE_TABLE(of, sdhci_dt_ids);
220
221static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata(
222						struct platform_device *pdev)
223{
224	struct tegra_sdhci_platform_data *plat;
225	struct device_node *np = pdev->dev.of_node;
 
 
 
 
 
226
227	if (!np)
228		return NULL;
 
229
230	plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
231	if (!plat) {
232		dev_err(&pdev->dev, "Can't allocate platform data\n");
233		return NULL;
234	}
 
 
 
 
 
235
236	plat->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
237	plat->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
238	plat->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
239	if (of_find_property(np, "support-8bit", NULL))
240		plat->is_8bit = 1;
241
242	return plat;
243}
 
 
 
 
 
 
 
244
245static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
246{
247	const struct of_device_id *match;
248	const struct sdhci_tegra_soc_data *soc_data;
249	struct sdhci_host *host;
250	struct sdhci_pltfm_host *pltfm_host;
251	struct tegra_sdhci_platform_data *plat;
252	struct sdhci_tegra *tegra_host;
253	struct clk *clk;
254	int rc;
255
256	match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
257	if (match)
258		soc_data = match->data;
259	else
260		soc_data = &soc_data_tegra20;
261
262	host = sdhci_pltfm_init(pdev, soc_data->pdata);
263	if (IS_ERR(host))
264		return PTR_ERR(host);
265
266	pltfm_host = sdhci_priv(host);
267
268	plat = pdev->dev.platform_data;
269
270	if (plat == NULL)
271		plat = sdhci_tegra_dt_parse_pdata(pdev);
272
273	if (plat == NULL) {
274		dev_err(mmc_dev(host->mmc), "missing platform data\n");
275		rc = -ENXIO;
276		goto err_no_plat;
277	}
278
279	tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL);
280	if (!tegra_host) {
281		dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n");
282		rc = -ENOMEM;
283		goto err_no_plat;
284	}
285
286	tegra_host->plat = plat;
287	tegra_host->soc_data = soc_data;
288
289	pltfm_host->priv = tegra_host;
 
 
290
291	if (gpio_is_valid(plat->power_gpio)) {
292		rc = gpio_request(plat->power_gpio, "sdhci_power");
293		if (rc) {
294			dev_err(mmc_dev(host->mmc),
295				"failed to allocate power gpio\n");
296			goto err_power_req;
297		}
298		gpio_direction_output(plat->power_gpio, 1);
299	}
300
301	if (gpio_is_valid(plat->cd_gpio)) {
302		rc = gpio_request(plat->cd_gpio, "sdhci_cd");
303		if (rc) {
304			dev_err(mmc_dev(host->mmc),
305				"failed to allocate cd gpio\n");
306			goto err_cd_req;
307		}
308		gpio_direction_input(plat->cd_gpio);
309
310		rc = request_irq(gpio_to_irq(plat->cd_gpio), carddetect_irq,
311				 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
312				 mmc_hostname(host->mmc), host);
313
314		if (rc)	{
315			dev_err(mmc_dev(host->mmc), "request irq error\n");
316			goto err_cd_irq_req;
317		}
318
 
 
 
 
 
319	}
320
321	if (gpio_is_valid(plat->wp_gpio)) {
322		rc = gpio_request(plat->wp_gpio, "sdhci_wp");
323		if (rc) {
324			dev_err(mmc_dev(host->mmc),
325				"failed to allocate wp gpio\n");
326			goto err_wp_req;
327		}
328		gpio_direction_input(plat->wp_gpio);
329	}
330
331	clk = clk_get(mmc_dev(host->mmc), NULL);
332	if (IS_ERR(clk)) {
333		dev_err(mmc_dev(host->mmc), "clk err\n");
334		rc = PTR_ERR(clk);
335		goto err_clk_get;
336	}
337	clk_enable(clk);
338	pltfm_host->clk = clk;
339
340	host->mmc->pm_caps = plat->pm_flags;
341
342	if (plat->is_8bit)
343		host->mmc->caps |= MMC_CAP_8_BIT_DATA;
344
345	rc = sdhci_add_host(host);
346	if (rc)
347		goto err_add_host;
348
349	return 0;
350
351err_add_host:
352	clk_disable(pltfm_host->clk);
353	clk_put(pltfm_host->clk);
354err_clk_get:
355	if (gpio_is_valid(plat->wp_gpio))
356		gpio_free(plat->wp_gpio);
357err_wp_req:
358	if (gpio_is_valid(plat->cd_gpio))
359		free_irq(gpio_to_irq(plat->cd_gpio), host);
360err_cd_irq_req:
361	if (gpio_is_valid(plat->cd_gpio))
362		gpio_free(plat->cd_gpio);
363err_cd_req:
364	if (gpio_is_valid(plat->power_gpio))
365		gpio_free(plat->power_gpio);
366err_power_req:
367err_no_plat:
368	sdhci_pltfm_free(pdev);
369	return rc;
370}
371
372static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
373{
374	struct sdhci_host *host = platform_get_drvdata(pdev);
375	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
376	struct sdhci_tegra *tegra_host = pltfm_host->priv;
377	const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
378	int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
379
380	sdhci_remove_host(host, dead);
381
382	if (gpio_is_valid(plat->wp_gpio))
383		gpio_free(plat->wp_gpio);
384
385	if (gpio_is_valid(plat->cd_gpio)) {
386		free_irq(gpio_to_irq(plat->cd_gpio), host);
387		gpio_free(plat->cd_gpio);
388	}
389
390	if (gpio_is_valid(plat->power_gpio))
391		gpio_free(plat->power_gpio);
392
393	clk_disable(pltfm_host->clk);
394	clk_put(pltfm_host->clk);
395
396	sdhci_pltfm_free(pdev);
397
398	return 0;
399}
400
401static struct platform_driver sdhci_tegra_driver = {
402	.driver		= {
403		.name	= "sdhci-tegra",
404		.owner	= THIS_MODULE,
405		.of_match_table = sdhci_tegra_dt_match,
406		.pm	= SDHCI_PLTFM_PMOPS,
407	},
408	.probe		= sdhci_tegra_probe,
409	.remove		= __devexit_p(sdhci_tegra_remove),
410};
411
412module_platform_driver(sdhci_tegra_driver);
413
414MODULE_DESCRIPTION("SDHCI driver for Tegra");
415MODULE_AUTHOR("Google, Inc.");
416MODULE_LICENSE("GPL v2");