Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (39 commits)
  mmc: davinci: add support for SDIO irq handling
  mmc: fix division by zero in MMC core
  mmc: tmio_mmc: fix CMD irq handling
  mmc: tmio_mmc: handle missing HW interrupts
  mfd: sh_mobile_sdhi: activate SDIO IRQ for tmio_mmc
  mmc: tmio_mmc: implement SDIO IRQ support
  mfd: sdhi: require the tmio-mmc driver to bounce unaligned buffers
  mmc: tmio_mmc: silence compiler warnings
  mmc: tmio_mmc: implement a bounce buffer for unaligned DMA
  mmc: tmio_mmc: merge the private header into the driver
  mmc: tmio_mmc: fix PIO fallback on DMA descriptor allocation failure
  mmc: tmio_mmc: allow multi-element scatter-gather lists
  mmc: Register debugfs dir before calling card probe function.
  mmc: MMC_BLOCK_MINORS should depend on MMC_BLOCK.
  mmc: Explain why we make adjacent mmc_bus_{put,get} calls during rescan.
  mmc: Fix sd/sdio/mmc initialization frequency retries
  mmc: fix mmc_set_bus_width_ddr() call without bus-width-test cap
  mmc: dw_mmc: Add Synopsys DesignWare mmc host driver.
  mmc: add sdhci-tegra driver for Tegra SoCs
  mmc: sdhci: add quirk for max len ADMA descriptors
  ...
			
			
This commit is contained in:
		
						commit
						edb2877f4a
					
				| @ -770,7 +770,7 @@ static struct resource dove_sdio0_resources[] = { | ||||
| }; | ||||
| 
 | ||||
| static struct platform_device dove_sdio0 = { | ||||
| 	.name		= "sdhci-mv", | ||||
| 	.name		= "sdhci-dove", | ||||
| 	.id		= 0, | ||||
| 	.dev		= { | ||||
| 		.dma_mask		= &sdio_dmamask, | ||||
| @ -798,7 +798,7 @@ static struct resource dove_sdio1_resources[] = { | ||||
| }; | ||||
| 
 | ||||
| static struct platform_device dove_sdio1 = { | ||||
| 	.name		= "sdhci-mv", | ||||
| 	.name		= "sdhci-dove", | ||||
| 	.id		= 1, | ||||
| 	.dev		= { | ||||
| 		.dma_mask		= &sdio_dmamask, | ||||
|  | ||||
							
								
								
									
										29
									
								
								arch/arm/mach-tegra/include/mach/sdhci.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								arch/arm/mach-tegra/include/mach/sdhci.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| /*
 | ||||
|  * include/asm-arm/arch-tegra/include/mach/sdhci.h | ||||
|  * | ||||
|  * Copyright (C) 2009 Palm, Inc. | ||||
|  * Author: Yvonne Yip <y@palm.com> | ||||
|  * | ||||
|  * This software is licensed under the terms of the GNU General Public | ||||
|  * License version 2, as published by the Free Software Foundation, and | ||||
|  * may be copied, distributed, and modified under those terms. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  */ | ||||
| #ifndef __ASM_ARM_ARCH_TEGRA_SDHCI_H | ||||
| #define __ASM_ARM_ARCH_TEGRA_SDHCI_H | ||||
| 
 | ||||
| #include <linux/mmc/host.h> | ||||
| 
 | ||||
| struct tegra_sdhci_platform_data { | ||||
| 	int cd_gpio; | ||||
| 	int wp_gpio; | ||||
| 	int power_gpio; | ||||
| 	int is_8bit; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
| @ -131,11 +131,17 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) | ||||
| 	 */ | ||||
| 	mmc_data->flags |= TMIO_MMC_BLKSZ_2BYTES; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * All SDHI blocks support SDIO IRQ signalling. | ||||
| 	 */ | ||||
| 	mmc_data->flags |= TMIO_MMC_SDIO_IRQ; | ||||
| 
 | ||||
| 	if (p && p->dma_slave_tx >= 0 && p->dma_slave_rx >= 0) { | ||||
| 		priv->param_tx.slave_id = p->dma_slave_tx; | ||||
| 		priv->param_rx.slave_id = p->dma_slave_rx; | ||||
| 		priv->dma_priv.chan_priv_tx = &priv->param_tx; | ||||
| 		priv->dma_priv.chan_priv_rx = &priv->param_rx; | ||||
| 		priv->dma_priv.alignment_shift = 1; /* 2-byte alignment */ | ||||
| 		mmc_data->dma = &priv->dma_priv; | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -16,6 +16,7 @@ config MMC_BLOCK | ||||
| 
 | ||||
| config MMC_BLOCK_MINORS | ||||
| 	int "Number of minors per block device" | ||||
| 	depends on MMC_BLOCK | ||||
| 	range 4 256 | ||||
| 	default 8 | ||||
| 	help | ||||
|  | ||||
| @ -16,3 +16,14 @@ config MMC_UNSAFE_RESUME | ||||
| 
 | ||||
| 	  This option sets a default which can be overridden by the | ||||
| 	  module parameter "removable=0" or "removable=1". | ||||
| 
 | ||||
| config MMC_CLKGATE | ||||
| 	bool "MMC host clock gating (EXPERIMENTAL)" | ||||
| 	depends on EXPERIMENTAL | ||||
| 	help | ||||
| 	  This will attempt to aggressively gate the clock to the MMC card. | ||||
| 	  This is done to save power due to gating off the logic and bus | ||||
| 	  noise when the MMC card is not in use. Your host driver has to | ||||
| 	  support handling this in order for it to be of any use. | ||||
| 
 | ||||
| 	  If unsure, say N. | ||||
|  | ||||
| @ -303,14 +303,14 @@ int mmc_add_card(struct mmc_card *card) | ||||
| 			type, card->rca); | ||||
| 	} | ||||
| 
 | ||||
| 	ret = device_add(&card->dev); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| #ifdef CONFIG_DEBUG_FS | ||||
| 	mmc_add_card_debugfs(card); | ||||
| #endif | ||||
| 
 | ||||
| 	ret = device_add(&card->dev); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	mmc_card_set_present(card); | ||||
| 
 | ||||
| 	return 0; | ||||
|  | ||||
| @ -22,6 +22,7 @@ | ||||
| #include <linux/scatterlist.h> | ||||
| #include <linux/log2.h> | ||||
| #include <linux/regulator/consumer.h> | ||||
| #include <linux/pm_runtime.h> | ||||
| 
 | ||||
| #include <linux/mmc/card.h> | ||||
| #include <linux/mmc/host.h> | ||||
| @ -130,6 +131,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) | ||||
| 
 | ||||
| 		if (mrq->done) | ||||
| 			mrq->done(mrq); | ||||
| 
 | ||||
| 		mmc_host_clk_gate(host); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -190,6 +193,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) | ||||
| 			mrq->stop->mrq = mrq; | ||||
| 		} | ||||
| 	} | ||||
| 	mmc_host_clk_ungate(host); | ||||
| 	host->ops->request(host, mrq); | ||||
| } | ||||
| 
 | ||||
| @ -295,8 +299,9 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) | ||||
| 		unsigned int timeout_us, limit_us; | ||||
| 
 | ||||
| 		timeout_us = data->timeout_ns / 1000; | ||||
| 		timeout_us += data->timeout_clks * 1000 / | ||||
| 			(card->host->ios.clock / 1000); | ||||
| 		if (mmc_host_clk_rate(card->host)) | ||||
| 			timeout_us += data->timeout_clks * 1000 / | ||||
| 				(mmc_host_clk_rate(card->host) / 1000); | ||||
| 
 | ||||
| 		if (data->flags & MMC_DATA_WRITE) | ||||
| 			/*
 | ||||
| @ -614,6 +619,8 @@ static inline void mmc_set_ios(struct mmc_host *host) | ||||
| 		 ios->power_mode, ios->chip_select, ios->vdd, | ||||
| 		 ios->bus_width, ios->timing); | ||||
| 
 | ||||
| 	if (ios->clock > 0) | ||||
| 		mmc_set_ungated(host); | ||||
| 	host->ops->set_ios(host, ios); | ||||
| } | ||||
| 
 | ||||
| @ -641,6 +648,61 @@ void mmc_set_clock(struct mmc_host *host, unsigned int hz) | ||||
| 	mmc_set_ios(host); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_MMC_CLKGATE | ||||
| /*
 | ||||
|  * This gates the clock by setting it to 0 Hz. | ||||
|  */ | ||||
| void mmc_gate_clock(struct mmc_host *host) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&host->clk_lock, flags); | ||||
| 	host->clk_old = host->ios.clock; | ||||
| 	host->ios.clock = 0; | ||||
| 	host->clk_gated = true; | ||||
| 	spin_unlock_irqrestore(&host->clk_lock, flags); | ||||
| 	mmc_set_ios(host); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * This restores the clock from gating by using the cached | ||||
|  * clock value. | ||||
|  */ | ||||
| void mmc_ungate_clock(struct mmc_host *host) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * We should previously have gated the clock, so the clock shall | ||||
| 	 * be 0 here! The clock may however be 0 during initialization, | ||||
| 	 * when some request operations are performed before setting | ||||
| 	 * the frequency. When ungate is requested in that situation | ||||
| 	 * we just ignore the call. | ||||
| 	 */ | ||||
| 	if (host->clk_old) { | ||||
| 		BUG_ON(host->ios.clock); | ||||
| 		/* This call will also set host->clk_gated to false */ | ||||
| 		mmc_set_clock(host, host->clk_old); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void mmc_set_ungated(struct mmc_host *host) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We've been given a new frequency while the clock is gated, | ||||
| 	 * so make sure we regard this as ungating it. | ||||
| 	 */ | ||||
| 	spin_lock_irqsave(&host->clk_lock, flags); | ||||
| 	host->clk_gated = false; | ||||
| 	spin_unlock_irqrestore(&host->clk_lock, flags); | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| void mmc_set_ungated(struct mmc_host *host) | ||||
| { | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * Change the bus mode (open drain/push-pull) of a host. | ||||
|  */ | ||||
| @ -1424,35 +1486,57 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen) | ||||
| } | ||||
| EXPORT_SYMBOL(mmc_set_blocklen); | ||||
| 
 | ||||
| static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) | ||||
| { | ||||
| 	host->f_init = freq; | ||||
| 
 | ||||
| #ifdef CONFIG_MMC_DEBUG | ||||
| 	pr_info("%s: %s: trying to init card at %u Hz\n", | ||||
| 		mmc_hostname(host), __func__, host->f_init); | ||||
| #endif | ||||
| 	mmc_power_up(host); | ||||
| 	sdio_reset(host); | ||||
| 	mmc_go_idle(host); | ||||
| 
 | ||||
| 	mmc_send_if_cond(host, host->ocr_avail); | ||||
| 
 | ||||
| 	/* Order's important: probe SDIO, then SD, then MMC */ | ||||
| 	if (!mmc_attach_sdio(host)) | ||||
| 		return 0; | ||||
| 	if (!mmc_attach_sd(host)) | ||||
| 		return 0; | ||||
| 	if (!mmc_attach_mmc(host)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	mmc_power_off(host); | ||||
| 	return -EIO; | ||||
| } | ||||
| 
 | ||||
| void mmc_rescan(struct work_struct *work) | ||||
| { | ||||
| 	static const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; | ||||
| 	struct mmc_host *host = | ||||
| 		container_of(work, struct mmc_host, detect.work); | ||||
| 	u32 ocr; | ||||
| 	int err; | ||||
| 	unsigned long flags; | ||||
| 	int i; | ||||
| 	const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&host->lock, flags); | ||||
| 
 | ||||
| 	if (host->rescan_disable) { | ||||
| 		spin_unlock_irqrestore(&host->lock, flags); | ||||
| 	if (host->rescan_disable) | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&host->lock, flags); | ||||
| 
 | ||||
| 
 | ||||
| 	mmc_bus_get(host); | ||||
| 
 | ||||
| 	/* if there is a card registered, check whether it is still present */ | ||||
| 	if ((host->bus_ops != NULL) && host->bus_ops->detect && !host->bus_dead) | ||||
| 	/*
 | ||||
| 	 * if there is a _removable_ card registered, check whether it is | ||||
| 	 * still present | ||||
| 	 */ | ||||
| 	if (host->bus_ops && host->bus_ops->detect && !host->bus_dead | ||||
| 	    && mmc_card_is_removable(host)) | ||||
| 		host->bus_ops->detect(host); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Let mmc_bus_put() free the bus/bus_ops if we've found that | ||||
| 	 * the card is no longer present. | ||||
| 	 */ | ||||
| 	mmc_bus_put(host); | ||||
| 
 | ||||
| 
 | ||||
| 	mmc_bus_get(host); | ||||
| 
 | ||||
| 	/* if there still is a card present, stop here */ | ||||
| @ -1461,8 +1545,6 @@ void mmc_rescan(struct work_struct *work) | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	/* detect a newly inserted card */ | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Only we can add a new handler, so it's safe to | ||||
| 	 * release the lock here. | ||||
| @ -1472,72 +1554,16 @@ void mmc_rescan(struct work_struct *work) | ||||
| 	if (host->ops->get_cd && host->ops->get_cd(host) == 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	mmc_claim_host(host); | ||||
| 	for (i = 0; i < ARRAY_SIZE(freqs); i++) { | ||||
| 		mmc_claim_host(host); | ||||
| 
 | ||||
| 		if (freqs[i] >= host->f_min) | ||||
| 			host->f_init = freqs[i]; | ||||
| 		else if (!i || freqs[i-1] > host->f_min) | ||||
| 			host->f_init = host->f_min; | ||||
| 		else { | ||||
| 			mmc_release_host(host); | ||||
| 			goto out; | ||||
| 		} | ||||
| #ifdef CONFIG_MMC_DEBUG | ||||
| 		pr_info("%s: %s: trying to init card at %u Hz\n", | ||||
| 			mmc_hostname(host), __func__, host->f_init); | ||||
| #endif | ||||
| 		mmc_power_up(host); | ||||
| 		sdio_reset(host); | ||||
| 		mmc_go_idle(host); | ||||
| 
 | ||||
| 		mmc_send_if_cond(host, host->ocr_avail); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * First we search for SDIO... | ||||
| 		 */ | ||||
| 		err = mmc_send_io_op_cond(host, 0, &ocr); | ||||
| 		if (!err) { | ||||
| 			if (mmc_attach_sdio(host, ocr)) { | ||||
| 				mmc_claim_host(host); | ||||
| 				/*
 | ||||
| 				 * Try SDMEM (but not MMC) even if SDIO | ||||
| 				 * is broken. | ||||
| 				 */ | ||||
| 				if (mmc_send_app_op_cond(host, 0, &ocr)) | ||||
| 					goto out_fail; | ||||
| 
 | ||||
| 				if (mmc_attach_sd(host, ocr)) | ||||
| 					mmc_power_off(host); | ||||
| 			} | ||||
| 			goto out; | ||||
| 		} | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * ...then normal SD... | ||||
| 		 */ | ||||
| 		err = mmc_send_app_op_cond(host, 0, &ocr); | ||||
| 		if (!err) { | ||||
| 			if (mmc_attach_sd(host, ocr)) | ||||
| 				mmc_power_off(host); | ||||
| 			goto out; | ||||
| 		} | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * ...and finally MMC. | ||||
| 		 */ | ||||
| 		err = mmc_send_op_cond(host, 0, &ocr); | ||||
| 		if (!err) { | ||||
| 			if (mmc_attach_mmc(host, ocr)) | ||||
| 				mmc_power_off(host); | ||||
| 			goto out; | ||||
| 		} | ||||
| 
 | ||||
| out_fail: | ||||
| 		mmc_release_host(host); | ||||
| 		mmc_power_off(host); | ||||
| 		if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) | ||||
| 			break; | ||||
| 		if (freqs[i] < host->f_min) | ||||
| 			break; | ||||
| 	} | ||||
| out: | ||||
| 	mmc_release_host(host); | ||||
| 
 | ||||
|  out: | ||||
| 	if (host->caps & MMC_CAP_NEEDS_POLL) | ||||
| 		mmc_schedule_delayed_work(&host->detect, HZ); | ||||
| } | ||||
| @ -1721,6 +1747,18 @@ int mmc_resume_host(struct mmc_host *host) | ||||
| 		if (!(host->pm_flags & MMC_PM_KEEP_POWER)) { | ||||
| 			mmc_power_up(host); | ||||
| 			mmc_select_voltage(host, host->ocr); | ||||
| 			/*
 | ||||
| 			 * Tell runtime PM core we just powered up the card, | ||||
| 			 * since it still believes the card is powered off. | ||||
| 			 * Note that currently runtime PM is only enabled | ||||
| 			 * for SDIO cards that are MMC_CAP_POWER_OFF_CARD | ||||
| 			 */ | ||||
| 			if (mmc_card_sdio(host->card) && | ||||
| 			    (host->caps & MMC_CAP_POWER_OFF_CARD)) { | ||||
| 				pm_runtime_disable(&host->card->dev); | ||||
| 				pm_runtime_set_active(&host->card->dev); | ||||
| 				pm_runtime_enable(&host->card->dev); | ||||
| 			} | ||||
| 		} | ||||
| 		BUG_ON(!host->bus_ops->resume); | ||||
| 		err = host->bus_ops->resume(host); | ||||
|  | ||||
| @ -33,6 +33,9 @@ void mmc_init_erase(struct mmc_card *card); | ||||
| 
 | ||||
| void mmc_set_chip_select(struct mmc_host *host, int mode); | ||||
| void mmc_set_clock(struct mmc_host *host, unsigned int hz); | ||||
| void mmc_gate_clock(struct mmc_host *host); | ||||
| void mmc_ungate_clock(struct mmc_host *host); | ||||
| void mmc_set_ungated(struct mmc_host *host); | ||||
| void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); | ||||
| void mmc_set_bus_width(struct mmc_host *host, unsigned int width); | ||||
| void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width, | ||||
| @ -54,9 +57,9 @@ void mmc_rescan(struct work_struct *work); | ||||
| void mmc_start_host(struct mmc_host *host); | ||||
| void mmc_stop_host(struct mmc_host *host); | ||||
| 
 | ||||
| int mmc_attach_mmc(struct mmc_host *host, u32 ocr); | ||||
| int mmc_attach_sd(struct mmc_host *host, u32 ocr); | ||||
| int mmc_attach_sdio(struct mmc_host *host, u32 ocr); | ||||
| int mmc_attach_mmc(struct mmc_host *host); | ||||
| int mmc_attach_sd(struct mmc_host *host); | ||||
| int mmc_attach_sdio(struct mmc_host *host); | ||||
| 
 | ||||
| /* Module parameters */ | ||||
| extern int use_spi_crc; | ||||
|  | ||||
| @ -183,6 +183,11 @@ void mmc_add_host_debugfs(struct mmc_host *host) | ||||
| 			&mmc_clock_fops)) | ||||
| 		goto err_node; | ||||
| 
 | ||||
| #ifdef CONFIG_MMC_CLKGATE | ||||
| 	if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR), | ||||
| 				root, &host->clk_delay)) | ||||
| 		goto err_node; | ||||
| #endif | ||||
| 	return; | ||||
| 
 | ||||
| err_node: | ||||
|  | ||||
| @ -3,6 +3,7 @@ | ||||
|  * | ||||
|  *  Copyright (C) 2003 Russell King, All Rights Reserved. | ||||
|  *  Copyright (C) 2007-2008 Pierre Ossman | ||||
|  *  Copyright (C) 2010 Linus Walleij | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
| @ -20,6 +21,7 @@ | ||||
| #include <linux/suspend.h> | ||||
| 
 | ||||
| #include <linux/mmc/host.h> | ||||
| #include <linux/mmc/card.h> | ||||
| 
 | ||||
| #include "core.h" | ||||
| #include "host.h" | ||||
| @ -50,6 +52,205 @@ void mmc_unregister_host_class(void) | ||||
| static DEFINE_IDR(mmc_host_idr); | ||||
| static DEFINE_SPINLOCK(mmc_host_lock); | ||||
| 
 | ||||
| #ifdef CONFIG_MMC_CLKGATE | ||||
| 
 | ||||
| /*
 | ||||
|  * Enabling clock gating will make the core call out to the host | ||||
|  * once up and once down when it performs a request or card operation | ||||
|  * intermingled in any fashion. The driver will see this through | ||||
|  * set_ios() operations with ios.clock field set to 0 to gate (disable) | ||||
|  * the block clock, and to the old frequency to enable it again. | ||||
|  */ | ||||
| static void mmc_host_clk_gate_delayed(struct mmc_host *host) | ||||
| { | ||||
| 	unsigned long tick_ns; | ||||
| 	unsigned long freq = host->ios.clock; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	if (!freq) { | ||||
| 		pr_debug("%s: frequency set to 0 in disable function, " | ||||
| 			 "this means the clock is already disabled.\n", | ||||
| 			 mmc_hostname(host)); | ||||
| 		return; | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * New requests may have appeared while we were scheduling, | ||||
| 	 * then there is no reason to delay the check before | ||||
| 	 * clk_disable(). | ||||
| 	 */ | ||||
| 	spin_lock_irqsave(&host->clk_lock, flags); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Delay n bus cycles (at least 8 from MMC spec) before attempting | ||||
| 	 * to disable the MCI block clock. The reference count may have | ||||
| 	 * gone up again after this delay due to rescheduling! | ||||
| 	 */ | ||||
| 	if (!host->clk_requests) { | ||||
| 		spin_unlock_irqrestore(&host->clk_lock, flags); | ||||
| 		tick_ns = DIV_ROUND_UP(1000000000, freq); | ||||
| 		ndelay(host->clk_delay * tick_ns); | ||||
| 	} else { | ||||
| 		/* New users appeared while waiting for this work */ | ||||
| 		spin_unlock_irqrestore(&host->clk_lock, flags); | ||||
| 		return; | ||||
| 	} | ||||
| 	mutex_lock(&host->clk_gate_mutex); | ||||
| 	spin_lock_irqsave(&host->clk_lock, flags); | ||||
| 	if (!host->clk_requests) { | ||||
| 		spin_unlock_irqrestore(&host->clk_lock, flags); | ||||
| 		/* This will set host->ios.clock to 0 */ | ||||
| 		mmc_gate_clock(host); | ||||
| 		spin_lock_irqsave(&host->clk_lock, flags); | ||||
| 		pr_debug("%s: gated MCI clock\n", mmc_hostname(host)); | ||||
| 	} | ||||
| 	spin_unlock_irqrestore(&host->clk_lock, flags); | ||||
| 	mutex_unlock(&host->clk_gate_mutex); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Internal work. Work to disable the clock at some later point. | ||||
|  */ | ||||
| static void mmc_host_clk_gate_work(struct work_struct *work) | ||||
| { | ||||
| 	struct mmc_host *host = container_of(work, struct mmc_host, | ||||
| 					      clk_gate_work); | ||||
| 
 | ||||
| 	mmc_host_clk_gate_delayed(host); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	mmc_host_clk_ungate - ungate hardware MCI clocks | ||||
|  *	@host: host to ungate. | ||||
|  * | ||||
|  *	Makes sure the host ios.clock is restored to a non-zero value | ||||
|  *	past this call.	Increase clock reference count and ungate clock | ||||
|  *	if we're the first user. | ||||
|  */ | ||||
| void mmc_host_clk_ungate(struct mmc_host *host) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	mutex_lock(&host->clk_gate_mutex); | ||||
| 	spin_lock_irqsave(&host->clk_lock, flags); | ||||
| 	if (host->clk_gated) { | ||||
| 		spin_unlock_irqrestore(&host->clk_lock, flags); | ||||
| 		mmc_ungate_clock(host); | ||||
| 		spin_lock_irqsave(&host->clk_lock, flags); | ||||
| 		pr_debug("%s: ungated MCI clock\n", mmc_hostname(host)); | ||||
| 	} | ||||
| 	host->clk_requests++; | ||||
| 	spin_unlock_irqrestore(&host->clk_lock, flags); | ||||
| 	mutex_unlock(&host->clk_gate_mutex); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	mmc_host_may_gate_card - check if this card may be gated | ||||
|  *	@card: card to check. | ||||
|  */ | ||||
| static bool mmc_host_may_gate_card(struct mmc_card *card) | ||||
| { | ||||
| 	/* If there is no card we may gate it */ | ||||
| 	if (!card) | ||||
| 		return true; | ||||
| 	/*
 | ||||
| 	 * Don't gate SDIO cards! These need to be clocked at all times | ||||
| 	 * since they may be independent systems generating interrupts | ||||
| 	 * and other events. The clock requests counter from the core will | ||||
| 	 * go down to zero since the core does not need it, but we will not | ||||
| 	 * gate the clock, because there is somebody out there that may still | ||||
| 	 * be using it. | ||||
| 	 */ | ||||
| 	if (mmc_card_sdio(card)) | ||||
| 		return false; | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	mmc_host_clk_gate - gate off hardware MCI clocks | ||||
|  *	@host: host to gate. | ||||
|  * | ||||
|  *	Calls the host driver with ios.clock set to zero as often as possible | ||||
|  *	in order to gate off hardware MCI clocks. Decrease clock reference | ||||
|  *	count and schedule disabling of clock. | ||||
|  */ | ||||
| void mmc_host_clk_gate(struct mmc_host *host) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&host->clk_lock, flags); | ||||
| 	host->clk_requests--; | ||||
| 	if (mmc_host_may_gate_card(host->card) && | ||||
| 	    !host->clk_requests) | ||||
| 		schedule_work(&host->clk_gate_work); | ||||
| 	spin_unlock_irqrestore(&host->clk_lock, flags); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	mmc_host_clk_rate - get current clock frequency setting | ||||
|  *	@host: host to get the clock frequency for. | ||||
|  * | ||||
|  *	Returns current clock frequency regardless of gating. | ||||
|  */ | ||||
| unsigned int mmc_host_clk_rate(struct mmc_host *host) | ||||
| { | ||||
| 	unsigned long freq; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&host->clk_lock, flags); | ||||
| 	if (host->clk_gated) | ||||
| 		freq = host->clk_old; | ||||
| 	else | ||||
| 		freq = host->ios.clock; | ||||
| 	spin_unlock_irqrestore(&host->clk_lock, flags); | ||||
| 	return freq; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	mmc_host_clk_init - set up clock gating code | ||||
|  *	@host: host with potential clock to control | ||||
|  */ | ||||
| static inline void mmc_host_clk_init(struct mmc_host *host) | ||||
| { | ||||
| 	host->clk_requests = 0; | ||||
| 	/* Hold MCI clock for 8 cycles by default */ | ||||
| 	host->clk_delay = 8; | ||||
| 	host->clk_gated = false; | ||||
| 	INIT_WORK(&host->clk_gate_work, mmc_host_clk_gate_work); | ||||
| 	spin_lock_init(&host->clk_lock); | ||||
| 	mutex_init(&host->clk_gate_mutex); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	mmc_host_clk_exit - shut down clock gating code | ||||
|  *	@host: host with potential clock to control | ||||
|  */ | ||||
| static inline void mmc_host_clk_exit(struct mmc_host *host) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * Wait for any outstanding gate and then make sure we're | ||||
| 	 * ungated before exiting. | ||||
| 	 */ | ||||
| 	if (cancel_work_sync(&host->clk_gate_work)) | ||||
| 		mmc_host_clk_gate_delayed(host); | ||||
| 	if (host->clk_gated) | ||||
| 		mmc_host_clk_ungate(host); | ||||
| 	/* There should be only one user now */ | ||||
| 	WARN_ON(host->clk_requests > 1); | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| static inline void mmc_host_clk_init(struct mmc_host *host) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static inline void mmc_host_clk_exit(struct mmc_host *host) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  *	mmc_alloc_host - initialise the per-host structure. | ||||
|  *	@extra: sizeof private data structure | ||||
| @ -82,6 +283,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) | ||||
| 	host->class_dev.class = &mmc_host_class; | ||||
| 	device_initialize(&host->class_dev); | ||||
| 
 | ||||
| 	mmc_host_clk_init(host); | ||||
| 
 | ||||
| 	spin_lock_init(&host->lock); | ||||
| 	init_waitqueue_head(&host->wq); | ||||
| 	INIT_DELAYED_WORK(&host->detect, mmc_rescan); | ||||
| @ -163,6 +366,8 @@ void mmc_remove_host(struct mmc_host *host) | ||||
| 	device_del(&host->class_dev); | ||||
| 
 | ||||
| 	led_trigger_unregister_simple(host->led); | ||||
| 
 | ||||
| 	mmc_host_clk_exit(host); | ||||
| } | ||||
| 
 | ||||
| EXPORT_SYMBOL(mmc_remove_host); | ||||
| @ -183,4 +388,3 @@ void mmc_free_host(struct mmc_host *host) | ||||
| } | ||||
| 
 | ||||
| EXPORT_SYMBOL(mmc_free_host); | ||||
| 
 | ||||
|  | ||||
| @ -10,10 +10,31 @@ | ||||
|  */ | ||||
| #ifndef _MMC_CORE_HOST_H | ||||
| #define _MMC_CORE_HOST_H | ||||
| #include <linux/mmc/host.h> | ||||
| 
 | ||||
| int mmc_register_host_class(void); | ||||
| void mmc_unregister_host_class(void); | ||||
| 
 | ||||
| #ifdef CONFIG_MMC_CLKGATE | ||||
| void mmc_host_clk_ungate(struct mmc_host *host); | ||||
| void mmc_host_clk_gate(struct mmc_host *host); | ||||
| unsigned int mmc_host_clk_rate(struct mmc_host *host); | ||||
| 
 | ||||
| #else | ||||
| static inline void mmc_host_clk_ungate(struct mmc_host *host) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static inline void mmc_host_clk_gate(struct mmc_host *host) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static inline unsigned int mmc_host_clk_rate(struct mmc_host *host) | ||||
| { | ||||
| 	return host->ios.clock; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| void mmc_host_deeper_disable(struct work_struct *work); | ||||
| 
 | ||||
| #endif | ||||
|  | ||||
| @ -534,39 +534,57 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, | ||||
| 	 */ | ||||
| 	if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) && | ||||
| 	    (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { | ||||
| 		unsigned ext_csd_bit, bus_width; | ||||
| 		static unsigned ext_csd_bits[][2] = { | ||||
| 			{ EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 }, | ||||
| 			{ EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 }, | ||||
| 			{ EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 }, | ||||
| 		}; | ||||
| 		static unsigned bus_widths[] = { | ||||
| 			MMC_BUS_WIDTH_8, | ||||
| 			MMC_BUS_WIDTH_4, | ||||
| 			MMC_BUS_WIDTH_1 | ||||
| 		}; | ||||
| 		unsigned idx, bus_width = 0; | ||||
| 
 | ||||
| 		if (host->caps & MMC_CAP_8_BIT_DATA) { | ||||
| 			if (ddr) | ||||
| 				ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8; | ||||
| 			else | ||||
| 				ext_csd_bit = EXT_CSD_BUS_WIDTH_8; | ||||
| 			bus_width = MMC_BUS_WIDTH_8; | ||||
| 		} else { | ||||
| 			if (ddr) | ||||
| 				ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_4; | ||||
| 			else | ||||
| 				ext_csd_bit = EXT_CSD_BUS_WIDTH_4; | ||||
| 			bus_width = MMC_BUS_WIDTH_4; | ||||
| 		if (host->caps & MMC_CAP_8_BIT_DATA) | ||||
| 			idx = 0; | ||||
| 		else | ||||
| 			idx = 1; | ||||
| 		for (; idx < ARRAY_SIZE(bus_widths); idx++) { | ||||
| 			bus_width = bus_widths[idx]; | ||||
| 			if (bus_width == MMC_BUS_WIDTH_1) | ||||
| 				ddr = 0; /* no DDR for 1-bit width */ | ||||
| 			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, | ||||
| 					 EXT_CSD_BUS_WIDTH, | ||||
| 					 ext_csd_bits[idx][0]); | ||||
| 			if (!err) { | ||||
| 				mmc_set_bus_width_ddr(card->host, | ||||
| 						      bus_width, MMC_SDR_MODE); | ||||
| 				/*
 | ||||
| 				 * If controller can't handle bus width test, | ||||
| 				 * use the highest bus width to maintain | ||||
| 				 * compatibility with previous MMC behavior. | ||||
| 				 */ | ||||
| 				if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) | ||||
| 					break; | ||||
| 				err = mmc_bus_test(card, bus_width); | ||||
| 				if (!err) | ||||
| 					break; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, | ||||
| 				 EXT_CSD_BUS_WIDTH, ext_csd_bit); | ||||
| 
 | ||||
| 		if (err && err != -EBADMSG) | ||||
| 			goto free_card; | ||||
| 
 | ||||
| 		if (!err && ddr) { | ||||
| 			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, | ||||
| 					EXT_CSD_BUS_WIDTH, | ||||
| 					ext_csd_bits[idx][1]); | ||||
| 		} | ||||
| 		if (err) { | ||||
| 			printk(KERN_WARNING "%s: switch to bus width %d ddr %d " | ||||
| 			       "failed\n", mmc_hostname(card->host), | ||||
| 			       1 << bus_width, ddr); | ||||
| 			err = 0; | ||||
| 		} else { | ||||
| 			if (ddr) | ||||
| 				mmc_card_set_ddr_mode(card); | ||||
| 			else | ||||
| 				ddr = MMC_SDR_MODE; | ||||
| 
 | ||||
| 				"failed\n", mmc_hostname(card->host), | ||||
| 				1 << bus_width, ddr); | ||||
| 			goto free_card; | ||||
| 		} else if (ddr) { | ||||
| 			mmc_card_set_ddr_mode(card); | ||||
| 			mmc_set_bus_width_ddr(card->host, bus_width, ddr); | ||||
| 		} | ||||
| 	} | ||||
| @ -737,14 +755,21 @@ static void mmc_attach_bus_ops(struct mmc_host *host) | ||||
| /*
 | ||||
|  * Starting point for MMC card init. | ||||
|  */ | ||||
| int mmc_attach_mmc(struct mmc_host *host, u32 ocr) | ||||
| int mmc_attach_mmc(struct mmc_host *host) | ||||
| { | ||||
| 	int err; | ||||
| 	u32 ocr; | ||||
| 
 | ||||
| 	BUG_ON(!host); | ||||
| 	WARN_ON(!host->claimed); | ||||
| 
 | ||||
| 	err = mmc_send_op_cond(host, 0, &ocr); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	mmc_attach_bus_ops(host); | ||||
| 	if (host->ocr_avail_mmc) | ||||
| 		host->ocr_avail = host->ocr_avail_mmc; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We need to get OCR a different way for SPI. | ||||
| @ -784,20 +809,20 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	mmc_release_host(host); | ||||
| 
 | ||||
| 	err = mmc_add_card(host->card); | ||||
| 	mmc_claim_host(host); | ||||
| 	if (err) | ||||
| 		goto remove_card; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| remove_card: | ||||
| 	mmc_release_host(host); | ||||
| 	mmc_remove_card(host->card); | ||||
| 	host->card = NULL; | ||||
| 	mmc_claim_host(host); | ||||
| 	host->card = NULL; | ||||
| err: | ||||
| 	mmc_detach_bus(host); | ||||
| 	mmc_release_host(host); | ||||
| 
 | ||||
| 	printk(KERN_ERR "%s: error %d whilst initialising MMC card\n", | ||||
| 		mmc_hostname(host), err); | ||||
|  | ||||
| @ -462,3 +462,104 @@ int mmc_send_status(struct mmc_card *card, u32 *status) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, | ||||
| 		  u8 len) | ||||
| { | ||||
| 	struct mmc_request mrq; | ||||
| 	struct mmc_command cmd; | ||||
| 	struct mmc_data data; | ||||
| 	struct scatterlist sg; | ||||
| 	u8 *data_buf; | ||||
| 	u8 *test_buf; | ||||
| 	int i, err; | ||||
| 	static u8 testdata_8bit[8] = { 0x55, 0xaa, 0, 0, 0, 0, 0, 0 }; | ||||
| 	static u8 testdata_4bit[4] = { 0x5a, 0, 0, 0 }; | ||||
| 
 | ||||
| 	/* dma onto stack is unsafe/nonportable, but callers to this
 | ||||
| 	 * routine normally provide temporary on-stack buffers ... | ||||
| 	 */ | ||||
| 	data_buf = kmalloc(len, GFP_KERNEL); | ||||
| 	if (!data_buf) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	if (len == 8) | ||||
| 		test_buf = testdata_8bit; | ||||
| 	else if (len == 4) | ||||
| 		test_buf = testdata_4bit; | ||||
| 	else { | ||||
| 		printk(KERN_ERR "%s: Invalid bus_width %d\n", | ||||
| 		       mmc_hostname(host), len); | ||||
| 		kfree(data_buf); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (opcode == MMC_BUS_TEST_W) | ||||
| 		memcpy(data_buf, test_buf, len); | ||||
| 
 | ||||
| 	memset(&mrq, 0, sizeof(struct mmc_request)); | ||||
| 	memset(&cmd, 0, sizeof(struct mmc_command)); | ||||
| 	memset(&data, 0, sizeof(struct mmc_data)); | ||||
| 
 | ||||
| 	mrq.cmd = &cmd; | ||||
| 	mrq.data = &data; | ||||
| 	cmd.opcode = opcode; | ||||
| 	cmd.arg = 0; | ||||
| 
 | ||||
| 	/* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
 | ||||
| 	 * rely on callers to never use this with "native" calls for reading | ||||
| 	 * CSD or CID.  Native versions of those commands use the R2 type, | ||||
| 	 * not R1 plus a data block. | ||||
| 	 */ | ||||
| 	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; | ||||
| 
 | ||||
| 	data.blksz = len; | ||||
| 	data.blocks = 1; | ||||
| 	if (opcode == MMC_BUS_TEST_R) | ||||
| 		data.flags = MMC_DATA_READ; | ||||
| 	else | ||||
| 		data.flags = MMC_DATA_WRITE; | ||||
| 
 | ||||
| 	data.sg = &sg; | ||||
| 	data.sg_len = 1; | ||||
| 	sg_init_one(&sg, data_buf, len); | ||||
| 	mmc_wait_for_req(host, &mrq); | ||||
| 	err = 0; | ||||
| 	if (opcode == MMC_BUS_TEST_R) { | ||||
| 		for (i = 0; i < len / 4; i++) | ||||
| 			if ((test_buf[i] ^ data_buf[i]) != 0xff) { | ||||
| 				err = -EIO; | ||||
| 				break; | ||||
| 			} | ||||
| 	} | ||||
| 	kfree(data_buf); | ||||
| 
 | ||||
| 	if (cmd.error) | ||||
| 		return cmd.error; | ||||
| 	if (data.error) | ||||
| 		return data.error; | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| int mmc_bus_test(struct mmc_card *card, u8 bus_width) | ||||
| { | ||||
| 	int err, width; | ||||
| 
 | ||||
| 	if (bus_width == MMC_BUS_WIDTH_8) | ||||
| 		width = 8; | ||||
| 	else if (bus_width == MMC_BUS_WIDTH_4) | ||||
| 		width = 4; | ||||
| 	else if (bus_width == MMC_BUS_WIDTH_1) | ||||
| 		return 0; /* no need for test */ | ||||
| 	else | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Ignore errors from BUS_TEST_W.  BUS_TEST_R will fail if there | ||||
| 	 * is a problem.  This improves chances that the test will work. | ||||
| 	 */ | ||||
| 	mmc_send_bus_test(card, card->host, MMC_BUS_TEST_W, width); | ||||
| 	err = mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width); | ||||
| 	return err; | ||||
| } | ||||
|  | ||||
| @ -26,6 +26,7 @@ int mmc_send_cid(struct mmc_host *host, u32 *cid); | ||||
| int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); | ||||
| int mmc_spi_set_crc(struct mmc_host *host, int use_crc); | ||||
| int mmc_card_sleepawake(struct mmc_host *host, int sleep); | ||||
| int mmc_bus_test(struct mmc_card *card, u8 bus_width); | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
|  | ||||
| @ -764,14 +764,21 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host) | ||||
| /*
 | ||||
|  * Starting point for SD card init. | ||||
|  */ | ||||
| int mmc_attach_sd(struct mmc_host *host, u32 ocr) | ||||
| int mmc_attach_sd(struct mmc_host *host) | ||||
| { | ||||
| 	int err; | ||||
| 	u32 ocr; | ||||
| 
 | ||||
| 	BUG_ON(!host); | ||||
| 	WARN_ON(!host->claimed); | ||||
| 
 | ||||
| 	err = mmc_send_app_op_cond(host, 0, &ocr); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	mmc_sd_attach_bus_ops(host); | ||||
| 	if (host->ocr_avail_sd) | ||||
| 		host->ocr_avail = host->ocr_avail_sd; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We need to get OCR a different way for SPI. | ||||
| @ -795,7 +802,8 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) | ||||
| 		ocr &= ~0x7F; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ocr & MMC_VDD_165_195) { | ||||
| 	if ((ocr & MMC_VDD_165_195) && | ||||
| 	    !(host->ocr_avail_sd & MMC_VDD_165_195)) { | ||||
| 		printk(KERN_WARNING "%s: SD card claims to support the " | ||||
| 		       "incompletely defined 'low voltage range'. This " | ||||
| 		       "will be ignored.\n", mmc_hostname(host)); | ||||
| @ -820,20 +828,20 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	mmc_release_host(host); | ||||
| 
 | ||||
| 	err = mmc_add_card(host->card); | ||||
| 	mmc_claim_host(host); | ||||
| 	if (err) | ||||
| 		goto remove_card; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| remove_card: | ||||
| 	mmc_release_host(host); | ||||
| 	mmc_remove_card(host->card); | ||||
| 	host->card = NULL; | ||||
| 	mmc_claim_host(host); | ||||
| err: | ||||
| 	mmc_detach_bus(host); | ||||
| 	mmc_release_host(host); | ||||
| 
 | ||||
| 	printk(KERN_ERR "%s: error %d whilst initialising SD card\n", | ||||
| 		mmc_hostname(host), err); | ||||
|  | ||||
| @ -627,15 +627,27 @@ static int mmc_sdio_suspend(struct mmc_host *host) | ||||
| 
 | ||||
| static int mmc_sdio_resume(struct mmc_host *host) | ||||
| { | ||||
| 	int i, err; | ||||
| 	int i, err = 0; | ||||
| 
 | ||||
| 	BUG_ON(!host); | ||||
| 	BUG_ON(!host->card); | ||||
| 
 | ||||
| 	/* Basic card reinitialization. */ | ||||
| 	mmc_claim_host(host); | ||||
| 	err = mmc_sdio_init_card(host, host->ocr, host->card, | ||||
| 
 | ||||
| 	/* No need to reinitialize powered-resumed nonremovable cards */ | ||||
| 	if (mmc_card_is_removable(host) || !mmc_card_is_powered_resumed(host)) | ||||
| 		err = mmc_sdio_init_card(host, host->ocr, host->card, | ||||
| 				 (host->pm_flags & MMC_PM_KEEP_POWER)); | ||||
| 	else if (mmc_card_is_powered_resumed(host)) { | ||||
| 		/* We may have switched to 1-bit mode during suspend */ | ||||
| 		err = sdio_enable_4bit_bus(host->card); | ||||
| 		if (err > 0) { | ||||
| 			mmc_set_bus_width(host, MMC_BUS_WIDTH_4); | ||||
| 			err = 0; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (!err && host->sdio_irqs) | ||||
| 		mmc_signal_sdio_irq(host); | ||||
| 	mmc_release_host(host); | ||||
| @ -690,16 +702,22 @@ static const struct mmc_bus_ops mmc_sdio_ops = { | ||||
| /*
 | ||||
|  * Starting point for SDIO card init. | ||||
|  */ | ||||
| int mmc_attach_sdio(struct mmc_host *host, u32 ocr) | ||||
| int mmc_attach_sdio(struct mmc_host *host) | ||||
| { | ||||
| 	int err; | ||||
| 	int i, funcs; | ||||
| 	int err, i, funcs; | ||||
| 	u32 ocr; | ||||
| 	struct mmc_card *card; | ||||
| 
 | ||||
| 	BUG_ON(!host); | ||||
| 	WARN_ON(!host->claimed); | ||||
| 
 | ||||
| 	err = mmc_send_io_op_cond(host, 0, &ocr); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	mmc_attach_bus(host, &mmc_sdio_ops); | ||||
| 	if (host->ocr_avail_sdio) | ||||
| 		host->ocr_avail = host->ocr_avail_sdio; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Sanity check the voltages that the card claims to | ||||
| @ -769,12 +787,12 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) | ||||
| 			pm_runtime_enable(&card->sdio_func[i]->dev); | ||||
| 	} | ||||
| 
 | ||||
| 	mmc_release_host(host); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * First add the card to the driver model... | ||||
| 	 */ | ||||
| 	mmc_release_host(host); | ||||
| 	err = mmc_add_card(host->card); | ||||
| 	mmc_claim_host(host); | ||||
| 	if (err) | ||||
| 		goto remove_added; | ||||
| 
 | ||||
| @ -792,15 +810,17 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) | ||||
| 
 | ||||
| remove_added: | ||||
| 	/* Remove without lock if the device has been added. */ | ||||
| 	mmc_release_host(host); | ||||
| 	mmc_sdio_remove(host); | ||||
| 	mmc_claim_host(host); | ||||
| remove: | ||||
| 	/* And with lock if it hasn't been added. */ | ||||
| 	mmc_release_host(host); | ||||
| 	if (host->card) | ||||
| 		mmc_sdio_remove(host); | ||||
| 	mmc_claim_host(host); | ||||
| err: | ||||
| 	mmc_detach_bus(host); | ||||
| 	mmc_release_host(host); | ||||
| 
 | ||||
| 	printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n", | ||||
| 		mmc_hostname(host), err); | ||||
|  | ||||
| @ -197,44 +197,12 @@ out: | ||||
| 
 | ||||
| #ifdef CONFIG_PM_RUNTIME | ||||
| 
 | ||||
| static int sdio_bus_pm_prepare(struct device *dev) | ||||
| { | ||||
| 	struct sdio_func *func = dev_to_sdio_func(dev); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Resume an SDIO device which was suspended at run time at this | ||||
| 	 * point, in order to allow standard SDIO suspend/resume paths | ||||
| 	 * to keep working as usual. | ||||
| 	 * | ||||
| 	 * Ultimately, the SDIO driver itself will decide (in its | ||||
| 	 * suspend handler, or lack thereof) whether the card should be | ||||
| 	 * removed or kept, and if kept, at what power state. | ||||
| 	 * | ||||
| 	 * At this point, PM core have increased our use count, so it's | ||||
| 	 * safe to directly resume the device. After system is resumed | ||||
| 	 * again, PM core will drop back its runtime PM use count, and if | ||||
| 	 * needed device will be suspended again. | ||||
| 	 * | ||||
| 	 * The end result is guaranteed to be a power state that is | ||||
| 	 * coherent with the device's runtime PM use count. | ||||
| 	 * | ||||
| 	 * The return value of pm_runtime_resume is deliberately unchecked | ||||
| 	 * since there is little point in failing system suspend if a | ||||
| 	 * device can't be resumed. | ||||
| 	 */ | ||||
| 	if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) | ||||
| 		pm_runtime_resume(dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct dev_pm_ops sdio_bus_pm_ops = { | ||||
| 	SET_RUNTIME_PM_OPS( | ||||
| 		pm_generic_runtime_suspend, | ||||
| 		pm_generic_runtime_resume, | ||||
| 		pm_generic_runtime_idle | ||||
| 	) | ||||
| 	.prepare = sdio_bus_pm_prepare, | ||||
| }; | ||||
| 
 | ||||
| #define SDIO_PM_OPS_PTR	(&sdio_bus_pm_ops) | ||||
|  | ||||
| @ -142,6 +142,27 @@ config MMC_SDHCI_ESDHC_IMX | ||||
| 
 | ||||
| 	  If unsure, say N. | ||||
| 
 | ||||
| config MMC_SDHCI_DOVE | ||||
| 	bool "SDHCI support on Marvell's Dove SoC" | ||||
| 	depends on ARCH_DOVE | ||||
| 	depends on MMC_SDHCI_PLTFM | ||||
| 	select MMC_SDHCI_IO_ACCESSORS | ||||
| 	help | ||||
| 	  This selects the Secure Digital Host Controller Interface in | ||||
| 	  Marvell's Dove SoC. | ||||
| 
 | ||||
| 	  If unsure, say N. | ||||
| 
 | ||||
| config MMC_SDHCI_TEGRA | ||||
| 	tristate "SDHCI platform support for the Tegra SD/MMC Controller" | ||||
| 	depends on MMC_SDHCI_PLTFM && ARCH_TEGRA | ||||
| 	select MMC_SDHCI_IO_ACCESSORS | ||||
| 	help | ||||
| 	  This selects the Tegra SD/MMC controller. If you have a Tegra | ||||
| 	  platform with SD or MMC devices, say Y or M here. | ||||
| 
 | ||||
| 	  If unsure, say N. | ||||
| 
 | ||||
| config MMC_SDHCI_S3C | ||||
| 	tristate "SDHCI support on Samsung S3C SoC" | ||||
| 	depends on MMC_SDHCI && PLAT_SAMSUNG | ||||
| @ -460,6 +481,22 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND | ||||
| 	help | ||||
| 	  If you say yes here SD-Cards may work on the EZkit. | ||||
| 
 | ||||
| config MMC_DW | ||||
| 	tristate "Synopsys DesignWare Memory Card Interface" | ||||
| 	depends on ARM | ||||
| 	help | ||||
| 	  This selects support for the Synopsys DesignWare Mobile Storage IP | ||||
| 	  block, this provides host support for SD and MMC interfaces, in both | ||||
| 	  PIO and external DMA modes. | ||||
| 
 | ||||
| config MMC_DW_IDMAC | ||||
| 	bool "Internal DMAC interface" | ||||
| 	depends on MMC_DW | ||||
| 	help | ||||
| 	  This selects support for the internal DMAC block within the Synopsys | ||||
| 	  Designware Mobile Storage IP block. This disables the external DMA | ||||
| 	  interface. | ||||
| 
 | ||||
| config MMC_SH_MMCIF | ||||
| 	tristate "SuperH Internal MMCIF support" | ||||
| 	depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE) | ||||
|  | ||||
| @ -31,6 +31,7 @@ obj-$(CONFIG_MMC_TMIO)		+= tmio_mmc.o | ||||
| obj-$(CONFIG_MMC_CB710)	+= cb710-mmc.o | ||||
| obj-$(CONFIG_MMC_VIA_SDMMC)	+= via-sdmmc.o | ||||
| obj-$(CONFIG_SDH_BFIN)		+= bfin_sdh.o | ||||
| obj-$(CONFIG_MMC_DW)		+= dw_mmc.o | ||||
| obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o | ||||
| obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o | ||||
| obj-$(CONFIG_MMC_USHC)		+= ushc.o | ||||
| @ -39,6 +40,8 @@ obj-$(CONFIG_MMC_SDHCI_PLTFM)			+= sdhci-platform.o | ||||
| sdhci-platform-y				:= sdhci-pltfm.o | ||||
| sdhci-platform-$(CONFIG_MMC_SDHCI_CNS3XXX)	+= sdhci-cns3xxx.o | ||||
| sdhci-platform-$(CONFIG_MMC_SDHCI_ESDHC_IMX)	+= sdhci-esdhc-imx.o | ||||
| sdhci-platform-$(CONFIG_MMC_SDHCI_DOVE)		+= sdhci-dove.o | ||||
| sdhci-platform-$(CONFIG_MMC_SDHCI_TEGRA)	+= sdhci-tegra.o | ||||
| 
 | ||||
| obj-$(CONFIG_MMC_SDHCI_OF)	+= sdhci-of.o | ||||
| sdhci-of-y				:= sdhci-of-core.o | ||||
|  | ||||
| @ -66,8 +66,8 @@ | ||||
| #define DAVINCI_MMCBLNC      0x60 | ||||
| #define DAVINCI_SDIOCTL      0x64 | ||||
| #define DAVINCI_SDIOST0      0x68 | ||||
| #define DAVINCI_SDIOEN       0x6C | ||||
| #define DAVINCI_SDIOST       0x70 | ||||
| #define DAVINCI_SDIOIEN      0x6C | ||||
| #define DAVINCI_SDIOIST      0x70 | ||||
| #define DAVINCI_MMCFIFOCTL   0x74 /* FIFO Control Register             */ | ||||
| 
 | ||||
| /* DAVINCI_MMCCTL definitions */ | ||||
| @ -131,6 +131,14 @@ | ||||
| #define MMCFIFOCTL_ACCWD_2    (2 << 3) /* access width of 2 bytes    */ | ||||
| #define MMCFIFOCTL_ACCWD_1    (3 << 3) /* access width of 1 byte     */ | ||||
| 
 | ||||
| /* DAVINCI_SDIOST0 definitions */ | ||||
| #define SDIOST0_DAT1_HI       BIT(0) | ||||
| 
 | ||||
| /* DAVINCI_SDIOIEN definitions */ | ||||
| #define SDIOIEN_IOINTEN       BIT(0) | ||||
| 
 | ||||
| /* DAVINCI_SDIOIST definitions */ | ||||
| #define SDIOIST_IOINT         BIT(0) | ||||
| 
 | ||||
| /* MMCSD Init clock in Hz in opendrain mode */ | ||||
| #define MMCSD_INIT_CLOCK		200000 | ||||
| @ -164,7 +172,7 @@ struct mmc_davinci_host { | ||||
| 	unsigned int mmc_input_clk; | ||||
| 	void __iomem *base; | ||||
| 	struct resource *mem_res; | ||||
| 	int irq; | ||||
| 	int mmc_irq, sdio_irq; | ||||
| 	unsigned char bus_mode; | ||||
| 
 | ||||
| #define DAVINCI_MMC_DATADIR_NONE	0 | ||||
| @ -184,6 +192,7 @@ struct mmc_davinci_host { | ||||
| 	u32 rxdma, txdma; | ||||
| 	bool use_dma; | ||||
| 	bool do_dma; | ||||
| 	bool sdio_int; | ||||
| 
 | ||||
| 	/* Scatterlist DMA uses one or more parameter RAM entries:
 | ||||
| 	 * the main one (associated with rxdma or txdma) plus zero or | ||||
| @ -480,7 +489,7 @@ static void mmc_davinci_send_dma_request(struct mmc_davinci_host *host, | ||||
| 	struct scatterlist	*sg; | ||||
| 	unsigned		sg_len; | ||||
| 	unsigned		bytes_left = host->bytes_left; | ||||
| 	const unsigned		shift = ffs(rw_threshold) - 1;; | ||||
| 	const unsigned		shift = ffs(rw_threshold) - 1; | ||||
| 
 | ||||
| 	if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) { | ||||
| 		template = &host->tx_template; | ||||
| @ -866,6 +875,19 @@ mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data) | ||||
| { | ||||
| 	host->data = NULL; | ||||
| 
 | ||||
| 	if (host->mmc->caps & MMC_CAP_SDIO_IRQ) { | ||||
| 		/*
 | ||||
| 		 * SDIO Interrupt Detection work-around as suggested by | ||||
| 		 * Davinci Errata (TMS320DM355 Silicon Revision 1.1 Errata | ||||
| 		 * 2.1.6): Signal SDIO interrupt only if it is enabled by core | ||||
| 		 */ | ||||
| 		if (host->sdio_int && !(readl(host->base + DAVINCI_SDIOST0) & | ||||
| 					SDIOST0_DAT1_HI)) { | ||||
| 			writel(SDIOIST_IOINT, host->base + DAVINCI_SDIOIST); | ||||
| 			mmc_signal_sdio_irq(host->mmc); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (host->do_dma) { | ||||
| 		davinci_abort_dma(host); | ||||
| 
 | ||||
| @ -932,6 +954,21 @@ davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data) | ||||
| 	mmc_davinci_reset_ctrl(host, 0); | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t mmc_davinci_sdio_irq(int irq, void *dev_id) | ||||
| { | ||||
| 	struct mmc_davinci_host *host = dev_id; | ||||
| 	unsigned int status; | ||||
| 
 | ||||
| 	status = readl(host->base + DAVINCI_SDIOIST); | ||||
| 	if (status & SDIOIST_IOINT) { | ||||
| 		dev_dbg(mmc_dev(host->mmc), | ||||
| 			"SDIO interrupt status %x\n", status); | ||||
| 		writel(status | SDIOIST_IOINT, host->base + DAVINCI_SDIOIST); | ||||
| 		mmc_signal_sdio_irq(host->mmc); | ||||
| 	} | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) | ||||
| { | ||||
| 	struct mmc_davinci_host *host = (struct mmc_davinci_host *)dev_id; | ||||
| @ -1076,11 +1113,32 @@ static int mmc_davinci_get_ro(struct mmc_host *mmc) | ||||
| 	return config->get_ro(pdev->id); | ||||
| } | ||||
| 
 | ||||
| static void mmc_davinci_enable_sdio_irq(struct mmc_host *mmc, int enable) | ||||
| { | ||||
| 	struct mmc_davinci_host *host = mmc_priv(mmc); | ||||
| 
 | ||||
| 	if (enable) { | ||||
| 		if (!(readl(host->base + DAVINCI_SDIOST0) & SDIOST0_DAT1_HI)) { | ||||
| 			writel(SDIOIST_IOINT, host->base + DAVINCI_SDIOIST); | ||||
| 			mmc_signal_sdio_irq(host->mmc); | ||||
| 		} else { | ||||
| 			host->sdio_int = true; | ||||
| 			writel(readl(host->base + DAVINCI_SDIOIEN) | | ||||
| 			       SDIOIEN_IOINTEN, host->base + DAVINCI_SDIOIEN); | ||||
| 		} | ||||
| 	} else { | ||||
| 		host->sdio_int = false; | ||||
| 		writel(readl(host->base + DAVINCI_SDIOIEN) & ~SDIOIEN_IOINTEN, | ||||
| 		       host->base + DAVINCI_SDIOIEN); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static struct mmc_host_ops mmc_davinci_ops = { | ||||
| 	.request	= mmc_davinci_request, | ||||
| 	.set_ios	= mmc_davinci_set_ios, | ||||
| 	.get_cd		= mmc_davinci_get_cd, | ||||
| 	.get_ro		= mmc_davinci_get_ro, | ||||
| 	.enable_sdio_irq = mmc_davinci_enable_sdio_irq, | ||||
| }; | ||||
| 
 | ||||
| /*----------------------------------------------------------------------*/ | ||||
| @ -1209,7 +1267,8 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) | ||||
| 		host->nr_sg = MAX_NR_SG; | ||||
| 
 | ||||
| 	host->use_dma = use_dma; | ||||
| 	host->irq = irq; | ||||
| 	host->mmc_irq = irq; | ||||
| 	host->sdio_irq = platform_get_irq(pdev, 1); | ||||
| 
 | ||||
| 	if (host->use_dma && davinci_acquire_dma_channels(host) != 0) | ||||
| 		host->use_dma = 0; | ||||
| @ -1270,6 +1329,13 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) | ||||
| 	if (ret) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	if (host->sdio_irq >= 0) { | ||||
| 		ret = request_irq(host->sdio_irq, mmc_davinci_sdio_irq, 0, | ||||
| 				  mmc_hostname(mmc), host); | ||||
| 		if (!ret) | ||||
| 			mmc->caps |= MMC_CAP_SDIO_IRQ; | ||||
| 	} | ||||
| 
 | ||||
| 	rename_region(mem, mmc_hostname(mmc)); | ||||
| 
 | ||||
| 	dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n", | ||||
| @ -1313,7 +1379,9 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev) | ||||
| 		mmc_davinci_cpufreq_deregister(host); | ||||
| 
 | ||||
| 		mmc_remove_host(host->mmc); | ||||
| 		free_irq(host->irq, host); | ||||
| 		free_irq(host->mmc_irq, host); | ||||
| 		if (host->mmc->caps & MMC_CAP_SDIO_IRQ) | ||||
| 			free_irq(host->sdio_irq, host); | ||||
| 
 | ||||
| 		davinci_release_dma_channels(host); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										1796
									
								
								drivers/mmc/host/dw_mmc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1796
									
								
								drivers/mmc/host/dw_mmc.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										168
									
								
								drivers/mmc/host/dw_mmc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								drivers/mmc/host/dw_mmc.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,168 @@ | ||||
| /*
 | ||||
|  * Synopsys DesignWare Multimedia Card Interface driver | ||||
|  *  (Based on NXP driver for lpc 31xx) | ||||
|  * | ||||
|  * Copyright (C) 2009 NXP Semiconductors | ||||
|  * Copyright (C) 2009, 2010 Imagination Technologies Ltd. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _DW_MMC_H_ | ||||
| #define _DW_MMC_H_ | ||||
| 
 | ||||
| #define SDMMC_CTRL		0x000 | ||||
| #define SDMMC_PWREN		0x004 | ||||
| #define SDMMC_CLKDIV		0x008 | ||||
| #define SDMMC_CLKSRC		0x00c | ||||
| #define SDMMC_CLKENA		0x010 | ||||
| #define SDMMC_TMOUT		0x014 | ||||
| #define SDMMC_CTYPE		0x018 | ||||
| #define SDMMC_BLKSIZ		0x01c | ||||
| #define SDMMC_BYTCNT		0x020 | ||||
| #define SDMMC_INTMASK		0x024 | ||||
| #define SDMMC_CMDARG		0x028 | ||||
| #define SDMMC_CMD		0x02c | ||||
| #define SDMMC_RESP0		0x030 | ||||
| #define SDMMC_RESP1		0x034 | ||||
| #define SDMMC_RESP2		0x038 | ||||
| #define SDMMC_RESP3		0x03c | ||||
| #define SDMMC_MINTSTS		0x040 | ||||
| #define SDMMC_RINTSTS		0x044 | ||||
| #define SDMMC_STATUS		0x048 | ||||
| #define SDMMC_FIFOTH		0x04c | ||||
| #define SDMMC_CDETECT		0x050 | ||||
| #define SDMMC_WRTPRT		0x054 | ||||
| #define SDMMC_GPIO		0x058 | ||||
| #define SDMMC_TCBCNT		0x05c | ||||
| #define SDMMC_TBBCNT		0x060 | ||||
| #define SDMMC_DEBNCE		0x064 | ||||
| #define SDMMC_USRID		0x068 | ||||
| #define SDMMC_VERID		0x06c | ||||
| #define SDMMC_HCON		0x070 | ||||
| #define SDMMC_BMOD		0x080 | ||||
| #define SDMMC_PLDMND		0x084 | ||||
| #define SDMMC_DBADDR		0x088 | ||||
| #define SDMMC_IDSTS		0x08c | ||||
| #define SDMMC_IDINTEN		0x090 | ||||
| #define SDMMC_DSCADDR		0x094 | ||||
| #define SDMMC_BUFADDR		0x098 | ||||
| #define SDMMC_DATA		0x100 | ||||
| #define SDMMC_DATA_ADR		0x100 | ||||
| 
 | ||||
| /* shift bit field */ | ||||
| #define _SBF(f, v)		((v) << (f)) | ||||
| 
 | ||||
| /* Control register defines */ | ||||
| #define SDMMC_CTRL_USE_IDMAC		BIT(25) | ||||
| #define SDMMC_CTRL_CEATA_INT_EN		BIT(11) | ||||
| #define SDMMC_CTRL_SEND_AS_CCSD		BIT(10) | ||||
| #define SDMMC_CTRL_SEND_CCSD		BIT(9) | ||||
| #define SDMMC_CTRL_ABRT_READ_DATA	BIT(8) | ||||
| #define SDMMC_CTRL_SEND_IRQ_RESP	BIT(7) | ||||
| #define SDMMC_CTRL_READ_WAIT		BIT(6) | ||||
| #define SDMMC_CTRL_DMA_ENABLE		BIT(5) | ||||
| #define SDMMC_CTRL_INT_ENABLE		BIT(4) | ||||
| #define SDMMC_CTRL_DMA_RESET		BIT(2) | ||||
| #define SDMMC_CTRL_FIFO_RESET		BIT(1) | ||||
| #define SDMMC_CTRL_RESET		BIT(0) | ||||
| /* Clock Enable register defines */ | ||||
| #define SDMMC_CLKEN_LOW_PWR		BIT(16) | ||||
| #define SDMMC_CLKEN_ENABLE		BIT(0) | ||||
| /* time-out register defines */ | ||||
| #define SDMMC_TMOUT_DATA(n)		_SBF(8, (n)) | ||||
| #define SDMMC_TMOUT_DATA_MSK		0xFFFFFF00 | ||||
| #define SDMMC_TMOUT_RESP(n)		((n) & 0xFF) | ||||
| #define SDMMC_TMOUT_RESP_MSK		0xFF | ||||
| /* card-type register defines */ | ||||
| #define SDMMC_CTYPE_8BIT		BIT(16) | ||||
| #define SDMMC_CTYPE_4BIT		BIT(0) | ||||
| #define SDMMC_CTYPE_1BIT		0 | ||||
| /* Interrupt status & mask register defines */ | ||||
| #define SDMMC_INT_SDIO			BIT(16) | ||||
| #define SDMMC_INT_EBE			BIT(15) | ||||
| #define SDMMC_INT_ACD			BIT(14) | ||||
| #define SDMMC_INT_SBE			BIT(13) | ||||
| #define SDMMC_INT_HLE			BIT(12) | ||||
| #define SDMMC_INT_FRUN			BIT(11) | ||||
| #define SDMMC_INT_HTO			BIT(10) | ||||
| #define SDMMC_INT_DTO			BIT(9) | ||||
| #define SDMMC_INT_RTO			BIT(8) | ||||
| #define SDMMC_INT_DCRC			BIT(7) | ||||
| #define SDMMC_INT_RCRC			BIT(6) | ||||
| #define SDMMC_INT_RXDR			BIT(5) | ||||
| #define SDMMC_INT_TXDR			BIT(4) | ||||
| #define SDMMC_INT_DATA_OVER		BIT(3) | ||||
| #define SDMMC_INT_CMD_DONE		BIT(2) | ||||
| #define SDMMC_INT_RESP_ERR		BIT(1) | ||||
| #define SDMMC_INT_CD			BIT(0) | ||||
| #define SDMMC_INT_ERROR			0xbfc2 | ||||
| /* Command register defines */ | ||||
| #define SDMMC_CMD_START			BIT(31) | ||||
| #define SDMMC_CMD_CCS_EXP		BIT(23) | ||||
| #define SDMMC_CMD_CEATA_RD		BIT(22) | ||||
| #define SDMMC_CMD_UPD_CLK		BIT(21) | ||||
| #define SDMMC_CMD_INIT			BIT(15) | ||||
| #define SDMMC_CMD_STOP			BIT(14) | ||||
| #define SDMMC_CMD_PRV_DAT_WAIT		BIT(13) | ||||
| #define SDMMC_CMD_SEND_STOP		BIT(12) | ||||
| #define SDMMC_CMD_STRM_MODE		BIT(11) | ||||
| #define SDMMC_CMD_DAT_WR		BIT(10) | ||||
| #define SDMMC_CMD_DAT_EXP		BIT(9) | ||||
| #define SDMMC_CMD_RESP_CRC		BIT(8) | ||||
| #define SDMMC_CMD_RESP_LONG		BIT(7) | ||||
| #define SDMMC_CMD_RESP_EXP		BIT(6) | ||||
| #define SDMMC_CMD_INDX(n)		((n) & 0x1F) | ||||
| /* Status register defines */ | ||||
| #define SDMMC_GET_FCNT(x)		(((x)>>17) & 0x1FF) | ||||
| #define SDMMC_FIFO_SZ			32 | ||||
| /* Internal DMAC interrupt defines */ | ||||
| #define SDMMC_IDMAC_INT_AI		BIT(9) | ||||
| #define SDMMC_IDMAC_INT_NI		BIT(8) | ||||
| #define SDMMC_IDMAC_INT_CES		BIT(5) | ||||
| #define SDMMC_IDMAC_INT_DU		BIT(4) | ||||
| #define SDMMC_IDMAC_INT_FBE		BIT(2) | ||||
| #define SDMMC_IDMAC_INT_RI		BIT(1) | ||||
| #define SDMMC_IDMAC_INT_TI		BIT(0) | ||||
| /* Internal DMAC bus mode bits */ | ||||
| #define SDMMC_IDMAC_ENABLE		BIT(7) | ||||
| #define SDMMC_IDMAC_FB			BIT(1) | ||||
| #define SDMMC_IDMAC_SWRESET		BIT(0) | ||||
| 
 | ||||
| /* Register access macros */ | ||||
| #define mci_readl(dev, reg)			\ | ||||
| 	__raw_readl(dev->regs + SDMMC_##reg) | ||||
| #define mci_writel(dev, reg, value)			\ | ||||
| 	__raw_writel((value), dev->regs + SDMMC_##reg) | ||||
| 
 | ||||
| /* 16-bit FIFO access macros */ | ||||
| #define mci_readw(dev, reg)			\ | ||||
| 	__raw_readw(dev->regs + SDMMC_##reg) | ||||
| #define mci_writew(dev, reg, value)			\ | ||||
| 	__raw_writew((value), dev->regs + SDMMC_##reg) | ||||
| 
 | ||||
| /* 64-bit FIFO access macros */ | ||||
| #ifdef readq | ||||
| #define mci_readq(dev, reg)			\ | ||||
| 	__raw_readq(dev->regs + SDMMC_##reg) | ||||
| #define mci_writeq(dev, reg, value)			\ | ||||
| 	__raw_writeq((value), dev->regs + SDMMC_##reg) | ||||
| #else | ||||
| /*
 | ||||
|  * Dummy readq implementation for architectures that don't define it. | ||||
|  * | ||||
|  * We would assume that none of these architectures would configure | ||||
|  * the IP block with a 64bit FIFO width, so this code will never be | ||||
|  * executed on those machines. Defining these macros here keeps the | ||||
|  * rest of the code free from ifdefs. | ||||
|  */ | ||||
| #define mci_readq(dev, reg)			\ | ||||
| 	(*(volatile u64 __force *)(dev->regs + SDMMC_##reg)) | ||||
| #define mci_writeq(dev, reg, value)			\ | ||||
| 	(*(volatile u64 __force *)(dev->regs + SDMMC_##reg) = value) | ||||
| #endif | ||||
| 
 | ||||
| #endif /* _DW_MMC_H_ */ | ||||
| @ -31,6 +31,7 @@ | ||||
| #include <linux/clk.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/gpio.h> | ||||
| #include <linux/regulator/consumer.h> | ||||
| 
 | ||||
| #include <asm/dma.h> | ||||
| #include <asm/irq.h> | ||||
| @ -141,10 +142,49 @@ struct mxcmci_host { | ||||
| 
 | ||||
| 	struct work_struct	datawork; | ||||
| 	spinlock_t		lock; | ||||
| 
 | ||||
| 	struct regulator	*vcc; | ||||
| }; | ||||
| 
 | ||||
| static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios); | ||||
| 
 | ||||
| static inline void mxcmci_init_ocr(struct mxcmci_host *host) | ||||
| { | ||||
| 	host->vcc = regulator_get(mmc_dev(host->mmc), "vmmc"); | ||||
| 
 | ||||
| 	if (IS_ERR(host->vcc)) { | ||||
| 		host->vcc = NULL; | ||||
| 	} else { | ||||
| 		host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc); | ||||
| 		if (host->pdata && host->pdata->ocr_avail) | ||||
| 			dev_warn(mmc_dev(host->mmc), | ||||
| 				"pdata->ocr_avail will not be used\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	if (host->vcc == NULL) { | ||||
| 		/* fall-back to platform data */ | ||||
| 		if (host->pdata && host->pdata->ocr_avail) | ||||
| 			host->mmc->ocr_avail = host->pdata->ocr_avail; | ||||
| 		else | ||||
| 			host->mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline void mxcmci_set_power(struct mxcmci_host *host, | ||||
| 				    unsigned char power_mode, | ||||
| 				    unsigned int vdd) | ||||
| { | ||||
| 	if (host->vcc) { | ||||
| 		if (power_mode == MMC_POWER_UP) | ||||
| 			mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); | ||||
| 		else if (power_mode == MMC_POWER_OFF) | ||||
| 			mmc_regulator_set_ocr(host->mmc, host->vcc, 0); | ||||
| 	} | ||||
| 
 | ||||
| 	if (host->pdata && host->pdata->setpower) | ||||
| 		host->pdata->setpower(mmc_dev(host->mmc), vdd); | ||||
| } | ||||
| 
 | ||||
| static inline int mxcmci_use_dma(struct mxcmci_host *host) | ||||
| { | ||||
| 	return host->do_dma; | ||||
| @ -680,9 +720,9 @@ static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | ||||
| 		host->cmdat &= ~CMD_DAT_CONT_BUS_WIDTH_4; | ||||
| 
 | ||||
| 	if (host->power_mode != ios->power_mode) { | ||||
| 		if (host->pdata && host->pdata->setpower) | ||||
| 			host->pdata->setpower(mmc_dev(mmc), ios->vdd); | ||||
| 		mxcmci_set_power(host, ios->power_mode, ios->vdd); | ||||
| 		host->power_mode = ios->power_mode; | ||||
| 
 | ||||
| 		if (ios->power_mode == MMC_POWER_ON) | ||||
| 			host->cmdat |= CMD_DAT_CONT_INIT; | ||||
| 	} | ||||
| @ -807,10 +847,7 @@ static int mxcmci_probe(struct platform_device *pdev) | ||||
| 	host->pdata = pdev->dev.platform_data; | ||||
| 	spin_lock_init(&host->lock); | ||||
| 
 | ||||
| 	if (host->pdata && host->pdata->ocr_avail) | ||||
| 		mmc->ocr_avail = host->pdata->ocr_avail; | ||||
| 	else | ||||
| 		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; | ||||
| 	mxcmci_init_ocr(host); | ||||
| 
 | ||||
| 	if (host->pdata && host->pdata->dat3_card_detect) | ||||
| 		host->default_irq_mask = | ||||
| @ -915,6 +952,9 @@ static int mxcmci_remove(struct platform_device *pdev) | ||||
| 
 | ||||
| 	mmc_remove_host(mmc); | ||||
| 
 | ||||
| 	if (host->vcc) | ||||
| 		regulator_put(host->vcc); | ||||
| 
 | ||||
| 	if (host->pdata && host->pdata->exit) | ||||
| 		host->pdata->exit(&pdev->dev, mmc); | ||||
| 
 | ||||
| @ -927,7 +967,6 @@ static int mxcmci_remove(struct platform_device *pdev) | ||||
| 	clk_put(host->clk); | ||||
| 
 | ||||
| 	release_mem_region(host->res->start, resource_size(host->res)); | ||||
| 	release_resource(host->res); | ||||
| 
 | ||||
| 	mmc_free_host(mmc); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										70
									
								
								drivers/mmc/host/sdhci-dove.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								drivers/mmc/host/sdhci-dove.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| /*
 | ||||
|  * sdhci-dove.c Support for SDHCI on Marvell's Dove SoC | ||||
|  * | ||||
|  * Author: Saeed Bishara <saeed@marvell.com> | ||||
|  *	   Mike Rapoport <mike@compulab.co.il> | ||||
|  * Based on sdhci-cns3xxx.c | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/io.h> | ||||
| #include <linux/mmc/host.h> | ||||
| 
 | ||||
| #include "sdhci.h" | ||||
| #include "sdhci-pltfm.h" | ||||
| 
 | ||||
| static u16 sdhci_dove_readw(struct sdhci_host *host, int reg) | ||||
| { | ||||
| 	u16 ret; | ||||
| 
 | ||||
| 	switch (reg) { | ||||
| 	case SDHCI_HOST_VERSION: | ||||
| 	case SDHCI_SLOT_INT_STATUS: | ||||
| 		/* those registers don't exist */ | ||||
| 		return 0; | ||||
| 	default: | ||||
| 		ret = readw(host->ioaddr + reg); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static u32 sdhci_dove_readl(struct sdhci_host *host, int reg) | ||||
| { | ||||
| 	u32 ret; | ||||
| 
 | ||||
| 	switch (reg) { | ||||
| 	case SDHCI_CAPABILITIES: | ||||
| 		ret = readl(host->ioaddr + reg); | ||||
| 		/* Mask the support for 3.0V */ | ||||
| 		ret &= ~SDHCI_CAN_VDD_300; | ||||
| 		break; | ||||
| 	default: | ||||
| 		ret = readl(host->ioaddr + reg); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static struct sdhci_ops sdhci_dove_ops = { | ||||
| 	.read_w	= sdhci_dove_readw, | ||||
| 	.read_l	= sdhci_dove_readl, | ||||
| }; | ||||
| 
 | ||||
| struct sdhci_pltfm_data sdhci_dove_pdata = { | ||||
| 	.ops	= &sdhci_dove_ops, | ||||
| 	.quirks	= SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | | ||||
| 		  SDHCI_QUIRK_NO_BUSY_IRQ | | ||||
| 		  SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | | ||||
| 		  SDHCI_QUIRK_FORCE_DMA, | ||||
| }; | ||||
| @ -176,6 +176,74 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc_sdio = { | ||||
| 	.quirks		= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, | ||||
| }; | ||||
| 
 | ||||
| /* O2Micro extra registers */ | ||||
| #define O2_SD_LOCK_WP		0xD3 | ||||
| #define O2_SD_MULTI_VCC3V	0xEE | ||||
| #define O2_SD_CLKREQ		0xEC | ||||
| #define O2_SD_CAPS		0xE0 | ||||
| #define O2_SD_ADMA1		0xE2 | ||||
| #define O2_SD_ADMA2		0xE7 | ||||
| #define O2_SD_INF_MOD		0xF1 | ||||
| 
 | ||||
| static int o2_probe(struct sdhci_pci_chip *chip) | ||||
| { | ||||
| 	int ret; | ||||
| 	u8 scratch; | ||||
| 
 | ||||
| 	switch (chip->pdev->device) { | ||||
| 	case PCI_DEVICE_ID_O2_8220: | ||||
| 	case PCI_DEVICE_ID_O2_8221: | ||||
| 	case PCI_DEVICE_ID_O2_8320: | ||||
| 	case PCI_DEVICE_ID_O2_8321: | ||||
| 		/* This extra setup is required due to broken ADMA. */ | ||||
| 		ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		scratch &= 0x7f; | ||||
| 		pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); | ||||
| 
 | ||||
| 		/* Set Multi 3 to VCC3V# */ | ||||
| 		pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08); | ||||
| 
 | ||||
| 		/* Disable CLK_REQ# support after media DET */ | ||||
| 		ret = pci_read_config_byte(chip->pdev, O2_SD_CLKREQ, &scratch); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		scratch |= 0x20; | ||||
| 		pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch); | ||||
| 
 | ||||
| 		/* Choose capabilities, enable SDMA.  We have to write 0x01
 | ||||
| 		 * to the capabilities register first to unlock it. | ||||
| 		 */ | ||||
| 		ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		scratch |= 0x01; | ||||
| 		pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch); | ||||
| 		pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73); | ||||
| 
 | ||||
| 		/* Disable ADMA1/2 */ | ||||
| 		pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39); | ||||
| 		pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08); | ||||
| 
 | ||||
| 		/* Disable the infinite transfer mode */ | ||||
| 		ret = pci_read_config_byte(chip->pdev, O2_SD_INF_MOD, &scratch); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		scratch |= 0x08; | ||||
| 		pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch); | ||||
| 
 | ||||
| 		/* Lock WP */ | ||||
| 		ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		scratch |= 0x80; | ||||
| 		pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int jmicron_pmos(struct sdhci_pci_chip *chip, int on) | ||||
| { | ||||
| 	u8 scratch; | ||||
| @ -204,6 +272,7 @@ static int jmicron_pmos(struct sdhci_pci_chip *chip, int on) | ||||
| static int jmicron_probe(struct sdhci_pci_chip *chip) | ||||
| { | ||||
| 	int ret; | ||||
| 	u16 mmcdev = 0; | ||||
| 
 | ||||
| 	if (chip->pdev->revision == 0) { | ||||
| 		chip->quirks |= SDHCI_QUIRK_32BIT_DMA_ADDR | | ||||
| @ -225,12 +294,17 @@ static int jmicron_probe(struct sdhci_pci_chip *chip) | ||||
| 	 * 2. The MMC interface has a lower subfunction number | ||||
| 	 *    than the SD interface. | ||||
| 	 */ | ||||
| 	if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_SD) { | ||||
| 	if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_SD) | ||||
| 		mmcdev = PCI_DEVICE_ID_JMICRON_JMB38X_MMC; | ||||
| 	else if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD) | ||||
| 		mmcdev = PCI_DEVICE_ID_JMICRON_JMB388_ESD; | ||||
| 
 | ||||
| 	if (mmcdev) { | ||||
| 		struct pci_dev *sd_dev; | ||||
| 
 | ||||
| 		sd_dev = NULL; | ||||
| 		while ((sd_dev = pci_get_device(PCI_VENDOR_ID_JMICRON, | ||||
| 			PCI_DEVICE_ID_JMICRON_JMB38X_MMC, sd_dev)) != NULL) { | ||||
| 						mmcdev, sd_dev)) != NULL) { | ||||
| 			if ((PCI_SLOT(chip->pdev->devfn) == | ||||
| 				PCI_SLOT(sd_dev->devfn)) && | ||||
| 				(chip->pdev->bus == sd_dev->bus)) | ||||
| @ -290,13 +364,25 @@ static int jmicron_probe_slot(struct sdhci_pci_slot *slot) | ||||
| 			slot->host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; | ||||
| 	} | ||||
| 
 | ||||
| 	/* JM388 MMC doesn't support 1.8V while SD supports it */ | ||||
| 	if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) { | ||||
| 		slot->host->ocr_avail_sd = MMC_VDD_32_33 | MMC_VDD_33_34 | | ||||
| 			MMC_VDD_29_30 | MMC_VDD_30_31 | | ||||
| 			MMC_VDD_165_195; /* allow 1.8V */ | ||||
| 		slot->host->ocr_avail_mmc = MMC_VDD_32_33 | MMC_VDD_33_34 | | ||||
| 			MMC_VDD_29_30 | MMC_VDD_30_31; /* no 1.8V for MMC */ | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The secondary interface requires a bit set to get the | ||||
| 	 * interrupts. | ||||
| 	 */ | ||||
| 	if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) | ||||
| 	if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || | ||||
| 	    slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) | ||||
| 		jmicron_enable_mmc(slot->host, 1); | ||||
| 
 | ||||
| 	slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| @ -305,7 +391,8 @@ static void jmicron_remove_slot(struct sdhci_pci_slot *slot, int dead) | ||||
| 	if (dead) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) | ||||
| 	if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || | ||||
| 	    slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) | ||||
| 		jmicron_enable_mmc(slot->host, 0); | ||||
| } | ||||
| 
 | ||||
| @ -313,7 +400,8 @@ static int jmicron_suspend(struct sdhci_pci_chip *chip, pm_message_t state) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) { | ||||
| 	if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || | ||||
| 	    chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) { | ||||
| 		for (i = 0;i < chip->num_slots;i++) | ||||
| 			jmicron_enable_mmc(chip->slots[i]->host, 0); | ||||
| 	} | ||||
| @ -325,7 +413,8 @@ static int jmicron_resume(struct sdhci_pci_chip *chip) | ||||
| { | ||||
| 	int ret, i; | ||||
| 
 | ||||
| 	if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) { | ||||
| 	if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || | ||||
| 	    chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) { | ||||
| 		for (i = 0;i < chip->num_slots;i++) | ||||
| 			jmicron_enable_mmc(chip->slots[i]->host, 1); | ||||
| 	} | ||||
| @ -339,6 +428,10 @@ static int jmicron_resume(struct sdhci_pci_chip *chip) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct sdhci_pci_fixes sdhci_o2 = { | ||||
| 	.probe		= o2_probe, | ||||
| }; | ||||
| 
 | ||||
| static const struct sdhci_pci_fixes sdhci_jmicron = { | ||||
| 	.probe		= jmicron_probe, | ||||
| 
 | ||||
| @ -509,6 +602,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = { | ||||
| 		.driver_data	= (kernel_ulong_t)&sdhci_jmicron, | ||||
| 	}, | ||||
| 
 | ||||
| 	{ | ||||
| 		.vendor		= PCI_VENDOR_ID_JMICRON, | ||||
| 		.device		= PCI_DEVICE_ID_JMICRON_JMB388_SD, | ||||
| 		.subvendor	= PCI_ANY_ID, | ||||
| 		.subdevice	= PCI_ANY_ID, | ||||
| 		.driver_data	= (kernel_ulong_t)&sdhci_jmicron, | ||||
| 	}, | ||||
| 
 | ||||
| 	{ | ||||
| 		.vendor		= PCI_VENDOR_ID_JMICRON, | ||||
| 		.device		= PCI_DEVICE_ID_JMICRON_JMB388_ESD, | ||||
| 		.subvendor	= PCI_ANY_ID, | ||||
| 		.subdevice	= PCI_ANY_ID, | ||||
| 		.driver_data	= (kernel_ulong_t)&sdhci_jmicron, | ||||
| 	}, | ||||
| 
 | ||||
| 	{ | ||||
| 		.vendor		= PCI_VENDOR_ID_SYSKONNECT, | ||||
| 		.device		= 0x8000, | ||||
| @ -589,6 +698,46 @@ static const struct pci_device_id pci_ids[] __devinitdata = { | ||||
| 		.driver_data	= (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio, | ||||
| 	}, | ||||
| 
 | ||||
| 	{ | ||||
| 		.vendor		= PCI_VENDOR_ID_O2, | ||||
| 		.device		= PCI_DEVICE_ID_O2_8120, | ||||
| 		.subvendor	= PCI_ANY_ID, | ||||
| 		.subdevice	= PCI_ANY_ID, | ||||
| 		.driver_data	= (kernel_ulong_t)&sdhci_o2, | ||||
| 	}, | ||||
| 
 | ||||
| 	{ | ||||
| 		.vendor		= PCI_VENDOR_ID_O2, | ||||
| 		.device		= PCI_DEVICE_ID_O2_8220, | ||||
| 		.subvendor	= PCI_ANY_ID, | ||||
| 		.subdevice	= PCI_ANY_ID, | ||||
| 		.driver_data	= (kernel_ulong_t)&sdhci_o2, | ||||
| 	}, | ||||
| 
 | ||||
| 	{ | ||||
| 		.vendor		= PCI_VENDOR_ID_O2, | ||||
| 		.device		= PCI_DEVICE_ID_O2_8221, | ||||
| 		.subvendor	= PCI_ANY_ID, | ||||
| 		.subdevice	= PCI_ANY_ID, | ||||
| 		.driver_data	= (kernel_ulong_t)&sdhci_o2, | ||||
| 	}, | ||||
| 
 | ||||
| 	{ | ||||
| 		.vendor		= PCI_VENDOR_ID_O2, | ||||
| 		.device		= PCI_DEVICE_ID_O2_8320, | ||||
| 		.subvendor	= PCI_ANY_ID, | ||||
| 		.subdevice	= PCI_ANY_ID, | ||||
| 		.driver_data	= (kernel_ulong_t)&sdhci_o2, | ||||
| 	}, | ||||
| 
 | ||||
| 	{ | ||||
| 		.vendor		= PCI_VENDOR_ID_O2, | ||||
| 		.device		= PCI_DEVICE_ID_O2_8321, | ||||
| 		.subvendor	= PCI_ANY_ID, | ||||
| 		.subdevice	= PCI_ANY_ID, | ||||
| 		.driver_data	= (kernel_ulong_t)&sdhci_o2, | ||||
| 	}, | ||||
| 
 | ||||
| 	{	/* Generic SD host controller */ | ||||
| 		PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) | ||||
| 	}, | ||||
|  | ||||
| @ -169,6 +169,12 @@ static const struct platform_device_id sdhci_pltfm_ids[] = { | ||||
| #endif | ||||
| #ifdef CONFIG_MMC_SDHCI_ESDHC_IMX | ||||
| 	{ "sdhci-esdhc-imx", (kernel_ulong_t)&sdhci_esdhc_imx_pdata }, | ||||
| #endif | ||||
| #ifdef CONFIG_MMC_SDHCI_DOVE | ||||
| 	{ "sdhci-dove", (kernel_ulong_t)&sdhci_dove_pdata }, | ||||
| #endif | ||||
| #ifdef CONFIG_MMC_SDHCI_TEGRA | ||||
| 	{ "sdhci-tegra", (kernel_ulong_t)&sdhci_tegra_pdata }, | ||||
| #endif | ||||
| 	{ }, | ||||
| }; | ||||
|  | ||||
| @ -22,5 +22,7 @@ struct sdhci_pltfm_host { | ||||
| 
 | ||||
| extern struct sdhci_pltfm_data sdhci_cns3xxx_pdata; | ||||
| extern struct sdhci_pltfm_data sdhci_esdhc_imx_pdata; | ||||
| extern struct sdhci_pltfm_data sdhci_dove_pdata; | ||||
| extern struct sdhci_pltfm_data sdhci_tegra_pdata; | ||||
| 
 | ||||
| #endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */ | ||||
|  | ||||
| @ -130,6 +130,15 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost, | ||||
| 	if (!clksrc) | ||||
| 		return UINT_MAX; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Clock divider's step is different as 1 from that of host controller | ||||
| 	 * when 'clk_type' is S3C_SDHCI_CLK_DIV_EXTERNAL. | ||||
| 	 */ | ||||
| 	if (ourhost->pdata->clk_type) { | ||||
| 		rate = clk_round_rate(clksrc, wanted); | ||||
| 		return wanted - rate; | ||||
| 	} | ||||
| 
 | ||||
| 	rate = clk_get_rate(clksrc); | ||||
| 
 | ||||
| 	for (div = 1; div < 256; div *= 2) { | ||||
| @ -232,6 +241,42 @@ static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host) | ||||
| 	return min; | ||||
| } | ||||
| 
 | ||||
| /* sdhci_cmu_get_max_clk - callback to get maximum clock frequency.*/ | ||||
| static unsigned int sdhci_cmu_get_max_clock(struct sdhci_host *host) | ||||
| { | ||||
| 	struct sdhci_s3c *ourhost = to_s3c(host); | ||||
| 
 | ||||
| 	return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], UINT_MAX); | ||||
| } | ||||
| 
 | ||||
| /* sdhci_cmu_get_min_clock - callback to get minimal supported clock value. */ | ||||
| static unsigned int sdhci_cmu_get_min_clock(struct sdhci_host *host) | ||||
| { | ||||
| 	struct sdhci_s3c *ourhost = to_s3c(host); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * initial clock can be in the frequency range of | ||||
| 	 * 100KHz-400KHz, so we set it as max value. | ||||
| 	 */ | ||||
| 	return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], 400000); | ||||
| } | ||||
| 
 | ||||
| /* sdhci_cmu_set_clock - callback on clock change.*/ | ||||
| static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) | ||||
| { | ||||
| 	struct sdhci_s3c *ourhost = to_s3c(host); | ||||
| 
 | ||||
| 	/* don't bother if the clock is going off */ | ||||
| 	if (clock == 0) | ||||
| 		return; | ||||
| 
 | ||||
| 	sdhci_s3c_set_clock(host, clock); | ||||
| 
 | ||||
| 	clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); | ||||
| 
 | ||||
| 	host->clock = clock; | ||||
| } | ||||
| 
 | ||||
| static struct sdhci_ops sdhci_s3c_ops = { | ||||
| 	.get_max_clock		= sdhci_s3c_get_max_clk, | ||||
| 	.set_clock		= sdhci_s3c_set_clock, | ||||
| @ -361,6 +406,13 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) | ||||
| 
 | ||||
| 		clks++; | ||||
| 		sc->clk_bus[ptr] = clk; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * save current clock index to know which clock bus | ||||
| 		 * is used later in overriding functions. | ||||
| 		 */ | ||||
| 		sc->cur_clk = ptr; | ||||
| 
 | ||||
| 		clk_enable(clk); | ||||
| 
 | ||||
| 		dev_info(dev, "clock source %d: %s (%ld Hz)\n", | ||||
| @ -427,6 +479,20 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) | ||||
| 	/* HSMMC on Samsung SoCs uses SDCLK as timeout clock */ | ||||
| 	host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If controller does not have internal clock divider, | ||||
| 	 * we can use overriding functions instead of default. | ||||
| 	 */ | ||||
| 	if (pdata->clk_type) { | ||||
| 		sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock; | ||||
| 		sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock; | ||||
| 		sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; | ||||
| 	} | ||||
| 
 | ||||
| 	/* It supports additional host capabilities if needed */ | ||||
| 	if (pdata->host_caps) | ||||
| 		host->mmc->caps |= pdata->host_caps; | ||||
| 
 | ||||
| 	ret = sdhci_add_host(host); | ||||
| 	if (ret) { | ||||
| 		dev_err(dev, "sdhci_add_host() failed\n"); | ||||
|  | ||||
							
								
								
									
										257
									
								
								drivers/mmc/host/sdhci-tegra.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								drivers/mmc/host/sdhci-tegra.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,257 @@ | ||||
| /*
 | ||||
|  * Copyright (C) 2010 Google, Inc. | ||||
|  * | ||||
|  * This software is licensed under the terms of the GNU General Public | ||||
|  * License version 2, as published by the Free Software Foundation, and | ||||
|  * may be copied, distributed, and modified under those terms. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/err.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/clk.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/gpio.h> | ||||
| #include <linux/mmc/card.h> | ||||
| #include <linux/mmc/host.h> | ||||
| 
 | ||||
| #include <mach/gpio.h> | ||||
| #include <mach/sdhci.h> | ||||
| 
 | ||||
| #include "sdhci.h" | ||||
| #include "sdhci-pltfm.h" | ||||
| 
 | ||||
| static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) | ||||
| { | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	if (unlikely(reg == SDHCI_PRESENT_STATE)) { | ||||
| 		/* Use wp_gpio here instead? */ | ||||
| 		val = readl(host->ioaddr + reg); | ||||
| 		return val | SDHCI_WRITE_PROTECT; | ||||
| 	} | ||||
| 
 | ||||
| 	return readl(host->ioaddr + reg); | ||||
| } | ||||
| 
 | ||||
| static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) | ||||
| { | ||||
| 	if (unlikely(reg == SDHCI_HOST_VERSION)) { | ||||
| 		/* Erratum: Version register is invalid in HW. */ | ||||
| 		return SDHCI_SPEC_200; | ||||
| 	} | ||||
| 
 | ||||
| 	return readw(host->ioaddr + reg); | ||||
| } | ||||
| 
 | ||||
| static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) | ||||
| { | ||||
| 	/* Seems like we're getting spurious timeout and crc errors, so
 | ||||
| 	 * disable signalling of them. In case of real errors software | ||||
| 	 * timers should take care of eventually detecting them. | ||||
| 	 */ | ||||
| 	if (unlikely(reg == SDHCI_SIGNAL_ENABLE)) | ||||
| 		val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC); | ||||
| 
 | ||||
