Commit edb2877f authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 5b2eef96 f9db92cb
...@@ -770,7 +770,7 @@ static struct resource dove_sdio0_resources[] = { ...@@ -770,7 +770,7 @@ static struct resource dove_sdio0_resources[] = {
}; };
static struct platform_device dove_sdio0 = { static struct platform_device dove_sdio0 = {
.name = "sdhci-mv", .name = "sdhci-dove",
.id = 0, .id = 0,
.dev = { .dev = {
.dma_mask = &sdio_dmamask, .dma_mask = &sdio_dmamask,
...@@ -798,7 +798,7 @@ static struct resource dove_sdio1_resources[] = { ...@@ -798,7 +798,7 @@ static struct resource dove_sdio1_resources[] = {
}; };
static struct platform_device dove_sdio1 = { static struct platform_device dove_sdio1 = {
.name = "sdhci-mv", .name = "sdhci-dove",
.id = 1, .id = 1,
.dev = { .dev = {
.dma_mask = &sdio_dmamask, .dma_mask = &sdio_dmamask,
......
/*
* 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) ...@@ -131,11 +131,17 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
*/ */
mmc_data->flags |= TMIO_MMC_BLKSZ_2BYTES; 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) { if (p && p->dma_slave_tx >= 0 && p->dma_slave_rx >= 0) {
priv->param_tx.slave_id = p->dma_slave_tx; priv->param_tx.slave_id = p->dma_slave_tx;
priv->param_rx.slave_id = p->dma_slave_rx; priv->param_rx.slave_id = p->dma_slave_rx;
priv->dma_priv.chan_priv_tx = &priv->param_tx; priv->dma_priv.chan_priv_tx = &priv->param_tx;
priv->dma_priv.chan_priv_rx = &priv->param_rx; priv->dma_priv.chan_priv_rx = &priv->param_rx;
priv->dma_priv.alignment_shift = 1; /* 2-byte alignment */
mmc_data->dma = &priv->dma_priv; mmc_data->dma = &priv->dma_priv;
} }
......
...@@ -16,6 +16,7 @@ config MMC_BLOCK ...@@ -16,6 +16,7 @@ config MMC_BLOCK
config MMC_BLOCK_MINORS config MMC_BLOCK_MINORS
int "Number of minors per block device" int "Number of minors per block device"
depends on MMC_BLOCK
range 4 256 range 4 256
default 8 default 8
help help
......
...@@ -16,3 +16,14 @@ config MMC_UNSAFE_RESUME ...@@ -16,3 +16,14 @@ config MMC_UNSAFE_RESUME
This option sets a default which can be overridden by the This option sets a default which can be overridden by the
module parameter "removable=0" or "removable=1". 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) ...@@ -303,14 +303,14 @@ int mmc_add_card(struct mmc_card *card)
type, card->rca); type, card->rca);
} }
ret = device_add(&card->dev);
if (ret)
return ret;
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
mmc_add_card_debugfs(card); mmc_add_card_debugfs(card);
#endif #endif
ret = device_add(&card->dev);
if (ret)
return ret;
mmc_card_set_present(card); mmc_card_set_present(card);
return 0; return 0;
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
...@@ -130,6 +131,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) ...@@ -130,6 +131,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
if (mrq->done) if (mrq->done)
mrq->done(mrq); mrq->done(mrq);
mmc_host_clk_gate(host);
} }
} }
...@@ -190,6 +193,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) ...@@ -190,6 +193,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
mrq->stop->mrq = mrq; mrq->stop->mrq = mrq;
} }
} }
mmc_host_clk_ungate(host);
host->ops->request(host, mrq); host->ops->request(host, mrq);
} }
...@@ -295,8 +299,9 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) ...@@ -295,8 +299,9 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
unsigned int timeout_us, limit_us; unsigned int timeout_us, limit_us;
timeout_us = data->timeout_ns / 1000; timeout_us = data->timeout_ns / 1000;
timeout_us += data->timeout_clks * 1000 / if (mmc_host_clk_rate(card->host))
(card->host->ios.clock / 1000); timeout_us += data->timeout_clks * 1000 /
(mmc_host_clk_rate(card->host) / 1000);
if (data->flags & MMC_DATA_WRITE) if (data->flags & MMC_DATA_WRITE)
/* /*
...@@ -614,6 +619,8 @@ static inline void mmc_set_ios(struct mmc_host *host) ...@@ -614,6 +619,8 @@ static inline void mmc_set_ios(struct mmc_host *host)
ios->power_mode, ios->chip_select, ios->vdd, ios->power_mode, ios->chip_select, ios->vdd,
ios->bus_width, ios->timing); ios->bus_width, ios->timing);
if (ios->clock > 0)
mmc_set_ungated(host);
host->ops->set_ios(host, ios); host->ops->set_ios(host, ios);
} }
...@@ -641,6 +648,61 @@ void mmc_set_clock(struct mmc_host *host, unsigned int hz) ...@@ -641,6 +648,61 @@ void mmc_set_clock(struct mmc_host *host, unsigned int hz)
mmc_set_ios(host); 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. * 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) ...@@ -1424,35 +1486,57 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
} }
EXPORT_SYMBOL(mmc_set_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) void mmc_rescan(struct work_struct *work)
{ {
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
struct mmc_host *host = struct mmc_host *host =
container_of(work, struct mmc_host, detect.work); container_of(work, struct mmc_host, detect.work);
u32 ocr;
int err;
unsigned long flags;
int i; int i;
const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
spin_lock_irqsave(&host->lock, flags);
if (host->rescan_disable) { if (host->rescan_disable)
spin_unlock_irqrestore(&host->lock, flags);
return; return;
}
spin_unlock_irqrestore(&host->lock, flags);
mmc_bus_get(host); 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); 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_put(host);
mmc_bus_get(host); mmc_bus_get(host);
/* if there still is a card present, stop here */ /* if there still is a card present, stop here */
...@@ -1461,8 +1545,6 @@ void mmc_rescan(struct work_struct *work) ...@@ -1461,8 +1545,6 @@ void mmc_rescan(struct work_struct *work)
goto out; goto out;
} }
/* detect a newly inserted card */
/* /*
* Only we can add a new handler, so it's safe to * Only we can add a new handler, so it's safe to
* release the lock here. * release the lock here.
...@@ -1472,72 +1554,16 @@ void mmc_rescan(struct work_struct *work) ...@@ -1472,72 +1554,16 @@ void mmc_rescan(struct work_struct *work)
if (host->ops->get_cd && host->ops->get_cd(host) == 0) if (host->ops->get_cd && host->ops->get_cd(host) == 0)
goto out; goto out;
mmc_claim_host(host);
for (i = 0; i < ARRAY_SIZE(freqs); i++) { for (i = 0; i < ARRAY_SIZE(freqs); i++) {
mmc_claim_host(host); if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
break;
if (freqs[i] >= host->f_min) if (freqs[i] < host->f_min)
host->f_init = freqs[i]; break;
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);
} }
out: mmc_release_host(host);
out:
if (host->caps & MMC_CAP_NEEDS_POLL) if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ); mmc_schedule_delayed_work(&host->detect, HZ);
} }
...@@ -1721,6 +1747,18 @@ int mmc_resume_host(struct mmc_host *host) ...@@ -1721,6 +1747,18 @@ int mmc_resume_host(struct mmc_host *host)
if (!(host->pm_flags & MMC_PM_KEEP_POWER)) { if (!(host->pm_flags & MMC_PM_KEEP_POWER)) {
mmc_power_up(host); mmc_power_up(host);
mmc_select_voltage(host, host->ocr); 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); BUG_ON(!host->bus_ops->resume);
err = host->bus_ops->resume(host); err = host->bus_ops->resume(host);
......
...@@ -33,6 +33,9 @@ void mmc_init_erase(struct mmc_card *card); ...@@ -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_chip_select(struct mmc_host *host, int mode);
void mmc_set_clock(struct mmc_host *host, unsigned int hz); 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_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(struct mmc_host *host, unsigned int width);
void mmc_set_bus_width_ddr(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); ...@@ -54,9 +57,9 @@ void mmc_rescan(struct work_struct *work);
void mmc_start_host(struct mmc_host *host); void mmc_start_host(struct mmc_host *host);
void mmc_stop_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_mmc(struct mmc_host *host);
int mmc_attach_sd(struct mmc_host *host, u32 ocr); int mmc_attach_sd(struct mmc_host *host);
int mmc_attach_sdio(struct mmc_host *host, u32 ocr); int mmc_attach_sdio(struct mmc_host *host);
/* Module parameters */ /* Module parameters */
extern int use_spi_crc; extern int use_spi_crc;
......
...@@ -183,6 +183,11 @@ void mmc_add_host_debugfs(struct mmc_host *host) ...@@ -183,6 +183,11 @@ void mmc_add_host_debugfs(struct mmc_host *host)
&mmc_clock_fops)) &mmc_clock_fops))
goto err_node; 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; return;
err_node: err_node:
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* *
* Copyright (C) 2003 Russell King, All Rights Reserved. * Copyright (C) 2003 Russell King, All Rights Reserved.
* Copyright (C) 2007-2008 Pierre Ossman * Copyright (C) 2007-2008 Pierre Ossman
* Copyright (C) 2010 Linus Walleij
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -20,6 +21,7 @@ ...@@ -20,6 +21,7 @@
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include "core.h" #include "core.h"
#include "host.h" #include "host.h"
...@@ -50,6 +52,205 @@ void mmc_unregister_host_class(void) ...@@ -50,6 +52,205 @@ void mmc_unregister_host_class(void)
static DEFINE_IDR(mmc_host_idr); static DEFINE_IDR(mmc_host_idr);
static DEFINE_SPINLOCK(mmc_host_lock); 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. * mmc_alloc_host - initialise the per-host structure.
* @extra: sizeof private data structure * @extra: sizeof private data structure
...@@ -82,6 +283,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) ...@@ -82,6 +283,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host->class_dev.class = &mmc_host_class; host->class_dev.class = &mmc_host_class;
device_initialize(&host->class_dev); device_initialize(&host->class_dev);
mmc_host_clk_init(host);
spin_lock_init(&host->lock); spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq); init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan); INIT_DELAYED_WORK(&host->detect, mmc_rescan);
...@@ -163,6 +366,8 @@ void mmc_remove_host(struct mmc_host *host) ...@@ -163,6 +366,8 @@ void mmc_remove_host(struct mmc_host *host)
device_del(&host->class_dev); device_del(&host->class_dev);
led_trigger_unregister_simple(host->led); led_trigger_unregister_simple(host->led);
mmc_host_clk_exit(host);
} }
EXPORT_SYMBOL(mmc_remove_host); EXPORT_SYMBOL(mmc_remove_host);
...@@ -183,4 +388,3 @@ void mmc_free_host(struct mmc_host *host) ...@@ -183,4 +388,3 @@ void mmc_free_host(struct mmc_host *host)
} }
EXPORT_SYMBOL(mmc_free_host); EXPORT_SYMBOL(mmc_free_host);
...@@ -10,10 +10,31 @@ ...@@ -10,10 +10,31 @@
*/ */
#ifndef _MMC_CORE_HOST_H #ifndef _MMC_CORE_HOST_H
#define _MMC_CORE_HOST_H #define _MMC_CORE_HOST_H
#include <linux/mmc/host.h>
int mmc_register_host_class(void); int mmc_register_host_class(void);
void mmc_unregister_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); void mmc_host_deeper_disable(struct work_struct *work);
#endif #endif
......
...@@ -534,39 +534,57 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -534,39 +534,57 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
*/ */
if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) && if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { (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 },
if (host->caps & MMC_CAP_8_BIT_DATA) { { EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 },
if (ddr) { EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 },
ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8; };
else static unsigned bus_widths[] = {
ext_csd_bit = EXT_CSD_BUS_WIDTH_8; MMC_BUS_WIDTH_8,
bus_width = MMC_BUS_WIDTH_8; MMC_BUS_WIDTH_4,
} else { MMC_BUS_WIDTH_1
if (ddr) };
ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_4; unsigned idx, bus_width = 0;
else
ext_csd_bit = EXT_CSD_BUS_WIDTH_4; if (host->caps & MMC_CAP_8_BIT_DATA)
bus_width = MMC_BUS_WIDTH_4; 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, if (!err && ddr) {
EXT_CSD_BUS_WIDTH, ext_csd_bit); err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
if (err && err != -EBADMSG) ext_csd_bits[idx][1]);
goto free_card; }
if (err) { if (err) {
printk(KERN_WARNING "%s: switch to bus width %d ddr %d " printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
"failed\n", mmc_hostname(card->host), "failed\n", mmc_hostname(card->host),
1 << bus_width, ddr); 1 << bus_width, ddr);
err = 0; goto free_card;
} else { } else if (ddr) {
if (ddr) mmc_card_set_ddr_mode(card);
mmc_card_set_ddr_mode(card);
else
ddr = MMC_SDR_MODE;
mmc_set_bus_width_ddr(card->host, bus_width, ddr); mmc_set_bus_width_ddr(card->host, bus_width, ddr);
} }
} }
...@@ -737,14 +755,21 @@ static void mmc_attach_bus_ops(struct mmc_host *host) ...@@ -737,14 +755,21 @@ static void mmc_attach_bus_ops(struct mmc_host *host)
/* /*
* Starting point for MMC card init. * 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; int err;
u32 ocr;
BUG_ON(!host); BUG_ON(!host);
WARN_ON(!host->claimed); WARN_ON(!host->claimed);
err = mmc_send_op_cond(host, 0, &ocr);
if (err)
return err;
mmc_attach_bus_ops(host); 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. * We need to get OCR a different way for SPI.
...@@ -784,20 +809,20 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) ...@@ -784,20 +809,20 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
goto err; goto err;
mmc_release_host(host); mmc_release_host(host);
err = mmc_add_card(host->card); err = mmc_add_card(host->card);
mmc_claim_host(host);
if (err) if (err)
goto remove_card; goto remove_card;
return 0; return 0;
remove_card: remove_card:
mmc_release_host(host);
mmc_remove_card(host->card); mmc_remove_card(host->card);
host->card = NULL;
mmc_claim_host(host); mmc_claim_host(host);
host->card = NULL;
err: err:
mmc_detach_bus(host); mmc_detach_bus(host);
mmc_release_host(host);
printk(KERN_ERR "%s: error %d whilst initialising MMC card\n", printk(KERN_ERR "%s: error %d whilst initialising MMC card\n",
mmc_hostname(host), err); mmc_hostname(host), err);
......
...@@ -462,3 +462,104 @@ int mmc_send_status(struct mmc_card *card, u32 *status) ...@@ -462,3 +462,104 @@ int mmc_send_status(struct mmc_card *card, u32 *status)
return 0; 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); ...@@ -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_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc); int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_card_sleepawake(struct mmc_host *host, int sleep); int mmc_card_sleepawake(struct mmc_host *host, int sleep);
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
#endif #endif
...@@ -764,14 +764,21 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host) ...@@ -764,14 +764,21 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host)
/* /*
* Starting point for SD card init. * 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; int err;
u32 ocr;
BUG_ON(!host); BUG_ON(!host);
WARN_ON(!host->claimed); WARN_ON(!host->claimed);
err = mmc_send_app_op_cond(host, 0, &ocr);
if (err)
return err;
mmc_sd_attach_bus_ops(host); 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. * We need to get OCR a different way for SPI.
...@@ -795,7 +802,8 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) ...@@ -795,7 +802,8 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
ocr &= ~0x7F; 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 " printk(KERN_WARNING "%s: SD card claims to support the "
"incompletely defined 'low voltage range'. This " "incompletely defined 'low voltage range'. This "
"will be ignored.\n", mmc_hostname(host)); "will be ignored.\n", mmc_hostname(host));
...@@ -820,20 +828,20 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) ...@@ -820,20 +828,20 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
goto err; goto err;
mmc_release_host(host); mmc_release_host(host);
err = mmc_add_card(host->card); err = mmc_add_card(host->card);
mmc_claim_host(host);
if (err) if (err)
goto remove_card; goto remove_card;
return 0; return 0;
remove_card: remove_card:
mmc_release_host(host);
mmc_remove_card(host->card); mmc_remove_card(host->card);
host->card = NULL; host->card = NULL;
mmc_claim_host(host); mmc_claim_host(host);
err: err:
mmc_detach_bus(host); mmc_detach_bus(host);
mmc_release_host(host);
printk(KERN_ERR "%s: error %d whilst initialising SD card\n", printk(KERN_ERR "%s: error %d whilst initialising SD card\n",
mmc_hostname(host), err); mmc_hostname(host), err);
......
...@@ -627,15 +627,27 @@ static int mmc_sdio_suspend(struct mmc_host *host) ...@@ -627,15 +627,27 @@ static int mmc_sdio_suspend(struct mmc_host *host)
static int mmc_sdio_resume(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);
BUG_ON(!host->card); BUG_ON(!host->card);
/* Basic card reinitialization. */ /* Basic card reinitialization. */
mmc_claim_host(host); 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)); (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) if (!err && host->sdio_irqs)
mmc_signal_sdio_irq(host); mmc_signal_sdio_irq(host);
mmc_release_host(host); mmc_release_host(host);
...@@ -690,16 +702,22 @@ static const struct mmc_bus_ops mmc_sdio_ops = { ...@@ -690,16 +702,22 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
/* /*
* Starting point for SDIO card init. * 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 err, i, funcs;
int i, funcs; u32 ocr;
struct mmc_card *card; struct mmc_card *card;
BUG_ON(!host); BUG_ON(!host);
WARN_ON(!host->claimed); WARN_ON(!host->claimed);
err = mmc_send_io_op_cond(host, 0, &ocr);
if (err)
return err;
mmc_attach_bus(host, &mmc_sdio_ops); 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 * Sanity check the voltages that the card claims to
...@@ -769,12 +787,12 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) ...@@ -769,12 +787,12 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
pm_runtime_enable(&card->sdio_func[i]->dev); pm_runtime_enable(&card->sdio_func[i]->dev);
} }
mmc_release_host(host);
/* /*
* First add the card to the driver model... * First add the card to the driver model...
*/ */
mmc_release_host(host);
err = mmc_add_card(host->card); err = mmc_add_card(host->card);
mmc_claim_host(host);
if (err) if (err)
goto remove_added; goto remove_added;
...@@ -792,15 +810,17 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) ...@@ -792,15 +810,17 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
remove_added: remove_added:
/* Remove without lock if the device has been added. */ /* Remove without lock if the device has been added. */
mmc_release_host(host);
mmc_sdio_remove(host); mmc_sdio_remove(host);
mmc_claim_host(host); mmc_claim_host(host);
remove: remove:
/* And with lock if it hasn't been added. */ /* And with lock if it hasn't been added. */
mmc_release_host(host);
if (host->card) if (host->card)
mmc_sdio_remove(host); mmc_sdio_remove(host);
mmc_claim_host(host);
err: err:
mmc_detach_bus(host); mmc_detach_bus(host);
mmc_release_host(host);
printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n", printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n",
mmc_hostname(host), err); mmc_hostname(host), err);
......
...@@ -197,44 +197,12 @@ static int sdio_bus_remove(struct device *dev) ...@@ -197,44 +197,12 @@ static int sdio_bus_remove(struct device *dev)
#ifdef CONFIG_PM_RUNTIME #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 = { static const struct dev_pm_ops sdio_bus_pm_ops = {
SET_RUNTIME_PM_OPS( SET_RUNTIME_PM_OPS(
pm_generic_runtime_suspend, pm_generic_runtime_suspend,
pm_generic_runtime_resume, pm_generic_runtime_resume,
pm_generic_runtime_idle pm_generic_runtime_idle
) )
.prepare = sdio_bus_pm_prepare,
}; };
#define SDIO_PM_OPS_PTR (&sdio_bus_pm_ops) #define SDIO_PM_OPS_PTR (&sdio_bus_pm_ops)
......
...@@ -142,6 +142,27 @@ config MMC_SDHCI_ESDHC_IMX ...@@ -142,6 +142,27 @@ config MMC_SDHCI_ESDHC_IMX
If unsure, say N. 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 config MMC_SDHCI_S3C
tristate "SDHCI support on Samsung S3C SoC" tristate "SDHCI support on Samsung S3C SoC"
depends on MMC_SDHCI && PLAT_SAMSUNG depends on MMC_SDHCI && PLAT_SAMSUNG
...@@ -460,6 +481,22 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND ...@@ -460,6 +481,22 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
help help
If you say yes here SD-Cards may work on the EZkit. 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 config MMC_SH_MMCIF
tristate "SuperH Internal MMCIF support" tristate "SuperH Internal MMCIF support"
depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE) depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
......
...@@ -31,6 +31,7 @@ obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o ...@@ -31,6 +31,7 @@ obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
obj-$(CONFIG_MMC_CB710) += cb710-mmc.o obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
obj-$(CONFIG_SDH_BFIN) += bfin_sdh.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_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_USHC) += ushc.o obj-$(CONFIG_MMC_USHC) += ushc.o
...@@ -39,6 +40,8 @@ obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o ...@@ -39,6 +40,8 @@ obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o
sdhci-platform-y := sdhci-pltfm.o sdhci-platform-y := sdhci-pltfm.o
sdhci-platform-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.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_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 obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o
sdhci-of-y := sdhci-of-core.o sdhci-of-y := sdhci-of-core.o
......
...@@ -66,8 +66,8 @@ ...@@ -66,8 +66,8 @@
#define DAVINCI_MMCBLNC 0x60 #define DAVINCI_MMCBLNC 0x60
#define DAVINCI_SDIOCTL 0x64 #define DAVINCI_SDIOCTL 0x64
#define DAVINCI_SDIOST0 0x68 #define DAVINCI_SDIOST0 0x68
#define DAVINCI_SDIOEN 0x6C #define DAVINCI_SDIOIEN 0x6C
#define DAVINCI_SDIOST 0x70 #define DAVINCI_SDIOIST 0x70
#define DAVINCI_MMCFIFOCTL 0x74 /* FIFO Control Register */ #define DAVINCI_MMCFIFOCTL 0x74 /* FIFO Control Register */
/* DAVINCI_MMCCTL definitions */ /* DAVINCI_MMCCTL definitions */
...@@ -131,6 +131,14 @@ ...@@ -131,6 +131,14 @@
#define MMCFIFOCTL_ACCWD_2 (2 << 3) /* access width of 2 bytes */ #define MMCFIFOCTL_ACCWD_2 (2 << 3) /* access width of 2 bytes */
#define MMCFIFOCTL_ACCWD_1 (3 << 3) /* access width of 1 byte */ #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 */ /* MMCSD Init clock in Hz in opendrain mode */
#define MMCSD_INIT_CLOCK 200000 #define MMCSD_INIT_CLOCK 200000
...@@ -164,7 +172,7 @@ struct mmc_davinci_host { ...@@ -164,7 +172,7 @@ struct mmc_davinci_host {
unsigned int mmc_input_clk; unsigned int mmc_input_clk;
void __iomem *base; void __iomem *base;
struct resource *mem_res; struct resource *mem_res;
int irq; int mmc_irq, sdio_irq;
unsigned char bus_mode; unsigned char bus_mode;
#define DAVINCI_MMC_DATADIR_NONE 0 #define DAVINCI_MMC_DATADIR_NONE 0
...@@ -184,6 +192,7 @@ struct mmc_davinci_host { ...@@ -184,6 +192,7 @@ struct mmc_davinci_host {
u32 rxdma, txdma; u32 rxdma, txdma;
bool use_dma; bool use_dma;
bool do_dma; bool do_dma;
bool sdio_int;
/* Scatterlist DMA uses one or more parameter RAM entries: /* Scatterlist DMA uses one or more parameter RAM entries:
* the main one (associated with rxdma or txdma) plus zero or * 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, ...@@ -480,7 +489,7 @@ static void mmc_davinci_send_dma_request(struct mmc_davinci_host *host,
struct scatterlist *sg; struct scatterlist *sg;
unsigned sg_len; unsigned sg_len;
unsigned bytes_left = host->bytes_left; 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) { if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
template = &host->tx_template; template = &host->tx_template;
...@@ -866,6 +875,19 @@ mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data) ...@@ -866,6 +875,19 @@ mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data)
{ {
host->data = NULL; 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) { if (host->do_dma) {
davinci_abort_dma(host); davinci_abort_dma(host);
...@@ -932,6 +954,21 @@ davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data) ...@@ -932,6 +954,21 @@ davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data)
mmc_davinci_reset_ctrl(host, 0); 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) static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
{ {
struct mmc_davinci_host *host = (struct mmc_davinci_host *)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) ...@@ -1076,11 +1113,32 @@ static int mmc_davinci_get_ro(struct mmc_host *mmc)
return config->get_ro(pdev->id); 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 = { static struct mmc_host_ops mmc_davinci_ops = {
.request = mmc_davinci_request, .request = mmc_davinci_request,
.set_ios = mmc_davinci_set_ios, .set_ios = mmc_davinci_set_ios,
.get_cd = mmc_davinci_get_cd, .get_cd = mmc_davinci_get_cd,
.get_ro = mmc_davinci_get_ro, .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) ...@@ -1209,7 +1267,8 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
host->nr_sg = MAX_NR_SG; host->nr_sg = MAX_NR_SG;
host->use_dma = use_dma; 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) if (host->use_dma && davinci_acquire_dma_channels(host) != 0)
host->use_dma = 0; host->use_dma = 0;
...@@ -1270,6 +1329,13 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) ...@@ -1270,6 +1329,13 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
if (ret) if (ret)
goto out; 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)); rename_region(mem, mmc_hostname(mmc));
dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n", 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) ...@@ -1313,7 +1379,9 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev)
mmc_davinci_cpufreq_deregister(host); mmc_davinci_cpufreq_deregister(host);
mmc_remove_host(host->mmc); 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); davinci_release_dma_channels(host);
......
This diff is collapsed.
/*
* 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 @@ ...@@ -31,6 +31,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <asm/dma.h> #include <asm/dma.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -141,10 +142,49 @@ struct mxcmci_host { ...@@ -141,10 +142,49 @@ struct mxcmci_host {
struct work_struct datawork; struct work_struct datawork;
spinlock_t lock; spinlock_t lock;
struct regulator *vcc;
}; };
static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios); 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) static inline int mxcmci_use_dma(struct mxcmci_host *host)
{ {
return host->do_dma; return host->do_dma;
...@@ -680,9 +720,9 @@ static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -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; host->cmdat &= ~CMD_DAT_CONT_BUS_WIDTH_4;
if (host->power_mode != ios->power_mode) { if (host->power_mode != ios->power_mode) {
if (host->pdata && host->pdata->setpower) mxcmci_set_power(host, ios->power_mode, ios->vdd);
host->pdata->setpower(mmc_dev(mmc), ios->vdd);
host->power_mode = ios->power_mode; host->power_mode = ios->power_mode;
if (ios->power_mode == MMC_POWER_ON) if (ios->power_mode == MMC_POWER_ON)
host->cmdat |= CMD_DAT_CONT_INIT; host->cmdat |= CMD_DAT_CONT_INIT;
} }
...@@ -807,10 +847,7 @@ static int mxcmci_probe(struct platform_device *pdev) ...@@ -807,10 +847,7 @@ static int mxcmci_probe(struct platform_device *pdev)
host->pdata = pdev->dev.platform_data; host->pdata = pdev->dev.platform_data;
spin_lock_init(&host->lock); spin_lock_init(&host->lock);
if (host->pdata && host->pdata->ocr_avail) mxcmci_init_ocr(host);
mmc->ocr_avail = host->pdata->ocr_avail;
else
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
if (host->pdata && host->pdata->dat3_card_detect) if (host->pdata && host->pdata->dat3_card_detect)
host->default_irq_mask = host->default_irq_mask =
...@@ -915,6 +952,9 @@ static int mxcmci_remove(struct platform_device *pdev) ...@@ -915,6 +952,9 @@ static int mxcmci_remove(struct platform_device *pdev)
mmc_remove_host(mmc); mmc_remove_host(mmc);
if (host->vcc)
regulator_put(host->vcc);
if (host->pdata && host->pdata->exit) if (host->pdata && host->pdata->exit)
host->pdata->exit(&pdev->dev, mmc); host->pdata->exit(&pdev->dev, mmc);
...@@ -927,7 +967,6 @@ static int mxcmci_remove(struct platform_device *pdev) ...@@ -927,7 +967,6 @@ static int mxcmci_remove(struct platform_device *pdev)
clk_put(host->clk); clk_put(host->clk);
release_mem_region(host->res->start, resource_size(host->res)); release_mem_region(host->res->start, resource_size(host->res));
release_resource(host->res);
mmc_free_host(mmc); mmc_free_host(mmc);
......
/*
* 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 = { ...@@ -176,6 +176,74 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc_sdio = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .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) static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)
{ {
u8 scratch; u8 scratch;
...@@ -204,6 +272,7 @@ static int jmicron_pmos(struct sdhci_pci_chip *chip, int on) ...@@ -204,6 +272,7 @@ static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)
static int jmicron_probe(struct sdhci_pci_chip *chip) static int jmicron_probe(struct sdhci_pci_chip *chip)
{ {
int ret; int ret;
u16 mmcdev = 0;
if (chip->pdev->revision == 0) { if (chip->pdev->revision == 0) {
chip->quirks |= SDHCI_QUIRK_32BIT_DMA_ADDR | chip->quirks |= SDHCI_QUIRK_32BIT_DMA_ADDR |
...@@ -225,12 +294,17 @@ static int jmicron_probe(struct sdhci_pci_chip *chip) ...@@ -225,12 +294,17 @@ static int jmicron_probe(struct sdhci_pci_chip *chip)
* 2. The MMC interface has a lower subfunction number * 2. The MMC interface has a lower subfunction number
* than the SD interface. * 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; struct pci_dev *sd_dev;
sd_dev = NULL; sd_dev = NULL;
while ((sd_dev = pci_get_device(PCI_VENDOR_ID_JMICRON, 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) == if ((PCI_SLOT(chip->pdev->devfn) ==
PCI_SLOT(sd_dev->devfn)) && PCI_SLOT(sd_dev->devfn)) &&
(chip->pdev->bus == sd_dev->bus)) (chip->pdev->bus == sd_dev->bus))
...@@ -290,13 +364,25 @@ static int jmicron_probe_slot(struct sdhci_pci_slot *slot) ...@@ -290,13 +364,25 @@ static int jmicron_probe_slot(struct sdhci_pci_slot *slot)
slot->host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; 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 * The secondary interface requires a bit set to get the
* interrupts. * 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); jmicron_enable_mmc(slot->host, 1);
slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST;
return 0; return 0;
} }
...@@ -305,7 +391,8 @@ static void jmicron_remove_slot(struct sdhci_pci_slot *slot, int dead) ...@@ -305,7 +391,8 @@ static void jmicron_remove_slot(struct sdhci_pci_slot *slot, int dead)
if (dead) if (dead)
return; 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); jmicron_enable_mmc(slot->host, 0);
} }
...@@ -313,7 +400,8 @@ static int jmicron_suspend(struct sdhci_pci_chip *chip, pm_message_t state) ...@@ -313,7 +400,8 @@ static int jmicron_suspend(struct sdhci_pci_chip *chip, pm_message_t state)
{ {
int i; 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++) for (i = 0;i < chip->num_slots;i++)
jmicron_enable_mmc(chip->slots[i]->host, 0); jmicron_enable_mmc(chip->slots[i]->host, 0);
} }
...@@ -325,7 +413,8 @@ static int jmicron_resume(struct sdhci_pci_chip *chip) ...@@ -325,7 +413,8 @@ static int jmicron_resume(struct sdhci_pci_chip *chip)
{ {
int ret, i; 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++) for (i = 0;i < chip->num_slots;i++)
jmicron_enable_mmc(chip->slots[i]->host, 1); jmicron_enable_mmc(chip->slots[i]->host, 1);
} }
...@@ -339,6 +428,10 @@ static int jmicron_resume(struct sdhci_pci_chip *chip) ...@@ -339,6 +428,10 @@ static int jmicron_resume(struct sdhci_pci_chip *chip)
return 0; return 0;
} }
static const struct sdhci_pci_fixes sdhci_o2 = {
.probe = o2_probe,
};
static const struct sdhci_pci_fixes sdhci_jmicron = { static const struct sdhci_pci_fixes sdhci_jmicron = {
.probe = jmicron_probe, .probe = jmicron_probe,
...@@ -509,6 +602,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = { ...@@ -509,6 +602,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
.driver_data = (kernel_ulong_t)&sdhci_jmicron, .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, .vendor = PCI_VENDOR_ID_SYSKONNECT,
.device = 0x8000, .device = 0x8000,
...@@ -589,6 +698,46 @@ static const struct pci_device_id pci_ids[] __devinitdata = { ...@@ -589,6 +698,46 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
.driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio, .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 */ { /* Generic SD host controller */
PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
}, },
......
...@@ -169,6 +169,12 @@ static const struct platform_device_id sdhci_pltfm_ids[] = { ...@@ -169,6 +169,12 @@ static const struct platform_device_id sdhci_pltfm_ids[] = {
#endif #endif
#ifdef CONFIG_MMC_SDHCI_ESDHC_IMX #ifdef CONFIG_MMC_SDHCI_ESDHC_IMX
{ "sdhci-esdhc-imx", (kernel_ulong_t)&sdhci_esdhc_imx_pdata }, { "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 #endif
{ }, { },
}; };
......
...@@ -22,5 +22,7 @@ struct sdhci_pltfm_host { ...@@ -22,5 +22,7 @@ struct sdhci_pltfm_host {
extern struct sdhci_pltfm_data sdhci_cns3xxx_pdata; extern struct sdhci_pltfm_data sdhci_cns3xxx_pdata;
extern struct sdhci_pltfm_data sdhci_esdhc_imx_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 */ #endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */
...@@ -130,6 +130,15 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost, ...@@ -130,6 +130,15 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
if (!clksrc) if (!clksrc)
return UINT_MAX; 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); rate = clk_get_rate(clksrc);
for (div = 1; div < 256; div *= 2) { for (div = 1; div < 256; div *= 2) {
...@@ -232,6 +241,42 @@ static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host) ...@@ -232,6 +241,42 @@ static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)
return min; 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 = { static struct sdhci_ops sdhci_s3c_ops = {
.get_max_clock = sdhci_s3c_get_max_clk, .get_max_clock = sdhci_s3c_get_max_clk,
.set_clock = sdhci_s3c_set_clock, .set_clock = sdhci_s3c_set_clock,
...@@ -361,6 +406,13 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) ...@@ -361,6 +406,13 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
clks++; clks++;
sc->clk_bus[ptr] = clk; 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); clk_enable(clk);
dev_info(dev, "clock source %d: %s (%ld Hz)\n", dev_info(dev, "clock source %d: %s (%ld Hz)\n",
...@@ -427,6 +479,20 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) ...@@ -427,6 +479,20 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
/* HSMMC on Samsung SoCs uses SDCLK as timeout clock */ /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */
host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; 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); ret = sdhci_add_host(host);
if (ret) { if (ret) {
dev_err(dev, "sdhci_add_host() failed\n"); dev_err(dev, "sdhci_add_host() failed\n");
......
/*
* 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 @@ ...@@ -23,6 +23,7 @@
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include "sdhci.h" #include "sdhci.h"
...@@ -77,8 +78,11 @@ static void sdhci_dumpregs(struct sdhci_host *host) ...@@ -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", printk(KERN_DEBUG DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
sdhci_readw(host, SDHCI_ACMD12_ERR), sdhci_readw(host, SDHCI_ACMD12_ERR),
sdhci_readw(host, SDHCI_SLOT_INT_STATUS)); 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),
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)); sdhci_readl(host, SDHCI_MAX_CURRENT));
if (host->flags & SDHCI_USE_ADMA) if (host->flags & SDHCI_USE_ADMA)
...@@ -1518,7 +1522,11 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) ...@@ -1518,7 +1522,11 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
if (intmask & SDHCI_INT_DATA_TIMEOUT) if (intmask & SDHCI_INT_DATA_TIMEOUT)
host->data->error = -ETIMEDOUT; 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; host->data->error = -EILSEQ;
else if (intmask & SDHCI_INT_ADMA_ERROR) { else if (intmask & SDHCI_INT_ADMA_ERROR) {
printk(KERN_ERR "%s: ADMA error\n", mmc_hostname(host->mmc)); printk(KERN_ERR "%s: ADMA error\n", mmc_hostname(host->mmc));
...@@ -1736,7 +1744,7 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host); ...@@ -1736,7 +1744,7 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host);
int sdhci_add_host(struct sdhci_host *host) int sdhci_add_host(struct sdhci_host *host)
{ {
struct mmc_host *mmc; struct mmc_host *mmc;
unsigned int caps; unsigned int caps, ocr_avail;
int ret; int ret;
WARN_ON(host == NULL); WARN_ON(host == NULL);
...@@ -1890,13 +1898,26 @@ int sdhci_add_host(struct sdhci_host *host) ...@@ -1890,13 +1898,26 @@ int sdhci_add_host(struct sdhci_host *host)
mmc_card_is_removable(mmc)) mmc_card_is_removable(mmc))
mmc->caps |= MMC_CAP_NEEDS_POLL; mmc->caps |= MMC_CAP_NEEDS_POLL;
mmc->ocr_avail = 0; ocr_avail = 0;
if (caps & SDHCI_CAN_VDD_330) 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) 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) 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) { if (mmc->ocr_avail == 0) {
printk(KERN_ERR "%s: Hardware doesn't report any " printk(KERN_ERR "%s: Hardware doesn't report any "
...@@ -1928,10 +1949,14 @@ int sdhci_add_host(struct sdhci_host *host) ...@@ -1928,10 +1949,14 @@ int sdhci_add_host(struct sdhci_host *host)
* of bytes. When doing hardware scatter/gather, each entry cannot * of bytes. When doing hardware scatter/gather, each entry cannot
* be larger than 64 KiB though. * be larger than 64 KiB though.
*/ */
if (host->flags & SDHCI_USE_ADMA) if (host->flags & SDHCI_USE_ADMA) {
mmc->max_seg_size = 65536; if (host->quirks & SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC)
else mmc->max_seg_size = 65535;
else
mmc->max_seg_size = 65536;
} else {
mmc->max_seg_size = mmc->max_req_size; mmc->max_seg_size = mmc->max_req_size;
}
/* /*
* Maximum block size. This varies from controller to controller and * Maximum block size. This varies from controller to controller and
......
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
#define SDHCI_CMD_RESP_SHORT_BUSY 0x03 #define SDHCI_CMD_RESP_SHORT_BUSY 0x03
#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff)) #define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
#define SDHCI_GET_CMD(c) ((c>>8) & 0x3f)
#define SDHCI_RESPONSE 0x10 #define SDHCI_RESPONSE 0x10
...@@ -165,7 +166,7 @@ ...@@ -165,7 +166,7 @@
#define SDHCI_CAN_VDD_180 0x04000000 #define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT 0x10000000 #define SDHCI_CAN_64BIT 0x10000000
/* 44-47 reserved for more caps */ #define SDHCI_CAPABILITIES_1 0x44
#define SDHCI_MAX_CURRENT 0x48 #define SDHCI_MAX_CURRENT 0x48
......
This diff is collapsed.
This diff is collapsed.
...@@ -57,6 +57,10 @@ ...@@ -57,6 +57,10 @@
* is configured in 4-bit mode. * is configured in 4-bit mode.
*/ */
#define TMIO_MMC_BLKSZ_2BYTES (1 << 1) #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_enable(void __iomem *cnf, int shift, unsigned long base);
int tmio_core_mmc_resume(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); ...@@ -66,6 +70,7 @@ void tmio_core_mmc_clk_div(void __iomem *cnf, int shift, int state);
struct tmio_mmc_dma { struct tmio_mmc_dma {
void *chan_priv_tx; void *chan_priv_tx;
void *chan_priv_rx; void *chan_priv_rx;
int alignment_shift;
}; };
/* /*
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment