Commit 8c1c77ff 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: (75 commits)
  mmc: core: eMMC bus width may not work on all platforms
  mmc: sdhci: Auto-CMD23 fixes.
  mmc: sdhci: Auto-CMD23 support.
  mmc: core: Block CMD23 support for UHS104/SDXC cards.
  mmc: sdhci: Implement MMC_CAP_CMD23 for SDHCI.
  mmc: core: Use CMD23 for multiblock transfers when we can.
  mmc: quirks: Add/remove quirks conditional support.
  mmc: Add new VUB300 USB-to-SD/SDIO/MMC driver
  mmc: sdhci-pxa: Add quirks for DMA/ADMA to match h/w
  mmc: core: duplicated trial with same freq in mmc_rescan_try_freq()
  mmc: core: add support for eMMC Dual Data Rate
  mmc: core: eMMC signal voltage does not use CMD11
  mmc: sdhci-pxa: add platform code for UHS signaling
  mmc: sdhci: add hooks for setting UHS in platform specific code
  mmc: core: clear MMC_PM_KEEP_POWER flag on resume
  mmc: dw_mmc: fixed wrong regulator_enable in suspend/resume
  mmc: sdhi: allow powering down controller with no card inserted
  mmc: tmio: runtime suspend the controller, where possible
  mmc: sdhi: support up to 3 interrupt sources
  mmc: sdhi: print physical base address and clock rate
  ...