| 	writel(val, host->ioaddr + reg); | ||||
| 
 | ||||
| 	if (unlikely(reg == SDHCI_INT_ENABLE)) { | ||||
| 		/* Erratum: Must enable block gap interrupt detection */ | ||||
| 		u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); | ||||
| 		if (val & SDHCI_INT_CARD_INT) | ||||
| 			gap_ctrl |= 0x8; | ||||
| 		else | ||||
| 			gap_ctrl &= ~0x8; | ||||
| 		writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci) | ||||
| { | ||||
| 	struct platform_device *pdev = to_platform_device(mmc_dev(sdhci->mmc)); | ||||
| 	struct tegra_sdhci_platform_data *plat; | ||||
| 
 | ||||
| 	plat = pdev->dev.platform_data; | ||||
| 
 | ||||
| 	if (!gpio_is_valid(plat->wp_gpio)) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	return gpio_get_value(plat->wp_gpio); | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t carddetect_irq(int irq, void *data) | ||||
| { | ||||
| 	struct sdhci_host *sdhost = (struct sdhci_host *)data; | ||||
| 
 | ||||
| 	tasklet_schedule(&sdhost->card_tasklet); | ||||
| 	return IRQ_HANDLED; | ||||
| }; | ||||
| 
 | ||||
| static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width) | ||||
| { | ||||
| 	struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); | ||||
| 	struct tegra_sdhci_platform_data *plat; | ||||
| 	u32 ctrl; | ||||
| 
 | ||||
