MMC fixes for 3.3-rc4:
* The most visible fix here is against a regression introduced in 3.3-rc1 that ran cards in Ultra High Speed mode even when they failed to initialize in that mode, leading to lower-speed cards failing to mount. * A lockdep warning introduced in 3.3-rc1 is fixed. * Various other small driver fixes, most notably for a NULL dereference when using highmem with dw_mmc. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJPObt1AAoJEHNBYZ7TNxYMy84P/0PJbzSXNLc9SZuGjGFFFzFA Y/T+nnG5vY0LU7YTsjqa83L1pRxfuPA+r34WgUM2oP2wxjNEdQ8tOrrwGdEbMc51 p0pe+Z8H1vjyqV/4Vum4iPaw0W3jmd/Lwqu/pcQDmC+2KLDBpOJ8VIw3kEp9Vqzo X/34Wkh9KRqS4Q7a4SiQfjNPF/uj1lkeDCagzJq01QzfYQZh5RitV1zow4nf8Kzj TWDOhZXeYUTvKn1Vv9hrtuhqoBVqoLhKsiTy5SUbyjgpjsMEmfoMkTtvFQkq72iO ZX7n6I8AOrtFC5idpPXRe7/5KERdKLvgvbqo98pnt2qJD4AeQj2O6xaO2TualXK3 ksMwC37dexWexxVuq3O+LVK8uiIaqg0zjv0el+wqreh9zG/rIIk7EnIah1iUStnZ TrT/7M0hQ7gkBCaAQ8cl9VRIEgtBN7s0GKFVSO/rfltqV5tNE4c7z1tmQbT9hjW5 PyTQzOyGsxF9F7kJJ9I/9MEeywwuaX0d9LAfgX2djk8j0q2ev6TuHAXNMAPAnHud 5oEUncvMtkLB8R6zd8a05BXYLQdeM6zDp4QR/jUWiu8XKo93pwv6Uh+jTTBtphpc ky49Ig2CGC61nidM05wUFSjmeDL9/smHAU7aYFhoYEJMb+7U2LsDgpEsBpH6/WNc F569rR3IFBqYRnRi3gI+ =XEG7 -----END PGP SIGNATURE----- Merge tag 'mmc-fixes-for-3.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc MMC fixes for 3.3-rc4: * The most visible fix here is against a regression introduced in 3.3-rc1 that ran cards in Ultra High Speed mode even when they failed to initialize in that mode, leading to lower-speed cards failing to mount. * A lockdep warning introduced in 3.3-rc1 is fixed. * Various other small driver fixes, most notably for a NULL dereference when using highmem with dw_mmc. * tag 'mmc-fixes-for-3.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: mmc: dw_mmc: Fix PIO mode with support of highmem mmc: atmel-mci: save and restore sdioirq when soft reset is performed mmc: block: Init ro_lock sysfs attr to fix lockdep warnings mmc: sh_mmcif: fix late delayed work initialisation mmc: tmio_mmc: fix card eject during IO with DMA mmc: core: Fix comparison issue in mmc_compare_ext_csds mmc: core: Fix PowerOff Notify suspend/resume mmc: sdhci-pci: set Medfield SDIO as non-removable mmc: core: add the capability for broken voltage mmc: core: Fix low speed mmc card detection failure mmc: esdhc: set the timeout to the max value mmc: esdhc: add PIO mode support mmc: core: Ensure clocks are always enabled before host interaction mmc: of_mmc_spi: fix little endian support mmc: core: UHS sdio card that fails should not exceed 50MHz mmc: esdhc: fix errors when booting kernel on Freescale eSDHC version 2.3
This commit is contained in:
commit
8b36ac50da
@ -1694,6 +1694,7 @@ static int mmc_add_disk(struct mmc_blk_data *md)
|
||||
|
||||
md->power_ro_lock.show = power_ro_lock_show;
|
||||
md->power_ro_lock.store = power_ro_lock_store;
|
||||
sysfs_attr_init(&md->power_ro_lock.attr);
|
||||
md->power_ro_lock.attr.mode = mode;
|
||||
md->power_ro_lock.attr.name =
|
||||
"ro_lock_until_next_power_on";
|
||||
|
@ -290,8 +290,11 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
|
||||
static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq,
|
||||
bool is_first_req)
|
||||
{
|
||||
if (host->ops->pre_req)
|
||||
if (host->ops->pre_req) {
|
||||
mmc_host_clk_hold(host);
|
||||
host->ops->pre_req(host, mrq, is_first_req);
|
||||
mmc_host_clk_release(host);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -306,8 +309,11 @@ static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq,
|
||||
static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
|
||||
int err)
|
||||
{
|
||||
if (host->ops->post_req)
|
||||
if (host->ops->post_req) {
|
||||
mmc_host_clk_hold(host);
|
||||
host->ops->post_req(host, mrq, err);
|
||||
mmc_host_clk_release(host);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -620,7 +626,9 @@ int mmc_host_enable(struct mmc_host *host)
|
||||
int err;
|
||||
|
||||
host->en_dis_recurs = 1;
|
||||
mmc_host_clk_hold(host);
|
||||
err = host->ops->enable(host);
|
||||
mmc_host_clk_release(host);
|
||||
host->en_dis_recurs = 0;
|
||||
|
||||
if (err) {
|
||||
@ -640,7 +648,9 @@ static int mmc_host_do_disable(struct mmc_host *host, int lazy)
|
||||
int err;
|
||||
|
||||
host->en_dis_recurs = 1;
|
||||
mmc_host_clk_hold(host);
|
||||
err = host->ops->disable(host, lazy);
|
||||
mmc_host_clk_release(host);
|
||||
host->en_dis_recurs = 0;
|
||||
|
||||
if (err < 0) {
|
||||
@ -1121,6 +1131,10 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
||||
* might not allow this operation
|
||||
*/
|
||||
voltage = regulator_get_voltage(supply);
|
||||
|
||||
if (mmc->caps2 & MMC_CAP2_BROKEN_VOLTAGE)
|
||||
min_uV = max_uV = voltage;
|
||||
|
||||
if (voltage < 0)
|
||||
result = voltage;
|
||||
else if (voltage < min_uV || voltage > max_uV)
|
||||
@ -1203,8 +1217,11 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11
|
||||
|
||||
host->ios.signal_voltage = signal_voltage;
|
||||
|
||||
if (host->ops->start_signal_voltage_switch)
|
||||
if (host->ops->start_signal_voltage_switch) {
|
||||
mmc_host_clk_hold(host);
|
||||
err = host->ops->start_signal_voltage_switch(host, &host->ios);
|
||||
mmc_host_clk_release(host);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -1239,6 +1256,7 @@ static void mmc_poweroff_notify(struct mmc_host *host)
|
||||
int err = 0;
|
||||
|
||||
card = host->card;
|
||||
mmc_claim_host(host);
|
||||
|
||||
/*
|
||||
* Send power notify command only if card
|
||||
@ -1269,6 +1287,7 @@ static void mmc_poweroff_notify(struct mmc_host *host)
|
||||
/* Set the card state to no notification after the poweroff */
|
||||
card->poweroff_notify_state = MMC_NO_POWER_NOTIFICATION;
|
||||
}
|
||||
mmc_release_host(host);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1327,12 +1346,28 @@ static void mmc_power_up(struct mmc_host *host)
|
||||
|
||||
void mmc_power_off(struct mmc_host *host)
|
||||
{
|
||||
int err = 0;
|
||||
mmc_host_clk_hold(host);
|
||||
|
||||
host->ios.clock = 0;
|
||||
host->ios.vdd = 0;
|
||||
|
||||
mmc_poweroff_notify(host);
|
||||
/*
|
||||
* For eMMC 4.5 device send AWAKE command before
|
||||
* POWER_OFF_NOTIFY command, because in sleep state
|
||||
* eMMC 4.5 devices respond to only RESET and AWAKE cmd
|
||||
*/
|
||||
if (host->card && mmc_card_is_sleep(host->card) &&
|
||||
host->bus_ops->resume) {
|
||||
err = host->bus_ops->resume(host);
|
||||
|
||||
if (!err)
|
||||
mmc_poweroff_notify(host);
|
||||
else
|
||||
pr_warning("%s: error %d during resume "
|
||||
"(continue with poweroff sequence)\n",
|
||||
mmc_hostname(host), err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset ocr mask to be the highest possible voltage supported for
|
||||
@ -2386,12 +2421,6 @@ int mmc_suspend_host(struct mmc_host *host)
|
||||
*/
|
||||
if (mmc_try_claim_host(host)) {
|
||||
if (host->bus_ops->suspend) {
|
||||
/*
|
||||
* For eMMC 4.5 device send notify command
|
||||
* before sleep, because in sleep state eMMC 4.5
|
||||
* devices respond to only RESET and AWAKE cmd
|
||||
*/
|
||||
mmc_poweroff_notify(host);
|
||||
err = host->bus_ops->suspend(host);
|
||||
}
|
||||
mmc_do_release_host(host);
|
||||
|
@ -14,27 +14,6 @@
|
||||
|
||||
int mmc_register_host_class(void);
|
||||
void mmc_unregister_host_class(void);
|
||||
|
||||
#ifdef CONFIG_MMC_CLKGATE
|
||||
void mmc_host_clk_hold(struct mmc_host *host);
|
||||
void mmc_host_clk_release(struct mmc_host *host);
|
||||
unsigned int mmc_host_clk_rate(struct mmc_host *host);
|
||||
|
||||
#else
|
||||
static inline void mmc_host_clk_hold(struct mmc_host *host)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mmc_host_clk_release(struct mmc_host *host)
|
||||
{
|
||||
}
|
||||
|
||||
static inline unsigned int mmc_host_clk_rate(struct mmc_host *host)
|
||||
{
|
||||
return host->ios.clock;
|
||||
}
|
||||
#endif
|
||||
|
||||
void mmc_host_deeper_disable(struct work_struct *work);
|
||||
|
||||
#endif
|
||||
|
@ -376,7 +376,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
}
|
||||
|
||||
card->ext_csd.raw_hc_erase_gap_size =
|
||||
ext_csd[EXT_CSD_PARTITION_ATTRIBUTE];
|
||||
ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
|
||||
card->ext_csd.raw_sec_trim_mult =
|
||||
ext_csd[EXT_CSD_SEC_TRIM_MULT];
|
||||
card->ext_csd.raw_sec_erase_mult =
|
||||
@ -551,7 +551,7 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
|
||||
goto out;
|
||||
|
||||
/* only compare read only fields */
|
||||
err = (!(card->ext_csd.raw_partition_support ==
|
||||
err = !((card->ext_csd.raw_partition_support ==
|
||||
bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) &&
|
||||
(card->ext_csd.raw_erased_mem_count ==
|
||||
bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) &&
|
||||
@ -1006,7 +1006,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
err = mmc_select_hs200(card);
|
||||
else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, 1, 0);
|
||||
EXT_CSD_HS_TIMING, 1,
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
|
||||
if (err && err != -EBADMSG)
|
||||
goto free_card;
|
||||
@ -1116,7 +1117,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
* Activate wide bus and DDR (if supported).
|
||||
*/
|
||||
if (!mmc_card_hs200(card) &&
|
||||
(card->csd.mmca_vsn >= CSD_SPEC_VER_3) &&
|
||||
(card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
|
||||
(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
|
||||
static unsigned ext_csd_bits[][2] = {
|
||||
{ EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 },
|
||||
@ -1315,11 +1316,13 @@ static int mmc_suspend(struct mmc_host *host)
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
if (mmc_card_can_sleep(host))
|
||||
if (mmc_card_can_sleep(host)) {
|
||||
err = mmc_card_sleep(host);
|
||||
else if (!mmc_host_is_spi(host))
|
||||
if (!err)
|
||||
mmc_card_set_sleep(host->card);
|
||||
} else if (!mmc_host_is_spi(host))
|
||||
mmc_deselect_cards(host);
|
||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
|
||||
mmc_release_host(host);
|
||||
|
||||
return err;
|
||||
@ -1339,7 +1342,11 @@ static int mmc_resume(struct mmc_host *host)
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
err = mmc_init_card(host, host->ocr, host->card);
|
||||
if (mmc_card_is_sleep(host->card)) {
|
||||
err = mmc_card_awake(host);
|
||||
mmc_card_clr_sleep(host->card);
|
||||
} else
|
||||
err = mmc_init_card(host, host->ocr, host->card);
|
||||
mmc_release_host(host);
|
||||
|
||||
return err;
|
||||
@ -1349,7 +1356,8 @@ static int mmc_power_restore(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
|
||||
mmc_card_clr_sleep(host->card);
|
||||
mmc_claim_host(host);
|
||||
ret = mmc_init_card(host, host->ocr, host->card);
|
||||
mmc_release_host(host);
|
||||
|
@ -451,9 +451,11 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
|
||||
* information and let the hardware specific code
|
||||
* return what is possible given the options
|
||||
*/
|
||||
mmc_host_clk_hold(card->host);
|
||||
drive_strength = card->host->ops->select_drive_strength(
|
||||
card->sw_caps.uhs_max_dtr,
|
||||
host_drv_type, card_drv_type);
|
||||
mmc_host_clk_release(card->host);
|
||||
|
||||
err = mmc_sd_switch(card, 1, 2, drive_strength, status);
|
||||
if (err)
|
||||
@ -660,9 +662,12 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
|
||||
goto out;
|
||||
|
||||
/* SPI mode doesn't define CMD19 */
|
||||
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
|
||||
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) {
|
||||
mmc_host_clk_hold(card->host);
|
||||
err = card->host->ops->execute_tuning(card->host,
|
||||
MMC_SEND_TUNING_BLOCK);
|
||||
mmc_host_clk_release(card->host);
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(status);
|
||||
@ -850,8 +855,11 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
|
||||
if (!reinit) {
|
||||
int ro = -1;
|
||||
|
||||
if (host->ops->get_ro)
|
||||
if (host->ops->get_ro) {
|
||||
mmc_host_clk_hold(card->host);
|
||||
ro = host->ops->get_ro(host);
|
||||
mmc_host_clk_release(card->host);
|
||||
}
|
||||
|
||||
if (ro < 0) {
|
||||
pr_warning("%s: host does not "
|
||||
@ -967,8 +975,11 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||
* Since initialization is now complete, enable preset
|
||||
* value registers for UHS-I cards.
|
||||
*/
|
||||
if (host->ops->enable_preset_value)
|
||||
if (host->ops->enable_preset_value) {
|
||||
mmc_host_clk_hold(card->host);
|
||||
host->ops->enable_preset_value(host, true);
|
||||
mmc_host_clk_release(card->host);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Attempt to change to high-speed (if supported)
|
||||
@ -1151,8 +1162,11 @@ int mmc_attach_sd(struct mmc_host *host)
|
||||
return err;
|
||||
|
||||
/* Disable preset value enable if already set since last time */
|
||||
if (host->ops->enable_preset_value)
|
||||
if (host->ops->enable_preset_value) {
|
||||
mmc_host_clk_hold(host);
|
||||
host->ops->enable_preset_value(host, false);
|
||||
mmc_host_clk_release(host);
|
||||
}
|
||||
|
||||
err = mmc_send_app_op_cond(host, 0, &ocr);
|
||||
if (err)
|
||||
|
@ -98,10 +98,11 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdio_read_cccr(struct mmc_card *card)
|
||||
static int sdio_read_cccr(struct mmc_card *card, u32 ocr)
|
||||
{
|
||||
int ret;
|
||||
int cccr_vsn;
|
||||
int uhs = ocr & R4_18V_PRESENT;
|
||||
unsigned char data;
|
||||
unsigned char speed;
|
||||
|
||||
@ -149,7 +150,7 @@ static int sdio_read_cccr(struct mmc_card *card)
|
||||
card->scr.sda_spec3 = 0;
|
||||
card->sw_caps.sd3_bus_mode = 0;
|
||||
card->sw_caps.sd3_drv_type = 0;
|
||||
if (cccr_vsn >= SDIO_CCCR_REV_3_00) {
|
||||
if (cccr_vsn >= SDIO_CCCR_REV_3_00 && uhs) {
|
||||
card->scr.sda_spec3 = 1;
|
||||
ret = mmc_io_rw_direct(card, 0, 0,
|
||||
SDIO_CCCR_UHS, 0, &data);
|
||||
@ -712,7 +713,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
||||
/*
|
||||
* Read the common registers.
|
||||
*/
|
||||
err = sdio_read_cccr(card);
|
||||
err = sdio_read_cccr(card, ocr);
|
||||
if (err)
|
||||
goto remove;
|
||||
|
||||
|
@ -146,15 +146,21 @@ static int sdio_irq_thread(void *_host)
|
||||
}
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (host->caps & MMC_CAP_SDIO_IRQ)
|
||||
if (host->caps & MMC_CAP_SDIO_IRQ) {
|
||||
mmc_host_clk_hold(host);
|
||||
host->ops->enable_sdio_irq(host, 1);
|
||||
mmc_host_clk_release(host);
|
||||
}
|
||||
if (!kthread_should_stop())
|
||||
schedule_timeout(period);
|
||||
set_current_state(TASK_RUNNING);
|
||||
} while (!kthread_should_stop());
|
||||
|
||||
if (host->caps & MMC_CAP_SDIO_IRQ)
|
||||
if (host->caps & MMC_CAP_SDIO_IRQ) {
|
||||
mmc_host_clk_hold(host);
|
||||
host->ops->enable_sdio_irq(host, 0);
|
||||
mmc_host_clk_release(host);
|
||||
}
|
||||
|
||||
pr_debug("%s: IRQ thread exiting with code %d\n",
|
||||
mmc_hostname(host), ret);
|
||||
|
@ -969,11 +969,14 @@ static void atmci_start_request(struct atmel_mci *host,
|
||||
host->data_status = 0;
|
||||
|
||||
if (host->need_reset) {
|
||||
iflags = atmci_readl(host, ATMCI_IMR);
|
||||
iflags &= (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB);
|
||||
atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
|
||||
atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
|
||||
atmci_writel(host, ATMCI_MR, host->mode_reg);
|
||||
if (host->caps.has_cfg_reg)
|
||||
atmci_writel(host, ATMCI_CFG, host->cfg_reg);
|
||||
atmci_writel(host, ATMCI_IER, iflags);
|
||||
host->need_reset = false;
|
||||
}
|
||||
atmci_writel(host, ATMCI_SDCR, slot->sdc_reg);
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
@ -502,8 +501,14 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
|
||||
host->dir_status = DW_MCI_SEND_STATUS;
|
||||
|
||||
if (dw_mci_submit_data_dma(host, data)) {
|
||||
int flags = SG_MITER_ATOMIC;
|
||||
if (host->data->flags & MMC_DATA_READ)
|
||||
flags |= SG_MITER_TO_SG;
|
||||
else
|
||||
flags |= SG_MITER_FROM_SG;
|
||||
|
||||
sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
|
||||
host->sg = data->sg;
|
||||
host->pio_offset = 0;
|
||||
host->part_buf_start = 0;
|
||||
host->part_buf_count = 0;
|
||||
|
||||
@ -972,6 +977,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
||||
* generates a block interrupt, hence setting
|
||||
* the scatter-gather pointer to NULL.
|
||||
*/
|
||||
sg_miter_stop(&host->sg_miter);
|
||||
host->sg = NULL;
|
||||
ctrl = mci_readl(host, CTRL);
|
||||
ctrl |= SDMMC_CTRL_FIFO_RESET;
|
||||
@ -1311,54 +1317,44 @@ static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt)
|
||||
|
||||
static void dw_mci_read_data_pio(struct dw_mci *host)
|
||||
{
|
||||
struct scatterlist *sg = host->sg;
|
||||
void *buf = sg_virt(sg);
|
||||
unsigned int offset = host->pio_offset;
|
||||
struct sg_mapping_iter *sg_miter = &host->sg_miter;
|
||||
void *buf;
|
||||
unsigned int offset;
|
||||
struct mmc_data *data = host->data;
|
||||
int shift = host->data_shift;
|
||||
u32 status;
|
||||
unsigned int nbytes = 0, len;
|
||||
unsigned int remain, fcnt;
|
||||
|
||||
do {
|
||||
len = host->part_buf_count +
|
||||
(SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift);
|
||||
if (offset + len <= sg->length) {
|
||||
dw_mci_pull_data(host, (void *)(buf + offset), len);
|
||||
if (!sg_miter_next(sg_miter))
|
||||
goto done;
|
||||
|
||||
host->sg = sg_miter->__sg;
|
||||
buf = sg_miter->addr;
|
||||
remain = sg_miter->length;
|
||||
offset = 0;
|
||||
|
||||
do {
|
||||
fcnt = (SDMMC_GET_FCNT(mci_readl(host, STATUS))
|
||||
<< shift) + host->part_buf_count;
|
||||
len = min(remain, fcnt);
|
||||
if (!len)
|
||||
break;
|
||||
dw_mci_pull_data(host, (void *)(buf + offset), len);
|
||||
offset += len;
|
||||
nbytes += len;
|
||||
|
||||
if (offset == sg->length) {
|
||||
flush_dcache_page(sg_page(sg));
|
||||
host->sg = sg = sg_next(sg);
|
||||
if (!sg)
|
||||
goto done;
|
||||
|
||||
offset = 0;
|
||||
buf = sg_virt(sg);
|
||||
}
|
||||
} else {
|
||||
unsigned int remaining = sg->length - offset;
|
||||
dw_mci_pull_data(host, (void *)(buf + offset),
|
||||
remaining);
|
||||
nbytes += remaining;
|
||||
|
||||
flush_dcache_page(sg_page(sg));
|
||||
host->sg = sg = sg_next(sg);
|
||||
if (!sg)
|
||||
goto done;
|
||||
|
||||
offset = len - remaining;
|
||||
buf = sg_virt(sg);
|
||||
dw_mci_pull_data(host, buf, offset);
|
||||
nbytes += offset;
|
||||
}
|
||||
remain -= len;
|
||||
} while (remain);
|
||||
sg_miter->consumed = offset;
|
||||
|
||||
status = mci_readl(host, MINTSTS);
|
||||
mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
|
||||
if (status & DW_MCI_DATA_ERROR_FLAGS) {
|
||||
host->data_status = status;
|
||||
data->bytes_xfered += nbytes;
|
||||
sg_miter_stop(sg_miter);
|
||||
host->sg = NULL;
|
||||
smp_wmb();
|
||||
|
||||
set_bit(EVENT_DATA_ERROR, &host->pending_events);
|
||||
@ -1367,65 +1363,66 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
|
||||
return;
|
||||
}
|
||||
} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/
|
||||
host->pio_offset = offset;
|
||||
data->bytes_xfered += nbytes;
|
||||
|
||||
if (!remain) {
|
||||
if (!sg_miter_next(sg_miter))
|
||||
goto done;
|
||||
sg_miter->consumed = 0;
|
||||
}
|
||||
sg_miter_stop(sg_miter);
|
||||
return;
|
||||
|
||||
done:
|
||||
data->bytes_xfered += nbytes;
|
||||
sg_miter_stop(sg_miter);
|
||||
host->sg = NULL;
|
||||
smp_wmb();
|
||||
set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
|
||||
}
|
||||
|
||||
static void dw_mci_write_data_pio(struct dw_mci *host)
|
||||
{
|
||||
struct scatterlist *sg = host->sg;
|
||||
void *buf = sg_virt(sg);
|
||||
unsigned int offset = host->pio_offset;
|
||||
struct sg_mapping_iter *sg_miter = &host->sg_miter;
|
||||
void *buf;
|
||||
unsigned int offset;
|
||||
struct mmc_data *data = host->data;
|
||||
int shift = host->data_shift;
|
||||
u32 status;
|
||||
unsigned int nbytes = 0, len;
|
||||
unsigned int fifo_depth = host->fifo_depth;
|
||||
unsigned int remain, fcnt;
|
||||
|
||||
do {
|
||||
len = ((host->fifo_depth -
|
||||
SDMMC_GET_FCNT(mci_readl(host, STATUS))) << shift)
|
||||
- host->part_buf_count;
|
||||
if (offset + len <= sg->length) {
|
||||
host->push_data(host, (void *)(buf + offset), len);
|
||||
if (!sg_miter_next(sg_miter))
|
||||
goto done;
|
||||
|
||||
host->sg = sg_miter->__sg;
|
||||
buf = sg_miter->addr;
|
||||
remain = sg_miter->length;
|
||||
offset = 0;
|
||||
|
||||
do {
|
||||
fcnt = ((fifo_depth -
|
||||
SDMMC_GET_FCNT(mci_readl(host, STATUS)))
|
||||
<< shift) - host->part_buf_count;
|
||||
len = min(remain, fcnt);
|
||||
if (!len)
|
||||
break;
|
||||
host->push_data(host, (void *)(buf + offset), len);
|
||||
offset += len;
|
||||
nbytes += len;
|
||||
if (offset == sg->length) {
|
||||
host->sg = sg = sg_next(sg);
|
||||
if (!sg)
|
||||
goto done;
|
||||
|
||||
offset = 0;
|
||||
buf = sg_virt(sg);
|
||||
}
|
||||
} else {
|
||||
unsigned int remaining = sg->length - offset;
|
||||
|
||||
host->push_data(host, (void *)(buf + offset),
|
||||
remaining);
|
||||
nbytes += remaining;
|
||||
|
||||
host->sg = sg = sg_next(sg);
|
||||
if (!sg)
|
||||
goto done;
|
||||
|
||||
offset = len - remaining;
|
||||
buf = sg_virt(sg);
|
||||
host->push_data(host, (void *)buf, offset);
|
||||
nbytes += offset;
|
||||
}
|
||||
remain -= len;
|
||||
} while (remain);
|
||||
sg_miter->consumed = offset;
|
||||
|
||||
status = mci_readl(host, MINTSTS);
|
||||
mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
|
||||
if (status & DW_MCI_DATA_ERROR_FLAGS) {
|
||||
host->data_status = status;
|
||||
data->bytes_xfered += nbytes;
|
||||
sg_miter_stop(sg_miter);
|
||||
host->sg = NULL;
|
||||
|
||||
smp_wmb();
|
||||
|
||||
@ -1435,12 +1432,20 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
|
||||
return;
|
||||
}
|
||||
} while (status & SDMMC_INT_TXDR); /* if TXDR write again */
|
||||
host->pio_offset = offset;
|
||||
data->bytes_xfered += nbytes;
|
||||
|
||||
if (!remain) {
|
||||
if (!sg_miter_next(sg_miter))
|
||||
goto done;
|
||||
sg_miter->consumed = 0;
|
||||
}
|
||||
sg_miter_stop(sg_miter);
|
||||
return;
|
||||
|
||||
done:
|
||||
data->bytes_xfered += nbytes;
|
||||
sg_miter_stop(sg_miter);
|
||||
host->sg = NULL;
|
||||
smp_wmb();
|
||||
set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
|
||||
}
|
||||
@ -1643,6 +1648,7 @@ static void dw_mci_work_routine_card(struct work_struct *work)
|
||||
* block interrupt, hence setting the
|
||||
* scatter-gather pointer to NULL.
|
||||
*/
|
||||
sg_miter_stop(&host->sg_miter);
|
||||
host->sg = NULL;
|
||||
|
||||
ctrl = mci_readl(host, CTRL);
|
||||
|
@ -113,8 +113,8 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi)
|
||||
const int j = i * 2;
|
||||
u32 mask;
|
||||
|
||||
mask = mmc_vddrange_to_ocrmask(voltage_ranges[j],
|
||||
voltage_ranges[j + 1]);
|
||||
mask = mmc_vddrange_to_ocrmask(be32_to_cpu(voltage_ranges[j]),
|
||||
be32_to_cpu(voltage_ranges[j + 1]));
|
||||
if (!mask) {
|
||||
ret = -EINVAL;
|
||||
dev_err(dev, "OF: voltage-range #%d is invalid\n", i);
|
||||
|
@ -38,6 +38,23 @@ static u8 esdhc_readb(struct sdhci_host *host, int reg)
|
||||
int base = reg & ~0x3;
|
||||
int shift = (reg & 0x3) * 8;
|
||||
u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff;
|
||||
|
||||
/*
|
||||
* "DMA select" locates at offset 0x28 in SD specification, but on
|
||||
* P5020 or P3041, it locates at 0x29.
|
||||
*/
|
||||
if (reg == SDHCI_HOST_CONTROL) {
|
||||
u32 dma_bits;
|
||||
|
||||
dma_bits = in_be32(host->ioaddr + reg);
|
||||
/* DMA select is 22,23 bits in Protocol Control Register */
|
||||
dma_bits = (dma_bits >> 5) & SDHCI_CTRL_DMA_MASK;
|
||||
|
||||
/* fixup the result */
|
||||
ret &= ~SDHCI_CTRL_DMA_MASK;
|
||||
ret |= dma_bits;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -56,6 +73,21 @@ static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
|
||||
|
||||
static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
|
||||
{
|
||||
/*
|
||||
* "DMA select" location is offset 0x28 in SD specification, but on
|
||||
* P5020 or P3041, it's located at 0x29.
|
||||
*/
|
||||
if (reg == SDHCI_HOST_CONTROL) {
|
||||
u32 dma_bits;
|
||||
|
||||
/* DMA select is 22,23 bits in Protocol Control Register */
|
||||
dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5;
|
||||
clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5,
|
||||
dma_bits);
|
||||
val &= ~SDHCI_CTRL_DMA_MASK;
|
||||
val |= in_be32(host->ioaddr + reg) & SDHCI_CTRL_DMA_MASK;
|
||||
}
|
||||
|
||||
/* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */
|
||||
if (reg == SDHCI_HOST_CONTROL)
|
||||
val &= ~ESDHC_HOST_CONTROL_RES;
|
||||
|
@ -250,7 +250,7 @@ static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||
|
||||
static int mfd_sdio_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD;
|
||||
slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
* sdhci-pltfm.c Support for SDHCI platform devices
|
||||
* Copyright (c) 2009 Intel Corporation
|
||||
*
|
||||
* Copyright (c) 2007 Freescale Semiconductor, Inc.
|
||||
* Copyright (c) 2007, 2011 Freescale Semiconductor, Inc.
|
||||
* Copyright (c) 2009 MontaVista Software, Inc.
|
||||
*
|
||||
* Authors: Xiaobo Xie <X.Xie@freescale.com>
|
||||
@ -71,6 +71,14 @@ void sdhci_get_of_property(struct platform_device *pdev)
|
||||
if (sdhci_of_wp_inverted(np))
|
||||
host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc"))
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_DMA;
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,p2020-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,p1010-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,mpc8536-esdhc"))
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||
|
||||
clk = of_get_property(np, "clock-frequency", &size);
|
||||
if (clk && size == sizeof(*clk) && *clk)
|
||||
pltfm_host->clock = be32_to_cpup(clk);
|
||||
|
@ -1327,7 +1327,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
goto clean_up2;
|
||||
|
||||
mmc_add_host(mmc);
|
||||
INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work);
|
||||
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
|
||||
|
||||
@ -1338,22 +1338,24 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
|
||||
}
|
||||
ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:int", host);
|
||||
if (ret) {
|
||||
free_irq(irq[0], host);
|
||||
dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n");
|
||||
goto clean_up3;
|
||||
goto clean_up4;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work);
|
||||
|
||||
mmc_detect_change(host->mmc, 0);
|
||||
ret = mmc_add_host(mmc);
|
||||
if (ret < 0)
|
||||
goto clean_up5;
|
||||
|
||||
dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION);
|
||||
dev_dbg(&pdev->dev, "chip ver H'%04x\n",
|
||||
sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff);
|
||||
return ret;
|
||||
|
||||
clean_up5:
|
||||
free_irq(irq[1], host);
|
||||
clean_up4:
|
||||
free_irq(irq[0], host);
|
||||
clean_up3:
|
||||
mmc_remove_host(mmc);
|
||||
pm_runtime_suspend(&pdev->dev);
|
||||
clean_up2:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
@ -20,8 +20,8 @@
|
||||
#include <linux/mmc/tmio.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
/* Definitions for values the CTRL_SDIO_STATUS register can take. */
|
||||
#define TMIO_SDIO_STAT_IOIRQ 0x0001
|
||||
@ -120,6 +120,7 @@ void tmio_mmc_start_dma(struct tmio_mmc_host *host, struct mmc_data *data);
|
||||
void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable);
|
||||
void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata);
|
||||
void tmio_mmc_release_dma(struct tmio_mmc_host *host);
|
||||
void tmio_mmc_abort_dma(struct tmio_mmc_host *host);
|
||||
#else
|
||||
static inline void tmio_mmc_start_dma(struct tmio_mmc_host *host,
|
||||
struct mmc_data *data)
|
||||
@ -140,6 +141,10 @@ static inline void tmio_mmc_request_dma(struct tmio_mmc_host *host,
|
||||
static inline void tmio_mmc_release_dma(struct tmio_mmc_host *host)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void tmio_mmc_abort_dma(struct tmio_mmc_host *host)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -34,6 +34,18 @@ void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable)
|
||||
#endif
|
||||
}
|
||||
|
||||
void tmio_mmc_abort_dma(struct tmio_mmc_host *host)
|
||||
{
|
||||
tmio_mmc_enable_dma(host, false);
|
||||
|
||||
if (host->chan_rx)
|
||||
dmaengine_terminate_all(host->chan_rx);
|
||||
if (host->chan_tx)
|
||||
dmaengine_terminate_all(host->chan_tx);
|
||||
|
||||
tmio_mmc_enable_dma(host, true);
|
||||
}
|
||||
|
||||
static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct scatterlist *sg = host->sg_ptr, *sg_tmp;
|
||||
|
@ -41,8 +41,8 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "tmio_mmc.h"
|
||||
|
||||
@ -246,6 +246,7 @@ static void tmio_mmc_reset_work(struct work_struct *work)
|
||||
/* Ready for new calls */
|
||||
host->mrq = NULL;
|
||||
|
||||
tmio_mmc_abort_dma(host);
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
}
|
||||
|
||||
@ -272,6 +273,9 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
|
||||
host->mrq = NULL;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
if (mrq->cmd->error || (mrq->data && mrq->data->error))
|
||||
tmio_mmc_abort_dma(host);
|
||||
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
}
|
||||
|
||||
|
@ -217,6 +217,7 @@ struct mmc_card {
|
||||
#define MMC_CARD_SDXC (1<<6) /* card is SDXC */
|
||||
#define MMC_CARD_REMOVED (1<<7) /* card has been removed */
|
||||
#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */
|
||||
#define MMC_STATE_SLEEP (1<<9) /* card is in sleep state */
|
||||
unsigned int quirks; /* card quirks */
|
||||
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
|
||||
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
|
||||
@ -382,6 +383,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
|
||||
#define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
|
||||
#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
|
||||
#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED))
|
||||
#define mmc_card_is_sleep(c) ((c)->state & MMC_STATE_SLEEP)
|
||||
|
||||
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
|
||||
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
|
||||
@ -393,7 +395,9 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
|
||||
#define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
|
||||
#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
|
||||
#define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
|
||||
#define mmc_card_set_sleep(c) ((c)->state |= MMC_STATE_SLEEP)
|
||||
|
||||
#define mmc_card_clr_sleep(c) ((c)->state &= ~MMC_STATE_SLEEP)
|
||||
/*
|
||||
* Quirk add/remove for MMC products.
|
||||
*/
|
||||
|
@ -14,6 +14,8 @@
|
||||
#ifndef LINUX_MMC_DW_MMC_H
|
||||
#define LINUX_MMC_DW_MMC_H
|
||||
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
#define MAX_MCI_SLOTS 2
|
||||
|
||||
enum dw_mci_state {
|
||||
@ -40,7 +42,7 @@ struct mmc_data;
|
||||
* @lock: Spinlock protecting the queue and associated data.
|
||||
* @regs: Pointer to MMIO registers.
|
||||
* @sg: Scatterlist entry currently being processed by PIO code, if any.
|
||||
* @pio_offset: Offset into the current scatterlist entry.
|
||||
* @sg_miter: PIO mapping scatterlist iterator.
|
||||
* @cur_slot: The slot which is currently using the controller.
|
||||
* @mrq: The request currently being processed on @cur_slot,
|
||||
* or NULL if the controller is idle.
|
||||
@ -115,7 +117,7 @@ struct dw_mci {
|
||||
void __iomem *regs;
|
||||
|
||||
struct scatterlist *sg;
|
||||
unsigned int pio_offset;
|
||||
struct sg_mapping_iter sg_miter;
|
||||
|
||||
struct dw_mci_slot *cur_slot;
|
||||
struct mmc_request *mrq;
|
||||
|
@ -257,6 +257,7 @@ struct mmc_host {
|
||||
#define MMC_CAP2_HS200_1_2V_SDR (1 << 6) /* can support */
|
||||
#define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \
|
||||
MMC_CAP2_HS200_1_2V_SDR)
|
||||
#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */
|
||||
|
||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||
unsigned int power_notify_type;
|
||||
@ -444,4 +445,23 @@ static inline int mmc_boot_partition_access(struct mmc_host *host)
|
||||
return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MMC_CLKGATE
|
||||
void mmc_host_clk_hold(struct mmc_host *host);
|
||||
void mmc_host_clk_release(struct mmc_host *host);
|
||||
unsigned int mmc_host_clk_rate(struct mmc_host *host);
|
||||
|
||||
#else
|
||||
static inline void mmc_host_clk_hold(struct mmc_host *host)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mmc_host_clk_release(struct mmc_host *host)
|
||||
{
|
||||
}
|
||||
|
||||
static inline unsigned int mmc_host_clk_rate(struct mmc_host *host)
|
||||
{
|
||||
return host->ios.clock;
|
||||
}
|
||||
#endif
|
||||
#endif /* LINUX_MMC_HOST_H */
|
||||
|
Loading…
Reference in New Issue
Block a user