parents f3ae1c75 08ee80cc
...@@ -304,6 +304,7 @@ Code Seq#(hex) Include File Comments ...@@ -304,6 +304,7 @@ Code Seq#(hex) Include File Comments
0xB0 all RATIO devices in development: 0xB0 all RATIO devices in development:
<mailto:vgo@ratio.de> <mailto:vgo@ratio.de>
0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca> 0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca>
0xB3 00 linux/mmc/ioctl.h
0xC0 00-0F linux/usb/iowarrior.h 0xC0 00-0F linux/usb/iowarrior.h
0xCB 00-1F CBM serial IEC bus in development: 0xCB 00-1F CBM serial IEC bus in development:
<mailto:michael.klein@puffin.lb.shuttle.de> <mailto:michael.klein@puffin.lb.shuttle.de>
......
...@@ -2,3 +2,5 @@ ...@@ -2,3 +2,5 @@
- this file - this file
mmc-dev-attrs.txt mmc-dev-attrs.txt
- info on SD and MMC device attributes - info on SD and MMC device attributes
mmc-dev-parts.txt
- info on SD and MMC device partitions
SD and MMC Block Device Attributes
==================================
These attributes are defined for the block devices associated with the
SD or MMC device.
The following attributes are read/write.
force_ro Enforce read-only access even if write protect switch is off.
SD and MMC Device Attributes SD and MMC Device Attributes
============================ ============================
......
SD and MMC Device Partitions
============================
Device partitions are additional logical block devices present on the
SD/MMC device.
As of this writing, MMC boot partitions as supported and exposed as
/dev/mmcblkXboot0 and /dev/mmcblkXboot1, where X is the index of the
parent /dev/mmcblkX.
MMC Boot Partitions
===================
Read and write access is provided to the two MMC boot partitions. Due to
the sensitive nature of the boot partition contents, which often store
a bootloader or bootloader configuration tables crucial to booting the
platform, write access is disabled by default to reduce the chance of
accidental bricking.
To enable write access to /dev/mmcblkXbootY, disable the forced read-only
access with:
echo 0 > /sys/block/mmcblkXbootY/force_ro
To re-enable read-only access:
echo 1 > /sys/block/mmcblkXbootY/force_ro
...@@ -6800,6 +6800,13 @@ L: lm-sensors@lm-sensors.org ...@@ -6800,6 +6800,13 @@ L: lm-sensors@lm-sensors.org
S: Maintained S: Maintained
F: drivers/hwmon/vt8231.c F: drivers/hwmon/vt8231.c
VUB300 USB to SDIO/SD/MMC bridge chip
M: Tony Olech <tony.olech@elandigitalsystems.com>
L: linux-mmc@vger.kernel.org
L: linux-usb@vger.kernel.org
S: Supported
F: drivers/mmc/host/vub300.c
W1 DALLAS'S 1-WIRE BUS W1 DALLAS'S 1-WIRE BUS
M: Evgeniy Polyakov <johnpol@2ka.mipt.ru> M: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
S: Maintained S: Maintained
......
...@@ -24,6 +24,7 @@ struct tegra_sdhci_platform_data { ...@@ -24,6 +24,7 @@ struct tegra_sdhci_platform_data {
int wp_gpio; int wp_gpio;
int power_gpio; int power_gpio;
int is_8bit; int is_8bit;
int pm_flags;
}; };
#endif #endif
This diff is collapsed.
This diff is collapsed.
...@@ -343,18 +343,14 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq) ...@@ -343,18 +343,14 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq)
*/ */
void mmc_queue_bounce_pre(struct mmc_queue *mq) void mmc_queue_bounce_pre(struct mmc_queue *mq)
{ {
unsigned long flags;
if (!mq->bounce_buf) if (!mq->bounce_buf)
return; return;
if (rq_data_dir(mq->req) != WRITE) if (rq_data_dir(mq->req) != WRITE)
return; return;
local_irq_save(flags);
sg_copy_to_buffer(mq->bounce_sg, mq->bounce_sg_len, sg_copy_to_buffer(mq->bounce_sg, mq->bounce_sg_len,
mq->bounce_buf, mq->sg[0].length); mq->bounce_buf, mq->sg[0].length);
local_irq_restore(flags);
} }
/* /*
...@@ -363,17 +359,13 @@ void mmc_queue_bounce_pre(struct mmc_queue *mq) ...@@ -363,17 +359,13 @@ void mmc_queue_bounce_pre(struct mmc_queue *mq)
*/ */
void mmc_queue_bounce_post(struct mmc_queue *mq) void mmc_queue_bounce_post(struct mmc_queue *mq)
{ {
unsigned long flags;
if (!mq->bounce_buf) if (!mq->bounce_buf)
return; return;
if (rq_data_dir(mq->req) != READ) if (rq_data_dir(mq->req) != READ)
return; return;
local_irq_save(flags);
sg_copy_from_buffer(mq->bounce_sg, mq->bounce_sg_len, sg_copy_from_buffer(mq->bounce_sg, mq->bounce_sg_len,
mq->bounce_buf, mq->sg[0].length); mq->bounce_buf, mq->sg[0].length);
local_irq_restore(flags);
} }
...@@ -274,8 +274,12 @@ int mmc_add_card(struct mmc_card *card) ...@@ -274,8 +274,12 @@ int mmc_add_card(struct mmc_card *card)
break; break;
case MMC_TYPE_SD: case MMC_TYPE_SD:
type = "SD"; type = "SD";
if (mmc_card_blockaddr(card)) if (mmc_card_blockaddr(card)) {
type = "SDHC"; if (mmc_card_ext_capacity(card))
type = "SDXC";
else
type = "SDHC";
}
break; break;
case MMC_TYPE_SDIO: case MMC_TYPE_SDIO:
type = "SDIO"; type = "SDIO";
...@@ -299,7 +303,8 @@ int mmc_add_card(struct mmc_card *card) ...@@ -299,7 +303,8 @@ int mmc_add_card(struct mmc_card *card)
} else { } else {
printk(KERN_INFO "%s: new %s%s%s card at address %04x\n", printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
mmc_hostname(card->host), mmc_hostname(card->host),
mmc_card_highspeed(card) ? "high speed " : "", mmc_sd_card_uhs(card) ? "ultra high speed " :
(mmc_card_highspeed(card) ? "high speed " : ""),
mmc_card_ddr_mode(card) ? "DDR " : "", mmc_card_ddr_mode(card) ? "DDR " : "",
type, card->rca); type, card->rca);
} }
......
...@@ -236,12 +236,10 @@ EXPORT_SYMBOL(mmc_wait_for_req); ...@@ -236,12 +236,10 @@ EXPORT_SYMBOL(mmc_wait_for_req);
*/ */
int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries) int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
{ {
struct mmc_request mrq; struct mmc_request mrq = {0};
WARN_ON(!host->claimed); WARN_ON(!host->claimed);
memset(&mrq, 0, sizeof(struct mmc_request));
memset(cmd->resp, 0, sizeof(cmd->resp)); memset(cmd->resp, 0, sizeof(cmd->resp));
cmd->retries = retries; cmd->retries = retries;
...@@ -719,23 +717,13 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode) ...@@ -719,23 +717,13 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
mmc_set_ios(host); mmc_set_ios(host);
} }
/*
* Change data bus width and DDR mode of a host.
*/
void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
unsigned int ddr)
{
host->ios.bus_width = width;
host->ios.ddr = ddr;
mmc_set_ios(host);
}
/* /*
* Change data bus width of a host. * Change data bus width of a host.
*/ */
void mmc_set_bus_width(struct mmc_host *host, unsigned int width) void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
{ {
mmc_set_bus_width_ddr(host, width, MMC_SDR_MODE); host->ios.bus_width = width;
mmc_set_ios(host);
} }
/** /**
...@@ -944,6 +932,38 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) ...@@ -944,6 +932,38 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
return ocr; return ocr;
} }
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11)
{
struct mmc_command cmd = {0};
int err = 0;
BUG_ON(!host);
/*
* Send CMD11 only if the request is to switch the card to
* 1.8V signalling.
*/
if ((signal_voltage != MMC_SIGNAL_VOLTAGE_330) && cmd11) {
cmd.opcode = SD_SWITCH_VOLTAGE;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
return err;
if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
return -EIO;
}
host->ios.signal_voltage = signal_voltage;
if (host->ops->start_signal_voltage_switch)
err = host->ops->start_signal_voltage_switch(host, &host->ios);
return err;
}
/* /*
* Select timing parameters for host. * Select timing parameters for host.
*/ */
...@@ -953,6 +973,15 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing) ...@@ -953,6 +973,15 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing)
mmc_set_ios(host); mmc_set_ios(host);
} }
/*
* Select appropriate driver type for host.
*/
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
{
host->ios.drv_type = drv_type;
mmc_set_ios(host);
}
/* /*
* Apply power to the MMC stack. This is a two-stage process. * Apply power to the MMC stack. This is a two-stage process.
* First, we enable power to the card without the clock running. * First, we enable power to the card without the clock running.
...@@ -1187,9 +1216,8 @@ void mmc_init_erase(struct mmc_card *card) ...@@ -1187,9 +1216,8 @@ void mmc_init_erase(struct mmc_card *card)
} }
} }
static void mmc_set_mmc_erase_timeout(struct mmc_card *card, static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card,
struct mmc_command *cmd, unsigned int arg, unsigned int qty)
unsigned int arg, unsigned int qty)
{ {
unsigned int erase_timeout; unsigned int erase_timeout;
...@@ -1246,44 +1274,48 @@ static void mmc_set_mmc_erase_timeout(struct mmc_card *card, ...@@ -1246,44 +1274,48 @@ static void mmc_set_mmc_erase_timeout(struct mmc_card *card,
if (mmc_host_is_spi(card->host) && erase_timeout < 1000) if (mmc_host_is_spi(card->host) && erase_timeout < 1000)
erase_timeout = 1000; erase_timeout = 1000;
cmd->erase_timeout = erase_timeout; return erase_timeout;
} }
static void mmc_set_sd_erase_timeout(struct mmc_card *card, static unsigned int mmc_sd_erase_timeout(struct mmc_card *card,
struct mmc_command *cmd, unsigned int arg, unsigned int arg,
unsigned int qty) unsigned int qty)
{ {
unsigned int erase_timeout;
if (card->ssr.erase_timeout) { if (card->ssr.erase_timeout) {
/* Erase timeout specified in SD Status Register (SSR) */ /* Erase timeout specified in SD Status Register (SSR) */
cmd->erase_timeout = card->ssr.erase_timeout * qty + erase_timeout = card->ssr.erase_timeout * qty +
card->ssr.erase_offset; card->ssr.erase_offset;
} else { } else {
/* /*
* Erase timeout not specified in SD Status Register (SSR) so * Erase timeout not specified in SD Status Register (SSR) so
* use 250ms per write block. * use 250ms per write block.
*/ */
cmd->erase_timeout = 250 * qty; erase_timeout = 250 * qty;
} }
/* Must not be less than 1 second */ /* Must not be less than 1 second */
if (cmd->erase_timeout < 1000) if (erase_timeout < 1000)
cmd->erase_timeout = 1000; erase_timeout = 1000;
return erase_timeout;
} }
static void mmc_set_erase_timeout(struct mmc_card *card, static unsigned int mmc_erase_timeout(struct mmc_card *card,
struct mmc_command *cmd, unsigned int arg, unsigned int arg,
unsigned int qty) unsigned int qty)
{ {
if (mmc_card_sd(card)) if (mmc_card_sd(card))
mmc_set_sd_erase_timeout(card, cmd, arg, qty); return mmc_sd_erase_timeout(card, arg, qty);
else else
mmc_set_mmc_erase_timeout(card, cmd, arg, qty); return mmc_mmc_erase_timeout(card, arg, qty);
} }
static int mmc_do_erase(struct mmc_card *card, unsigned int from, static int mmc_do_erase(struct mmc_card *card, unsigned int from,
unsigned int to, unsigned int arg) unsigned int to, unsigned int arg)
{ {
struct mmc_command cmd; struct mmc_command cmd = {0};
unsigned int qty = 0; unsigned int qty = 0;
int err; int err;
...@@ -1317,7 +1349,6 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, ...@@ -1317,7 +1349,6 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
to <<= 9; to <<= 9;
} }
memset(&cmd, 0, sizeof(struct mmc_command));
if (mmc_card_sd(card)) if (mmc_card_sd(card))
cmd.opcode = SD_ERASE_WR_BLK_START; cmd.opcode = SD_ERASE_WR_BLK_START;
else else
...@@ -1351,7 +1382,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, ...@@ -1351,7 +1382,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
cmd.opcode = MMC_ERASE; cmd.opcode = MMC_ERASE;
cmd.arg = arg; cmd.arg = arg;
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
mmc_set_erase_timeout(card, &cmd, arg, qty); cmd.cmd_timeout_ms = mmc_erase_timeout(card, arg, qty);
err = mmc_wait_for_cmd(card->host, &cmd, 0); err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) { if (err) {
printk(KERN_ERR "mmc_erase: erase error %d, status %#x\n", printk(KERN_ERR "mmc_erase: erase error %d, status %#x\n",
...@@ -1487,12 +1518,11 @@ EXPORT_SYMBOL(mmc_erase_group_aligned); ...@@ -1487,12 +1518,11 @@ EXPORT_SYMBOL(mmc_erase_group_aligned);
int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen) int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
{ {
struct mmc_command cmd; struct mmc_command cmd = {0};
if (mmc_card_blockaddr(card) || mmc_card_ddr_mode(card)) if (mmc_card_blockaddr(card) || mmc_card_ddr_mode(card))
return 0; return 0;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SET_BLOCKLEN; cmd.opcode = MMC_SET_BLOCKLEN;
cmd.arg = blocklen; cmd.arg = blocklen;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
...@@ -1578,7 +1608,7 @@ void mmc_rescan(struct work_struct *work) ...@@ -1578,7 +1608,7 @@ void mmc_rescan(struct work_struct *work)
for (i = 0; i < ARRAY_SIZE(freqs); i++) { for (i = 0; i < ARRAY_SIZE(freqs); i++) {
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
break; break;
if (freqs[i] < host->f_min) if (freqs[i] <= host->f_min)
break; break;
} }
mmc_release_host(host); mmc_release_host(host);
...@@ -1746,7 +1776,7 @@ int mmc_suspend_host(struct mmc_host *host) ...@@ -1746,7 +1776,7 @@ int mmc_suspend_host(struct mmc_host *host)
} }
mmc_bus_put(host); mmc_bus_put(host);
if (!err && !(host->pm_flags & MMC_PM_KEEP_POWER)) if (!err && !mmc_card_keep_power(host))
mmc_power_off(host); mmc_power_off(host);
return err; return err;
...@@ -1764,7 +1794,7 @@ int mmc_resume_host(struct mmc_host *host) ...@@ -1764,7 +1794,7 @@ int mmc_resume_host(struct mmc_host *host)
mmc_bus_get(host); mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) { if (host->bus_ops && !host->bus_dead) {
if (!(host->pm_flags & MMC_PM_KEEP_POWER)) { if (!mmc_card_keep_power(host)) {
mmc_power_up(host); mmc_power_up(host);
mmc_select_voltage(host, host->ocr); mmc_select_voltage(host, host->ocr);
/* /*
...@@ -1789,6 +1819,7 @@ int mmc_resume_host(struct mmc_host *host) ...@@ -1789,6 +1819,7 @@ int mmc_resume_host(struct mmc_host *host)
err = 0; err = 0;
} }
} }
host->pm_flags &= ~MMC_PM_KEEP_POWER;
mmc_bus_put(host); mmc_bus_put(host);
return err; return err;
......
...@@ -38,10 +38,11 @@ void mmc_ungate_clock(struct mmc_host *host); ...@@ -38,10 +38,11 @@ void mmc_ungate_clock(struct mmc_host *host);
void mmc_set_ungated(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,
unsigned int ddr);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage,
bool cmd11);
void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_timing(struct mmc_host *host, unsigned int timing);
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
static inline void mmc_delay(unsigned int ms) static inline void mmc_delay(unsigned int ms)
{ {
...@@ -61,8 +62,6 @@ int mmc_attach_mmc(struct mmc_host *host); ...@@ -61,8 +62,6 @@ int mmc_attach_mmc(struct mmc_host *host);
int mmc_attach_sd(struct mmc_host *host); int mmc_attach_sd(struct mmc_host *host);
int mmc_attach_sdio(struct mmc_host *host); int mmc_attach_sdio(struct mmc_host *host);
void mmc_fixup_device(struct mmc_card *card);
/* Module parameters */ /* Module parameters */
extern int use_spi_crc; extern int use_spi_crc;
......
...@@ -325,12 +325,12 @@ int mmc_add_host(struct mmc_host *host) ...@@ -325,12 +325,12 @@ int mmc_add_host(struct mmc_host *host)
WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
!host->ops->enable_sdio_irq); !host->ops->enable_sdio_irq);
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
err = device_add(&host->class_dev); err = device_add(&host->class_dev);
if (err) if (err)
return err; return err;
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
mmc_add_host_debugfs(host); mmc_add_host_debugfs(host);
#endif #endif
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "core.h" #include "core.h"
#include "bus.h" #include "bus.h"
#include "mmc_ops.h" #include "mmc_ops.h"
#include "sd_ops.h"
static const unsigned int tran_exp[] = { static const unsigned int tran_exp[] = {
10000, 100000, 1000000, 10000000, 10000, 100000, 1000000, 10000000,
...@@ -173,14 +174,17 @@ static int mmc_decode_csd(struct mmc_card *card) ...@@ -173,14 +174,17 @@ static int mmc_decode_csd(struct mmc_card *card)
} }
/* /*
* Read and decode extended CSD. * Read extended CSD.
*/ */
static int mmc_read_ext_csd(struct mmc_card *card) static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
{ {
int err; int err;
u8 *ext_csd; u8 *ext_csd;
BUG_ON(!card); BUG_ON(!card);
BUG_ON(!new_ext_csd);
*new_ext_csd = NULL;
if (card->csd.mmca_vsn < CSD_SPEC_VER_4) if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
return 0; return 0;
...@@ -198,12 +202,15 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -198,12 +202,15 @@ static int mmc_read_ext_csd(struct mmc_card *card)
err = mmc_send_ext_csd(card, ext_csd); err = mmc_send_ext_csd(card, ext_csd);
if (err) { if (err) {
kfree(ext_csd);
*new_ext_csd = NULL;
/* If the host or the card can't do the switch, /* If the host or the card can't do the switch,
* fail more gracefully. */ * fail more gracefully. */
if ((err != -EINVAL) if ((err != -EINVAL)
&& (err != -ENOSYS) && (err != -ENOSYS)
&& (err != -EFAULT)) && (err != -EFAULT))
goto out; return err;
/* /*
* High capacity cards should have this "magic" size * High capacity cards should have this "magic" size
...@@ -221,9 +228,23 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -221,9 +228,23 @@ static int mmc_read_ext_csd(struct mmc_card *card)
mmc_hostname(card->host)); mmc_hostname(card->host));
err = 0; err = 0;
} }
} else
*new_ext_csd = ext_csd;
goto out; return err;
} }
/*
* Decode extended CSD.
*/
static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
{
int err = 0;
BUG_ON(!card);
if (!ext_csd)
return 0;
/* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */ /* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
if (card->csd.structure == 3) { if (card->csd.structure == 3) {
...@@ -288,6 +309,10 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -288,6 +309,10 @@ static int mmc_read_ext_csd(struct mmc_card *card)
if (card->ext_csd.rev >= 3) { if (card->ext_csd.rev >= 3) {
u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT]; u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT];
card->ext_csd.part_config = ext_csd[EXT_CSD_PART_CONFIG];
/* EXT_CSD value is in units of 10ms, but we store in ms */
card->ext_csd.part_time = 10 * ext_csd[EXT_CSD_PART_SWITCH_TIME];
/* Sleep / awake timeout in 100ns units */ /* Sleep / awake timeout in 100ns units */
if (sa_shift > 0 && sa_shift <= 0x17) if (sa_shift > 0 && sa_shift <= 0x17)
...@@ -299,6 +324,14 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -299,6 +324,14 @@ static int mmc_read_ext_csd(struct mmc_card *card)
ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]; ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT];
card->ext_csd.hc_erase_size = card->ext_csd.hc_erase_size =
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] << 10; ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] << 10;
card->ext_csd.rel_sectors = ext_csd[EXT_CSD_REL_WR_SEC_C];
/*
* There are two boot regions of equal size, defined in
* multiples of 128K.
*/
card->ext_csd.boot_size = ext_csd[EXT_CSD_BOOT_MULT] << 17;
} }
if (card->ext_csd.rev >= 4) { if (card->ext_csd.rev >= 4) {
...@@ -350,14 +383,78 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -350,14 +383,78 @@ static int mmc_read_ext_csd(struct mmc_card *card)
ext_csd[EXT_CSD_TRIM_MULT]; ext_csd[EXT_CSD_TRIM_MULT];
} }
if (card->ext_csd.rev >= 5)
card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
if (ext_csd[EXT_CSD_ERASED_MEM_CONT]) if (ext_csd[EXT_CSD_ERASED_MEM_CONT])
card->erased_byte = 0xFF; card->erased_byte = 0xFF;
else else
card->erased_byte = 0x0; card->erased_byte = 0x0;
out: out:
return err;
}
static inline void mmc_free_ext_csd(u8 *ext_csd)
{
kfree(ext_csd); kfree(ext_csd);
}
static int mmc_compare_ext_csds(struct mmc_card *card, u8 *ext_csd,
unsigned bus_width)
{
u8 *bw_ext_csd;
int err;
err = mmc_get_ext_csd(card, &bw_ext_csd);
if (err)
return err;
if ((ext_csd == NULL || bw_ext_csd == NULL)) {
if (bus_width != MMC_BUS_WIDTH_1)
err = -EINVAL;
goto out;
}
if (bus_width == MMC_BUS_WIDTH_1)
goto out;
/* only compare read only fields */
err = (!(ext_csd[EXT_CSD_PARTITION_SUPPORT] ==
bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) &&
(ext_csd[EXT_CSD_ERASED_MEM_CONT] ==
bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) &&
(ext_csd[EXT_CSD_REV] ==
bw_ext_csd[EXT_CSD_REV]) &&
(ext_csd[EXT_CSD_STRUCTURE] ==
bw_ext_csd[EXT_CSD_STRUCTURE]) &&
(ext_csd[EXT_CSD_CARD_TYPE] ==
bw_ext_csd[EXT_CSD_CARD_TYPE]) &&
(ext_csd[EXT_CSD_S_A_TIMEOUT] ==
bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) &&
(ext_csd[EXT_CSD_HC_WP_GRP_SIZE] ==
bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) &&
(ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] ==
bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) &&
(ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] ==
bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) &&
(ext_csd[EXT_CSD_SEC_TRIM_MULT] ==
bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) &&
(ext_csd[EXT_CSD_SEC_ERASE_MULT] ==
bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) &&
(ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] ==
bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) &&
(ext_csd[EXT_CSD_TRIM_MULT] ==
bw_ext_csd[EXT_CSD_TRIM_MULT]) &&
memcmp(&ext_csd[EXT_CSD_SEC_CNT],
&bw_ext_csd[EXT_CSD_SEC_CNT],
4) != 0);
if (err)
err = -EINVAL;
out:
mmc_free_ext_csd(bw_ext_csd);
return err; return err;
} }
...@@ -422,6 +519,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -422,6 +519,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
u32 cid[4]; u32 cid[4];
unsigned int max_dtr; unsigned int max_dtr;
u32 rocr; u32 rocr;
u8 *ext_csd = NULL;
BUG_ON(!host); BUG_ON(!host);
WARN_ON(!host->claimed); WARN_ON(!host->claimed);
...@@ -520,7 +618,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -520,7 +618,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
/* /*
* Fetch and process extended CSD. * Fetch and process extended CSD.
*/ */
err = mmc_read_ext_csd(card);
err = mmc_get_ext_csd(card, &ext_csd);
if (err)
goto free_card;
err = mmc_read_ext_csd(card, ext_csd);
if (err) if (err)
goto free_card; goto free_card;
...@@ -542,7 +644,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -542,7 +644,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
*/ */
if (card->ext_csd.enhanced_area_en) { if (card->ext_csd.enhanced_area_en) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_ERASE_GROUP_DEF, 1); EXT_CSD_ERASE_GROUP_DEF, 1, 0);
if (err && err != -EBADMSG) if (err && err != -EBADMSG)
goto free_card; goto free_card;
...@@ -567,13 +669,25 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -567,13 +669,25 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
} }
} }
/*
* Ensure eMMC user default partition is enabled
*/
if (card->ext_csd.part_config & EXT_CSD_PART_CONFIG_ACC_MASK) {
card->ext_csd.part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONFIG,
card->ext_csd.part_config,
card->ext_csd.part_time);
if (err && err != -EBADMSG)
goto free_card;
}
/* /*
* Activate high speed (if supported) * Activate high speed (if supported)
*/ */
if ((card->ext_csd.hs_max_dtr != 0) && if ((card->ext_csd.hs_max_dtr != 0) &&
(host->caps & MMC_CAP_MMC_HIGHSPEED)) { (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, 1); EXT_CSD_HS_TIMING, 1, 0);
if (err && err != -EBADMSG) if (err && err != -EBADMSG)
goto free_card; goto free_card;
...@@ -606,10 +720,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -606,10 +720,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
*/ */
if (mmc_card_highspeed(card)) { if (mmc_card_highspeed(card)) {
if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
&& (host->caps & (MMC_CAP_1_8V_DDR))) && ((host->caps & (MMC_CAP_1_8V_DDR |
MMC_CAP_UHS_DDR50))
== (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50)))
ddr = MMC_1_8V_DDR_MODE; ddr = MMC_1_8V_DDR_MODE;
else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
&& (host->caps & (MMC_CAP_1_2V_DDR))) && ((host->caps & (MMC_CAP_1_2V_DDR |
MMC_CAP_UHS_DDR50))
== (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50)))
ddr = MMC_1_2V_DDR_MODE; ddr = MMC_1_2V_DDR_MODE;
} }
...@@ -640,18 +758,22 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -640,18 +758,22 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
ddr = 0; /* no DDR for 1-bit width */ ddr = 0; /* no DDR for 1-bit width */
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH,
ext_csd_bits[idx][0]); ext_csd_bits[idx][0],
0);
if (!err) { if (!err) {
mmc_set_bus_width_ddr(card->host, mmc_set_bus_width(card->host, bus_width);
bus_width, MMC_SDR_MODE);
/* /*
* If controller can't handle bus width test, * If controller can't handle bus width test,
* use the highest bus width to maintain * compare ext_csd previously read in 1 bit mode
* compatibility with previous MMC behavior. * against ext_csd at new bus width
*/ */
if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
break; err = mmc_compare_ext_csds(card,
err = mmc_bus_test(card, bus_width); ext_csd,
bus_width);
else
err = mmc_bus_test(card, bus_width);
if (!err) if (!err)
break; break;
} }
...@@ -659,8 +781,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -659,8 +781,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
if (!err && ddr) { if (!err && ddr) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH,
ext_csd_bits[idx][1]); ext_csd_bits[idx][1],
0);
} }
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 "
...@@ -668,20 +791,43 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -668,20 +791,43 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
1 << bus_width, ddr); 1 << bus_width, ddr);
goto free_card; goto free_card;
} else if (ddr) { } else if (ddr) {
/*
* eMMC cards can support 3.3V to 1.2V i/o (vccq)
* signaling.
*
* EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq.
*
* 1.8V vccq at 3.3V core voltage (vcc) is not required
* in the JEDEC spec for DDR.
*
* Do not force change in vccq since we are obviously
* working and no change to vccq is needed.
*
* WARNING: eMMC rules are NOT the same as SD DDR
*/
if (ddr == EXT_CSD_CARD_TYPE_DDR_1_2V) {
err = mmc_set_signal_voltage(host,
MMC_SIGNAL_VOLTAGE_120, 0);
if (err)
goto err;
}
mmc_card_set_ddr_mode(card); mmc_card_set_ddr_mode(card);
mmc_set_bus_width_ddr(card->host, bus_width, ddr); mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50);
mmc_set_bus_width(card->host, bus_width);
} }
} }
if (!oldcard) if (!oldcard)
host->card = card; host->card = card;
mmc_free_ext_csd(ext_csd);
return 0; return 0;
free_card: free_card:
if (!oldcard) if (!oldcard)
mmc_remove_card(card); mmc_remove_card(card);
err: err:
mmc_free_ext_csd(ext_csd);
return err; return err;
} }
......
...@@ -23,12 +23,10 @@ ...@@ -23,12 +23,10 @@
static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card) static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
{ {
int err; int err;
struct mmc_command cmd; struct mmc_command cmd = {0};
BUG_ON(!host); BUG_ON(!host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SELECT_CARD; cmd.opcode = MMC_SELECT_CARD;
if (card) { if (card) {
...@@ -60,15 +58,13 @@ int mmc_deselect_cards(struct mmc_host *host) ...@@ -60,15 +58,13 @@ int mmc_deselect_cards(struct mmc_host *host)
int mmc_card_sleepawake(struct mmc_host *host, int sleep) int mmc_card_sleepawake(struct mmc_host *host, int sleep)
{ {
struct mmc_command cmd; struct mmc_command cmd = {0};
struct mmc_card *card = host->card; struct mmc_card *card = host->card;
int err; int err;
if (sleep) if (sleep)
mmc_deselect_cards(host); mmc_deselect_cards(host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SLEEP_AWAKE; cmd.opcode = MMC_SLEEP_AWAKE;
cmd.arg = card->rca << 16; cmd.arg = card->rca << 16;
if (sleep) if (sleep)
...@@ -97,7 +93,7 @@ int mmc_card_sleepawake(struct mmc_host *host, int sleep) ...@@ -97,7 +93,7 @@ int mmc_card_sleepawake(struct mmc_host *host, int sleep)
int mmc_go_idle(struct mmc_host *host) int mmc_go_idle(struct mmc_host *host)
{ {
int err; int err;
struct mmc_command cmd; struct mmc_command cmd = {0};
/* /*
* Non-SPI hosts need to prevent chipselect going active during * Non-SPI hosts need to prevent chipselect going active during
...@@ -113,8 +109,6 @@ int mmc_go_idle(struct mmc_host *host) ...@@ -113,8 +109,6 @@ int mmc_go_idle(struct mmc_host *host)
mmc_delay(1); mmc_delay(1);
} }
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_GO_IDLE_STATE; cmd.opcode = MMC_GO_IDLE_STATE;
cmd.arg = 0; cmd.arg = 0;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
...@@ -135,13 +129,11 @@ int mmc_go_idle(struct mmc_host *host) ...@@ -135,13 +129,11 @@ int mmc_go_idle(struct mmc_host *host)
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{ {
struct mmc_command cmd; struct mmc_command cmd = {0};
int i, err = 0; int i, err = 0;
BUG_ON(!host); BUG_ON(!host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_OP_COND; cmd.opcode = MMC_SEND_OP_COND;
cmd.arg = mmc_host_is_spi(host) ? 0 : ocr; cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
...@@ -178,13 +170,11 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) ...@@ -178,13 +170,11 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
int mmc_all_send_cid(struct mmc_host *host, u32 *cid) int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
{ {
int err; int err;
struct mmc_command cmd; struct mmc_command cmd = {0};
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!cid); BUG_ON(!cid);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_ALL_SEND_CID; cmd.opcode = MMC_ALL_SEND_CID;
cmd.arg = 0; cmd.arg = 0;
cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
...@@ -201,13 +191,11 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid) ...@@ -201,13 +191,11 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
int mmc_set_relative_addr(struct mmc_card *card) int mmc_set_relative_addr(struct mmc_card *card)
{ {
int err; int err;
struct mmc_command cmd; struct mmc_command cmd = {0};
BUG_ON(!card); BUG_ON(!card);
BUG_ON(!card->host); BUG_ON(!card->host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SET_RELATIVE_ADDR; cmd.opcode = MMC_SET_RELATIVE_ADDR;
cmd.arg = card->rca << 16; cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
...@@ -223,13 +211,11 @@ static int ...@@ -223,13 +211,11 @@ static int
mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode) mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
{ {
int err; int err;
struct mmc_command cmd; struct mmc_command cmd = {0};
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!cxd); BUG_ON(!cxd);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = opcode; cmd.opcode = opcode;
cmd.arg = arg; cmd.arg = arg;
cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
...@@ -247,9 +233,9 @@ static int ...@@ -247,9 +233,9 @@ static int
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
u32 opcode, void *buf, unsigned len) u32 opcode, void *buf, unsigned len)
{ {
struct mmc_request mrq; struct mmc_request mrq = {0};
struct mmc_command cmd; struct mmc_command cmd = {0};
struct mmc_data data; struct mmc_data data = {0};
struct scatterlist sg; struct scatterlist sg;
void *data_buf; void *data_buf;
...@@ -260,10 +246,6 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, ...@@ -260,10 +246,6 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
if (data_buf == NULL) if (data_buf == NULL)
return -ENOMEM; return -ENOMEM;
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.cmd = &cmd;
mrq.data = &data; mrq.data = &data;
...@@ -355,11 +337,9 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) ...@@ -355,11 +337,9 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
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)
{ {
struct mmc_command cmd; struct mmc_command cmd = {0};
int err; int err;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SPI_READ_OCR; cmd.opcode = MMC_SPI_READ_OCR;
cmd.arg = highcap ? (1 << 30) : 0; cmd.arg = highcap ? (1 << 30) : 0;
cmd.flags = MMC_RSP_SPI_R3; cmd.flags = MMC_RSP_SPI_R3;
...@@ -372,11 +352,9 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp) ...@@ -372,11 +352,9 @@ 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)
{ {
struct mmc_command cmd; struct mmc_command cmd = {0};
int err; int err;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SPI_CRC_ON_OFF; cmd.opcode = MMC_SPI_CRC_ON_OFF;
cmd.flags = MMC_RSP_SPI_R1; cmd.flags = MMC_RSP_SPI_R1;
cmd.arg = use_crc; cmd.arg = use_crc;
...@@ -387,23 +365,34 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc) ...@@ -387,23 +365,34 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
return err; return err;
} }
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) /**
* mmc_switch - modify EXT_CSD register
* @card: the MMC card associated with the data transfer
* @set: cmd set values
* @index: EXT_CSD register index
* @value: value to program into EXT_CSD register
* @timeout_ms: timeout (ms) for operation performed by register write,
* timeout of zero implies maximum possible timeout
*
* Modifies the EXT_CSD register for selected card.
*/
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms)
{ {
int err; int err;
struct mmc_command cmd; struct mmc_command cmd = {0};
u32 status; u32 status;
BUG_ON(!card); BUG_ON(!card);
BUG_ON(!card->host); BUG_ON(!card->host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SWITCH; cmd.opcode = MMC_SWITCH;
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(index << 16) | (index << 16) |
(value << 8) | (value << 8) |
set; set;
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
cmd.cmd_timeout_ms = timeout_ms;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err) if (err)
...@@ -433,17 +422,16 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) ...@@ -433,17 +422,16 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(mmc_switch);
int mmc_send_status(struct mmc_card *card, u32 *status) int mmc_send_status(struct mmc_card *card, u32 *status)
{ {
int err; int err;
struct mmc_command cmd; struct mmc_command cmd = {0};
BUG_ON(!card); BUG_ON(!card);
BUG_ON(!card->host); BUG_ON(!card->host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_STATUS; cmd.opcode = MMC_SEND_STATUS;
if (!mmc_host_is_spi(card->host)) if (!mmc_host_is_spi(card->host))
cmd.arg = card->rca << 16; cmd.arg = card->rca << 16;
...@@ -466,9 +454,9 @@ static int ...@@ -466,9 +454,9 @@ static int
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
u8 len) u8 len)
{ {
struct mmc_request mrq; struct mmc_request mrq = {0};
struct mmc_command cmd; struct mmc_command cmd = {0};
struct mmc_data data; struct mmc_data data = {0};
struct scatterlist sg; struct scatterlist sg;
u8 *data_buf; u8 *data_buf;
u8 *test_buf; u8 *test_buf;
...@@ -497,10 +485,6 @@ mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, ...@@ -497,10 +485,6 @@ mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
if (opcode == MMC_BUS_TEST_W) if (opcode == MMC_BUS_TEST_W)
memcpy(data_buf, test_buf, len); 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.cmd = &cmd;
mrq.data = &data; mrq.data = &data;
cmd.opcode = opcode; cmd.opcode = opcode;
......
...@@ -20,7 +20,6 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid); ...@@ -20,7 +20,6 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid);
int mmc_set_relative_addr(struct mmc_card *card); int mmc_set_relative_addr(struct mmc_card *card);
int mmc_send_csd(struct mmc_card *card, u32 *csd); int mmc_send_csd(struct mmc_card *card, u32 *csd);
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
int mmc_send_status(struct mmc_card *card, u32 *status); int mmc_send_status(struct mmc_card *card, u32 *status);
int mmc_send_cid(struct mmc_host *host, u32 *cid); 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);
......
/* /*
* This file contains work-arounds for many known sdio hardware * This file contains work-arounds for many known SD/MMC
* bugs. * and SDIO hardware bugs.
* *
* Copyright (c) 2011 Andrei Warkentin <andreiw@motorola.com>
* Copyright (c) 2011 Pierre Tardy <tardyp@gmail.com> * Copyright (c) 2011 Pierre Tardy <tardyp@gmail.com>
* Inspired from pci fixup code: * Inspired from pci fixup code:
* Copyright (c) 1999 Martin Mares <mj@ucw.cz> * Copyright (c) 1999 Martin Mares <mj@ucw.cz>
...@@ -11,34 +12,14 @@ ...@@ -11,34 +12,14 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mod_devicetable.h>
/* #ifndef SDIO_VENDOR_ID_TI
* The world is not perfect and supplies us with broken mmc/sdio devices. #define SDIO_VENDOR_ID_TI 0x0097
* For at least a part of these bugs we need a work-around #endif
*/
struct mmc_fixup {
u16 vendor, device; /* You can use SDIO_ANY_ID here of course */
void (*vendor_fixup)(struct mmc_card *card, int data);
int data;
};
/*
* This hook just adds a quirk unconditionnally
*/
static void __maybe_unused add_quirk(struct mmc_card *card, int data)
{
card->quirks |= data;
}
/* #ifndef SDIO_DEVICE_ID_TI_WL1271
* This hook just removes a quirk unconditionnally #define SDIO_DEVICE_ID_TI_WL1271 0x4076
*/ #endif
static void __maybe_unused remove_quirk(struct mmc_card *card, int data)
{
card->quirks &= ~data;
}
/* /*
* This hook just adds a quirk for all sdio devices * This hook just adds a quirk for all sdio devices
...@@ -49,33 +30,47 @@ static void add_quirk_for_sdio_devices(struct mmc_card *card, int data) ...@@ -49,33 +30,47 @@ static void add_quirk_for_sdio_devices(struct mmc_card *card, int data)
card->quirks |= data; card->quirks |= data;
} }
#ifndef SDIO_VENDOR_ID_TI
#define SDIO_VENDOR_ID_TI 0x0097
#endif
#ifndef SDIO_DEVICE_ID_TI_WL1271
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
#endif
static const struct mmc_fixup mmc_fixup_methods[] = { static const struct mmc_fixup mmc_fixup_methods[] = {
/* by default sdio devices are considered CLK_GATING broken */ /* by default sdio devices are considered CLK_GATING broken */
/* good cards will be whitelisted as they are tested */ /* good cards will be whitelisted as they are tested */
{ SDIO_ANY_ID, SDIO_ANY_ID, SDIO_FIXUP(SDIO_ANY_ID, SDIO_ANY_ID,
add_quirk_for_sdio_devices, MMC_QUIRK_BROKEN_CLK_GATING }, add_quirk_for_sdio_devices,
{ SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, MMC_QUIRK_BROKEN_CLK_GATING),
remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING },
{ 0 } SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
add_quirk, MMC_QUIRK_NONSTD_FUNC_IF),
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
add_quirk, MMC_QUIRK_DISABLE_CD),
END_FIXUP
}; };
void mmc_fixup_device(struct mmc_card *card) void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table)
{ {
const struct mmc_fixup *f; const struct mmc_fixup *f;
u64 rev = cid_rev_card(card);
/* Non-core specific workarounds. */
if (!table)
table = mmc_fixup_methods;
for (f = mmc_fixup_methods; f->vendor_fixup; f++) { for (f = table; f->vendor_fixup; f++) {
if ((f->vendor == card->cis.vendor if ((f->manfid == CID_MANFID_ANY ||
|| f->vendor == (u16) SDIO_ANY_ID) && f->manfid == card->cid.manfid) &&
(f->device == card->cis.device (f->oemid == CID_OEMID_ANY ||
|| f->device == (u16) SDIO_ANY_ID)) { f->oemid == card->cid.oemid) &&
(f->name == CID_NAME_ANY ||
!strncmp(f->name, card->cid.prod_name,
sizeof(card->cid.prod_name))) &&
(f->cis_vendor == card->cis.vendor ||
f->cis_vendor == (u16) SDIO_ANY_ID) &&
(f->cis_device == card->cis.device ||
f->cis_device == (u16) SDIO_ANY_ID) &&
rev >= f->rev_start && rev <= f->rev_end) {
dev_dbg(&card->dev, "calling %pF\n", f->vendor_fixup); dev_dbg(&card->dev, "calling %pF\n", f->vendor_fixup);
f->vendor_fixup(card, f->data); f->vendor_fixup(card, f->data);
} }
......
This diff is collapsed.
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
extern struct device_type sd_type; extern struct device_type sd_type;
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid); int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr);
int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card); int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card);
void mmc_decode_cid(struct mmc_card *card); void mmc_decode_cid(struct mmc_card *card);
int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
......
...@@ -21,10 +21,10 @@ ...@@ -21,10 +21,10 @@
#include "core.h" #include "core.h"
#include "sd_ops.h" #include "sd_ops.h"
static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
{ {
int err; int err;
struct mmc_command cmd; struct mmc_command cmd = {0};
BUG_ON(!host); BUG_ON(!host);
BUG_ON(card && (card->host != host)); BUG_ON(card && (card->host != host));
...@@ -49,6 +49,7 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) ...@@ -49,6 +49,7 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(mmc_app_cmd);
/** /**
* mmc_wait_for_app_cmd - start an application command and wait for * mmc_wait_for_app_cmd - start an application command and wait for
...@@ -66,7 +67,7 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) ...@@ -66,7 +67,7 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
struct mmc_command *cmd, int retries) struct mmc_command *cmd, int retries)
{ {
struct mmc_request mrq; struct mmc_request mrq = {0};
int i, err; int i, err;
...@@ -119,13 +120,11 @@ EXPORT_SYMBOL(mmc_wait_for_app_cmd); ...@@ -119,13 +120,11 @@ EXPORT_SYMBOL(mmc_wait_for_app_cmd);
int mmc_app_set_bus_width(struct mmc_card *card, int width) int mmc_app_set_bus_width(struct mmc_card *card, int width)
{ {
int err; int err;
struct mmc_command cmd; struct mmc_command cmd = {0};
BUG_ON(!card); BUG_ON(!card);
BUG_ON(!card->host); BUG_ON(!card->host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_APP_SET_BUS_WIDTH; cmd.opcode = SD_APP_SET_BUS_WIDTH;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
...@@ -149,13 +148,11 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width) ...@@ -149,13 +148,11 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{ {
struct mmc_command cmd; struct mmc_command cmd = {0};
int i, err = 0; int i, err = 0;
BUG_ON(!host); BUG_ON(!host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_APP_OP_COND; cmd.opcode = SD_APP_OP_COND;
if (mmc_host_is_spi(host)) if (mmc_host_is_spi(host))
cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */ cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
...@@ -194,7 +191,7 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) ...@@ -194,7 +191,7 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
int mmc_send_if_cond(struct mmc_host *host, u32 ocr) int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
{ {
struct mmc_command cmd; struct mmc_command cmd = {0};
int err; int err;
static const u8 test_pattern = 0xAA; static const u8 test_pattern = 0xAA;
u8 result_pattern; u8 result_pattern;
...@@ -226,13 +223,11 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr) ...@@ -226,13 +223,11 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca) int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
{ {
int err; int err;
struct mmc_command cmd; struct mmc_command cmd = {0};
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!rca); BUG_ON(!rca);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_SEND_RELATIVE_ADDR; cmd.opcode = SD_SEND_RELATIVE_ADDR;
cmd.arg = 0; cmd.arg = 0;
cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
...@@ -249,9 +244,9 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca) ...@@ -249,9 +244,9 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
int mmc_app_send_scr(struct mmc_card *card, u32 *scr) int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
{ {
int err; int err;
struct mmc_request mrq; struct mmc_request mrq = {0};
struct mmc_command cmd; struct mmc_command cmd = {0};
struct mmc_data data; struct mmc_data data = {0};
struct scatterlist sg; struct scatterlist sg;
void *data_buf; void *data_buf;
...@@ -272,10 +267,6 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) ...@@ -272,10 +267,6 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
if (data_buf == NULL) if (data_buf == NULL)
return -ENOMEM; return -ENOMEM;
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.cmd = &cmd;
mrq.data = &data; mrq.data = &data;
...@@ -312,9 +303,9 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) ...@@ -312,9 +303,9 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
int mmc_sd_switch(struct mmc_card *card, int mode, int group, int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp) u8 value, u8 *resp)
{ {
struct mmc_request mrq; struct mmc_request mrq = {0};
struct mmc_command cmd; struct mmc_command cmd = {0};
struct mmc_data data; struct mmc_data data = {0};
struct scatterlist sg; struct scatterlist sg;
BUG_ON(!card); BUG_ON(!card);
...@@ -325,10 +316,6 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, ...@@ -325,10 +316,6 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
mode = !!mode; mode = !!mode;
value &= 0xF; value &= 0xF;
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.cmd = &cmd;
mrq.data = &data; mrq.data = &data;
...@@ -361,9 +348,9 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, ...@@ -361,9 +348,9 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
int mmc_app_sd_status(struct mmc_card *card, void *ssr) int mmc_app_sd_status(struct mmc_card *card, void *ssr)
{ {
int err; int err;
struct mmc_request mrq; struct mmc_request mrq = {0};
struct mmc_command cmd; struct mmc_command cmd = {0};
struct mmc_data data; struct mmc_data data = {0};
struct scatterlist sg; struct scatterlist sg;
BUG_ON(!card); BUG_ON(!card);
...@@ -376,10 +363,6 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr) ...@@ -376,10 +363,6 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr)
if (err) if (err)
return err; return err;
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.cmd = &cmd;
mrq.data = &data; mrq.data = &data;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/sdio.h> #include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h> #include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#include "core.h" #include "core.h"
#include "bus.h" #include "bus.h"
...@@ -31,6 +32,11 @@ static int sdio_read_fbr(struct sdio_func *func) ...@@ -31,6 +32,11 @@ static int sdio_read_fbr(struct sdio_func *func)
int ret; int ret;
unsigned char data; unsigned char data;
if (mmc_card_nonstd_func_interface(func->card)) {
func->class = SDIO_CLASS_NONE;
return 0;
}
ret = mmc_io_rw_direct(func->card, 0, 0, ret = mmc_io_rw_direct(func->card, 0, 0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF, 0, &data); SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF, 0, &data);
if (ret) if (ret)
...@@ -181,7 +187,7 @@ static int sdio_disable_cd(struct mmc_card *card) ...@@ -181,7 +187,7 @@ static int sdio_disable_cd(struct mmc_card *card)
int ret; int ret;
u8 ctrl; u8 ctrl;
if (!card->cccr.disable_cd) if (!mmc_card_disable_cd(card))
return 0; return 0;
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl); ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl);
...@@ -363,8 +369,8 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, ...@@ -363,8 +369,8 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
goto err; goto err;
} }
if (ocr & R4_MEMORY_PRESENT if ((ocr & R4_MEMORY_PRESENT) &&
&& mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid) == 0) { mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid, NULL) == 0) {
card->type = MMC_TYPE_SD_COMBO; card->type = MMC_TYPE_SD_COMBO;
if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO || if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
...@@ -466,7 +472,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, ...@@ -466,7 +472,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
card = oldcard; card = oldcard;
} }
mmc_fixup_device(card); mmc_fixup_device(card, NULL);
if (card->type == MMC_TYPE_SD_COMBO) { if (card->type == MMC_TYPE_SD_COMBO) {
err = mmc_sd_setup_card(host, card, oldcard != NULL); err = mmc_sd_setup_card(host, card, oldcard != NULL);
...@@ -625,7 +631,7 @@ static int mmc_sdio_suspend(struct mmc_host *host) ...@@ -625,7 +631,7 @@ static int mmc_sdio_suspend(struct mmc_host *host)
} }
} }
if (!err && host->pm_flags & MMC_PM_KEEP_POWER) { if (!err && mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
mmc_claim_host(host); mmc_claim_host(host);
sdio_disable_wide(host->card); sdio_disable_wide(host->card);
mmc_release_host(host); mmc_release_host(host);
...@@ -645,10 +651,10 @@ static int mmc_sdio_resume(struct mmc_host *host) ...@@ -645,10 +651,10 @@ static int mmc_sdio_resume(struct mmc_host *host)
mmc_claim_host(host); mmc_claim_host(host);
/* No need to reinitialize powered-resumed nonremovable cards */ /* No need to reinitialize powered-resumed nonremovable cards */
if (mmc_card_is_removable(host) || !mmc_card_is_powered_resumed(host)) if (mmc_card_is_removable(host) || !mmc_card_keep_power(host))
err = mmc_sdio_init_card(host, host->ocr, host->card, err = mmc_sdio_init_card(host, host->ocr, host->card,
(host->pm_flags & MMC_PM_KEEP_POWER)); mmc_card_keep_power(host));
else if (mmc_card_is_powered_resumed(host)) { else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
/* We may have switched to 1-bit mode during suspend */ /* We may have switched to 1-bit mode during suspend */
err = sdio_enable_4bit_bus(host->card); err = sdio_enable_4bit_bus(host->card);
if (err > 0) { if (err > 0) {
...@@ -691,7 +697,7 @@ static int mmc_sdio_power_restore(struct mmc_host *host) ...@@ -691,7 +697,7 @@ static int mmc_sdio_power_restore(struct mmc_host *host)
mmc_claim_host(host); mmc_claim_host(host);
ret = mmc_sdio_init_card(host, host->ocr, host->card, ret = mmc_sdio_init_card(host, host->ocr, host->card,
(host->pm_flags & MMC_PM_KEEP_POWER)); mmc_card_keep_power(host));
if (!ret && host->sdio_irqs) if (!ret && host->sdio_irqs)
mmc_signal_sdio_irq(host); mmc_signal_sdio_irq(host);
mmc_release_host(host); mmc_release_host(host);
......
...@@ -31,6 +31,17 @@ static int process_sdio_pending_irqs(struct mmc_card *card) ...@@ -31,6 +31,17 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
{ {
int i, ret, count; int i, ret, count;
unsigned char pending; unsigned char pending;
struct sdio_func *func;
/*
* Optimization, if there is only 1 function interrupt registered
* call irq handler directly
*/
func = card->sdio_single_irq;
if (func) {
func->irq_handler(func);
return 1;
}
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
if (ret) { if (ret) {
...@@ -42,7 +53,7 @@ static int process_sdio_pending_irqs(struct mmc_card *card) ...@@ -42,7 +53,7 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
count = 0; count = 0;
for (i = 1; i <= 7; i++) { for (i = 1; i <= 7; i++) {
if (pending & (1 << i)) { if (pending & (1 << i)) {
struct sdio_func *func = card->sdio_func[i - 1]; func = card->sdio_func[i - 1];
if (!func) { if (!func) {
printk(KERN_WARNING "%s: pending IRQ for " printk(KERN_WARNING "%s: pending IRQ for "
"non-existent function\n", "non-existent function\n",
...@@ -186,6 +197,24 @@ static int sdio_card_irq_put(struct mmc_card *card) ...@@ -186,6 +197,24 @@ static int sdio_card_irq_put(struct mmc_card *card)
return 0; return 0;
} }
/* If there is only 1 function registered set sdio_single_irq */
static void sdio_single_irq_set(struct mmc_card *card)
{
struct sdio_func *func;
int i;
card->sdio_single_irq = NULL;
if ((card->host->caps & MMC_CAP_SDIO_IRQ) &&
card->host->sdio_irqs == 1)
for (i = 0; i < card->sdio_funcs; i++) {
func = card->sdio_func[i];
if (func && func->irq_handler) {
card->sdio_single_irq = func;
break;
}
}
}
/** /**
* sdio_claim_irq - claim the IRQ for a SDIO function * sdio_claim_irq - claim the IRQ for a SDIO function
* @func: SDIO function * @func: SDIO function
...@@ -227,6 +256,7 @@ int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler) ...@@ -227,6 +256,7 @@ int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
ret = sdio_card_irq_get(func->card); ret = sdio_card_irq_get(func->card);
if (ret) if (ret)
func->irq_handler = NULL; func->irq_handler = NULL;
sdio_single_irq_set(func->card);
return ret; return ret;
} }
...@@ -251,6 +281,7 @@ int sdio_release_irq(struct sdio_func *func) ...@@ -251,6 +281,7 @@ int sdio_release_irq(struct sdio_func *func)
if (func->irq_handler) { if (func->irq_handler) {
func->irq_handler = NULL; func->irq_handler = NULL;
sdio_card_irq_put(func->card); sdio_card_irq_put(func->card);
sdio_single_irq_set(func->card);
} }
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg); ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);
......
...@@ -21,13 +21,11 @@ ...@@ -21,13 +21,11 @@
int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{ {
struct mmc_command cmd; struct mmc_command cmd = {0};
int i, err = 0; int i, err = 0;
BUG_ON(!host); BUG_ON(!host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_IO_SEND_OP_COND; cmd.opcode = SD_IO_SEND_OP_COND;
cmd.arg = ocr; cmd.arg = ocr;
cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR; cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR;
...@@ -70,7 +68,7 @@ int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) ...@@ -70,7 +68,7 @@ int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn, static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn,
unsigned addr, u8 in, u8 *out) unsigned addr, u8 in, u8 *out)
{ {
struct mmc_command cmd; struct mmc_command cmd = {0};
int err; int err;
BUG_ON(!host); BUG_ON(!host);
...@@ -80,8 +78,6 @@ static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn, ...@@ -80,8 +78,6 @@ static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn,
if (addr & ~0x1FFFF) if (addr & ~0x1FFFF)
return -EINVAL; return -EINVAL;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_IO_RW_DIRECT; cmd.opcode = SD_IO_RW_DIRECT;
cmd.arg = write ? 0x80000000 : 0x00000000; cmd.arg = write ? 0x80000000 : 0x00000000;
cmd.arg |= fn << 28; cmd.arg |= fn << 28;
...@@ -125,9 +121,9 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, ...@@ -125,9 +121,9 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz) unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
{ {
struct mmc_request mrq; struct mmc_request mrq = {0};
struct mmc_command cmd; struct mmc_command cmd = {0};
struct mmc_data data; struct mmc_data data = {0};
struct scatterlist sg; struct scatterlist sg;
BUG_ON(!card); BUG_ON(!card);
...@@ -140,10 +136,6 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, ...@@ -140,10 +136,6 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
if (addr & ~0x1FFFF) if (addr & ~0x1FFFF)
return -EINVAL; return -EINVAL;
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.cmd = &cmd;
mrq.data = &data; mrq.data = &data;
......
...@@ -154,7 +154,7 @@ config MMC_SDHCI_DOVE ...@@ -154,7 +154,7 @@ config MMC_SDHCI_DOVE
If unsure, say N. If unsure, say N.
config MMC_SDHCI_TEGRA config MMC_SDHCI_TEGRA
tristate "SDHCI platform support for the Tegra SD/MMC Controller" bool "SDHCI platform support for the Tegra SD/MMC Controller"
depends on MMC_SDHCI_PLTFM && ARCH_TEGRA depends on MMC_SDHCI_PLTFM && ARCH_TEGRA
select MMC_SDHCI_IO_ACCESSORS select MMC_SDHCI_IO_ACCESSORS
help help
...@@ -535,6 +535,37 @@ config MMC_JZ4740 ...@@ -535,6 +535,37 @@ config MMC_JZ4740
If you have a board based on such a SoC and with a SD/MMC slot, If you have a board based on such a SoC and with a SD/MMC slot,
say Y or M here. say Y or M here.
config MMC_VUB300
tristate "VUB300 USB to SDIO/SD/MMC Host Controller support"
depends on USB
help
This selects support for Elan Digital Systems' VUB300 chip.
The VUB300 is a USB-SDIO Host Controller Interface chip
that enables the host computer to use SDIO/SD/MMC cards
via a USB 2.0 or USB 1.1 host.
The VUB300 chip will be found in both physically separate
USB to SDIO/SD/MMC adapters and embedded on some motherboards.
The VUB300 chip supports SD and MMC memory cards in addition
to single and multifunction SDIO cards.
Some SDIO cards will need a firmware file to be loaded and
sent to VUB300 chip in order to achieve better data throughput.
Download these "Offload Pseudocode" from Elan Digital Systems'
web-site http://www.elandigitalsystems.com/support/downloads.php
and put them in /lib/firmware. Note that without these additional
firmware files the VUB300 chip will still function, but not at
the best obtainable data rate.
To compile this mmc host controller driver as a module,
choose M here: the module will be called vub300.
If you have a computer with an embedded VUB300 chip
or if you intend connecting a USB adapter based on a
VUB300 chip say Y or M here.
config MMC_USHC config MMC_USHC
tristate "USB SD Host Controller (USHC) support" tristate "USB SD Host Controller (USHC) support"
depends on USB depends on USB
......
...@@ -41,6 +41,7 @@ obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o ...@@ -41,6 +41,7 @@ obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
obj-$(CONFIG_MMC_DW) += dw_mmc.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_VUB300) += vub300.o
obj-$(CONFIG_MMC_USHC) += ushc.o obj-$(CONFIG_MMC_USHC) += ushc.o
obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o
......
...@@ -1769,9 +1769,6 @@ static int dw_mci_suspend(struct platform_device *pdev, pm_message_t mesg) ...@@ -1769,9 +1769,6 @@ static int dw_mci_suspend(struct platform_device *pdev, pm_message_t mesg)
int i, ret; int i, ret;
struct dw_mci *host = platform_get_drvdata(pdev); struct dw_mci *host = platform_get_drvdata(pdev);
if (host->vmmc)
regulator_enable(host->vmmc);
for (i = 0; i < host->num_slots; i++) { for (i = 0; i < host->num_slots; i++) {
struct dw_mci_slot *slot = host->slot[i]; struct dw_mci_slot *slot = host->slot[i];
if (!slot) if (!slot)
...@@ -1798,6 +1795,9 @@ static int dw_mci_resume(struct platform_device *pdev) ...@@ -1798,6 +1795,9 @@ static int dw_mci_resume(struct platform_device *pdev)
int i, ret; int i, ret;
struct dw_mci *host = platform_get_drvdata(pdev); struct dw_mci *host = platform_get_drvdata(pdev);
if (host->vmmc)
regulator_enable(host->vmmc);
if (host->dma_ops->init) if (host->dma_ops->init)
host->dma_ops->init(host); host->dma_ops->init(host);
......
...@@ -18,11 +18,9 @@ ...@@ -18,11 +18,9 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/scatterlist.h>
#include <asm/scatterlist.h> #include <linux/io.h>
#include <asm/io.h>
#include "sdhci.h" #include "sdhci.h"
...@@ -46,14 +44,14 @@ struct sdhci_pci_slot; ...@@ -46,14 +44,14 @@ struct sdhci_pci_slot;
struct sdhci_pci_fixes { struct sdhci_pci_fixes {
unsigned int quirks; unsigned int quirks;
int (*probe)(struct sdhci_pci_chip*); int (*probe) (struct sdhci_pci_chip *);
int (*probe_slot)(struct sdhci_pci_slot*); int (*probe_slot) (struct sdhci_pci_slot *);
void (*remove_slot)(struct sdhci_pci_slot*, int); void (*remove_slot) (struct sdhci_pci_slot *, int);
int (*suspend)(struct sdhci_pci_chip*, int (*suspend) (struct sdhci_pci_chip *,
pm_message_t); pm_message_t);
int (*resume)(struct sdhci_pci_chip*); int (*resume) (struct sdhci_pci_chip *);
}; };
struct sdhci_pci_slot { struct sdhci_pci_slot {
...@@ -329,6 +327,11 @@ static int jmicron_probe(struct sdhci_pci_chip *chip) ...@@ -329,6 +327,11 @@ static int jmicron_probe(struct sdhci_pci_chip *chip)
return ret; return ret;
} }
/* quirk for unsable RO-detection on JM388 chips */
if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD ||
chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD)
chip->quirks |= SDHCI_QUIRK_UNSTABLE_RO_DETECT;
return 0; return 0;
} }
...@@ -402,7 +405,7 @@ static int jmicron_suspend(struct sdhci_pci_chip *chip, pm_message_t state) ...@@ -402,7 +405,7 @@ static int jmicron_suspend(struct sdhci_pci_chip *chip, pm_message_t state)
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) { 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);
} }
...@@ -415,7 +418,7 @@ static int jmicron_resume(struct sdhci_pci_chip *chip) ...@@ -415,7 +418,7 @@ static int jmicron_resume(struct sdhci_pci_chip *chip)
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) { 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);
} }
...@@ -798,7 +801,7 @@ static struct sdhci_ops sdhci_pci_ops = { ...@@ -798,7 +801,7 @@ static struct sdhci_ops sdhci_pci_ops = {
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state) static int sdhci_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{ {
struct sdhci_pci_chip *chip; struct sdhci_pci_chip *chip;
struct sdhci_pci_slot *slot; struct sdhci_pci_slot *slot;
...@@ -810,7 +813,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state) ...@@ -810,7 +813,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
if (!chip) if (!chip)
return 0; return 0;
for (i = 0;i < chip->num_slots;i++) { for (i = 0; i < chip->num_slots; i++) {
slot = chip->slots[i]; slot = chip->slots[i];
if (!slot) if (!slot)
continue; continue;
...@@ -818,7 +821,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state) ...@@ -818,7 +821,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
ret = sdhci_suspend_host(slot->host, state); ret = sdhci_suspend_host(slot->host, state);
if (ret) { if (ret) {
for (i--;i >= 0;i--) for (i--; i >= 0; i--)
sdhci_resume_host(chip->slots[i]->host); sdhci_resume_host(chip->slots[i]->host);
return ret; return ret;
} }
...@@ -833,7 +836,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state) ...@@ -833,7 +836,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
if (chip->fixes && chip->fixes->suspend) { if (chip->fixes && chip->fixes->suspend) {
ret = chip->fixes->suspend(chip, state); ret = chip->fixes->suspend(chip, state);
if (ret) { if (ret) {
for (i = chip->num_slots - 1;i >= 0;i--) for (i = chip->num_slots - 1; i >= 0; i--)
sdhci_resume_host(chip->slots[i]->host); sdhci_resume_host(chip->slots[i]->host);
return ret; return ret;
} }
...@@ -855,7 +858,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state) ...@@ -855,7 +858,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
return 0; return 0;
} }
static int sdhci_pci_resume (struct pci_dev *pdev) static int sdhci_pci_resume(struct pci_dev *pdev)
{ {
struct sdhci_pci_chip *chip; struct sdhci_pci_chip *chip;
struct sdhci_pci_slot *slot; struct sdhci_pci_slot *slot;
...@@ -877,7 +880,7 @@ static int sdhci_pci_resume (struct pci_dev *pdev) ...@@ -877,7 +880,7 @@ static int sdhci_pci_resume (struct pci_dev *pdev)
return ret; return ret;
} }
for (i = 0;i < chip->num_slots;i++) { for (i = 0; i < chip->num_slots; i++) {
slot = chip->slots[i]; slot = chip->slots[i];
if (!slot) if (!slot)
continue; continue;
...@@ -1059,7 +1062,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, ...@@ -1059,7 +1062,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
} }
chip->pdev = pdev; chip->pdev = pdev;
chip->fixes = (const struct sdhci_pci_fixes*)ent->driver_data; chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data;
if (chip->fixes) if (chip->fixes)
chip->quirks = chip->fixes->quirks; chip->quirks = chip->fixes->quirks;
chip->num_slots = slots; chip->num_slots = slots;
...@@ -1074,10 +1077,10 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, ...@@ -1074,10 +1077,10 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
slots = chip->num_slots; /* Quirk may have changed this */ slots = chip->num_slots; /* Quirk may have changed this */
for (i = 0;i < slots;i++) { for (i = 0; i < slots; i++) {
slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i); slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i);
if (IS_ERR(slot)) { if (IS_ERR(slot)) {
for (i--;i >= 0;i--) for (i--; i >= 0; i--)
sdhci_pci_remove_slot(chip->slots[i]); sdhci_pci_remove_slot(chip->slots[i]);
ret = PTR_ERR(slot); ret = PTR_ERR(slot);
goto free; goto free;
...@@ -1105,7 +1108,7 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev) ...@@ -1105,7 +1108,7 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
chip = pci_get_drvdata(pdev); chip = pci_get_drvdata(pdev);
if (chip) { if (chip) {
for (i = 0;i < chip->num_slots; i++) for (i = 0; i < chip->num_slots; i++)
sdhci_pci_remove_slot(chip->slots[i]); sdhci_pci_remove_slot(chip->slots[i]);
pci_set_drvdata(pdev, NULL); pci_set_drvdata(pdev, NULL);
...@@ -1116,9 +1119,9 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev) ...@@ -1116,9 +1119,9 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
} }
static struct pci_driver sdhci_driver = { static struct pci_driver sdhci_driver = {
.name = "sdhci-pci", .name = "sdhci-pci",
.id_table = pci_ids, .id_table = pci_ids,
.probe = sdhci_pci_probe, .probe = sdhci_pci_probe,
.remove = __devexit_p(sdhci_pci_remove), .remove = __devexit_p(sdhci_pci_remove),
.suspend = sdhci_pci_suspend, .suspend = sdhci_pci_suspend,
.resume = sdhci_pci_resume, .resume = sdhci_pci_resume,
......
...@@ -69,7 +69,45 @@ static void set_clock(struct sdhci_host *host, unsigned int clock) ...@@ -69,7 +69,45 @@ static void set_clock(struct sdhci_host *host, unsigned int clock)
} }
} }
static int set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
{
u16 ctrl_2;
/*
* Set V18_EN -- UHS modes do not work without this.
* does not change signaling voltage
*/
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/* Select Bus Speed Mode for host */
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
switch (uhs) {
case MMC_TIMING_UHS_SDR12:
ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
break;
case MMC_TIMING_UHS_SDR25:
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
break;
case MMC_TIMING_UHS_SDR50:
ctrl_2 |= SDHCI_CTRL_UHS_SDR50 | SDHCI_CTRL_VDD_180;
break;
case MMC_TIMING_UHS_SDR104:
ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180;
break;
case MMC_TIMING_UHS_DDR50:
ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180;
break;
}
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
pr_debug("%s:%s uhs = %d, ctrl_2 = %04X\n",
__func__, mmc_hostname(host->mmc), uhs, ctrl_2);
return 0;
}
static struct sdhci_ops sdhci_pxa_ops = { static struct sdhci_ops sdhci_pxa_ops = {
.set_uhs_signaling = set_uhs_signaling,
.set_clock = set_clock, .set_clock = set_clock,
}; };
...@@ -136,11 +174,19 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev) ...@@ -136,11 +174,19 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
host->hw_name = "MMC"; host->hw_name = "MMC";
host->ops = &sdhci_pxa_ops; host->ops = &sdhci_pxa_ops;
host->irq = irq; host->irq = irq;
host->quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; host->quirks = SDHCI_QUIRK_BROKEN_ADMA
| SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
| SDHCI_QUIRK_32BIT_DMA_ADDR
| SDHCI_QUIRK_32BIT_DMA_SIZE
| SDHCI_QUIRK_32BIT_ADMA_SIZE
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
if (pdata->quirks) if (pdata->quirks)
host->quirks |= pdata->quirks; host->quirks |= pdata->quirks;
/* enable 1/8V DDR capable */
host->mmc->caps |= MMC_CAP_1_8V_DDR;
/* If slot design supports 8 bit data, indicate this to MMC. */ /* If slot design supports 8 bit data, indicate this to MMC. */
if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT) if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
host->mmc->caps |= MMC_CAP_8_BIT_DATA; host->mmc->caps |= MMC_CAP_8_BIT_DATA;
......
...@@ -184,6 +184,8 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host, ...@@ -184,6 +184,8 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host,
clk_enable(clk); clk_enable(clk);
pltfm_host->clk = clk; pltfm_host->clk = clk;
host->mmc->pm_caps = plat->pm_flags;
if (plat->is_8bit) if (plat->is_8bit)
host->mmc->caps |= MMC_CAP_8_BIT_DATA; host->mmc->caps |= MMC_CAP_8_BIT_DATA;
......
This diff is collapsed.
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
*/ */
#define SDHCI_DMA_ADDRESS 0x00 #define SDHCI_DMA_ADDRESS 0x00
#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS
#define SDHCI_BLOCK_SIZE 0x04 #define SDHCI_BLOCK_SIZE 0x04
#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF)) #define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
...@@ -36,7 +37,8 @@ ...@@ -36,7 +37,8 @@
#define SDHCI_TRANSFER_MODE 0x0C #define SDHCI_TRANSFER_MODE 0x0C
#define SDHCI_TRNS_DMA 0x01 #define SDHCI_TRNS_DMA 0x01
#define SDHCI_TRNS_BLK_CNT_EN 0x02 #define SDHCI_TRNS_BLK_CNT_EN 0x02
#define SDHCI_TRNS_ACMD12 0x04 #define SDHCI_TRNS_AUTO_CMD12 0x04
#define SDHCI_TRNS_AUTO_CMD23 0x08
#define SDHCI_TRNS_READ 0x10 #define SDHCI_TRNS_READ 0x10
#define SDHCI_TRNS_MULTI 0x20 #define SDHCI_TRNS_MULTI 0x20
...@@ -68,8 +70,10 @@ ...@@ -68,8 +70,10 @@
#define SDHCI_DATA_AVAILABLE 0x00000800 #define SDHCI_DATA_AVAILABLE 0x00000800
#define SDHCI_CARD_PRESENT 0x00010000 #define SDHCI_CARD_PRESENT 0x00010000
#define SDHCI_WRITE_PROTECT 0x00080000 #define SDHCI_WRITE_PROTECT 0x00080000
#define SDHCI_DATA_LVL_MASK 0x00F00000
#define SDHCI_DATA_LVL_SHIFT 20
#define SDHCI_HOST_CONTROL 0x28 #define SDHCI_HOST_CONTROL 0x28
#define SDHCI_CTRL_LED 0x01 #define SDHCI_CTRL_LED 0x01
#define SDHCI_CTRL_4BITBUS 0x02 #define SDHCI_CTRL_4BITBUS 0x02
#define SDHCI_CTRL_HISPD 0x04 #define SDHCI_CTRL_HISPD 0x04
...@@ -99,6 +103,7 @@ ...@@ -99,6 +103,7 @@
#define SDHCI_DIV_MASK 0xFF #define SDHCI_DIV_MASK 0xFF
#define SDHCI_DIV_MASK_LEN 8 #define SDHCI_DIV_MASK_LEN 8
#define SDHCI_DIV_HI_MASK 0x300 #define SDHCI_DIV_HI_MASK 0x300
#define SDHCI_PROG_CLOCK_MODE 0x0020
#define SDHCI_CLOCK_CARD_EN 0x0004 #define SDHCI_CLOCK_CARD_EN 0x0004
#define SDHCI_CLOCK_INT_STABLE 0x0002 #define SDHCI_CLOCK_INT_STABLE 0x0002
#define SDHCI_CLOCK_INT_EN 0x0001 #define SDHCI_CLOCK_INT_EN 0x0001
...@@ -146,7 +151,22 @@ ...@@ -146,7 +151,22 @@
#define SDHCI_ACMD12_ERR 0x3C #define SDHCI_ACMD12_ERR 0x3C
/* 3E-3F reserved */ #define SDHCI_HOST_CONTROL2 0x3E
#define SDHCI_CTRL_UHS_MASK 0x0007
#define SDHCI_CTRL_UHS_SDR12 0x0000
#define SDHCI_CTRL_UHS_SDR25 0x0001
#define SDHCI_CTRL_UHS_SDR50 0x0002
#define SDHCI_CTRL_UHS_SDR104 0x0003
#define SDHCI_CTRL_UHS_DDR50 0x0004
#define SDHCI_CTRL_VDD_180 0x0008
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
#define SDHCI_CTRL_DRV_TYPE_A 0x0010
#define SDHCI_CTRL_DRV_TYPE_C 0x0020
#define SDHCI_CTRL_DRV_TYPE_D 0x0030
#define SDHCI_CTRL_EXEC_TUNING 0x0040
#define SDHCI_CTRL_TUNED_CLK 0x0080
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
#define SDHCI_CAPABILITIES 0x40 #define SDHCI_CAPABILITIES 0x40
#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F #define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
...@@ -167,9 +187,30 @@ ...@@ -167,9 +187,30 @@
#define SDHCI_CAN_VDD_180 0x04000000 #define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT 0x10000000 #define SDHCI_CAN_64BIT 0x10000000
#define SDHCI_SUPPORT_SDR50 0x00000001
#define SDHCI_SUPPORT_SDR104 0x00000002
#define SDHCI_SUPPORT_DDR50 0x00000004
#define SDHCI_DRIVER_TYPE_A 0x00000010
#define SDHCI_DRIVER_TYPE_C 0x00000020
#define SDHCI_DRIVER_TYPE_D 0x00000040
#define SDHCI_RETUNING_TIMER_COUNT_MASK 0x00000F00
#define SDHCI_RETUNING_TIMER_COUNT_SHIFT 8
#define SDHCI_USE_SDR50_TUNING 0x00002000
#define SDHCI_RETUNING_MODE_MASK 0x0000C000
#define SDHCI_RETUNING_MODE_SHIFT 14
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
#define SDHCI_CLOCK_MUL_SHIFT 16
#define SDHCI_CAPABILITIES_1 0x44 #define SDHCI_CAPABILITIES_1 0x44
#define SDHCI_MAX_CURRENT 0x48 #define SDHCI_MAX_CURRENT 0x48
#define SDHCI_MAX_CURRENT_330_MASK 0x0000FF
#define SDHCI_MAX_CURRENT_330_SHIFT 0
#define SDHCI_MAX_CURRENT_300_MASK 0x00FF00
#define SDHCI_MAX_CURRENT_300_SHIFT 8
#define SDHCI_MAX_CURRENT_180_MASK 0xFF0000
#define SDHCI_MAX_CURRENT_180_SHIFT 16
#define SDHCI_MAX_CURRENT_MULTIPLIER 4
/* 4C-4F reserved for more max current */ /* 4C-4F reserved for more max current */
...@@ -202,6 +243,12 @@ ...@@ -202,6 +243,12 @@
#define SDHCI_MAX_DIV_SPEC_200 256 #define SDHCI_MAX_DIV_SPEC_200 256
#define SDHCI_MAX_DIV_SPEC_300 2046 #define SDHCI_MAX_DIV_SPEC_300 2046
/*
* Host SDMA buffer boundary. Valid values from 4K to 512K in powers of 2.
*/
#define SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024)
#define SDHCI_DEFAULT_BOUNDARY_ARG (ilog2(SDHCI_DEFAULT_BOUNDARY_SIZE) - 12)
struct sdhci_ops { struct sdhci_ops {
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
u32 (*read_l)(struct sdhci_host *host, int reg); u32 (*read_l)(struct sdhci_host *host, int reg);
...@@ -223,6 +270,10 @@ struct sdhci_ops { ...@@ -223,6 +270,10 @@ struct sdhci_ops {
void (*platform_send_init_74_clocks)(struct sdhci_host *host, void (*platform_send_init_74_clocks)(struct sdhci_host *host,
u8 power_mode); u8 power_mode);
unsigned int (*get_ro)(struct sdhci_host *host); unsigned int (*get_ro)(struct sdhci_host *host);
void (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
void (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
}; };
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
......
This diff is collapsed.
...@@ -62,7 +62,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -62,7 +62,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
struct tmio_mmc_host *host; struct tmio_mmc_host *host;
char clk_name[8]; char clk_name[8];
int ret; int i, irq, ret;
priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL); priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
if (priv == NULL) { if (priv == NULL) {
...@@ -71,6 +71,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -71,6 +71,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
} }
mmc_data = &priv->mmc_data; mmc_data = &priv->mmc_data;
p->pdata = mmc_data;
snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id); snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id);
priv->clk = clk_get(&pdev->dev, clk_name); priv->clk = clk_get(&pdev->dev, clk_name);
...@@ -116,11 +117,36 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -116,11 +117,36 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
if (ret < 0) if (ret < 0)
goto eprobe; goto eprobe;
pr_info("%s at 0x%08lx irq %d\n", mmc_hostname(host->mmc), for (i = 0; i < 3; i++) {
(unsigned long)host->ctl, host->irq); irq = platform_get_irq(pdev, i);
if (irq < 0) {
if (i) {
continue;
} else {
ret = irq;
goto eirq;
}
}
ret = request_irq(irq, tmio_mmc_irq, 0,
dev_name(&pdev->dev), host);
if (ret) {
while (i--) {
irq = platform_get_irq(pdev, i);
if (irq >= 0)
free_irq(irq, host);
}
goto eirq;
}
}
dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
mmc_hostname(host->mmc), (unsigned long)
(platform_get_resource(pdev,IORESOURCE_MEM, 0)->start),
mmc_data->hclk / 1000000);
return ret; return ret;
eirq:
tmio_mmc_host_remove(host);
eprobe: eprobe:
clk_disable(priv->clk); clk_disable(priv->clk);
clk_put(priv->clk); clk_put(priv->clk);
...@@ -134,6 +160,16 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev) ...@@ -134,6 +160,16 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
struct mmc_host *mmc = platform_get_drvdata(pdev); struct mmc_host *mmc = platform_get_drvdata(pdev);
struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_host *host = mmc_priv(mmc);
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
int i, irq;
p->pdata = NULL;
for (i = 0; i < 3; i++) {
irq = platform_get_irq(pdev, i);
if (irq >= 0)
free_irq(irq, host);
}
tmio_mmc_host_remove(host); tmio_mmc_host_remove(host);
clk_disable(priv->clk); clk_disable(priv->clk);
...@@ -143,10 +179,18 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev) ...@@ -143,10 +179,18 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
return 0; return 0;
} }
static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
.suspend = tmio_mmc_host_suspend,
.resume = tmio_mmc_host_resume,
.runtime_suspend = tmio_mmc_host_runtime_suspend,
.runtime_resume = tmio_mmc_host_runtime_resume,
};
static struct platform_driver sh_mobile_sdhi_driver = { static struct platform_driver sh_mobile_sdhi_driver = {
.driver = { .driver = {
.name = "sh_mobile_sdhi", .name = "sh_mobile_sdhi",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &tmio_mmc_dev_pm_ops,
}, },
.probe = sh_mobile_sdhi_probe, .probe = sh_mobile_sdhi_probe,
.remove = __devexit_p(sh_mobile_sdhi_remove), .remove = __devexit_p(sh_mobile_sdhi_remove),
......
...@@ -30,7 +30,7 @@ static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state) ...@@ -30,7 +30,7 @@ static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state)
struct mmc_host *mmc = platform_get_drvdata(dev); struct mmc_host *mmc = platform_get_drvdata(dev);
int ret; int ret;
ret = mmc_suspend_host(mmc); ret = tmio_mmc_host_suspend(&dev->dev);
/* Tell MFD core it can disable us now.*/ /* Tell MFD core it can disable us now.*/
if (!ret && cell->disable) if (!ret && cell->disable)
...@@ -46,15 +46,12 @@ static int tmio_mmc_resume(struct platform_device *dev) ...@@ -46,15 +46,12 @@ static int tmio_mmc_resume(struct platform_device *dev)
int ret = 0; int ret = 0;
/* Tell the MFD core we are ready to be enabled */ /* Tell the MFD core we are ready to be enabled */
if (cell->resume) { if (cell->resume)
ret = cell->resume(dev); ret = cell->resume(dev);
if (ret)
goto out;
}
mmc_resume_host(mmc); if (!ret)
ret = tmio_mmc_host_resume(&dev->dev);
out:
return ret; return ret;
} }
#else #else
...@@ -67,7 +64,7 @@ static int __devinit tmio_mmc_probe(struct platform_device *pdev) ...@@ -67,7 +64,7 @@ static int __devinit tmio_mmc_probe(struct platform_device *pdev)
const struct mfd_cell *cell = mfd_get_cell(pdev); const struct mfd_cell *cell = mfd_get_cell(pdev);
struct tmio_mmc_data *pdata; struct tmio_mmc_data *pdata;
struct tmio_mmc_host *host; struct tmio_mmc_host *host;
int ret = -EINVAL; int ret = -EINVAL, irq;
if (pdev->num_resources != 2) if (pdev->num_resources != 2)
goto out; goto out;
...@@ -76,6 +73,12 @@ static int __devinit tmio_mmc_probe(struct platform_device *pdev) ...@@ -76,6 +73,12 @@ static int __devinit tmio_mmc_probe(struct platform_device *pdev)
if (!pdata || !pdata->hclk) if (!pdata || !pdata->hclk)
goto out; goto out;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
goto out;
}
/* Tell the MFD core we are ready to be enabled */ /* Tell the MFD core we are ready to be enabled */
if (cell->enable) { if (cell->enable) {
ret = cell->enable(pdev); ret = cell->enable(pdev);
...@@ -87,11 +90,18 @@ static int __devinit tmio_mmc_probe(struct platform_device *pdev) ...@@ -87,11 +90,18 @@ static int __devinit tmio_mmc_probe(struct platform_device *pdev)
if (ret) if (ret)
goto cell_disable; goto cell_disable;
ret = request_irq(irq, tmio_mmc_irq, IRQF_DISABLED |
IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), host);
if (ret)
goto host_remove;
pr_info("%s at 0x%08lx irq %d\n", mmc_hostname(host->mmc), pr_info("%s at 0x%08lx irq %d\n", mmc_hostname(host->mmc),
(unsigned long)host->ctl, host->irq); (unsigned long)host->ctl, irq);
return 0; return 0;
host_remove:
tmio_mmc_host_remove(host);
cell_disable: cell_disable:
if (cell->disable) if (cell->disable)
cell->disable(pdev); cell->disable(pdev);
...@@ -107,7 +117,9 @@ static int __devexit tmio_mmc_remove(struct platform_device *pdev) ...@@ -107,7 +117,9 @@ static int __devexit tmio_mmc_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
if (mmc) { if (mmc) {
tmio_mmc_host_remove(mmc_priv(mmc)); struct tmio_mmc_host *host = mmc_priv(mmc);
free_irq(platform_get_irq(pdev, 0), host);
tmio_mmc_host_remove(host);
if (cell->disable) if (cell->disable)
cell->disable(pdev); cell->disable(pdev);
} }
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/mmc/tmio.h> #include <linux/mmc/tmio.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/spinlock.h>
/* Definitions for values the CTRL_SDIO_STATUS register can take. */ /* Definitions for values the CTRL_SDIO_STATUS register can take. */
#define TMIO_SDIO_STAT_IOIRQ 0x0001 #define TMIO_SDIO_STAT_IOIRQ 0x0001
...@@ -44,13 +45,14 @@ struct tmio_mmc_host { ...@@ -44,13 +45,14 @@ struct tmio_mmc_host {
struct mmc_request *mrq; struct mmc_request *mrq;
struct mmc_data *data; struct mmc_data *data;
struct mmc_host *mmc; struct mmc_host *mmc;
int irq;
unsigned int sdio_irq_enabled; unsigned int sdio_irq_enabled;
/* Callbacks for clock / power control */ /* Callbacks for clock / power control */
void (*set_pwr)(struct platform_device *host, int state); void (*set_pwr)(struct platform_device *host, int state);
void (*set_clk_div)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state);
int pm_error;
/* pio related stuff */ /* pio related stuff */
struct scatterlist *sg_ptr; struct scatterlist *sg_ptr;
struct scatterlist *sg_orig; struct scatterlist *sg_orig;
...@@ -83,6 +85,7 @@ void tmio_mmc_do_data_irq(struct tmio_mmc_host *host); ...@@ -83,6 +85,7 @@ void tmio_mmc_do_data_irq(struct tmio_mmc_host *host);
void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i); void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i); void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
irqreturn_t tmio_mmc_irq(int irq, void *devid);
static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg, static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg,
unsigned long *flags) unsigned long *flags)
...@@ -120,4 +123,15 @@ static inline void tmio_mmc_release_dma(struct tmio_mmc_host *host) ...@@ -120,4 +123,15 @@ static inline void tmio_mmc_release_dma(struct tmio_mmc_host *host)
} }
#endif #endif
#ifdef CONFIG_PM
int tmio_mmc_host_suspend(struct device *dev);
int tmio_mmc_host_resume(struct device *dev);
#else
#define tmio_mmc_host_suspend NULL
#define tmio_mmc_host_resume NULL
#endif
int tmio_mmc_host_runtime_suspend(struct device *dev);
int tmio_mmc_host_runtime_resume(struct device *dev);
#endif #endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -4,6 +4,7 @@ header-y += caif/ ...@@ -4,6 +4,7 @@ header-y += caif/
header-y += dvb/ header-y += dvb/
header-y += hdlc/ header-y += hdlc/
header-y += isdn/ header-y += isdn/
header-y += mmc/
header-y += nfsd/ header-y += nfsd/
header-y += raid/ header-y += raid/
header-y += spi/ header-y += spi/
......
This diff is collapsed.
header-y += ioctl.h
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -3,12 +3,16 @@ ...@@ -3,12 +3,16 @@
#include <linux/types.h> #include <linux/types.h>
struct platform_device;
struct tmio_mmc_data;
struct sh_mobile_sdhi_info { struct sh_mobile_sdhi_info {
int dma_slave_tx; int dma_slave_tx;
int dma_slave_rx; int dma_slave_rx;
unsigned long tmio_flags; unsigned long tmio_flags;
unsigned long tmio_caps; unsigned long tmio_caps;
u32 tmio_ocr_mask; /* available MMC voltages */ u32 tmio_ocr_mask; /* available MMC voltages */
struct tmio_mmc_data *pdata;
void (*set_pwr)(struct platform_device *pdev, int state); void (*set_pwr)(struct platform_device *pdev, int state);
int (*get_cd)(struct platform_device *pdev); int (*get_cd)(struct platform_device *pdev);
}; };
......
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