| 	plat = pdev->dev.platform_data; | ||||
| 
 | ||||
| 	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); | ||||
| 	if (plat->is_8bit && bus_width == MMC_BUS_WIDTH_8) { | ||||
| 		ctrl &= ~SDHCI_CTRL_4BITBUS; | ||||
| 		ctrl |= SDHCI_CTRL_8BITBUS; | ||||
| 	} else { | ||||
| 		ctrl &= ~SDHCI_CTRL_8BITBUS; | ||||
| 		if (bus_width == MMC_BUS_WIDTH_4) | ||||
| 			ctrl |= SDHCI_CTRL_4BITBUS; | ||||
| 		else | ||||
| 			ctrl &= ~SDHCI_CTRL_4BITBUS; | ||||
| 	} | ||||
| 	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int tegra_sdhci_pltfm_init(struct sdhci_host *host, | ||||
| 				  struct sdhci_pltfm_data *pdata) | ||||
| { | ||||
| 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | ||||
| 	struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); | ||||
| 	struct tegra_sdhci_platform_data *plat; | ||||
| 	struct clk *clk; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	plat = pdev->dev.platform_data; | ||||
| 	if (plat == NULL) { | ||||
| 		dev_err(mmc_dev(host->mmc), "missing platform data\n"); | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 
 | ||||
| 	if (gpio_is_valid(plat->power_gpio)) { | ||||
| 		rc = gpio_request(plat->power_gpio, "sdhci_power"); | ||||
| 		if (rc) { | ||||
| 			dev_err(mmc_dev(host->mmc), | ||||
| 				"failed to allocate power gpio\n"); | ||||
| 			goto out; | ||||
| 		} | ||||
| 		tegra_gpio_enable(plat->power_gpio); | ||||
| 		gpio_direction_output(plat->power_gpio, 1); | ||||
| 	} | ||||
| 
 | ||||
| 	if (gpio_is_valid(plat->cd_gpio)) { | ||||
| 		rc = gpio_request(plat->cd_gpio, "sdhci_cd"); | ||||
| 		if (rc) { | ||||
| 			dev_err(mmc_dev(host->mmc), | ||||
| 				"failed to allocate cd gpio\n"); | ||||
| 			goto out_power; | ||||
| 		} | ||||
| 		tegra_gpio_enable(plat->cd_gpio); | ||||
| 		gpio_direction_input(plat->cd_gpio); | ||||
| 
 | ||||
| 		rc = request_irq(gpio_to_irq(plat->cd_gpio), carddetect_irq, | ||||
| 				 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, | ||||
| 				 mmc_hostname(host->mmc), host); | ||||
| 
 | ||||
| 		if (rc)	{ | ||||
| 			dev_err(mmc_dev(host->mmc), "request irq error\n"); | ||||
| 			goto out_cd; | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	if (gpio_is_valid(plat->wp_gpio)) { | ||||
| 		rc = gpio_request(plat->wp_gpio, "sdhci_wp"); | ||||
| 		if (rc) { | ||||
| 			dev_err(mmc_dev(host->mmc), | ||||
| 				"failed to allocate wp gpio\n"); | ||||
| 			goto out_cd; | ||||
| 		} | ||||
| 		tegra_gpio_enable(plat->wp_gpio); | ||||
| 		gpio_direction_input(plat->wp_gpio); | ||||
| 	} | ||||
| 
 | ||||
| 	clk = clk_get(mmc_dev(host->mmc), NULL); | ||||
| 	if (IS_ERR(clk)) { | ||||
| 		dev_err(mmc_dev(host->mmc), "clk err\n"); | ||||
| 		rc = PTR_ERR(clk); | ||||
| 		goto out_wp; | ||||
| 	} | ||||
| 	clk_enable(clk); | ||||
| 	pltfm_host->clk = clk; | ||||
| 
 | ||||
| 	if (plat->is_8bit) | ||||
| 		host->mmc->caps |= MMC_CAP_8_BIT_DATA; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| out_wp: | ||||
| 	if (gpio_is_valid(plat->wp_gpio)) { | ||||
| 		tegra_gpio_disable(plat->wp_gpio); | ||||
| 		gpio_free(plat->wp_gpio); | ||||
| 	} | ||||
| 
 | ||||
| out_cd: | ||||
| 	if (gpio_is_valid(plat->cd_gpio)) { | ||||
| 		tegra_gpio_disable(plat->cd_gpio); | ||||
| 		gpio_free(plat->cd_gpio); | ||||
| 	} | ||||
| 
 | ||||
| out_power: | ||||
| 	if (gpio_is_valid(plat->power_gpio)) { | ||||
| 		tegra_gpio_disable(plat->power_gpio); | ||||
| 		gpio_free(plat->power_gpio); | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static void tegra_sdhci_pltfm_exit(struct sdhci_host *host) | ||||
| { | ||||
| 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | ||||
| 	struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); | ||||
| 	struct tegra_sdhci_platform_data *plat; | ||||
| 
 | ||||
| 	plat = pdev->dev.platform_data; | ||||
| 
 | ||||
| 	if (gpio_is_valid(plat->wp_gpio)) { | ||||
| 		tegra_gpio_disable(plat->wp_gpio); | ||||
| 		gpio_free(plat->wp_gpio); | ||||
| 	} | ||||
| 
 | ||||
| 	if (gpio_is_valid(plat->cd_gpio)) { | ||||
| 		tegra_gpio_disable(plat->cd_gpio); | ||||
| 		gpio_free(plat->cd_gpio); | ||||
| 	} | ||||
| 
 | ||||
| 	if (gpio_is_valid(plat->power_gpio)) { | ||||
| 		tegra_gpio_disable(plat->power_gpio); | ||||
| 		gpio_free(plat->power_gpio); | ||||
| 	} | ||||
| 
 | ||||
| 	clk_disable(pltfm_host->clk); | ||||
| 	clk_put(pltfm_host->clk); | ||||
| } | ||||
| 
 | ||||
| static struct sdhci_ops tegra_sdhci_ops = { | ||||
| 	.get_ro     = tegra_sdhci_get_ro, | ||||
| 	.read_l     = tegra_sdhci_readl, | ||||
| 	.read_w     = tegra_sdhci_readw, | ||||
| 	.write_l    = tegra_sdhci_writel, | ||||
| 	.platform_8bit_width = tegra_sdhci_8bit, | ||||
| }; | ||||
| 
 | ||||
| struct sdhci_pltfm_data sdhci_tegra_pdata = { | ||||
| 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | | ||||
| 		  SDHCI_QUIRK_SINGLE_POWER_WRITE | | ||||
| 		  SDHCI_QUIRK_NO_HISPD_BIT | | ||||
| 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, | ||||
| 	.ops  = &tegra_sdhci_ops, | ||||
| 	.init = tegra_sdhci_pltfm_init, | ||||
| 	.exit = tegra_sdhci_pltfm_exit, | ||||
| }; | ||||
| @ -23,6 +23,7 @@ | ||||
| 
 | ||||
| #include <linux/leds.h> | ||||
| 
 | ||||
| #include <linux/mmc/mmc.h> | ||||
| #include <linux/mmc/host.h> | ||||
| 
 | ||||
| #include "sdhci.h" | ||||
| @ -77,8 +78,11 @@ static void sdhci_dumpregs(struct sdhci_host *host) | ||||
| 	printk(KERN_DEBUG DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", | ||||
| 		sdhci_readw(host, SDHCI_ACMD12_ERR), | ||||
| 		sdhci_readw(host, SDHCI_SLOT_INT_STATUS)); | ||||
| 	printk(KERN_DEBUG DRIVER_NAME ": Caps:     0x%08x | Max curr: 0x%08x\n", | ||||
| 	printk(KERN_DEBUG DRIVER_NAME ": Caps:     0x%08x | Caps_1:   0x%08x\n", | ||||
| 		sdhci_readl(host, SDHCI_CAPABILITIES), | ||||
| 		sdhci_readl(host, SDHCI_CAPABILITIES_1)); | ||||
| 	printk(KERN_DEBUG DRIVER_NAME ": Cmd:      0x%08x | Max curr: 0x%08x\n", | ||||
| 		sdhci_readw(host, SDHCI_COMMAND), | ||||
| 		sdhci_readl(host, SDHCI_MAX_CURRENT)); | ||||
| 
 | ||||
| 	if (host->flags & SDHCI_USE_ADMA) | ||||
| @ -1518,7 +1522,11 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) | ||||
| 
 | ||||
| 	if (intmask & SDHCI_INT_DATA_TIMEOUT) | ||||
| 		host->data->error = -ETIMEDOUT; | ||||
| 	else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT)) | ||||
| 	else if (intmask & SDHCI_INT_DATA_END_BIT) | ||||
| 		host->data->error = -EILSEQ; | ||||
| 	else if ((intmask & SDHCI_INT_DATA_CRC) && | ||||
| 		SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) | ||||
| 			!= MMC_BUS_TEST_R) | ||||
| 		host->data->error = -EILSEQ; | ||||
| 	else if (intmask & SDHCI_INT_ADMA_ERROR) { | ||||
| 		printk(KERN_ERR "%s: ADMA error\n", mmc_hostname(host->mmc)); | ||||
| @ -1736,7 +1744,7 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host); | ||||
| int sdhci_add_host(struct sdhci_host *host) | ||||
| { | ||||
| 	struct mmc_host *mmc; | ||||
| 	unsigned int caps; | ||||
| 	unsigned int caps, ocr_avail; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	WARN_ON(host == NULL); | ||||
| @ -1890,13 +1898,26 @@ int sdhci_add_host(struct sdhci_host *host) | ||||
| 	    mmc_card_is_removable(mmc)) | ||||
| 		mmc->caps |= MMC_CAP_NEEDS_POLL; | ||||
| 
 | ||||
| 	mmc->ocr_avail = 0; | ||||
| 	ocr_avail = 0; | ||||
| 	if (caps & SDHCI_CAN_VDD_330) | ||||
| 		mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34; | ||||
| 		ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34; | ||||
| 	if (caps & SDHCI_CAN_VDD_300) | ||||
| 		mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31; | ||||
| 		ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31; | ||||
| 	if (caps & SDHCI_CAN_VDD_180) | ||||
| 		mmc->ocr_avail |= MMC_VDD_165_195; | ||||
| 		ocr_avail |= MMC_VDD_165_195; | ||||
| 
 | ||||
| 	mmc->ocr_avail = ocr_avail; | ||||
| 	mmc->ocr_avail_sdio = ocr_avail; | ||||
| 	if (host->ocr_avail_sdio) | ||||
| 		mmc->ocr_avail_sdio &= host->ocr_avail_sdio; | ||||
| 	mmc->ocr_avail_sd = ocr_avail; | ||||
| 	if (host->ocr_avail_sd) | ||||
| 		mmc->ocr_avail_sd &= host->ocr_avail_sd; | ||||
| 	else /* normal SD controllers don't support 1.8V */ | ||||
| 		mmc->ocr_avail_sd &= ~MMC_VDD_165_195; | ||||
| 	mmc->ocr_avail_mmc = ocr_avail; | ||||
| 	if (host->ocr_avail_mmc) | ||||
| 		mmc->ocr_avail_mmc &= host->ocr_avail_mmc; | ||||
| 
 | ||||
| 	if (mmc->ocr_avail == 0) { | ||||
| 		printk(KERN_ERR "%s: Hardware doesn't report any " | ||||
| @ -1928,10 +1949,14 @@ int sdhci_add_host(struct sdhci_host *host) | ||||
| 	 * of bytes. When doing hardware scatter/gather, each entry cannot | ||||
| 	 * be larger than 64 KiB though. | ||||
| 	 */ | ||||
| 	if (host->flags & SDHCI_USE_ADMA) | ||||
| 		mmc->max_seg_size = 65536; | ||||
| 	else | ||||
| 	if (host->flags & SDHCI_USE_ADMA) { | ||||
| 		if (host->quirks & SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC) | ||||
| 			mmc->max_seg_size = 65535; | ||||
| 		else | ||||
| 			mmc->max_seg_size = 65536; | ||||
| 	} else { | ||||
| 		mmc->max_seg_size = mmc->max_req_size; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Maximum block size. This varies from controller to controller and | ||||
|  | ||||
| @ -52,6 +52,7 @@ | ||||
| #define  SDHCI_CMD_RESP_SHORT_BUSY 0x03 | ||||
| 
 | ||||
| #define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff)) | ||||
| #define SDHCI_GET_CMD(c) ((c>>8) & 0x3f) | ||||
| 
 | ||||
| #define SDHCI_RESPONSE		0x10 | ||||
| 
 | ||||
| @ -165,7 +166,7 @@ | ||||
| #define  SDHCI_CAN_VDD_180	0x04000000 | ||||
| #define  SDHCI_CAN_64BIT	0x10000000 | ||||
| 
 | ||||
| /* 44-47 reserved for more caps */ | ||||
| #define SDHCI_CAPABILITIES_1	0x44 | ||||
| 
 | ||||
| #define SDHCI_MAX_CURRENT	0x48 | ||||
| 
 | ||||
|  | ||||
| @ -25,16 +25,261 @@ | ||||
|  *   double buffer support | ||||
|  * | ||||
|  */ | ||||
| #include <linux/module.h> | ||||
| #include <linux/irq.h> | ||||
| #include <linux/device.h> | ||||
| 
 | ||||
| #include <linux/delay.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/dmaengine.h> | ||||
| #include <linux/mmc/host.h> | ||||
| #include <linux/highmem.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/irq.h> | ||||
| #include <linux/mfd/core.h> | ||||
| #include <linux/mfd/tmio.h> | ||||
| #include <linux/mmc/host.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/pagemap.h> | ||||
| #include <linux/scatterlist.h> | ||||
| #include <linux/workqueue.h> | ||||
| #include <linux/spinlock.h> | ||||
| 
 | ||||
| #include "tmio_mmc.h" | ||||
| #define CTL_SD_CMD 0x00 | ||||
| #define CTL_ARG_REG 0x04 | ||||
| #define CTL_STOP_INTERNAL_ACTION 0x08 | ||||
| #define CTL_XFER_BLK_COUNT 0xa | ||||
| #define CTL_RESPONSE 0x0c | ||||
| #define CTL_STATUS 0x1c | ||||
| #define CTL_IRQ_MASK 0x20 | ||||
| #define CTL_SD_CARD_CLK_CTL 0x24 | ||||
| #define CTL_SD_XFER_LEN 0x26 | ||||
| #define CTL_SD_MEM_CARD_OPT 0x28 | ||||
| #define CTL_SD_ERROR_DETAIL_STATUS 0x2c | ||||
| #define CTL_SD_DATA_PORT 0x30 | ||||
| #define CTL_TRANSACTION_CTL 0x34 | ||||
| #define CTL_SDIO_STATUS 0x36 | ||||
| #define CTL_SDIO_IRQ_MASK 0x38 | ||||
| #define CTL_RESET_SD 0xe0 | ||||
| #define CTL_SDIO_REGS 0x100 | ||||
| #define CTL_CLK_AND_WAIT_CTL 0x138 | ||||
| #define CTL_RESET_SDIO 0x1e0 | ||||
| 
 | ||||
| /* Definitions for values the CTRL_STATUS register can take. */ | ||||
| #define TMIO_STAT_CMDRESPEND    0x00000001 | ||||
| #define TMIO_STAT_DATAEND       0x00000004 | ||||
| #define TMIO_STAT_CARD_REMOVE   0x00000008 | ||||
| #define TMIO_STAT_CARD_INSERT   0x00000010 | ||||
| #define TMIO_STAT_SIGSTATE      0x00000020 | ||||
| #define TMIO_STAT_WRPROTECT     0x00000080 | ||||
| #define TMIO_STAT_CARD_REMOVE_A 0x00000100 | ||||
| #define TMIO_STAT_CARD_INSERT_A 0x00000200 | ||||
| #define TMIO_STAT_SIGSTATE_A    0x00000400 | ||||
| #define TMIO_STAT_CMD_IDX_ERR   0x00010000 | ||||
| #define TMIO_STAT_CRCFAIL       0x00020000 | ||||
| #define TMIO_STAT_STOPBIT_ERR   0x00040000 | ||||
| #define TMIO_STAT_DATATIMEOUT   0x00080000 | ||||
| #define TMIO_STAT_RXOVERFLOW    0x00100000 | ||||
| #define TMIO_STAT_TXUNDERRUN    0x00200000 | ||||
| #define TMIO_STAT_CMDTIMEOUT    0x00400000 | ||||
| #define TMIO_STAT_RXRDY         0x01000000 | ||||
| #define TMIO_STAT_TXRQ          0x02000000 | ||||
| #define TMIO_STAT_ILL_FUNC      0x20000000 | ||||
| #define TMIO_STAT_CMD_BUSY      0x40000000 | ||||
| #define TMIO_STAT_ILL_ACCESS    0x80000000 | ||||
| 
 | ||||
| /* Definitions for values the CTRL_SDIO_STATUS register can take. */ | ||||
| #define TMIO_SDIO_STAT_IOIRQ	0x0001 | ||||
| #define TMIO_SDIO_STAT_EXPUB52	0x4000 | ||||
| #define TMIO_SDIO_STAT_EXWT	0x8000 | ||||
| #define TMIO_SDIO_MASK_ALL	0xc007 | ||||
| 
 | ||||
| /* Define some IRQ masks */ | ||||
| /* This is the mask used at reset by the chip */ | ||||
| #define TMIO_MASK_ALL           0x837f031d | ||||
| #define TMIO_MASK_READOP  (TMIO_STAT_RXRDY | TMIO_STAT_DATAEND) | ||||
| #define TMIO_MASK_WRITEOP (TMIO_STAT_TXRQ | TMIO_STAT_DATAEND) | ||||
| #define TMIO_MASK_CMD     (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT | \ | ||||
| 		TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT) | ||||
| #define TMIO_MASK_IRQ     (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD) | ||||
| 
 | ||||
| #define enable_mmc_irqs(host, i) \ | ||||
| 	do { \ | ||||
| 		u32 mask;\ | ||||
| 		mask  = sd_ctrl_read32((host), CTL_IRQ_MASK); \ | ||||
| 		mask &= ~((i) & TMIO_MASK_IRQ); \ | ||||
| 		sd_ctrl_write32((host), CTL_IRQ_MASK, mask); \ | ||||
| 	} while (0) | ||||
| 
 | ||||
| #define disable_mmc_irqs(host, i) \ | ||||
| 	do { \ | ||||
| 		u32 mask;\ | ||||
| 		mask  = sd_ctrl_read32((host), CTL_IRQ_MASK); \ | ||||
| 		mask |= ((i) & TMIO_MASK_IRQ); \ | ||||
| 		sd_ctrl_write32((host), CTL_IRQ_MASK, mask); \ | ||||
| 	} while (0) | ||||
| 
 | ||||
| #define ack_mmc_irqs(host, i) \ | ||||
| 	do { \ | ||||
| 		sd_ctrl_write32((host), CTL_STATUS, ~(i)); \ | ||||
| 	} while (0) | ||||
| 
 | ||||
| /* This is arbitrary, just noone needed any higher alignment yet */ | ||||
| #define MAX_ALIGN 4 | ||||
| 
 | ||||
| struct tmio_mmc_host { | ||||
| 	void __iomem *ctl; | ||||
| 	unsigned long bus_shift; | ||||
| 	struct mmc_command      *cmd; | ||||
| 	struct mmc_request      *mrq; | ||||
| 	struct mmc_data         *data; | ||||
| 	struct mmc_host         *mmc; | ||||
| 	int                     irq; | ||||
| 	unsigned int		sdio_irq_enabled; | ||||
| 
 | ||||
| 	/* Callbacks for clock / power control */ | ||||
| 	void (*set_pwr)(struct platform_device *host, int state); | ||||
| 	void (*set_clk_div)(struct platform_device *host, int state); | ||||
| 
 | ||||
| 	/* pio related stuff */ | ||||
| 	struct scatterlist      *sg_ptr; | ||||
| 	struct scatterlist      *sg_orig; | ||||
| 	unsigned int            sg_len; | ||||
| 	unsigned int            sg_off; | ||||
| 
 | ||||
| 	struct platform_device *pdev; | ||||
| 
 | ||||
| 	/* DMA support */ | ||||
| 	struct dma_chan		*chan_rx; | ||||
| 	struct dma_chan		*chan_tx; | ||||
| 	struct tasklet_struct	dma_complete; | ||||
| 	struct tasklet_struct	dma_issue; | ||||
| #ifdef CONFIG_TMIO_MMC_DMA | ||||
| 	unsigned int            dma_sglen; | ||||
| 	u8			bounce_buf[PAGE_CACHE_SIZE] __attribute__((aligned(MAX_ALIGN))); | ||||
| 	struct scatterlist	bounce_sg; | ||||
| #endif | ||||
| 
 | ||||
| 	/* Track lost interrupts */ | ||||
| 	struct delayed_work	delayed_reset_work; | ||||
| 	spinlock_t		lock; | ||||
| 	unsigned long		last_req_ts; | ||||
| }; | ||||
| 
 | ||||
| static void tmio_check_bounce_buffer(struct tmio_mmc_host *host); | ||||
| 
 | ||||
| static u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr) | ||||
| { | ||||
| 	return readw(host->ctl + (addr << host->bus_shift)); | ||||
| } | ||||
| 
 | ||||
| static void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr, | ||||
| 		u16 *buf, int count) | ||||
| { | ||||
| 	readsw(host->ctl + (addr << host->bus_shift), buf, count); | ||||
| } | ||||
| 
 | ||||
| static u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr) | ||||
| { | ||||
| 	return readw(host->ctl + (addr << host->bus_shift)) | | ||||
| 	       readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16; | ||||
| } | ||||
| 
 | ||||
| static void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val) | ||||
| { | ||||
| 	writew(val, host->ctl + (addr << host->bus_shift)); | ||||
| } | ||||
| 
 | ||||
| static void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr, | ||||
| 		u16 *buf, int count) | ||||
| { | ||||
| 	writesw(host->ctl + (addr << host->bus_shift), buf, count); | ||||
| } | ||||
| 
 | ||||
| static void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, u32 val) | ||||
| { | ||||
| 	writew(val, host->ctl + (addr << host->bus_shift)); | ||||
| 	writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift)); | ||||
| } | ||||
| 
 | ||||
| static void tmio_mmc_init_sg(struct tmio_mmc_host *host, struct mmc_data *data) | ||||
| { | ||||
| 	host->sg_len = data->sg_len; | ||||
| 	host->sg_ptr = data->sg; | ||||
| 	host->sg_orig = data->sg; | ||||
| 	host->sg_off = 0; | ||||
| } | ||||
| 
 | ||||
| static int tmio_mmc_next_sg(struct tmio_mmc_host *host) | ||||
| { | ||||
| 	host->sg_ptr = sg_next(host->sg_ptr); | ||||
| 	host->sg_off = 0; | ||||
| 	return --host->sg_len; | ||||
| } | ||||
| 
 | ||||
| static char *tmio_mmc_kmap_atomic(struct scatterlist *sg, unsigned long *flags) | ||||
| { | ||||
| 	local_irq_save(*flags); | ||||
| 	return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; | ||||
| } | ||||
| 
 | ||||
| static void tmio_mmc_kunmap_atomic(void *virt, unsigned long *flags) | ||||
| { | ||||
| 	kunmap_atomic(virt, KM_BIO_SRC_IRQ); | ||||
| 	local_irq_restore(*flags); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_MMC_DEBUG | ||||
| 
 | ||||
| #define STATUS_TO_TEXT(a) \ | ||||
| 	do { \ | ||||
| 		if (status & TMIO_STAT_##a) \ | ||||
| 			printk(#a); \ | ||||
| 	} while (0) | ||||
| 
 | ||||
| void pr_debug_status(u32 status) | ||||
| { | ||||
| 	printk(KERN_DEBUG "status: %08x = ", status); | ||||
| 	STATUS_TO_TEXT(CARD_REMOVE); | ||||
| 	STATUS_TO_TEXT(CARD_INSERT); | ||||
| 	STATUS_TO_TEXT(SIGSTATE); | ||||
| 	STATUS_TO_TEXT(WRPROTECT); | ||||
| 	STATUS_TO_TEXT(CARD_REMOVE_A); | ||||
| 	STATUS_TO_TEXT(CARD_INSERT_A); | ||||
| 	STATUS_TO_TEXT(SIGSTATE_A); | ||||
| 	STATUS_TO_TEXT(CMD_IDX_ERR); | ||||
| 	STATUS_TO_TEXT(STOPBIT_ERR); | ||||
| 	STATUS_TO_TEXT(ILL_FUNC); | ||||
| 	STATUS_TO_TEXT(CMD_BUSY); | ||||
| 	STATUS_TO_TEXT(CMDRESPEND); | ||||
| 	STATUS_TO_TEXT(DATAEND); | ||||
| 	STATUS_TO_TEXT(CRCFAIL); | ||||
| 	STATUS_TO_TEXT(DATATIMEOUT); | ||||
| 	STATUS_TO_TEXT(CMDTIMEOUT); | ||||
| 	STATUS_TO_TEXT(RXOVERFLOW); | ||||
| 	STATUS_TO_TEXT(TXUNDERRUN); | ||||
| 	STATUS_TO_TEXT(RXRDY); | ||||
| 	STATUS_TO_TEXT(TXRQ); | ||||
| 	STATUS_TO_TEXT(ILL_ACCESS); | ||||
| 	printk("\n"); | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| #define pr_debug_status(s)  do { } while (0) | ||||
| #endif | ||||
| 
 | ||||
| static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) | ||||
| { | ||||
| 	struct tmio_mmc_host *host = mmc_priv(mmc); | ||||
| 
 | ||||
| 	if (enable) { | ||||
| 		host->sdio_irq_enabled = 1; | ||||
| 		sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001); | ||||
| 		sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, | ||||
| 			(TMIO_SDIO_MASK_ALL & ~TMIO_SDIO_STAT_IOIRQ)); | ||||
| 	} else { | ||||
| 		sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, TMIO_SDIO_MASK_ALL); | ||||
| 		sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000); | ||||
| 		host->sdio_irq_enabled = 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock) | ||||
| { | ||||
| @ -55,8 +300,23 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock) | ||||
| 
 | ||||
| static void tmio_mmc_clk_stop(struct tmio_mmc_host *host) | ||||
| { | ||||
| 	struct mfd_cell *cell = host->pdev->dev.platform_data; | ||||
| 	struct tmio_mmc_data *pdata = cell->driver_data; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Testing on sh-mobile showed that SDIO IRQs are unmasked when | ||||
| 	 * CTL_CLK_AND_WAIT_CTL gets written, so we have to disable the | ||||
| 	 * device IRQ here and restore the SDIO IRQ mask before | ||||
| 	 * re-enabling the device IRQ. | ||||
| 	 */ | ||||
| 	if (pdata->flags & TMIO_MMC_SDIO_IRQ) | ||||
| 		disable_irq(host->irq); | ||||
| 	sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000); | ||||
| 	msleep(10); | ||||
| 	if (pdata->flags & TMIO_MMC_SDIO_IRQ) { | ||||
| 		tmio_mmc_enable_sdio_irq(host->mmc, host->sdio_irq_enabled); | ||||
| 		enable_irq(host->irq); | ||||
| 	} | ||||
| 	sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 & | ||||
| 		sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | ||||
| 	msleep(10); | ||||
| @ -64,11 +324,21 @@ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host) | ||||
| 
 | ||||
| static void tmio_mmc_clk_start(struct tmio_mmc_host *host) | ||||
| { | ||||
| 	struct mfd_cell *cell = host->pdev->dev.platform_data; | ||||
| 	struct tmio_mmc_data *pdata = cell->driver_data; | ||||
| 
 | ||||
| 	sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 | | ||||
| 		sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | ||||
| 	msleep(10); | ||||
| 	/* see comment in tmio_mmc_clk_stop above */ | ||||
| 	if (pdata->flags & TMIO_MMC_SDIO_IRQ) | ||||
| 		disable_irq(host->irq); | ||||
| 	sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100); | ||||
| 	msleep(10); | ||||
| 	if (pdata->flags & TMIO_MMC_SDIO_IRQ) { | ||||
| 		tmio_mmc_enable_sdio_irq(host->mmc, host->sdio_irq_enabled); | ||||
| 		enable_irq(host->irq); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void reset(struct tmio_mmc_host *host) | ||||
| @ -82,15 +352,60 @@ static void reset(struct tmio_mmc_host *host) | ||||
| 	msleep(10); | ||||
| } | ||||
| 
 | ||||
| static void tmio_mmc_reset_work(struct work_struct *work) | ||||
| { | ||||
| 	struct tmio_mmc_host *host = container_of(work, struct tmio_mmc_host, | ||||
| 						  delayed_reset_work.work); | ||||
| 	struct mmc_request *mrq; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&host->lock, flags); | ||||
| 	mrq = host->mrq; | ||||
| 
 | ||||
| 	/* request already finished */ | ||||
| 	if (!mrq | ||||
| 	    || time_is_after_jiffies(host->last_req_ts + | ||||
| 		msecs_to_jiffies(2000))) { | ||||
| 		spin_unlock_irqrestore(&host->lock, flags); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	dev_warn(&host->pdev->dev, | ||||
| 		"timeout waiting for hardware interrupt (CMD%u)\n", | ||||
| 		mrq->cmd->opcode); | ||||
| 
 | ||||
| 	if (host->data) | ||||
| 		host->data->error = -ETIMEDOUT; | ||||
| 	else if (host->cmd) | ||||
| 		host->cmd->error = -ETIMEDOUT; | ||||
| 	else | ||||
| 		mrq->cmd->error = -ETIMEDOUT; | ||||
| 
 | ||||
| 	host->cmd = NULL; | ||||
| 	host->data = NULL; | ||||
| 	host->mrq = NULL; | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&host->lock, flags); | ||||
| 
 | ||||
| 	reset(host); | ||||
| 
 | ||||
| 	mmc_request_done(host->mmc, mrq); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| tmio_mmc_finish_request(struct tmio_mmc_host *host) | ||||
| { | ||||
| 	struct mmc_request *mrq = host->mrq; | ||||
| 
 | ||||
| 	if (!mrq) | ||||
| 		return; | ||||
| 
 | ||||
| 	host->mrq = NULL; | ||||
| 	host->cmd = NULL; | ||||
| 	host->data = NULL; | ||||
| 
 | ||||
| 	cancel_delayed_work(&host->delayed_reset_work); | ||||
| 
 | ||||
| 	mmc_request_done(host->mmc, mrq); | ||||
| } | ||||
| 
 | ||||
| @ -200,6 +515,7 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host) | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| /* needs to be called with host->lock held */ | ||||
| static void tmio_mmc_do_data_irq(struct tmio_mmc_host *host) | ||||
| { | ||||
| 	struct mmc_data *data = host->data; | ||||
| @ -233,6 +549,8 @@ static void tmio_mmc_do_data_irq(struct tmio_mmc_host *host) | ||||
| 	if (data->flags & MMC_DATA_READ) { | ||||
| 		if (!host->chan_rx) | ||||
| 			disable_mmc_irqs(host, TMIO_MASK_READOP); | ||||
| 		else | ||||
| 			tmio_check_bounce_buffer(host); | ||||
| 		dev_dbg(&host->pdev->dev, "Complete Rx request %p\n", | ||||
| 			host->mrq); | ||||
| 	} else { | ||||
| @ -254,10 +572,12 @@ static void tmio_mmc_do_data_irq(struct tmio_mmc_host *host) | ||||
| 
 | ||||
| static void tmio_mmc_data_irq(struct tmio_mmc_host *host) | ||||
| { | ||||
| 	struct mmc_data *data = host->data; | ||||
| 	struct mmc_data *data; | ||||
| 	spin_lock(&host->lock); | ||||
| 	data = host->data; | ||||
| 
 | ||||
| 	if (!data) | ||||
| 		return; | ||||
| 		goto out; | ||||
| 
 | ||||
| 	if (host->chan_tx && (data->flags & MMC_DATA_WRITE)) { | ||||
| 		/*
 | ||||
| @ -278,6 +598,8 @@ static void tmio_mmc_data_irq(struct tmio_mmc_host *host) | ||||
| 	} else { | ||||
| 		tmio_mmc_do_data_irq(host); | ||||
| 	} | ||||
| out: | ||||
| 	spin_unlock(&host->lock); | ||||
| } | ||||
| 
 | ||||
| static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, | ||||
| @ -286,9 +608,11 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, | ||||
| 	struct mmc_command *cmd = host->cmd; | ||||
| 	int i, addr; | ||||
| 
 | ||||
| 	spin_lock(&host->lock); | ||||
| 
 | ||||
| 	if (!host->cmd) { | ||||
| 		pr_debug("Spurious CMD irq\n"); | ||||
| 		return; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	host->cmd = NULL; | ||||
| @ -324,8 +648,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, | ||||
| 			if (!host->chan_rx) | ||||
| 				enable_mmc_irqs(host, TMIO_MASK_READOP); | ||||
| 		} else { | ||||
| 			struct dma_chan *chan = host->chan_tx; | ||||
| 			if (!chan) | ||||
| 			if (!host->chan_tx) | ||||
| 				enable_mmc_irqs(host, TMIO_MASK_WRITEOP); | ||||
| 			else | ||||
| 				tasklet_schedule(&host->dma_issue); | ||||
| @ -334,13 +657,19 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, | ||||
| 		tmio_mmc_finish_request(host); | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	spin_unlock(&host->lock); | ||||
| 
 | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t tmio_mmc_irq(int irq, void *devid) | ||||
| { | ||||
| 	struct tmio_mmc_host *host = devid; | ||||
| 	struct mfd_cell	*cell = host->pdev->dev.platform_data; | ||||
| 	struct tmio_mmc_data *pdata = cell->driver_data; | ||||
| 	unsigned int ireg, irq_mask, status; | ||||
| 	unsigned int sdio_ireg, sdio_irq_mask, sdio_status; | ||||
| 
 | ||||
| 	pr_debug("MMC IRQ begin\n"); | ||||
| 
 | ||||
| @ -348,6 +677,29 @@ static irqreturn_t tmio_mmc_irq(int irq, void *devid) | ||||
| 	irq_mask = sd_ctrl_read32(host, CTL_IRQ_MASK); | ||||
| 	ireg = status & TMIO_MASK_IRQ & ~irq_mask; | ||||
| 
 | ||||
| 	sdio_ireg = 0; | ||||
| 	if (!ireg && pdata->flags & TMIO_MMC_SDIO_IRQ) { | ||||
| 		sdio_status = sd_ctrl_read16(host, CTL_SDIO_STATUS); | ||||
| 		sdio_irq_mask = sd_ctrl_read16(host, CTL_SDIO_IRQ_MASK); | ||||
| 		sdio_ireg = sdio_status & TMIO_SDIO_MASK_ALL & ~sdio_irq_mask; | ||||
| 
 | ||||
| 		sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status & ~TMIO_SDIO_MASK_ALL); | ||||
| 
 | ||||
| 		if (sdio_ireg && !host->sdio_irq_enabled) { | ||||
| 			pr_warning("tmio_mmc: Spurious SDIO IRQ, disabling! 0x%04x 0x%04x 0x%04x\n", | ||||
| 				   sdio_status, sdio_irq_mask, sdio_ireg); | ||||
| 			tmio_mmc_enable_sdio_irq(host->mmc, 0); | ||||
| 			goto out; | ||||
| 		} | ||||
| 
 | ||||
| 		if (host->mmc->caps & MMC_CAP_SDIO_IRQ && | ||||
| 			sdio_ireg & TMIO_SDIO_STAT_IOIRQ) | ||||
| 			mmc_signal_sdio_irq(host->mmc); | ||||
| 
 | ||||
| 		if (sdio_ireg) | ||||
| 			goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	pr_debug_status(status); | ||||
| 	pr_debug_status(ireg); | ||||
| 
 | ||||
| @ -375,8 +727,10 @@ static irqreturn_t tmio_mmc_irq(int irq, void *devid) | ||||
|  */ | ||||
| 
 | ||||
| 		/* Command completion */ | ||||
| 		if (ireg & TMIO_MASK_CMD) { | ||||
| 			ack_mmc_irqs(host, TMIO_MASK_CMD); | ||||
| 		if (ireg & (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT)) { | ||||
| 			ack_mmc_irqs(host, | ||||
| 				     TMIO_STAT_CMDRESPEND | | ||||
| 				     TMIO_STAT_CMDTIMEOUT); | ||||
| 			tmio_mmc_cmd_irq(host, status); | ||||
| 		} | ||||
| 
 | ||||
| @ -407,6 +761,16 @@ out: | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_TMIO_MMC_DMA | ||||
| static void tmio_check_bounce_buffer(struct tmio_mmc_host *host) | ||||
| { | ||||
| 	if (host->sg_ptr == &host->bounce_sg) { | ||||
| 		unsigned long flags; | ||||
| 		void *sg_vaddr = tmio_mmc_kmap_atomic(host->sg_orig, &flags); | ||||
| 		memcpy(sg_vaddr, host->bounce_buf, host->bounce_sg.length); | ||||
| 		tmio_mmc_kunmap_atomic(sg_vaddr, &flags); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable) | ||||
| { | ||||
| #if defined(CONFIG_SUPERH) || defined(CONFIG_ARCH_SHMOBILE) | ||||
| @ -427,12 +791,39 @@ static void tmio_dma_complete(void *arg) | ||||
| 		enable_mmc_irqs(host, TMIO_STAT_DATAEND); | ||||
| } | ||||
| 
 | ||||
| static int tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) | ||||
| static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) | ||||
| { | ||||
| 	struct scatterlist *sg = host->sg_ptr; | ||||
| 	struct scatterlist *sg = host->sg_ptr, *sg_tmp; | ||||
| 	struct dma_async_tx_descriptor *desc = NULL; | ||||
| 	struct dma_chan *chan = host->chan_rx; | ||||
| 	int ret; | ||||
| 	struct mfd_cell	*cell = host->pdev->dev.platform_data; | ||||
| 	struct tmio_mmc_data *pdata = cell->driver_data; | ||||
| 	dma_cookie_t cookie; | ||||
| 	int ret, i; | ||||
| 	bool aligned = true, multiple = true; | ||||
| 	unsigned int align = (1 << pdata->dma->alignment_shift) - 1; | ||||
| 
 | ||||
| 	for_each_sg(sg, sg_tmp, host->sg_len, i) { | ||||
| 		if (sg_tmp->offset & align) | ||||
| 			aligned = false; | ||||
| 		if (sg_tmp->length & align) { | ||||
| 			multiple = false; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_CACHE_SIZE || | ||||
| 			  align >= MAX_ALIGN)) || !multiple) { | ||||
| 		ret = -EINVAL; | ||||
| 		goto pio; | ||||
| 	} | ||||
| 
 | ||||
| 	/* The only sg element can be unaligned, use our bounce buffer then */ | ||||
| 	if (!aligned) { | ||||
| 		sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length); | ||||
| 		host->sg_ptr = &host->bounce_sg; | ||||
| 		sg = host->sg_ptr; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, DMA_FROM_DEVICE); | ||||
| 	if (ret > 0) { | ||||
| @ -442,21 +833,21 @@ static int tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) | ||||
| 	} | ||||
| 
 | ||||
| 	if (desc) { | ||||
| 		host->desc = desc; | ||||
| 		desc->callback = tmio_dma_complete; | ||||
| 		desc->callback_param = host; | ||||
| 		host->cookie = desc->tx_submit(desc); | ||||
| 		if (host->cookie < 0) { | ||||
| 			host->desc = NULL; | ||||
| 			ret = host->cookie; | ||||
| 		cookie = desc->tx_submit(desc); | ||||
| 		if (cookie < 0) { | ||||
| 			desc = NULL; | ||||
| 			ret = cookie; | ||||
| 		} else { | ||||
| 			chan->device->device_issue_pending(chan); | ||||
| 		} | ||||
| 	} | ||||
| 	dev_dbg(&host->pdev->dev, "%s(): mapped %d -> %d, cookie %d, rq %p\n", | ||||
| 		__func__, host->sg_len, ret, host->cookie, host->mrq); | ||||
| 		__func__, host->sg_len, ret, cookie, host->mrq); | ||||
| 
 | ||||
| 	if (!host->desc) { | ||||
| pio: | ||||
| 	if (!desc) { | ||||
| 		/* DMA failed, fall back to PIO */ | ||||
| 		if (ret >= 0) | ||||
| 			ret = -EIO; | ||||
| @ -471,24 +862,49 @@ static int tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) | ||||
| 		dev_warn(&host->pdev->dev, | ||||
| 			 "DMA failed: %d, falling back to PIO\n", ret); | ||||
| 		tmio_mmc_enable_dma(host, false); | ||||
| 		reset(host); | ||||
| 		/* Fail this request, let above layers recover */ | ||||
| 		host->mrq->cmd->error = ret; | ||||
| 		tmio_mmc_finish_request(host); | ||||
| 	} | ||||
| 
 | ||||
| 	dev_dbg(&host->pdev->dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__, | ||||
| 		desc, host->cookie, host->sg_len); | ||||
| 
 | ||||
| 	return ret > 0 ? 0 : ret; | ||||
| 		desc, cookie, host->sg_len); | ||||
| } | ||||
| 
 | ||||
| static int tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) | ||||
| static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) | ||||
| { | ||||
| 	struct scatterlist *sg = host->sg_ptr; | ||||
| 	struct scatterlist *sg = host->sg_ptr, *sg_tmp; | ||||
| 	struct dma_async_tx_descriptor *desc = NULL; | ||||
| 	struct dma_chan *chan = host->chan_tx; | ||||
| 	int ret; | ||||
| 	struct mfd_cell	*cell = host->pdev->dev.platform_data; | ||||
| 	struct tmio_mmc_data *pdata = cell->driver_data; | ||||
| 	dma_cookie_t cookie; | ||||
| 	int ret, i; | ||||
| 	bool aligned = true, multiple = true; | ||||
| 	unsigned int align = (1 << pdata->dma->alignment_shift) - 1; | ||||
| 
 | ||||
| 	for_each_sg(sg, sg_tmp, host->sg_len, i) { | ||||
| 		if (sg_tmp->offset & align) | ||||
| 			aligned = false; | ||||
| 		if (sg_tmp->length & align) { | ||||
| 			multiple = false; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_CACHE_SIZE || | ||||
| 			  align >= MAX_ALIGN)) || !multiple) { | ||||
| 		ret = -EINVAL; | ||||
| 		goto pio; | ||||
| 	} | ||||
| 
 | ||||
| 	/* The only sg element can be unaligned, use our bounce buffer then */ | ||||
| 	if (!aligned) { | ||||
| 		unsigned long flags; | ||||
| 		void *sg_vaddr = tmio_mmc_kmap_atomic(sg, &flags); | ||||
| 		sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length); | ||||
| 		memcpy(host->bounce_buf, sg_vaddr, host->bounce_sg.length); | ||||
| 		tmio_mmc_kunmap_atomic(sg_vaddr, &flags); | ||||
| 		host->sg_ptr = &host->bounce_sg; | ||||
| 		sg = host->sg_ptr; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, DMA_TO_DEVICE); | ||||
| 	if (ret > 0) { | ||||
| @ -498,19 +914,19 @@ static int tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) | ||||
| 	} | ||||
| 
 | ||||
| 	if (desc) { | ||||
| 		host->desc = desc; | ||||
| 		desc->callback = tmio_dma_complete; | ||||
| 		desc->callback_param = host; | ||||
| 		host->cookie = desc->tx_submit(desc); | ||||
| 		if (host->cookie < 0) { | ||||
| 			host->desc = NULL; | ||||
| 			ret = host->cookie; | ||||
| 		cookie = desc->tx_submit(desc); | ||||
| 		if (cookie < 0) { | ||||
| 			desc = NULL; | ||||
| 			ret = cookie; | ||||
| 		} | ||||
| 	} | ||||
| 	dev_dbg(&host->pdev->dev, "%s(): mapped %d -> %d, cookie %d, rq %p\n", | ||||
| 		__func__, host->sg_len, ret, host->cookie, host->mrq); | ||||
| 		__func__, host->sg_len, ret, cookie, host->mrq); | ||||
| 
 | ||||
| 	if (!host->desc) { | ||||
| pio: | ||||
| 	if (!desc) { | ||||
| 		/* DMA failed, fall back to PIO */ | ||||
| 		if (ret >= 0) | ||||
| 			ret = -EIO; | ||||
| @ -525,30 +941,22 @@ static int tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) | ||||
| 		dev_warn(&host->pdev->dev, | ||||
| 			 "DMA failed: %d, falling back to PIO\n", ret); | ||||
| 		tmio_mmc_enable_dma(host, false); | ||||
| 		reset(host); | ||||
| 		/* Fail this request, let above layers recover */ | ||||
| 		host->mrq->cmd->error = ret; | ||||
| 		tmio_mmc_finish_request(host); | ||||
| 	} | ||||
| 
 | ||||
| 	dev_dbg(&host->pdev->dev, "%s(): desc %p, cookie %d\n", __func__, | ||||
| 		desc, host->cookie); | ||||
| 
 | ||||
| 	return ret > 0 ? 0 : ret; | ||||
| 		desc, cookie); | ||||
| } | ||||
| 
 | ||||
| static int tmio_mmc_start_dma(struct tmio_mmc_host *host, | ||||
| static void tmio_mmc_start_dma(struct tmio_mmc_host *host, | ||||
| 			       struct mmc_data *data) | ||||
| { | ||||
| 	if (data->flags & MMC_DATA_READ) { | ||||
| 		if (host->chan_rx) | ||||
| 			return tmio_mmc_start_dma_rx(host); | ||||
| 			tmio_mmc_start_dma_rx(host); | ||||
| 	} else { | ||||
| 		if (host->chan_tx) | ||||
| 			return tmio_mmc_start_dma_tx(host); | ||||
| 			tmio_mmc_start_dma_tx(host); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void tmio_issue_tasklet_fn(unsigned long priv) | ||||
| @ -562,6 +970,12 @@ static void tmio_issue_tasklet_fn(unsigned long priv) | ||||
| static void tmio_tasklet_fn(unsigned long arg) | ||||
| { | ||||
| 	struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&host->lock, flags); | ||||
| 
 | ||||
| 	if (!host->data) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	if (host->data->flags & MMC_DATA_READ) | ||||
| 		dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->dma_sglen, | ||||
| @ -571,6 +985,8 @@ static void tmio_tasklet_fn(unsigned long arg) | ||||
| 			     DMA_TO_DEVICE); | ||||
| 
 | ||||
| 	tmio_mmc_do_data_irq(host); | ||||
| out: | ||||
| 	spin_unlock_irqrestore(&host->lock, flags); | ||||
| } | ||||
| 
 | ||||
| /* It might be necessary to make filter MFD specific */ | ||||
| @ -584,9 +1000,6 @@ static bool tmio_mmc_filter(struct dma_chan *chan, void *arg) | ||||
| static void tmio_mmc_request_dma(struct tmio_mmc_host *host, | ||||
| 				 struct tmio_mmc_data *pdata) | ||||
| { | ||||
| 	host->cookie = -EINVAL; | ||||
| 	host->desc = NULL; | ||||
| 
 | ||||
| 	/* We can only either use DMA for both Tx and Rx or not use it at all */ | ||||
| 	if (pdata->dma) { | ||||
| 		dma_cap_mask_t mask; | ||||
| @ -632,15 +1045,15 @@ static void tmio_mmc_release_dma(struct tmio_mmc_host *host) | ||||
| 		host->chan_rx = NULL; | ||||
| 		dma_release_channel(chan); | ||||
| 	} | ||||
| 
 | ||||
| 	host->cookie = -EINVAL; | ||||
| 	host->desc = NULL; | ||||
| } | ||||
| #else | ||||
| static int tmio_mmc_start_dma(struct tmio_mmc_host *host, | ||||
| static void tmio_check_bounce_buffer(struct tmio_mmc_host *host) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static void tmio_mmc_start_dma(struct tmio_mmc_host *host, | ||||
| 			       struct mmc_data *data) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void tmio_mmc_request_dma(struct tmio_mmc_host *host, | ||||
| @ -682,7 +1095,9 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host, | ||||
| 	sd_ctrl_write16(host, CTL_SD_XFER_LEN, data->blksz); | ||||
| 	sd_ctrl_write16(host, CTL_XFER_BLK_COUNT, data->blocks); | ||||
| 
 | ||||
| 	return tmio_mmc_start_dma(host, data); | ||||
| 	tmio_mmc_start_dma(host, data); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Process requests from the MMC layer */ | ||||
| @ -694,6 +1109,8 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) | ||||
| 	if (host->mrq) | ||||
| 		pr_debug("request not null\n"); | ||||
| 
 | ||||
| 	host->last_req_ts = jiffies; | ||||
| 	wmb(); | ||||
| 	host->mrq = mrq; | ||||
| 
 | ||||
| 	if (mrq->data) { | ||||
| @ -703,10 +1120,14 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) | ||||
| 	} | ||||
| 
 | ||||
| 	ret = tmio_mmc_start_command(host, mrq->cmd); | ||||
| 	if (!ret) | ||||
| 	if (!ret) { | ||||
| 		schedule_delayed_work(&host->delayed_reset_work, | ||||
| 				      msecs_to_jiffies(2000)); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| fail: | ||||
| 	host->mrq = NULL; | ||||
| 	mrq->cmd->error = ret; | ||||
| 	mmc_request_done(mmc, mrq); | ||||
| } | ||||
| @ -780,6 +1201,7 @@ static const struct mmc_host_ops tmio_mmc_ops = { | ||||
| 	.set_ios	= tmio_mmc_set_ios, | ||||
| 	.get_ro         = tmio_mmc_get_ro, | ||||
| 	.get_cd		= tmio_mmc_get_cd, | ||||
| 	.enable_sdio_irq = tmio_mmc_enable_sdio_irq, | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_PM | ||||
| @ -864,10 +1286,15 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) | ||||
| 		goto host_free; | ||||
| 
 | ||||
| 	mmc->ops = &tmio_mmc_ops; | ||||
| 	mmc->caps = MMC_CAP_4_BIT_DATA; | ||||
| 	mmc->caps |= pdata->capabilities; | ||||
| 	mmc->caps = MMC_CAP_4_BIT_DATA | pdata->capabilities; | ||||
| 	mmc->f_max = pdata->hclk; | ||||
| 	mmc->f_min = mmc->f_max / 512; | ||||
| 	mmc->max_segs = 32; | ||||
| 	mmc->max_blk_size = 512; | ||||
| 	mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) * | ||||
| 		mmc->max_segs; | ||||
| 	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; | ||||
| 	mmc->max_seg_size = mmc->max_req_size; | ||||
| 	if (pdata->ocr_mask) | ||||
| 		mmc->ocr_avail = pdata->ocr_mask; | ||||
| 	else | ||||
| @ -890,12 +1317,19 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) | ||||
| 		goto cell_disable; | ||||
| 
 | ||||
| 	disable_mmc_irqs(host, TMIO_MASK_ALL); | ||||
| 	if (pdata->flags & TMIO_MMC_SDIO_IRQ) | ||||
| 		tmio_mmc_enable_sdio_irq(mmc, 0); | ||||
| 
 | ||||
| 	ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED | | ||||
| 		IRQF_TRIGGER_FALLING, dev_name(&dev->dev), host); | ||||
| 	if (ret) | ||||
| 		goto cell_disable; | ||||
| 
 | ||||
| 	spin_lock_init(&host->lock); | ||||
| 
 | ||||
| 	/* Init delayed work for request timeouts */ | ||||
| 	INIT_DELAYED_WORK(&host->delayed_reset_work, tmio_mmc_reset_work); | ||||
| 
 | ||||
| 	/* See if we also get DMA */ | ||||
| 	tmio_mmc_request_dma(host, pdata); | ||||
| 
 | ||||
| @ -934,6 +1368,7 @@ static int __devexit tmio_mmc_remove(struct platform_device *dev) | ||||
| 	if (mmc) { | ||||
| 		struct tmio_mmc_host *host = mmc_priv(mmc); | ||||
| 		mmc_remove_host(mmc); | ||||
| 		cancel_delayed_work_sync(&host->delayed_reset_work); | ||||
| 		tmio_mmc_release_dma(host); | ||||
| 		free_irq(host->irq, host); | ||||
| 		if (cell->disable) | ||||
|  | ||||
| @ -1,228 +0,0 @@ | ||||
| /* Definitons for use with the tmio_mmc.c
 | ||||
|  * | ||||
|  * (c) 2004 Ian Molton <spyro@f2s.com> | ||||
|  * (c) 2007 Ian Molton <spyro@f2s.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/highmem.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/dmaengine.h> | ||||
| 
 | ||||
| #define CTL_SD_CMD 0x00 | ||||
| #define CTL_ARG_REG 0x04 | ||||
| #define CTL_STOP_INTERNAL_ACTION 0x08 | ||||
| #define CTL_XFER_BLK_COUNT 0xa | ||||
| #define CTL_RESPONSE 0x0c | ||||
| #define CTL_STATUS 0x1c | ||||
| #define CTL_IRQ_MASK 0x20 | ||||
| #define CTL_SD_CARD_CLK_CTL 0x24 | ||||
| #define CTL_SD_XFER_LEN 0x26 | ||||
| #define CTL_SD_MEM_CARD_OPT 0x28 | ||||
| #define CTL_SD_ERROR_DETAIL_STATUS 0x2c | ||||
| #define CTL_SD_DATA_PORT 0x30 | ||||
| #define CTL_TRANSACTION_CTL 0x34 | ||||
| #define CTL_RESET_SD 0xe0 | ||||
| #define CTL_SDIO_REGS 0x100 | ||||
| #define CTL_CLK_AND_WAIT_CTL 0x138 | ||||
| #define CTL_RESET_SDIO 0x1e0 | ||||
| 
 | ||||
| /* Definitions for values the CTRL_STATUS register can take. */ | ||||
| #define TMIO_STAT_CMDRESPEND    0x00000001 | ||||
| #define TMIO_STAT_DATAEND       0x00000004 | ||||
| #define TMIO_STAT_CARD_REMOVE   0x00000008 | ||||
| #define TMIO_STAT_CARD_INSERT   0x00000010 | ||||
| #define TMIO_STAT_SIGSTATE      0x00000020 | ||||
| #define TMIO_STAT_WRPROTECT     0x00000080 | ||||
| #define TMIO_STAT_CARD_REMOVE_A 0x00000100 | ||||
| #define TMIO_STAT_CARD_INSERT_A 0x00000200 | ||||
| #define TMIO_STAT_SIGSTATE_A    0x00000400 | ||||
| #define TMIO_STAT_CMD_IDX_ERR   0x00010000 | ||||
| #define TMIO_STAT_CRCFAIL       0x00020000 | ||||
| #define TMIO_STAT_STOPBIT_ERR   0x00040000 | ||||
| #define TMIO_STAT_DATATIMEOUT   0x00080000 | ||||
| #define TMIO_STAT_RXOVERFLOW    0x00100000 | ||||
| #define TMIO_STAT_TXUNDERRUN    0x00200000 | ||||
| #define TMIO_STAT_CMDTIMEOUT    0x00400000 | ||||
| #define TMIO_STAT_RXRDY         0x01000000 | ||||
| #define TMIO_STAT_TXRQ          0x02000000 | ||||
| #define TMIO_STAT_ILL_FUNC      0x20000000 | ||||
| #define TMIO_STAT_CMD_BUSY      0x40000000 | ||||
| #define TMIO_STAT_ILL_ACCESS    0x80000000 | ||||
| 
 | ||||
| /* Define some IRQ masks */ | ||||
| /* This is the mask used at reset by the chip */ | ||||
| #define TMIO_MASK_ALL           0x837f031d | ||||
| #define TMIO_MASK_READOP  (TMIO_STAT_RXRDY | TMIO_STAT_DATAEND) | ||||
| #define TMIO_MASK_WRITEOP (TMIO_STAT_TXRQ | TMIO_STAT_DATAEND) | ||||
| #define TMIO_MASK_CMD     (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT | \ | ||||
| 		TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT) | ||||
| #define TMIO_MASK_IRQ     (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD) | ||||
| 
 | ||||
| 
 | ||||
| #define enable_mmc_irqs(host, i) \ | ||||
| 	do { \ | ||||
| 		u32 mask;\ | ||||
| 		mask  = sd_ctrl_read32((host), CTL_IRQ_MASK); \ | ||||
| 		mask &= ~((i) & TMIO_MASK_IRQ); \ | ||||
| 		sd_ctrl_write32((host), CTL_IRQ_MASK, mask); \ | ||||
| 	} while (0) | ||||
| 
 | ||||
| #define disable_mmc_irqs(host, i) \ | ||||
| 	do { \ | ||||
| 		u32 mask;\ | ||||
| 		mask  = sd_ctrl_read32((host), CTL_IRQ_MASK); \ | ||||
| 		mask |= ((i) & TMIO_MASK_IRQ); \ | ||||
| 		sd_ctrl_write32((host), CTL_IRQ_MASK, mask); \ | ||||
| 	} while (0) | ||||
| 
 | ||||
| #define ack_mmc_irqs(host, i) \ | ||||
| 	do { \ | ||||
| 		sd_ctrl_write32((host), CTL_STATUS, ~(i)); \ | ||||
| 	} while (0) | ||||
| 
 | ||||
| 
 | ||||
| struct tmio_mmc_host { | ||||
| 	void __iomem *ctl; | ||||
| 	unsigned long bus_shift; | ||||
| 	struct mmc_command      *cmd; | ||||
| 	struct mmc_request      *mrq; | ||||
| 	struct mmc_data         *data; | ||||
| 	struct mmc_host         *mmc; | ||||
| 	int                     irq; | ||||
| 
 | ||||
| 	/* Callbacks for clock / power control */ | ||||
| 	void (*set_pwr)(struct platform_device *host, int state); | ||||
| 	void (*set_clk_div)(struct platform_device *host, int state); | ||||
| 
 | ||||
| 	/* pio related stuff */ | ||||
| 	struct scatterlist      *sg_ptr; | ||||
| 	unsigned int            sg_len; | ||||
| 	unsigned int            sg_off; | ||||
| 
 | ||||
| 	struct platform_device *pdev; | ||||
| 
 | ||||
| 	/* DMA support */ | ||||
| 	struct dma_chan		*chan_rx; | ||||
| 	struct dma_chan		*chan_tx; | ||||
| 	struct tasklet_struct	dma_complete; | ||||
| 	struct tasklet_struct	dma_issue; | ||||
| #ifdef CONFIG_TMIO_MMC_DMA | ||||
| 	struct dma_async_tx_descriptor *desc; | ||||
| 	unsigned int            dma_sglen; | ||||
| 	dma_cookie_t		cookie; | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| #include <linux/io.h> | ||||
| 
 | ||||
| static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr) | ||||
| { | ||||
| 	return readw(host->ctl + (addr << host->bus_shift)); | ||||
| } | ||||
| 
 | ||||
| static inline void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr, | ||||
| 		u16 *buf, int count) | ||||
| { | ||||
| 	readsw(host->ctl + (addr << host->bus_shift), buf, count); | ||||
| } | ||||
| 
 | ||||
| static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr) | ||||
| { | ||||
| 	return readw(host->ctl + (addr << host->bus_shift)) | | ||||
| 	       readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16; | ||||
| } | ||||
| 
 | ||||
| static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, | ||||
| 		u16 val) | ||||
| { | ||||
| 	writew(val, host->ctl + (addr << host->bus_shift)); | ||||
| } | ||||
| 
 | ||||
| static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr, | ||||
| 		u16 *buf, int count) | ||||
| { | ||||
| 	writesw(host->ctl + (addr << host->bus_shift), buf, count); | ||||
| } | ||||
| 
 | ||||
| static inline void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, | ||||
| 		u32 val) | ||||
| { | ||||
| 	writew(val, host->ctl + (addr << host->bus_shift)); | ||||
| 	writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift)); | ||||
| } | ||||
| 
 | ||||
| #include <linux/scatterlist.h> | ||||
| #include <linux/blkdev.h> | ||||
| 
 | ||||
| static inline void tmio_mmc_init_sg(struct tmio_mmc_host *host, | ||||
| 	struct mmc_data *data) | ||||
| { | ||||
| 	host->sg_len = data->sg_len; | ||||
| 	host->sg_ptr = data->sg; | ||||
| 	host->sg_off = 0; | ||||
| } | ||||
| 
 | ||||
| static inline int tmio_mmc_next_sg(struct tmio_mmc_host *host) | ||||
| { | ||||
| 	host->sg_ptr = sg_next(host->sg_ptr); | ||||
| 	host->sg_off = 0; | ||||
| 	return --host->sg_len; | ||||
| } | ||||
| 
 | ||||
| static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg, | ||||
| 	unsigned long *flags) | ||||
| { | ||||
| 	local_irq_save(*flags); | ||||
| 	return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; | ||||
| } | ||||
| 
 | ||||
| static inline void tmio_mmc_kunmap_atomic(void *virt, | ||||
| 	unsigned long *flags) | ||||
| { | ||||
| 	kunmap_atomic(virt, KM_BIO_SRC_IRQ); | ||||
| 	local_irq_restore(*flags); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_MMC_DEBUG | ||||
| 
 | ||||
| #define STATUS_TO_TEXT(a) \ | ||||
| 	do { \ | ||||
| 		if (status & TMIO_STAT_##a) \ | ||||
| 			printk(#a); \ | ||||
| 	} while (0) | ||||
| 
 | ||||
| void pr_debug_status(u32 status) | ||||
| { | ||||
| 	printk(KERN_DEBUG "status: %08x = ", status); | ||||
| 	STATUS_TO_TEXT(CARD_REMOVE); | ||||
| 	STATUS_TO_TEXT(CARD_INSERT); | ||||
| 	STATUS_TO_TEXT(SIGSTATE); | ||||
| 	STATUS_TO_TEXT(WRPROTECT); | ||||
| 	STATUS_TO_TEXT(CARD_REMOVE_A); | ||||
| 	STATUS_TO_TEXT(CARD_INSERT_A); | ||||
| 	STATUS_TO_TEXT(SIGSTATE_A); | ||||
| 	STATUS_TO_TEXT(CMD_IDX_ERR); | ||||
| 	STATUS_TO_TEXT(STOPBIT_ERR); | ||||
| 	STATUS_TO_TEXT(ILL_FUNC); | ||||
| 	STATUS_TO_TEXT(CMD_BUSY); | ||||
| 	STATUS_TO_TEXT(CMDRESPEND); | ||||
| 	STATUS_TO_TEXT(DATAEND); | ||||
| 	STATUS_TO_TEXT(CRCFAIL); | ||||
| 	STATUS_TO_TEXT(DATATIMEOUT); | ||||
| 	STATUS_TO_TEXT(CMDTIMEOUT); | ||||
| 	STATUS_TO_TEXT(RXOVERFLOW); | ||||
| 	STATUS_TO_TEXT(TXUNDERRUN); | ||||
| 	STATUS_TO_TEXT(RXRDY); | ||||
| 	STATUS_TO_TEXT(TXRQ); | ||||
| 	STATUS_TO_TEXT(ILL_ACCESS); | ||||
| 	printk("\n"); | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| #define pr_debug_status(s)  do { } while (0) | ||||
| #endif | ||||
| @ -57,6 +57,10 @@ | ||||
|  * is configured in 4-bit mode. | ||||
|  */ | ||||
| #define TMIO_MMC_BLKSZ_2BYTES		(1 << 1) | ||||
| /*
 | ||||
|  * Some controllers can support SDIO IRQ signalling. | ||||
|  */ | ||||
| #define TMIO_MMC_SDIO_IRQ		(1 << 2) | ||||
| 
 | ||||
| int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base); | ||||
| int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base); | ||||
| @ -66,6 +70,7 @@ void tmio_core_mmc_clk_div(void __iomem *cnf, int shift, int state); | ||||
| struct tmio_mmc_dma { | ||||
| 	void *chan_priv_tx; | ||||
| 	void *chan_priv_rx; | ||||
| 	int alignment_shift; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  | ||||
							
								
								
									
										217
									
								
								include/linux/mmc/dw_mmc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								include/linux/mmc/dw_mmc.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,217 @@ | ||||
| /*
 | ||||
|  * Synopsys DesignWare Multimedia Card Interface driver | ||||
|  *  (Based on NXP driver for lpc 31xx) | ||||
|  * | ||||
|  * Copyright (C) 2009 NXP Semiconductors | ||||
|  * Copyright (C) 2009, 2010 Imagination Technologies Ltd. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _LINUX_MMC_DW_MMC_H_ | ||||
| #define _LINUX_MMC_DW_MMC_H_ | ||||
| 
 | ||||
| #define MAX_MCI_SLOTS	2 | ||||
| 
 | ||||
| enum dw_mci_state { | ||||
| 	STATE_IDLE = 0, | ||||
| 	STATE_SENDING_CMD, | ||||
| 	STATE_SENDING_DATA, | ||||
| 	STATE_DATA_BUSY, | ||||
| 	STATE_SENDING_STOP, | ||||
| 	STATE_DATA_ERROR, | ||||
| }; | ||||
| 
 | ||||
| enum { | ||||
| 	EVENT_CMD_COMPLETE = 0, | ||||
| 	EVENT_XFER_COMPLETE, | ||||
| 	EVENT_DATA_COMPLETE, | ||||
| 	EVENT_DATA_ERROR, | ||||
| 	EVENT_XFER_ERROR | ||||
| }; | ||||
| 
 | ||||
| struct mmc_data; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct dw_mci - MMC controller state shared between all slots | ||||
|  * @lock: Spinlock protecting the queue and associated data. | ||||
|  * @regs: Pointer to MMIO registers. | ||||
|  * @sg: Scatterlist entry currently being processed by PIO code, if any. | ||||
|  * @pio_offset: Offset into the current scatterlist entry. | ||||
|  * @cur_slot: The slot which is currently using the controller. | ||||
|  * @mrq: The request currently being processed on @cur_slot, | ||||
|  *	or NULL if the controller is idle. | ||||
|  * @cmd: The command currently being sent to the card, or NULL. | ||||
|  * @data: The data currently being transferred, or NULL if no data | ||||
|  *	transfer is in progress. | ||||
|  * @use_dma: Whether DMA channel is initialized or not. | ||||
|  * @sg_dma: Bus address of DMA buffer. | ||||
|  * @sg_cpu: Virtual address of DMA buffer. | ||||
|  * @dma_ops: Pointer to platform-specific DMA callbacks. | ||||
|  * @cmd_status: Snapshot of SR taken upon completion of the current | ||||
|  *	command. Only valid when EVENT_CMD_COMPLETE is pending. | ||||
|  * @data_status: Snapshot of SR taken upon completion of the current | ||||
|  *	data transfer. Only valid when EVENT_DATA_COMPLETE or | ||||
|  *	EVENT_DATA_ERROR is pending. | ||||
|  * @stop_cmdr: Value to be loaded into CMDR when the stop command is | ||||
|  *	to be sent. | ||||
|  * @dir_status: Direction of current transfer. | ||||
|  * @tasklet: Tasklet running the request state machine. | ||||
|  * @card_tasklet: Tasklet handling card detect. | ||||
|  * @pending_events: Bitmask of events flagged by the interrupt handler | ||||
|  *	to be processed by the tasklet. | ||||
|  * @completed_events: Bitmask of events which the state machine has | ||||
|  *	processed. | ||||
|  * @state: Tasklet state. | ||||
|  * @queue: List of slots waiting for access to the controller. | ||||
|  * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus | ||||
|  *	rate and timeout calculations. | ||||
|  * @current_speed: Configured rate of the controller. | ||||
|  * @num_slots: Number of slots available. | ||||
|  * @pdev: Platform device associated with the MMC controller. | ||||
|  * @pdata: Platform data associated with the MMC controller. | ||||
|  * @slot: Slots sharing this MMC controller. | ||||
|  * @data_shift: log2 of FIFO item size. | ||||
|  * @push_data: Pointer to FIFO push function. | ||||
|  * @pull_data: Pointer to FIFO pull function. | ||||
|  * @quirks: Set of quirks that apply to specific versions of the IP. | ||||
|  * | ||||
|  * Locking | ||||
|  * ======= | ||||
|  * | ||||
|  * @lock is a softirq-safe spinlock protecting @queue as well as | ||||
|  * @cur_slot, @mrq and @state. These must always be updated | ||||
|  * at the same time while holding @lock. | ||||
|  * | ||||
|  * The @mrq field of struct dw_mci_slot is also protected by @lock, | ||||
|  * and must always be written at the same time as the slot is added to | ||||
|  * @queue. | ||||
|  * | ||||
|  * @pending_events and @completed_events are accessed using atomic bit | ||||
|  * operations, so they don't need any locking. | ||||
|  * | ||||
|  * None of the fields touched by the interrupt handler need any | ||||
|  * locking. However, ordering is important: Before EVENT_DATA_ERROR or | ||||
|  * EVENT_DATA_COMPLETE is set in @pending_events, all data-related | ||||
|  * interrupts must be disabled and @data_status updated with a | ||||
|  * snapshot of SR. Similarly, before EVENT_CMD_COMPLETE is set, the | ||||
|  * CMDRDY interupt must be disabled and @cmd_status updated with a | ||||
|  * snapshot of SR, and before EVENT_XFER_COMPLETE can be set, the | ||||
|  * bytes_xfered field of @data must be written. This is ensured by | ||||
|  * using barriers. | ||||
|  */ | ||||
| struct dw_mci { | ||||
| 	spinlock_t		lock; | ||||
| 	void __iomem		*regs; | ||||
| 
 | ||||
| 	struct scatterlist	*sg; | ||||
| 	unsigned int		pio_offset; | ||||
| 
 | ||||
| 	struct dw_mci_slot	*cur_slot; | ||||
| 	struct mmc_request	*mrq; | ||||
| 	struct mmc_command	*cmd; | ||||
| 	struct mmc_data		*data; | ||||
| 
 | ||||
| 	/* DMA interface members*/ | ||||
| 	int			use_dma; | ||||
| 
 | ||||
| 	dma_addr_t		sg_dma; | ||||
| 	void			*sg_cpu; | ||||
| 	struct dw_mci_dma_ops	*dma_ops; | ||||
| #ifdef CONFIG_MMC_DW_IDMAC | ||||
| 	unsigned int		ring_size; | ||||
| #else | ||||
| 	struct dw_mci_dma_data	*dma_data; | ||||
| #endif | ||||
| 	u32			cmd_status; | ||||
| 	u32			data_status; | ||||
| 	u32			stop_cmdr; | ||||
| 	u32			dir_status; | ||||
| 	struct tasklet_struct	tasklet; | ||||
| 	struct tasklet_struct	card_tasklet; | ||||
| 	unsigned long		pending_events; | ||||
| 	unsigned long		completed_events; | ||||
| 	enum dw_mci_state	state; | ||||
| 	struct list_head	queue; | ||||
| 
 | ||||
| 	u32			bus_hz; | ||||
| 	u32			current_speed; | ||||
| 	u32			num_slots; | ||||
| 	struct platform_device	*pdev; | ||||
| 	struct dw_mci_board	*pdata; | ||||
| 	struct dw_mci_slot	*slot[MAX_MCI_SLOTS]; | ||||
| 
 | ||||
| 	/* FIFO push and pull */ | ||||
| 	int			data_shift; | ||||
| 	void (*push_data)(struct dw_mci *host, void *buf, int cnt); | ||||
| 	void (*pull_data)(struct dw_mci *host, void *buf, int cnt); | ||||
| 
 | ||||
| 	/* Workaround flags */ | ||||
| 	u32			quirks; | ||||
| }; | ||||
| 
 | ||||
| /* DMA ops for Internal/External DMAC interface */ | ||||
| struct dw_mci_dma_ops { | ||||
| 	/* DMA Ops */ | ||||
| 	int (*init)(struct dw_mci *host); | ||||
| 	void (*start)(struct dw_mci *host, unsigned int sg_len); | ||||
| 	void (*complete)(struct dw_mci *host); | ||||
| 	void (*stop)(struct dw_mci *host); | ||||
| 	void (*cleanup)(struct dw_mci *host); | ||||
| 	void (*exit)(struct dw_mci *host); | ||||
| }; | ||||
| 
 | ||||
| /* IP Quirks/flags. */ | ||||
| /* No special quirks or flags to cater for */ | ||||
| #define DW_MCI_QUIRK_NONE		0 | ||||
| /* DTO fix for command transmission with IDMAC configured */ | ||||
| #define DW_MCI_QUIRK_IDMAC_DTO		1 | ||||
| /* delay needed between retries on some 2.11a implementations */ | ||||
| #define DW_MCI_QUIRK_RETRY_DELAY	2 | ||||
| /* High Speed Capable - Supports HS cards (upto 50MHz) */ | ||||
| #define DW_MCI_QUIRK_HIGHSPEED		4 | ||||
| 
 | ||||
| 
 | ||||
| struct dma_pdata; | ||||
| 
 | ||||
| struct block_settings { | ||||
| 	unsigned short	max_segs;	/* see blk_queue_max_segments */ | ||||
| 	unsigned int	max_blk_size;	/* maximum size of one mmc block */ | ||||
| 	unsigned int	max_blk_count;	/* maximum number of blocks in one req*/ | ||||
| 	unsigned int	max_req_size;	/* maximum number of bytes in one req*/ | ||||
| 	unsigned int	max_seg_size;	/* see blk_queue_max_segment_size */ | ||||
| }; | ||||
| 
 | ||||
| /* Board platform data */ | ||||
| struct dw_mci_board { | ||||
| 	u32 num_slots; | ||||
| 
 | ||||
| 	u32 quirks; /* Workaround / Quirk flags */ | ||||
| 	unsigned int bus_hz; /* Bus speed */ | ||||
| 
 | ||||
| 	/* delay in mS before detecting cards after interrupt */ | ||||
| 	u32 detect_delay_ms; | ||||
| 
 | ||||
| 	int (*init)(u32 slot_id, irq_handler_t , void *); | ||||
| 	int (*get_ro)(u32 slot_id); | ||||
| 	int (*get_cd)(u32 slot_id); | ||||
| 	int (*get_ocr)(u32 slot_id); | ||||
| 	int (*get_bus_wd)(u32 slot_id); | ||||
| 	/*
 | ||||
| 	 * Enable power to selected slot and set voltage to desired level. | ||||
| 	 * Voltage levels are specified using MMC_VDD_xxx defines defined | ||||
| 	 * in linux/mmc/host.h file. | ||||
| 	 */ | ||||
| 	void (*setpower)(u32 slot_id, u32 volt); | ||||
| 	void (*exit)(u32 slot_id); | ||||
| 	void (*select_slot)(u32 slot_id); | ||||
| 
 | ||||
| 	struct dw_mci_dma_ops *dma_ops; | ||||
| 	struct dma_pdata *data; | ||||
| 	struct block_settings *blk_settings; | ||||
| }; | ||||
| 
 | ||||
| #endif /* _LINUX_MMC_DW_MMC_H_ */ | ||||
| @ -131,6 +131,9 @@ struct mmc_host { | ||||
| 	unsigned int		f_max; | ||||
| 	unsigned int		f_init; | ||||
| 	u32			ocr_avail; | ||||
| 	u32			ocr_avail_sdio;	/* SDIO-specific OCR */ | ||||
| 	u32			ocr_avail_sd;	/* SD-specific OCR */ | ||||
| 	u32			ocr_avail_mmc;	/* MMC-specific OCR */ | ||||
| 	struct notifier_block	pm_notify; | ||||
| 
 | ||||
| #define MMC_VDD_165_195		0x00000080	/* VDD voltage 1.65 - 1.95 */ | ||||
| @ -169,9 +172,20 @@ struct mmc_host { | ||||
| #define MMC_CAP_1_2V_DDR	(1 << 12)	/* can support */ | ||||
| 						/* DDR mode at 1.2V */ | ||||
| #define MMC_CAP_POWER_OFF_CARD	(1 << 13)	/* Can power off after boot */ | ||||
| #define MMC_CAP_BUS_WIDTH_TEST	(1 << 14)	/* CMD14/CMD19 bus width ok */ | ||||
| 
 | ||||
| 	mmc_pm_flag_t		pm_caps;	/* supported pm features */ | ||||
| 
 | ||||
| #ifdef CONFIG_MMC_CLKGATE | ||||
| 	int			clk_requests;	/* internal reference counter */ | ||||
| 	unsigned int		clk_delay;	/* number of MCI clk hold cycles */ | ||||
| 	bool			clk_gated;	/* clock gated */ | ||||
| 	struct work_struct	clk_gate_work; /* delayed clock gate */ | ||||
| 	unsigned int		clk_old;	/* old clock value cache */ | ||||
| 	spinlock_t		clk_lock;	/* lock for clk fields */ | ||||
| 	struct mutex		clk_gate_mutex;	/* mutex for clock gating */ | ||||
| #endif | ||||
| 
 | ||||
| 	/* host specific block data */ | ||||
| 	unsigned int		max_seg_size;	/* see blk_queue_max_segment_size */ | ||||
| 	unsigned short		max_segs;	/* see blk_queue_max_segments */ | ||||
| @ -307,5 +321,10 @@ static inline int mmc_card_is_removable(struct mmc_host *host) | ||||
| 	return !(host->caps & MMC_CAP_NONREMOVABLE) && mmc_assume_removable; | ||||
| } | ||||
| 
 | ||||
| static inline int mmc_card_is_powered_resumed(struct mmc_host *host) | ||||
| { | ||||
| 	return host->pm_flags & MMC_PM_KEEP_POWER; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
|  | ||||
| @ -40,7 +40,9 @@ | ||||
| #define MMC_READ_DAT_UNTIL_STOP  11   /* adtc [31:0] dadr        R1  */ | ||||
| #define MMC_STOP_TRANSMISSION    12   /* ac                      R1b */ | ||||
| #define MMC_SEND_STATUS          13   /* ac   [31:16] RCA        R1  */ | ||||
| #define MMC_BUS_TEST_R           14   /* adtc                    R1  */ | ||||
| #define MMC_GO_INACTIVE_STATE    15   /* ac   [31:16] RCA            */ | ||||
| #define MMC_BUS_TEST_W           19   /* adtc                    R1  */ | ||||
| #define MMC_SPI_READ_OCR         58   /* spi                  spi_R3 */ | ||||
| #define MMC_SPI_CRC_ON_OFF       59   /* spi  [0:0] flag      spi_R1 */ | ||||
| 
 | ||||
|  | ||||
| @ -83,6 +83,8 @@ struct sdhci_host { | ||||
| #define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12		(1<<28) | ||||
| /* Controller doesn't have HISPD bit field in HI-SPEED SD card */ | ||||
| #define SDHCI_QUIRK_NO_HISPD_BIT			(1<<29) | ||||
| /* Controller treats ADMA descriptors with length 0000h incorrectly */ | ||||
| #define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC		(1<<30) | ||||
| 
 | ||||
| 	int irq;		/* Device IRQ */ | ||||
| 	void __iomem *ioaddr;	/* Mapped address */ | ||||
| @ -139,6 +141,10 @@ struct sdhci_host { | ||||
| 
 | ||||
| 	unsigned int caps;	/* Alternative capabilities */ | ||||
| 
 | ||||
| 	unsigned int            ocr_avail_sdio;	/* OCR bit masks */ | ||||
| 	unsigned int            ocr_avail_sd; | ||||
| 	unsigned int            ocr_avail_mmc; | ||||
| 
 | ||||
| 	unsigned long private[0] ____cacheline_aligned; | ||||
| }; | ||||
| #endif /* __SDHCI_H */ | ||||
|  | ||||
| @ -1650,6 +1650,11 @@ | ||||
| #define PCI_DEVICE_ID_O2_6836		0x6836 | ||||
| #define PCI_DEVICE_ID_O2_6812		0x6872 | ||||
| #define PCI_DEVICE_ID_O2_6933		0x6933 | ||||
| #define PCI_DEVICE_ID_O2_8120		0x8120 | ||||
| #define PCI_DEVICE_ID_O2_8220		0x8220 | ||||
| #define PCI_DEVICE_ID_O2_8221		0x8221 | ||||
| #define PCI_DEVICE_ID_O2_8320		0x8320 | ||||
| #define PCI_DEVICE_ID_O2_8321		0x8321 | ||||
| 
 | ||||
| #define PCI_VENDOR_ID_3DFX		0x121a | ||||
| #define PCI_DEVICE_ID_3DFX_VOODOO	0x0001 | ||||
| @ -2363,6 +2368,8 @@ | ||||
| #define PCI_DEVICE_ID_JMICRON_JMB38X_SD	0x2381 | ||||
| #define PCI_DEVICE_ID_JMICRON_JMB38X_MMC 0x2382 | ||||
| #define PCI_DEVICE_ID_JMICRON_JMB38X_MS	0x2383 | ||||
| #define PCI_DEVICE_ID_JMICRON_JMB388_SD	0x2391 | ||||
| #define PCI_DEVICE_ID_JMICRON_JMB388_ESD 0x2392 | ||||
| 
 | ||||
| #define PCI_VENDOR_ID_KORENIX		0x1982 | ||||
| #define PCI_DEVICE_ID_KORENIX_JETCARDF0	0x1600 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user