MMC highlights for 3.8:
Core: - Expose access to the eMMC RPMB ("Replay Protected Memory Block") area by extending the existing mmc_block ioctl. - Add SDIO powered-suspend DT properties to the core MMC DT binding. - Add no-1-8-v DT flag for boards where the SD controller reports that it supports 1.8V but the board itself has no way to switch to 1.8V. - More work on switching to 1.8V UHS support using a vqmmc regulator. - Fix up a case where the slot-gpio helper may fail to reset the host controller properly if a card was removed during a transfer. - Fix several cases where a broken device could cause an infinite loop while we wait for a register to update. Drivers: - at91-mci: Remove obsolete driver, atmel-mci handles these devices now. - sdhci-dove: Allow using GPIOs for card-detect notifications. - sdhci-esdhc: Fix for recovering from ADMA errors on broken silicon. - sdhci-s3c: Add pinctrl support. - wmt-sdmmc: New driver for WonderMedia SD/MMC controllers. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJQxpwoAAoJEHNBYZ7TNxYMir0P/in5xv6vVq+06hMb9JTA2+M0 +eSgNAoSQt3Zn3dTxGtcaMbrnVvegMD2sRmnHncSKoi6iUgWh7RhuT7o8vp2zgJe KbuOF/OF5+RSnLwgCMcOCjauhBtHO7s+NPNbUH4DMN2/xQ7fpiY+QIsxrKWtImyO E4v62zRm/6aa1IfGDgYTAlX8Y1QfEQhH55OYrF9UzU1m7TKsSGxYTXwx+LeLQZQ0 j6HO+FVlmu5jbZ4V69z3qNIdiklRGQBE7D+7cJqW6btv7x4oLmyygEoNA8M8ncxX g5BD5oEt9oxW5OW8hYcEJ/flDErgekZekH8n+fZoNVDVq8gMJxHOV73mnN+wxRnd lRGLhniVOzl4cU+ObcYTBzdUV+2yR3sRHSK8nwtb+HFATetM70gBlpoWni1+sGr5 qvFhnpzNrj0xCwoQpLerwrvCwPpyko9uxZQwO51HqhugFwWlJuTqQsnCgj+Q4MDE CRUq4R6TCH7oMGeGin0qyD7jyigDnh7g4pPNQmuIYJSLsDwvinisz98MQ0XC8szc Z1H9YXKch2woLHLPFWzNyAIlJWHXakjk5vb9uSQmkUIto/y7q+LiGVCc5uvny3rU pJmda+U/8R8YmSWS7pUi6OmPRfcnPuAIllEwo3lBypVpdP/fcZ6aribrbGEo6DLb XLFIHnl3O9/sjfUcMl+L =oSX3 -----END PGP SIGNATURE----- Merge tag 'mmc-updates-for-3.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc Pull MMC updates from Chris Ball: "MMC highlights for 3.8: Core: - Expose access to the eMMC RPMB ("Replay Protected Memory Block") area by extending the existing mmc_block ioctl. - Add SDIO powered-suspend DT properties to the core MMC DT binding. - Add no-1-8-v DT flag for boards where the SD controller reports that it supports 1.8V but the board itself has no way to switch to 1.8V. - More work on switching to 1.8V UHS support using a vqmmc regulator. - Fix up a case where the slot-gpio helper may fail to reset the host controller properly if a card was removed during a transfer. - Fix several cases where a broken device could cause an infinite loop while we wait for a register to update. Drivers: - at91-mci: Remove obsolete driver, atmel-mci handles these devices now. - sdhci-dove: Allow using GPIOs for card-detect notifications. - sdhci-esdhc: Fix for recovering from ADMA errors on broken silicon. - sdhci-s3c: Add pinctrl support. - wmt-sdmmc: New driver for WonderMedia SD/MMC controllers." * tag 'mmc-updates-for-3.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (65 commits) mmc: sdhci: implement the .card_event() method mmc: extend the slot-gpio card-detection to use host's .card_event() method mmc: add a card-event host operation mmc: sdhci-s3c: Fix compilation warning mmc: sdhci-pci: Enable SDHCI_CAN_DO_HISPD for Ricoh SDHCI controller mmc: sdhci-dove: allow GPIOs to be used for card detection on Dove mmc: sdhci-dove: use two-stage initialization for sdhci-pltfm mmc: sdhci-dove: use devm_clk_get() mmc: eSDHC: Recover from ADMA errors mmc: dw_mmc: remove duplicated buswidth code mmc: dw_mmc: relocate where dw_mci_setup_bus() is called from mmc: Limit MMC speed to 52MHz if not HS200 mmc: dw_mmc: use devres functions in dw_mmc mmc: sh_mmcif: remove unneeded clock connection ID mmc: sh_mobile_sdhi: remove unneeded clock connection ID mmc: sh_mobile_sdhi: fix clock frequency printing mmc: Remove redundant null check before kfree in bus.c mmc: Remove redundant null check before kfree in sdio_bus.c mmc: sdhci-imx-esdhc: use more devm_* functions mmc: dt: add no-1-8-v device tree flag ...
This commit is contained in:
commit
11b84c5857
|
@ -21,6 +21,12 @@ Optional properties:
|
|||
- cd-inverted: when present, polarity on the cd gpio line is inverted
|
||||
- wp-inverted: when present, polarity on the wp gpio line is inverted
|
||||
- max-frequency: maximum operating clock frequency
|
||||
- no-1-8-v: when present, denotes that 1.8v card voltage is not supported on
|
||||
this system, even if the controller claims it is.
|
||||
|
||||
Optional SDIO properties:
|
||||
- keep-power-in-suspend: Preserves card power during a suspend/resume cycle
|
||||
- enable-sdio-wakeup: Enables wake up of host system on SDIO IRQ assertion
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -33,4 +39,6 @@ sdhci@ab000000 {
|
|||
cd-inverted;
|
||||
wp-gpios = <&gpio 70 0>;
|
||||
max-frequency = <50000000>;
|
||||
keep-power-in-suspend;
|
||||
enable-sdio-wakeup;
|
||||
}
|
||||
|
|
|
@ -12,10 +12,6 @@ is used. The Samsung's SDHCI controller bindings extends this as listed below.
|
|||
[A] The property "samsung,cd-pinmux-gpio" can be used as stated in the
|
||||
"Optional Board Specific Properties" section below.
|
||||
|
||||
[B] If core card-detect bindings and "samsung,cd-pinmux-gpio" property
|
||||
is not specified, it is assumed that there is no card detection
|
||||
mechanism used.
|
||||
|
||||
Required SoC Specific Properties:
|
||||
- compatible: should be one of the following
|
||||
- "samsung,s3c6410-sdhci": For controllers compatible with s3c6410 sdhci
|
||||
|
@ -24,14 +20,18 @@ Required SoC Specific Properties:
|
|||
controller.
|
||||
|
||||
Required Board Specific Properties:
|
||||
- gpios: Should specify the gpios used for clock, command and data lines. The
|
||||
gpio specifier format depends on the gpio controller.
|
||||
- Samsung GPIO variant (will be completely replaced by pinctrl):
|
||||
- gpios: Should specify the gpios used for clock, command and data lines. The
|
||||
gpio specifier format depends on the gpio controller.
|
||||
- Pinctrl variant (preferred if available):
|
||||
- pinctrl-0: Should specify pin control groups used for this controller.
|
||||
- pinctrl-names: Should contain only one value - "default".
|
||||
|
||||
Optional Board Specific Properties:
|
||||
- samsung,cd-pinmux-gpio: Specifies the card detect line that is routed
|
||||
through a pinmux to the card-detect pin of the card slot. This property
|
||||
should be used only if none of the mmc core card-detect properties are
|
||||
used.
|
||||
used. Only for Samsung GPIO variant.
|
||||
|
||||
Example:
|
||||
sdhci@12530000 {
|
||||
|
@ -40,12 +40,18 @@ Example:
|
|||
interrupts = <0 75 0>;
|
||||
bus-width = <4>;
|
||||
cd-gpios = <&gpk2 2 2 3 3>;
|
||||
|
||||
/* Samsung GPIO variant */
|
||||
gpios = <&gpk2 0 2 0 3>, /* clock line */
|
||||
<&gpk2 1 2 0 3>, /* command line */
|
||||
<&gpk2 3 2 3 3>, /* data line 0 */
|
||||
<&gpk2 4 2 3 3>, /* data line 1 */
|
||||
<&gpk2 5 2 3 3>, /* data line 2 */
|
||||
<&gpk2 6 2 3 3>; /* data line 3 */
|
||||
|
||||
/* Pinctrl variant */
|
||||
pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus4>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
Note: This example shows both SoC specific and board specific properties
|
||||
|
|
|
@ -19,6 +19,7 @@ ti,dual-volt: boolean, supports dual voltage cards
|
|||
"supply-name" examples are "vmmc", "vmmc_aux" etc
|
||||
ti,non-removable: non-removable slot (like eMMC)
|
||||
ti,needs-special-reset: Requires a special softreset sequence
|
||||
ti,needs-special-hs-handling: HSMMC IP needs special setting for handling High Speed
|
||||
|
||||
Example:
|
||||
mmc1: mmc@0x4809c000 {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
* Wondermedia WM8505/WM8650 SD/MMC Host Controller
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties used by the wmt-sdmmc driver.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "wm,wm8505-sdhc".
|
||||
- interrupts: Two interrupts are required - regular irq and dma irq.
|
||||
|
||||
Optional properties:
|
||||
- sdon-inverted: SD_ON bit is inverted on the controller
|
||||
|
||||
Examples:
|
||||
|
||||
sdhc@d800a000 {
|
||||
compatible = "wm,wm8505-sdhc";
|
||||
reg = <0xd800a000 0x1000>;
|
||||
interrupts = <20 21>;
|
||||
clocks = <&sdhc>;
|
||||
bus-width = <4>;
|
||||
sdon-inverted;
|
||||
};
|
||||
|
|
@ -25,6 +25,8 @@ All attributes are read-only.
|
|||
serial Product Serial Number (from CID Register)
|
||||
erase_size Erase group size
|
||||
preferred_erase_size Preferred erase size
|
||||
raw_rpmb_size_mult RPMB partition size
|
||||
rel_sectors Reliable write sector count
|
||||
|
||||
Note on Erase Size and Preferred Erase Size:
|
||||
|
||||
|
@ -65,6 +67,11 @@ Note on Erase Size and Preferred Erase Size:
|
|||
|
||||
"preferred_erase_size" is in bytes.
|
||||
|
||||
Note on raw_rpmb_size_mult:
|
||||
"raw_rpmb_size_mult" is a mutliple of 128kB block.
|
||||
RPMB size in byte is calculated by using the following equation:
|
||||
RPMB partition size = 128kB x raw_rpmb_size_mult
|
||||
|
||||
SD/MMC/SDIO Clock Gating Attribute
|
||||
==================================
|
||||
|
||||
|
|
|
@ -1237,6 +1237,7 @@ F: drivers/video/wm8505fb*
|
|||
F: drivers/video/wmt_ge_rops.*
|
||||
F: drivers/tty/serial/vt8500_serial.c
|
||||
F: drivers/rtc/rtc-vt8500-c
|
||||
F: drivers/mmc/host/wmt-sdmmc.c
|
||||
|
||||
ARM/ZIPIT Z2 SUPPORT
|
||||
M: Marek Vasut <marek.vasut@gmail.com>
|
||||
|
@ -1368,14 +1369,6 @@ S: Maintained
|
|||
F: drivers/atm/
|
||||
F: include/linux/atm*
|
||||
|
||||
ATMEL AT91 MCI DRIVER
|
||||
M: Ludovic Desroches <ludovic.desroches@atmel.com>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
W: http://www.atmel.com/products/AT91/
|
||||
W: http://www.at91.com/
|
||||
S: Maintained
|
||||
F: drivers/mmc/host/at91_mci.c
|
||||
|
||||
ATMEL AT91 / AT32 MCI DRIVER
|
||||
M: Ludovic Desroches <ludovic.desroches@atmel.com>
|
||||
S: Maintained
|
||||
|
|
|
@ -70,16 +70,6 @@ struct at91_cf_data {
|
|||
extern void __init at91_add_device_cf(struct at91_cf_data *data);
|
||||
|
||||
/* MMC / SD */
|
||||
/* at91_mci platform config */
|
||||
struct at91_mmc_data {
|
||||
int det_pin; /* card detect IRQ */
|
||||
unsigned slot_b:1; /* uses Slot B */
|
||||
unsigned wire4:1; /* (SD) supports DAT0..DAT3 */
|
||||
int wp_pin; /* (SD) writeprotect detect */
|
||||
int vcc_pin; /* power switching (high == on) */
|
||||
};
|
||||
extern void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data);
|
||||
|
||||
/* atmel-mci platform config */
|
||||
extern void __init at91_add_device_mci(short mmc_id, struct mci_platform_data *data);
|
||||
|
||||
|
|
|
@ -126,6 +126,7 @@ struct omap_mmc_platform_data {
|
|||
/* we can put the features above into this variable */
|
||||
#define HSMMC_HAS_PBIAS (1 << 0)
|
||||
#define HSMMC_HAS_UPDATED_RESET (1 << 1)
|
||||
#define HSMMC_HAS_HSPE_SUPPORT (1 << 2)
|
||||
unsigned features;
|
||||
|
||||
int switch_pin; /* gpio (card detect) */
|
||||
|
|
|
@ -57,6 +57,7 @@ MODULE_ALIAS("mmc:block");
|
|||
#define INAND_CMD38_ARG_SECERASE 0x80
|
||||
#define INAND_CMD38_ARG_SECTRIM1 0x81
|
||||
#define INAND_CMD38_ARG_SECTRIM2 0x88
|
||||
#define MMC_BLK_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
|
||||
|
||||
static DEFINE_MUTEX(block_mutex);
|
||||
|
||||
|
@ -126,6 +127,10 @@ enum mmc_blk_status {
|
|||
module_param(perdev_minors, int, 0444);
|
||||
MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device");
|
||||
|
||||
static inline int mmc_blk_part_switch(struct mmc_card *card,
|
||||
struct mmc_blk_data *md);
|
||||
static int get_card_status(struct mmc_card *card, u32 *status, int retries);
|
||||
|
||||
static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
|
||||
{
|
||||
struct mmc_blk_data *md;
|
||||
|
@ -357,6 +362,38 @@ out:
|
|||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status,
|
||||
u32 retries_max)
|
||||
{
|
||||
int err;
|
||||
u32 retry_count = 0;
|
||||
|
||||
if (!status || !retries_max)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
err = get_card_status(card, status, 5);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
if (!R1_STATUS(*status) &&
|
||||
(R1_CURRENT_STATE(*status) != R1_STATE_PRG))
|
||||
break; /* RPMB programming operation complete */
|
||||
|
||||
/*
|
||||
* Rechedule to give the MMC device a chance to continue
|
||||
* processing the previous command without being polled too
|
||||
* frequently.
|
||||
*/
|
||||
usleep_range(1000, 5000);
|
||||
} while (++retry_count < retries_max);
|
||||
|
||||
if (retry_count == retries_max)
|
||||
err = -EPERM;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
struct mmc_ioc_cmd __user *ic_ptr)
|
||||
{
|
||||
|
@ -368,6 +405,8 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
|||
struct mmc_request mrq = {NULL};
|
||||
struct scatterlist sg;
|
||||
int err;
|
||||
int is_rpmb = false;
|
||||
u32 status = 0;
|
||||
|
||||
/*
|
||||
* The caller must have CAP_SYS_RAWIO, and must be calling this on the
|
||||
|
@ -387,6 +426,9 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
|||
goto cmd_err;
|
||||
}
|
||||
|
||||
if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
|
||||
is_rpmb = true;
|
||||
|
||||
card = md->queue.card;
|
||||
if (IS_ERR(card)) {
|
||||
err = PTR_ERR(card);
|
||||
|
@ -437,12 +479,23 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
|||
|
||||
mmc_claim_host(card->host);
|
||||
|
||||
err = mmc_blk_part_switch(card, md);
|
||||
if (err)
|
||||
goto cmd_rel_host;
|
||||
|
||||
if (idata->ic.is_acmd) {
|
||||
err = mmc_app_cmd(card->host, card);
|
||||
if (err)
|
||||
goto cmd_rel_host;
|
||||
}
|
||||
|
||||
if (is_rpmb) {
|
||||
err = mmc_set_blockcount(card, data.blocks,
|
||||
idata->ic.write_flag & (1 << 31));
|
||||
if (err)
|
||||
goto cmd_rel_host;
|
||||
}
|
||||
|
||||
mmc_wait_for_req(card->host, &mrq);
|
||||
|
||||
if (cmd.error) {
|
||||
|
@ -478,6 +531,18 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
|||
}
|
||||
}
|
||||
|
||||
if (is_rpmb) {
|
||||
/*
|
||||
* Ensure RPMB command has completed by polling CMD13
|
||||
* "Send Status".
|
||||
*/
|
||||
err = ioctl_rpmb_card_status_poll(card, &status, 5);
|
||||
if (err)
|
||||
dev_err(mmc_dev(card->host),
|
||||
"%s: Card Status=0x%08X, error %d\n",
|
||||
__func__, status, err);
|
||||
}
|
||||
|
||||
cmd_rel_host:
|
||||
mmc_release_host(card->host);
|
||||
|
||||
|
@ -1034,6 +1099,9 @@ static int mmc_blk_err_check(struct mmc_card *card,
|
|||
*/
|
||||
if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
|
||||
u32 status;
|
||||
unsigned long timeout;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(MMC_BLK_TIMEOUT_MS);
|
||||
do {
|
||||
int err = get_card_status(card, &status, 5);
|
||||
if (err) {
|
||||
|
@ -1041,6 +1109,17 @@ static int mmc_blk_err_check(struct mmc_card *card,
|
|||
req->rq_disk->disk_name, err);
|
||||
return MMC_BLK_CMD_ERR;
|
||||
}
|
||||
|
||||
/* Timeout if the device never becomes ready for data
|
||||
* and never leaves the program state.
|
||||
*/
|
||||
if (time_after(jiffies, timeout)) {
|
||||
pr_err("%s: Card stuck in programming state!"\
|
||||
" %s %s\n", mmc_hostname(card->host),
|
||||
req->rq_disk->disk_name, __func__);
|
||||
|
||||
return MMC_BLK_CMD_ERR;
|
||||
}
|
||||
/*
|
||||
* Some cards mishandle the status bits,
|
||||
* so make sure to check both the busy
|
||||
|
@ -1504,6 +1583,8 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
|||
md->disk->queue = md->queue.queue;
|
||||
md->disk->driverfs_dev = parent;
|
||||
set_disk_ro(md->disk, md->read_only || default_ro);
|
||||
if (area_type & MMC_BLK_DATA_AREA_RPMB)
|
||||
md->disk->flags |= GENHD_FL_NO_PART_SCAN;
|
||||
|
||||
/*
|
||||
* As discussed on lkml, GENHD_FL_REMOVABLE should:
|
||||
|
|
|
@ -68,6 +68,16 @@ static int mmc_queue_thread(void *d)
|
|||
if (req || mq->mqrq_prev->req) {
|
||||
set_current_state(TASK_RUNNING);
|
||||
mq->issue_fn(mq, req);
|
||||
|
||||
/*
|
||||
* Current request becomes previous request
|
||||
* and vice versa.
|
||||
*/
|
||||
mq->mqrq_prev->brq.mrq.data = NULL;
|
||||
mq->mqrq_prev->req = NULL;
|
||||
tmp = mq->mqrq_prev;
|
||||
mq->mqrq_prev = mq->mqrq_cur;
|
||||
mq->mqrq_cur = tmp;
|
||||
} else {
|
||||
if (kthread_should_stop()) {
|
||||
set_current_state(TASK_RUNNING);
|
||||
|
@ -77,13 +87,6 @@ static int mmc_queue_thread(void *d)
|
|||
schedule();
|
||||
down(&mq->thread_sem);
|
||||
}
|
||||
|
||||
/* Current request becomes previous request and vice versa. */
|
||||
mq->mqrq_prev->brq.mrq.data = NULL;
|
||||
mq->mqrq_prev->req = NULL;
|
||||
tmp = mq->mqrq_prev;
|
||||
mq->mqrq_prev = mq->mqrq_cur;
|
||||
mq->mqrq_cur = tmp;
|
||||
} while (1);
|
||||
up(&mq->thread_sem);
|
||||
|
||||
|
|
|
@ -225,8 +225,7 @@ static void mmc_release_card(struct device *dev)
|
|||
|
||||
sdio_free_common_cis(card);
|
||||
|
||||
if (card->info)
|
||||
kfree(card->info);
|
||||
kfree(card->info);
|
||||
|
||||
kfree(card);
|
||||
}
|
||||
|
|
|
@ -42,6 +42,9 @@
|
|||
#include "sd_ops.h"
|
||||
#include "sdio_ops.h"
|
||||
|
||||
/* If the device is not responding */
|
||||
#define MMC_CORE_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
|
||||
|
||||
/*
|
||||
* Background operations can take a long time, depending on the housekeeping
|
||||
* operations the card has to perform.
|
||||
|
@ -1631,6 +1634,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
|||
{
|
||||
struct mmc_command cmd = {0};
|
||||
unsigned int qty = 0;
|
||||
unsigned long timeout;
|
||||
int err;
|
||||
|
||||
/*
|
||||
|
@ -1708,6 +1712,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
|||
if (mmc_host_is_spi(card->host))
|
||||
goto out;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(MMC_CORE_TIMEOUT_MS);
|
||||
do {
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
cmd.opcode = MMC_SEND_STATUS;
|
||||
|
@ -1721,8 +1726,19 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
|||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Timeout if the device never becomes ready for data and
|
||||
* never leaves the program state.
|
||||
*/
|
||||
if (time_after(jiffies, timeout)) {
|
||||
pr_err("%s: Card stuck in programming state! %s\n",
|
||||
mmc_hostname(card->host), __func__);
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
|
||||
R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG);
|
||||
(R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG));
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
@ -1942,6 +1958,20 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
|
|||
}
|
||||
EXPORT_SYMBOL(mmc_set_blocklen);
|
||||
|
||||
int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount,
|
||||
bool is_rel_write)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
cmd.opcode = MMC_SET_BLOCK_COUNT;
|
||||
cmd.arg = blockcount & 0x0000FFFF;
|
||||
if (is_rel_write)
|
||||
cmd.arg |= 1 << 31;
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||
return mmc_wait_for_cmd(card->host, &cmd, 5);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_set_blockcount);
|
||||
|
||||
static void mmc_hw_reset_for_init(struct mmc_host *host)
|
||||
{
|
||||
if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
|
||||
|
|
|
@ -144,6 +144,22 @@ static int mmc_ios_show(struct seq_file *s, void *data)
|
|||
}
|
||||
seq_printf(s, "timing spec:\t%u (%s)\n", ios->timing, str);
|
||||
|
||||
switch (ios->signal_voltage) {
|
||||
case MMC_SIGNAL_VOLTAGE_330:
|
||||
str = "3.30 V";
|
||||
break;
|
||||
case MMC_SIGNAL_VOLTAGE_180:
|
||||
str = "1.80 V";
|
||||
break;
|
||||
case MMC_SIGNAL_VOLTAGE_120:
|
||||
str = "1.20 V";
|
||||
break;
|
||||
default:
|
||||
str = "invalid";
|
||||
break;
|
||||
}
|
||||
seq_printf(s, "signal voltage:\t%u (%s)\n", ios->chip_select, str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -239,7 +239,7 @@ static void mmc_select_card_type(struct mmc_card *card)
|
|||
{
|
||||
struct mmc_host *host = card->host;
|
||||
u8 card_type = card->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_MASK;
|
||||
unsigned int caps = host->caps, caps2 = host->caps2;
|
||||
u32 caps = host->caps, caps2 = host->caps2;
|
||||
unsigned int hs_max_dtr = 0;
|
||||
|
||||
if (card_type & EXT_CSD_CARD_TYPE_26)
|
||||
|
@ -491,6 +491,17 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||
|
||||
card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
|
||||
card->ext_csd.rst_n_function = ext_csd[EXT_CSD_RST_N_FUNCTION];
|
||||
|
||||
/*
|
||||
* RPMB regions are defined in multiples of 128K.
|
||||
*/
|
||||
card->ext_csd.raw_rpmb_size_mult = ext_csd[EXT_CSD_RPMB_MULT];
|
||||
if (ext_csd[EXT_CSD_RPMB_MULT]) {
|
||||
mmc_part_add(card, ext_csd[EXT_CSD_RPMB_MULT] << 17,
|
||||
EXT_CSD_PART_CONFIG_ACC_RPMB,
|
||||
"rpmb", 0, false,
|
||||
MMC_BLK_DATA_AREA_RPMB);
|
||||
}
|
||||
}
|
||||
|
||||
card->ext_csd.raw_erased_mem_count = ext_csd[EXT_CSD_ERASED_MEM_CONT];
|
||||
|
@ -615,6 +626,8 @@ MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
|
|||
MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
|
||||
card->ext_csd.enhanced_area_offset);
|
||||
MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
|
||||
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
|
||||
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
|
||||
|
||||
static struct attribute *mmc_std_attrs[] = {
|
||||
&dev_attr_cid.attr,
|
||||
|
@ -630,6 +643,8 @@ static struct attribute *mmc_std_attrs[] = {
|
|||
&dev_attr_serial.attr,
|
||||
&dev_attr_enhanced_area_offset.attr,
|
||||
&dev_attr_enhanced_area_size.attr,
|
||||
&dev_attr_raw_rpmb_size_mult.attr,
|
||||
&dev_attr_rel_sectors.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -1051,6 +1066,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
|
||||
if (max_dtr > card->ext_csd.hs_max_dtr)
|
||||
max_dtr = card->ext_csd.hs_max_dtr;
|
||||
if (mmc_card_highspeed(card) && (max_dtr > 52000000))
|
||||
max_dtr = 52000000;
|
||||
} else if (max_dtr > card->csd.max_dtr) {
|
||||
max_dtr = card->csd.max_dtr;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "core.h"
|
||||
#include "mmc_ops.h"
|
||||
|
||||
#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
|
||||
|
||||
static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
|
@ -409,6 +411,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
|||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
unsigned long timeout;
|
||||
u32 status;
|
||||
|
||||
BUG_ON(!card);
|
||||
|
@ -437,6 +440,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
|||
return 0;
|
||||
|
||||
/* Must check status to be sure of no errors */
|
||||
timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS);
|
||||
do {
|
||||
err = mmc_send_status(card, &status);
|
||||
if (err)
|
||||
|
@ -445,6 +449,13 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
|||
break;
|
||||
if (mmc_host_is_spi(card->host))
|
||||
break;
|
||||
|
||||
/* Timeout if the device never leaves the program state. */
|
||||
if (time_after(jiffies, timeout)) {
|
||||
pr_err("%s: Card stuck in programming state! %s\n",
|
||||
mmc_hostname(card->host), __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
|
||||
|
||||
if (mmc_host_is_spi(card->host)) {
|
||||
|
|
|
@ -193,7 +193,21 @@ static int sdio_bus_remove(struct device *dev)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int pm_no_operation(struct device *dev)
|
||||
{
|
||||
/*
|
||||
* Prevent the PM core from calling SDIO device drivers' suspend
|
||||
* callback routines, which it is not supposed to do, by using this
|
||||
* empty function as the bus type suspend callaback for SDIO.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops sdio_bus_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_no_operation, pm_no_operation)
|
||||
SET_RUNTIME_PM_OPS(
|
||||
pm_generic_runtime_suspend,
|
||||
pm_generic_runtime_resume,
|
||||
|
@ -258,8 +272,7 @@ static void sdio_release_func(struct device *dev)
|
|||
|
||||
sdio_free_func_cis(func);
|
||||
|
||||
if (func->info)
|
||||
kfree(func->info);
|
||||
kfree(func->info);
|
||||
|
||||
kfree(func);
|
||||
}
|
||||
|
|
|
@ -188,8 +188,7 @@ EXPORT_SYMBOL_GPL(sdio_set_block_size);
|
|||
*/
|
||||
static inline unsigned int sdio_max_byte_size(struct sdio_func *func)
|
||||
{
|
||||
unsigned mval = min(func->card->host->max_seg_size,
|
||||
func->card->host->max_blk_size);
|
||||
unsigned mval = func->card->host->max_blk_size;
|
||||
|
||||
if (mmc_blksz_for_byte_mode(func->card))
|
||||
mval = min(mval, func->cur_blksize);
|
||||
|
@ -311,11 +310,8 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
|
|||
/* Do the bulk of the transfer using block mode (if supported). */
|
||||
if (func->card->cccr.multi_block && (size > sdio_max_byte_size(func))) {
|
||||
/* Blocks per command is limited by host count, host transfer
|
||||
* size (we only use a single sg entry) and the maximum for
|
||||
* IO_RW_EXTENDED of 511 blocks. */
|
||||
max_blocks = min(func->card->host->max_blk_count,
|
||||
func->card->host->max_seg_size / func->cur_blksize);
|
||||
max_blocks = min(max_blocks, 511u);
|
||||
* size and the maximum for IO_RW_EXTENDED of 511 blocks. */
|
||||
max_blocks = min(func->card->host->max_blk_count, 511u);
|
||||
|
||||
while (remainder >= func->cur_blksize) {
|
||||
unsigned blocks;
|
||||
|
|
|
@ -124,7 +124,10 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
|
|||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct scatterlist sg;
|
||||
struct scatterlist sg, *sg_ptr;
|
||||
struct sg_table sgtable;
|
||||
unsigned int nents, left_size, i;
|
||||
unsigned int seg_size = card->host->max_seg_size;
|
||||
|
||||
BUG_ON(!card);
|
||||
BUG_ON(fn > 7);
|
||||
|
@ -152,15 +155,36 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
|
|||
/* Code in host drivers/fwk assumes that "blocks" always is >=1 */
|
||||
data.blocks = blocks ? blocks : 1;
|
||||
data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
|
||||
data.sg = &sg;
|
||||
data.sg_len = 1;
|
||||
|
||||
sg_init_one(&sg, buf, data.blksz * data.blocks);
|
||||
left_size = data.blksz * data.blocks;
|
||||
nents = (left_size - 1) / seg_size + 1;
|
||||
if (nents > 1) {
|
||||
if (sg_alloc_table(&sgtable, nents, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
data.sg = sgtable.sgl;
|
||||
data.sg_len = nents;
|
||||
|
||||
for_each_sg(data.sg, sg_ptr, data.sg_len, i) {
|
||||
sg_set_page(sg_ptr, virt_to_page(buf + (i * seg_size)),
|
||||
min(seg_size, left_size),
|
||||
offset_in_page(buf + (i * seg_size)));
|
||||
left_size = left_size - seg_size;
|
||||
}
|
||||
} else {
|
||||
data.sg = &sg;
|
||||
data.sg_len = 1;
|
||||
|
||||
sg_init_one(&sg, buf, left_size);
|
||||
}
|
||||
|
||||
mmc_set_data_timeout(&data, card);
|
||||
|
||||
mmc_wait_for_req(card->host, &mrq);
|
||||
|
||||
if (nents > 1)
|
||||
sg_free_table(&sgtable);
|
||||
|
||||
if (cmd.error)
|
||||
return cmd.error;
|
||||
if (data.error)
|
||||
|
|
|
@ -27,7 +27,13 @@ struct mmc_gpio {
|
|||
static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
|
||||
{
|
||||
/* Schedule a card detection after a debounce timeout */
|
||||
mmc_detect_change(dev_id, msecs_to_jiffies(100));
|
||||
struct mmc_host *host = dev_id;
|
||||
|
||||
if (host->ops->card_event)
|
||||
host->ops->card_event(host);
|
||||
|
||||
mmc_detect_change(host, msecs_to_jiffies(200));
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
|
@ -270,26 +270,8 @@ config MMC_AU1X
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
choice
|
||||
prompt "Atmel SD/MMC Driver"
|
||||
depends on AVR32 || ARCH_AT91
|
||||
default MMC_ATMELMCI if AVR32
|
||||
help
|
||||
Choose which driver to use for the Atmel MCI Silicon
|
||||
|
||||
config MMC_AT91
|
||||
tristate "AT91 SD/MMC Card Interface support (DEPRECATED)"
|
||||
depends on ARCH_AT91
|
||||
help
|
||||
This selects the AT91 MCI controller. This driver will
|
||||
be removed soon (for more information have a look to
|
||||
Documentation/feature-removal-schedule.txt). Please use
|
||||
MMC_ATMEL_MCI.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_ATMELMCI
|
||||
tristate "Atmel Multimedia Card Interface support"
|
||||
tristate "Atmel SD/MMC Driver (Multimedia Card Interface)"
|
||||
depends on AVR32 || ARCH_AT91
|
||||
help
|
||||
This selects the Atmel Multimedia Card Interface driver. If
|
||||
|
@ -298,8 +280,6 @@ config MMC_ATMELMCI
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
endchoice
|
||||
|
||||
config MMC_ATMELMCI_DMA
|
||||
bool "Atmel MCI DMA support"
|
||||
depends on MMC_ATMELMCI && (AVR32 || ARCH_AT91SAM9G45) && DMA_ENGINE
|
||||
|
@ -621,3 +601,14 @@ config MMC_USHC
|
|||
|
||||
Note: These controllers only support SDIO cards and do not
|
||||
support MMC or SD memory cards.
|
||||
|
||||
config MMC_WMT
|
||||
tristate "Wondermedia SD/MMC Host Controller support"
|
||||
depends on ARCH_VT8500
|
||||
default y
|
||||
help
|
||||
This selects support for the SD/MMC Host Controller on
|
||||
Wondermedia WM8505/WM8650 based SoCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called wmt-sdmmc.
|
||||
|
|
|
@ -17,7 +17,6 @@ obj-$(CONFIG_MMC_WBSD) += wbsd.o
|
|||
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
|
||||
obj-$(CONFIG_MMC_OMAP) += omap.o
|
||||
obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o
|
||||
obj-$(CONFIG_MMC_AT91) += at91_mci.o
|
||||
obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o
|
||||
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
|
||||
obj-$(CONFIG_MMC_MSM) += msm_sdcc.o
|
||||
|
@ -45,6 +44,7 @@ obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
|
|||
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
|
||||
obj-$(CONFIG_MMC_VUB300) += vub300.o
|
||||
obj-$(CONFIG_MMC_USHC) += ushc.o
|
||||
obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
|
||||
|
||||
obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
|
||||
obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,115 +0,0 @@
|
|||
/*
|
||||
* drivers/mmc/host/at91_mci.h
|
||||
*
|
||||
* Copyright (C) 2005 Ivan Kokshaysky
|
||||
* Copyright (C) SAN People
|
||||
*
|
||||
* MultiMedia Card Interface (MCI) registers.
|
||||
* Based on AT91RM9200 datasheet revision F.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef AT91_MCI_H
|
||||
#define AT91_MCI_H
|
||||
|
||||
#define AT91_MCI_CR 0x00 /* Control Register */
|
||||
#define AT91_MCI_MCIEN (1 << 0) /* Multi-Media Interface Enable */
|
||||
#define AT91_MCI_MCIDIS (1 << 1) /* Multi-Media Interface Disable */
|
||||
#define AT91_MCI_PWSEN (1 << 2) /* Power Save Mode Enable */
|
||||
#define AT91_MCI_PWSDIS (1 << 3) /* Power Save Mode Disable */
|
||||
#define AT91_MCI_SWRST (1 << 7) /* Software Reset */
|
||||
|
||||
#define AT91_MCI_MR 0x04 /* Mode Register */
|
||||
#define AT91_MCI_CLKDIV (0xff << 0) /* Clock Divider */
|
||||
#define AT91_MCI_PWSDIV (7 << 8) /* Power Saving Divider */
|
||||
#define AT91_MCI_RDPROOF (1 << 11) /* Read Proof Enable [SAM926[03] only] */
|
||||
#define AT91_MCI_WRPROOF (1 << 12) /* Write Proof Enable [SAM926[03] only] */
|
||||
#define AT91_MCI_PDCFBYTE (1 << 13) /* PDC Force Byte Transfer [SAM926[03] only] */
|
||||
#define AT91_MCI_PDCPADV (1 << 14) /* PDC Padding Value */
|
||||
#define AT91_MCI_PDCMODE (1 << 15) /* PDC-orientated Mode */
|
||||
#define AT91_MCI_BLKLEN (0xfff << 18) /* Data Block Length */
|
||||
|
||||
#define AT91_MCI_DTOR 0x08 /* Data Timeout Register */
|
||||
#define AT91_MCI_DTOCYC (0xf << 0) /* Data Timeout Cycle Number */
|
||||
#define AT91_MCI_DTOMUL (7 << 4) /* Data Timeout Multiplier */
|
||||
#define AT91_MCI_DTOMUL_1 (0 << 4)
|
||||
#define AT91_MCI_DTOMUL_16 (1 << 4)
|
||||
#define AT91_MCI_DTOMUL_128 (2 << 4)
|
||||
#define AT91_MCI_DTOMUL_256 (3 << 4)
|
||||
#define AT91_MCI_DTOMUL_1K (4 << 4)
|
||||
#define AT91_MCI_DTOMUL_4K (5 << 4)
|
||||
#define AT91_MCI_DTOMUL_64K (6 << 4)
|
||||
#define AT91_MCI_DTOMUL_1M (7 << 4)
|
||||
|
||||
#define AT91_MCI_SDCR 0x0c /* SD Card Register */
|
||||
#define AT91_MCI_SDCSEL (3 << 0) /* SD Card Selector */
|
||||
#define AT91_MCI_SDCBUS (1 << 7) /* 1-bit or 4-bit bus */
|
||||
|
||||
#define AT91_MCI_ARGR 0x10 /* Argument Register */
|
||||
|
||||
#define AT91_MCI_CMDR 0x14 /* Command Register */
|
||||
#define AT91_MCI_CMDNB (0x3f << 0) /* Command Number */
|
||||
#define AT91_MCI_RSPTYP (3 << 6) /* Response Type */
|
||||
#define AT91_MCI_RSPTYP_NONE (0 << 6)
|
||||
#define AT91_MCI_RSPTYP_48 (1 << 6)
|
||||
#define AT91_MCI_RSPTYP_136 (2 << 6)
|
||||
#define AT91_MCI_SPCMD (7 << 8) /* Special Command */
|
||||
#define AT91_MCI_SPCMD_NONE (0 << 8)
|
||||
#define AT91_MCI_SPCMD_INIT (1 << 8)
|
||||
#define AT91_MCI_SPCMD_SYNC (2 << 8)
|
||||
#define AT91_MCI_SPCMD_ICMD (4 << 8)
|
||||
#define AT91_MCI_SPCMD_IRESP (5 << 8)
|
||||
#define AT91_MCI_OPDCMD (1 << 11) /* Open Drain Command */
|
||||
#define AT91_MCI_MAXLAT (1 << 12) /* Max Latency for Command to Response */
|
||||
#define AT91_MCI_TRCMD (3 << 16) /* Transfer Command */
|
||||
#define AT91_MCI_TRCMD_NONE (0 << 16)
|
||||
#define AT91_MCI_TRCMD_START (1 << 16)
|
||||
#define AT91_MCI_TRCMD_STOP (2 << 16)
|
||||
#define AT91_MCI_TRDIR (1 << 18) /* Transfer Direction */
|
||||
#define AT91_MCI_TRTYP (3 << 19) /* Transfer Type */
|
||||
#define AT91_MCI_TRTYP_BLOCK (0 << 19)
|
||||
#define AT91_MCI_TRTYP_MULTIPLE (1 << 19)
|
||||
#define AT91_MCI_TRTYP_STREAM (2 << 19)
|
||||
#define AT91_MCI_TRTYP_SDIO_BYTE (4 << 19)
|
||||
#define AT91_MCI_TRTYP_SDIO_BLOCK (5 << 19)
|
||||
|
||||
#define AT91_MCI_BLKR 0x18 /* Block Register */
|
||||
#define AT91_MCI_BLKR_BCNT(n) ((0xffff & (n)) << 0) /* Block count */
|
||||
#define AT91_MCI_BLKR_BLKLEN(n) ((0xffff & (n)) << 16) /* Block length */
|
||||
|
||||
#define AT91_MCI_RSPR(n) (0x20 + ((n) * 4)) /* Response Registers 0-3 */
|
||||
#define AT91_MCR_RDR 0x30 /* Receive Data Register */
|
||||
#define AT91_MCR_TDR 0x34 /* Transmit Data Register */
|
||||
|
||||
#define AT91_MCI_SR 0x40 /* Status Register */
|
||||
#define AT91_MCI_CMDRDY (1 << 0) /* Command Ready */
|
||||
#define AT91_MCI_RXRDY (1 << 1) /* Receiver Ready */
|
||||
#define AT91_MCI_TXRDY (1 << 2) /* Transmit Ready */
|
||||
#define AT91_MCI_BLKE (1 << 3) /* Data Block Ended */
|
||||
#define AT91_MCI_DTIP (1 << 4) /* Data Transfer in Progress */
|
||||
#define AT91_MCI_NOTBUSY (1 << 5) /* Data Not Busy */
|
||||
#define AT91_MCI_ENDRX (1 << 6) /* End of RX Buffer */
|
||||
#define AT91_MCI_ENDTX (1 << 7) /* End fo TX Buffer */
|
||||
#define AT91_MCI_SDIOIRQA (1 << 8) /* SDIO Interrupt for Slot A */
|
||||
#define AT91_MCI_SDIOIRQB (1 << 9) /* SDIO Interrupt for Slot B */
|
||||
#define AT91_MCI_RXBUFF (1 << 14) /* RX Buffer Full */
|
||||
#define AT91_MCI_TXBUFE (1 << 15) /* TX Buffer Empty */
|
||||
#define AT91_MCI_RINDE (1 << 16) /* Response Index Error */
|
||||
#define AT91_MCI_RDIRE (1 << 17) /* Response Direction Error */
|
||||
#define AT91_MCI_RCRCE (1 << 18) /* Response CRC Error */
|
||||
#define AT91_MCI_RENDE (1 << 19) /* Response End Bit Error */
|
||||
#define AT91_MCI_RTOE (1 << 20) /* Response Time-out Error */
|
||||
#define AT91_MCI_DCRCE (1 << 21) /* Data CRC Error */
|
||||
#define AT91_MCI_DTOE (1 << 22) /* Data Time-out Error */
|
||||
#define AT91_MCI_OVRE (1 << 30) /* Overrun */
|
||||
#define AT91_MCI_UNRE (1 << 31) /* Underrun */
|
||||
|
||||
#define AT91_MCI_IER 0x44 /* Interrupt Enable Register */
|
||||
#define AT91_MCI_IDR 0x48 /* Interrupt Disable Register */
|
||||
#define AT91_MCI_IMR 0x4c /* Interrupt Mask Register */
|
||||
|
||||
#endif
|
|
@ -134,7 +134,7 @@ static struct pci_driver dw_mci_pci_driver = {
|
|||
.name = "dw_mmc_pci",
|
||||
.id_table = dw_mci_pci_id,
|
||||
.probe = dw_mci_pci_probe,
|
||||
.remove = dw_mci_pci_remove,
|
||||
.remove = __devexit_p(dw_mci_pci_remove),
|
||||
.driver = {
|
||||
.pm = &dw_mci_pci_pmops
|
||||
},
|
||||
|
|
|
@ -119,7 +119,8 @@ static const struct of_device_id dw_mci_pltfm_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
|
||||
|
||||
static struct platform_driver dw_mci_pltfm_driver = {
|
||||
.remove = __exit_p(dw_mci_pltfm_remove),
|
||||
.probe = dw_mci_pltfm_probe,
|
||||
.remove = __devexit_p(dw_mci_pltfm_remove),
|
||||
.driver = {
|
||||
.name = "dw_mmc",
|
||||
.of_match_table = of_match_ptr(dw_mci_pltfm_match),
|
||||
|
@ -127,18 +128,7 @@ static struct platform_driver dw_mci_pltfm_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static int __init dw_mci_init(void)
|
||||
{
|
||||
return platform_driver_probe(&dw_mci_pltfm_driver, dw_mci_pltfm_probe);
|
||||
}
|
||||
|
||||
static void __exit dw_mci_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&dw_mci_pltfm_driver);
|
||||
}
|
||||
|
||||
module_init(dw_mci_init);
|
||||
module_exit(dw_mci_exit);
|
||||
module_platform_driver(dw_mci_pltfm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
|
||||
MODULE_AUTHOR("NXP Semiconductor VietNam");
|
||||
|
|
|
@ -232,7 +232,7 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
|
|||
{
|
||||
struct mmc_data *data;
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct dw_mci_drv_data *drv_data = slot->host->drv_data;
|
||||
const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
|
||||
u32 cmdr;
|
||||
cmd->error = -EINPROGRESS;
|
||||
|
||||
|
@ -617,13 +617,13 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
|
|||
cmd, arg, cmd_status);
|
||||
}
|
||||
|
||||
static void dw_mci_setup_bus(struct dw_mci_slot *slot)
|
||||
static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
|
||||
{
|
||||
struct dw_mci *host = slot->host;
|
||||
u32 div;
|
||||
u32 clk_en_a;
|
||||
|
||||
if (slot->clock != host->current_speed) {
|
||||
if (slot->clock != host->current_speed || force_clkinit) {
|
||||
div = host->bus_hz / slot->clock;
|
||||
if (host->bus_hz % slot->clock && host->bus_hz > slot->clock)
|
||||
/*
|
||||
|
@ -683,9 +683,6 @@ static void __dw_mci_start_request(struct dw_mci *host,
|
|||
if (host->pdata->select_slot)
|
||||
host->pdata->select_slot(slot->id);
|
||||
|
||||
/* Slot specific timing and width adjustment */
|
||||
dw_mci_setup_bus(slot);
|
||||
|
||||
host->cur_slot = slot;
|
||||
host->mrq = mrq;
|
||||
|
||||
|
@ -773,22 +770,19 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct dw_mci_drv_data *drv_data = slot->host->drv_data;
|
||||
const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
|
||||
u32 regs;
|
||||
|
||||
/* set default 1 bit mode */
|
||||
slot->ctype = SDMMC_CTYPE_1BIT;
|
||||
|
||||
switch (ios->bus_width) {
|
||||
case MMC_BUS_WIDTH_1:
|
||||
slot->ctype = SDMMC_CTYPE_1BIT;
|
||||
break;
|
||||
case MMC_BUS_WIDTH_4:
|
||||
slot->ctype = SDMMC_CTYPE_4BIT;
|
||||
break;
|
||||
case MMC_BUS_WIDTH_8:
|
||||
slot->ctype = SDMMC_CTYPE_8BIT;
|
||||
break;
|
||||
default:
|
||||
/* set default 1 bit mode */
|
||||
slot->ctype = SDMMC_CTYPE_1BIT;
|
||||
}
|
||||
|
||||
regs = mci_readl(slot->host, UHS_REG);
|
||||
|
@ -812,6 +806,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
if (drv_data && drv_data->set_ios)
|
||||
drv_data->set_ios(slot->host, ios);
|
||||
|
||||
/* Slot specific timing and width adjustment */
|
||||
dw_mci_setup_bus(slot, false);
|
||||
|
||||
switch (ios->power_mode) {
|
||||
case MMC_POWER_UP:
|
||||
set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
|
||||
|
@ -1817,7 +1814,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||
{
|
||||
struct mmc_host *mmc;
|
||||
struct dw_mci_slot *slot;
|
||||
struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
int ctrl_id, ret;
|
||||
u8 bus_width;
|
||||
|
||||
|
@ -1850,6 +1847,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||
if (host->pdata->caps)
|
||||
mmc->caps = host->pdata->caps;
|
||||
|
||||
if (host->pdata->pm_caps)
|
||||
mmc->pm_caps = host->pdata->pm_caps;
|
||||
|
||||
if (host->dev->of_node) {
|
||||
ctrl_id = of_alias_get_id(host->dev->of_node, "mshc");
|
||||
if (ctrl_id < 0)
|
||||
|
@ -1911,7 +1911,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||
#endif /* CONFIG_MMC_DW_IDMAC */
|
||||
}
|
||||
|
||||
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
|
||||
host->vmmc = devm_regulator_get(mmc_dev(mmc), "vmmc");
|
||||
if (IS_ERR(host->vmmc)) {
|
||||
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
|
||||
host->vmmc = NULL;
|
||||
|
@ -1960,7 +1960,7 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
|
|||
static void dw_mci_init_dma(struct dw_mci *host)
|
||||
{
|
||||
/* Alloc memory for sg translation */
|
||||
host->sg_cpu = dma_alloc_coherent(host->dev, PAGE_SIZE,
|
||||
host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE,
|
||||
&host->sg_dma, GFP_KERNEL);
|
||||
if (!host->sg_cpu) {
|
||||
dev_err(host->dev, "%s: could not alloc DMA memory\n",
|
||||
|
@ -2038,7 +2038,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
|||
struct dw_mci_board *pdata;
|
||||
struct device *dev = host->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
int idx, ret;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
|
@ -2072,6 +2072,12 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (of_find_property(np, "keep-power-in-suspend", NULL))
|
||||
pdata->pm_caps |= MMC_PM_KEEP_POWER;
|
||||
|
||||
if (of_find_property(np, "enable-sdio-wakeup", NULL))
|
||||
pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
|
@ -2084,7 +2090,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
|||
|
||||
int dw_mci_probe(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
int width, i, ret = 0;
|
||||
u32 fifo_size;
|
||||
int init_slots = 0;
|
||||
|
@ -2103,26 +2109,24 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
host->biu_clk = clk_get(host->dev, "biu");
|
||||
host->biu_clk = devm_clk_get(host->dev, "biu");
|
||||
if (IS_ERR(host->biu_clk)) {
|
||||
dev_dbg(host->dev, "biu clock not available\n");
|
||||
} else {
|
||||
ret = clk_prepare_enable(host->biu_clk);
|
||||
if (ret) {
|
||||
dev_err(host->dev, "failed to enable biu clock\n");
|
||||
clk_put(host->biu_clk);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
host->ciu_clk = clk_get(host->dev, "ciu");
|
||||
host->ciu_clk = devm_clk_get(host->dev, "ciu");
|
||||
if (IS_ERR(host->ciu_clk)) {
|
||||
dev_dbg(host->dev, "ciu clock not available\n");
|
||||
} else {
|
||||
ret = clk_prepare_enable(host->ciu_clk);
|
||||
if (ret) {
|
||||
dev_err(host->dev, "failed to enable ciu clock\n");
|
||||
clk_put(host->ciu_clk);
|
||||
goto err_clk_biu;
|
||||
}
|
||||
}
|
||||
|
@ -2224,7 +2228,8 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
if (!host->card_workqueue)
|
||||
goto err_dmaunmap;
|
||||
INIT_WORK(&host->card_work, dw_mci_work_routine_card);
|
||||
ret = request_irq(host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host);
|
||||
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
|
||||
host->irq_flags, "dw-mci", host);
|
||||
if (ret)
|
||||
goto err_workqueue;
|
||||
|
||||
|
@ -2262,7 +2267,7 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
} else {
|
||||
dev_dbg(host->dev, "attempted to initialize %d slots, "
|
||||
"but failed on all\n", host->num_slots);
|
||||
goto err_init_slot;
|
||||
goto err_workqueue;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2282,33 +2287,24 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
|
||||
return 0;
|
||||
|
||||
err_init_slot:
|
||||
free_irq(host->irq, host);
|
||||
|
||||
err_workqueue:
|
||||
destroy_workqueue(host->card_workqueue);
|
||||
|
||||
err_dmaunmap:
|
||||
if (host->use_dma && host->dma_ops->exit)
|
||||
host->dma_ops->exit(host);
|
||||
dma_free_coherent(host->dev, PAGE_SIZE,
|
||||
host->sg_cpu, host->sg_dma);
|
||||
|
||||
if (host->vmmc) {
|
||||
if (host->vmmc)
|
||||
regulator_disable(host->vmmc);
|
||||
regulator_put(host->vmmc);
|
||||
}
|
||||
|
||||
err_clk_ciu:
|
||||
if (!IS_ERR(host->ciu_clk)) {
|
||||
if (!IS_ERR(host->ciu_clk))
|
||||
clk_disable_unprepare(host->ciu_clk);
|
||||
clk_put(host->ciu_clk);
|
||||
}
|
||||
|
||||
err_clk_biu:
|
||||
if (!IS_ERR(host->biu_clk)) {
|
||||
if (!IS_ERR(host->biu_clk))
|
||||
clk_disable_unprepare(host->biu_clk);
|
||||
clk_put(host->biu_clk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(dw_mci_probe);
|
||||
|
@ -2330,24 +2326,19 @@ void dw_mci_remove(struct dw_mci *host)
|
|||
mci_writel(host, CLKENA, 0);
|
||||
mci_writel(host, CLKSRC, 0);
|
||||
|
||||
free_irq(host->irq, host);
|
||||
destroy_workqueue(host->card_workqueue);
|
||||
dma_free_coherent(host->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
|
||||
|
||||
if (host->use_dma && host->dma_ops->exit)
|
||||
host->dma_ops->exit(host);
|
||||
|
||||
if (host->vmmc) {
|
||||
if (host->vmmc)
|
||||
regulator_disable(host->vmmc);
|
||||
regulator_put(host->vmmc);
|
||||
}
|
||||
|
||||
if (!IS_ERR(host->ciu_clk))
|
||||
clk_disable_unprepare(host->ciu_clk);
|
||||
|
||||
if (!IS_ERR(host->biu_clk))
|
||||
clk_disable_unprepare(host->biu_clk);
|
||||
clk_put(host->ciu_clk);
|
||||
clk_put(host->biu_clk);
|
||||
}
|
||||
EXPORT_SYMBOL(dw_mci_remove);
|
||||
|
||||
|
@ -2411,6 +2402,11 @@ int dw_mci_resume(struct dw_mci *host)
|
|||
struct dw_mci_slot *slot = host->slot[i];
|
||||
if (!slot)
|
||||
continue;
|
||||
if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) {
|
||||
dw_mci_set_ios(slot->mmc, &slot->mmc->ios);
|
||||
dw_mci_setup_bus(slot, true);
|
||||
}
|
||||
|
||||
ret = mmc_resume_host(host->slot[i]->mmc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
@ -240,7 +240,7 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
|||
return 0;
|
||||
|
||||
for_each_sg(data->sg, sg, data->sg_len, i) {
|
||||
if (sg->offset & 3 || sg->length & 3) {
|
||||
if (sg->offset & 3 || sg->length & 3 || sg->length < 512) {
|
||||
host->do_dma = 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/stmp_device.h>
|
||||
#include <linux/mmc/mxs-mmc.h>
|
||||
#include <linux/spi/mxs-spi.h>
|
||||
|
||||
#define DRIVER_NAME "mxs-mmc"
|
||||
|
@ -593,13 +592,13 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
struct mxs_mmc_host *host;
|
||||
struct mmc_host *mmc;
|
||||
struct resource *iores, *dmares;
|
||||
struct mxs_mmc_platform_data *pdata;
|
||||
struct pinctrl *pinctrl;
|
||||
int ret = 0, irq_err, irq_dma;
|
||||
dma_cap_mask_t mask;
|
||||
struct regulator *reg_vmmc;
|
||||
enum of_gpio_flags flags;
|
||||
struct mxs_ssp *ssp;
|
||||
u32 bus_width = 0;
|
||||
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
|
@ -682,25 +681,15 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
|
||||
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL;
|
||||
|
||||
pdata = mmc_dev(host->mmc)->platform_data;
|
||||
if (!pdata) {
|
||||
u32 bus_width = 0;
|
||||
of_property_read_u32(np, "bus-width", &bus_width);
|
||||
if (bus_width == 4)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
else if (bus_width == 8)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
|
||||
host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0,
|
||||
&flags);
|
||||
if (flags & OF_GPIO_ACTIVE_LOW)
|
||||
host->wp_inverted = 1;
|
||||
} else {
|
||||
if (pdata->flags & SLOTF_8_BIT_CAPABLE)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
|
||||
if (pdata->flags & SLOTF_4_BIT_CAPABLE)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
host->wp_gpio = pdata->wp_gpio;
|
||||
}
|
||||
of_property_read_u32(np, "bus-width", &bus_width);
|
||||
if (bus_width == 4)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
else if (bus_width == 8)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
|
||||
host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags);
|
||||
|
||||
if (flags & OF_GPIO_ACTIVE_LOW)
|
||||
host->wp_inverted = 1;
|
||||
|
||||
mmc->f_min = 400000;
|
||||
mmc->f_max = 288000000;
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <plat/mmc.h>
|
||||
|
@ -62,6 +63,7 @@
|
|||
|
||||
#define VS18 (1 << 26)
|
||||
#define VS30 (1 << 25)
|
||||
#define HSS (1 << 21)
|
||||
#define SDVS18 (0x5 << 9)
|
||||
#define SDVS30 (0x6 << 9)
|
||||
#define SDVS33 (0x7 << 9)
|
||||
|
@ -78,28 +80,17 @@
|
|||
#define CLKD_SHIFT 6
|
||||
#define DTO_MASK 0x000F0000
|
||||
#define DTO_SHIFT 16
|
||||
#define INT_EN_MASK 0x307F0033
|
||||
#define BWR_ENABLE (1 << 4)
|
||||
#define BRR_ENABLE (1 << 5)
|
||||
#define DTO_ENABLE (1 << 20)
|
||||
#define INIT_STREAM (1 << 1)
|
||||
#define DP_SELECT (1 << 21)
|
||||
#define DDIR (1 << 4)
|
||||
#define DMA_EN 0x1
|
||||
#define DMAE 0x1
|
||||
#define MSBS (1 << 5)
|
||||
#define BCE (1 << 1)
|
||||
#define FOUR_BIT (1 << 1)
|
||||
#define HSPE (1 << 2)
|
||||
#define DDR (1 << 19)
|
||||
#define DW8 (1 << 5)
|
||||
#define CC 0x1
|
||||
#define TC 0x02
|
||||
#define OD 0x1
|
||||
#define ERR (1 << 15)
|
||||
#define CMD_TIMEOUT (1 << 16)
|
||||
#define DATA_TIMEOUT (1 << 20)
|
||||
#define CMD_CRC (1 << 17)
|
||||
#define DATA_CRC (1 << 21)
|
||||
#define CARD_ERR (1 << 28)
|
||||
#define STAT_CLEAR 0xFFFFFFFF
|
||||
#define INIT_STREAM_CMD 0x00000000
|
||||
#define DUAL_VOLT_OCR_BIT 7
|
||||
|
@ -108,6 +99,26 @@
|
|||
#define SOFTRESET (1 << 1)
|
||||
#define RESETDONE (1 << 0)
|
||||
|
||||
/* Interrupt masks for IE and ISE register */
|
||||
#define CC_EN (1 << 0)
|
||||
#define TC_EN (1 << 1)
|
||||
#define BWR_EN (1 << 4)
|
||||
#define BRR_EN (1 << 5)
|
||||
#define ERR_EN (1 << 15)
|
||||
#define CTO_EN (1 << 16)
|
||||
#define CCRC_EN (1 << 17)
|
||||
#define CEB_EN (1 << 18)
|
||||
#define CIE_EN (1 << 19)
|
||||
#define DTO_EN (1 << 20)
|
||||
#define DCRC_EN (1 << 21)
|
||||
#define DEB_EN (1 << 22)
|
||||
#define CERR_EN (1 << 28)
|
||||
#define BADA_EN (1 << 29)
|
||||
|
||||
#define INT_EN_MASK (BADA_EN | CERR_EN | DEB_EN | DCRC_EN |\
|
||||
DTO_EN | CIE_EN | CEB_EN | CCRC_EN | CTO_EN | \
|
||||
BRR_EN | BWR_EN | TC_EN | CC_EN)
|
||||
|
||||
#define MMC_AUTOSUSPEND_DELAY 100
|
||||
#define MMC_TIMEOUT_MS 20
|
||||
#define OMAP_MMC_MIN_CLOCK 400000
|
||||
|
@ -302,7 +313,7 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
|
|||
|
||||
reg = regulator_get(host->dev, "vmmc");
|
||||
if (IS_ERR(reg)) {
|
||||
dev_dbg(host->dev, "vmmc regulator missing\n");
|
||||
dev_err(host->dev, "vmmc regulator missing\n");
|
||||
return PTR_ERR(reg);
|
||||
} else {
|
||||
mmc_slot(host).set_power = omap_hsmmc_set_power;
|
||||
|
@ -455,13 +466,13 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
|
|||
unsigned int irq_mask;
|
||||
|
||||
if (host->use_dma)
|
||||
irq_mask = INT_EN_MASK & ~(BRR_ENABLE | BWR_ENABLE);
|
||||
irq_mask = INT_EN_MASK & ~(BRR_EN | BWR_EN);
|
||||
else
|
||||
irq_mask = INT_EN_MASK;
|
||||
|
||||
/* Disable timeout for erases */
|
||||
if (cmd->opcode == MMC_ERASE)
|
||||
irq_mask &= ~DTO_ENABLE;
|
||||
irq_mask &= ~DTO_EN;
|
||||
|
||||
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
|
||||
OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
|
||||
|
@ -494,6 +505,7 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
|
|||
struct mmc_ios *ios = &host->mmc->ios;
|
||||
unsigned long regval;
|
||||
unsigned long timeout;
|
||||
unsigned long clkdiv;
|
||||
|
||||
dev_vdbg(mmc_dev(host->mmc), "Set clock to %uHz\n", ios->clock);
|
||||
|
||||
|
@ -501,7 +513,8 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
|
|||
|
||||
regval = OMAP_HSMMC_READ(host->base, SYSCTL);
|
||||
regval = regval & ~(CLKD_MASK | DTO_MASK);
|
||||
regval = regval | (calc_divisor(host, ios) << 6) | (DTO << 16);
|
||||
clkdiv = calc_divisor(host, ios);
|
||||
regval = regval | (clkdiv << 6) | (DTO << 16);
|
||||
OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
|
||||
OMAP_HSMMC_WRITE(host->base, SYSCTL,
|
||||
OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
|
||||
|
@ -512,6 +525,27 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
|
|||
&& time_before(jiffies, timeout))
|
||||
cpu_relax();
|
||||
|
||||
/*
|
||||
* Enable High-Speed Support
|
||||
* Pre-Requisites
|
||||
* - Controller should support High-Speed-Enable Bit
|
||||
* - Controller should not be using DDR Mode
|
||||
* - Controller should advertise that it supports High Speed
|
||||
* in capabilities register
|
||||
* - MMC/SD clock coming out of controller > 25MHz
|
||||
*/
|
||||
if ((mmc_slot(host).features & HSMMC_HAS_HSPE_SUPPORT) &&
|
||||
(ios->timing != MMC_TIMING_UHS_DDR50) &&
|
||||
((OMAP_HSMMC_READ(host->base, CAPA) & HSS) == HSS)) {
|
||||
regval = OMAP_HSMMC_READ(host->base, HCTL);
|
||||
if (clkdiv && (clk_get_rate(host->fclk)/clkdiv) > 25000000)
|
||||
regval |= HSPE;
|
||||
else
|
||||
regval &= ~HSPE;
|
||||
|
||||
OMAP_HSMMC_WRITE(host->base, HCTL, regval);
|
||||
}
|
||||
|
||||
omap_hsmmc_start_clock(host);
|
||||
}
|
||||
|
||||
|
@ -676,8 +710,8 @@ static void send_init_stream(struct omap_hsmmc_host *host)
|
|||
OMAP_HSMMC_WRITE(host->base, CMD, INIT_STREAM_CMD);
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
|
||||
while ((reg != CC) && time_before(jiffies, timeout))
|
||||
reg = OMAP_HSMMC_READ(host->base, STAT) & CC;
|
||||
while ((reg != CC_EN) && time_before(jiffies, timeout))
|
||||
reg = OMAP_HSMMC_READ(host->base, STAT) & CC_EN;
|
||||
|
||||
OMAP_HSMMC_WRITE(host->base, CON,
|
||||
OMAP_HSMMC_READ(host->base, CON) & ~INIT_STREAM);
|
||||
|
@ -768,7 +802,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
|
|||
}
|
||||
|
||||
if (host->use_dma)
|
||||
cmdreg |= DMA_EN;
|
||||
cmdreg |= DMAE;
|
||||
|
||||
host->req_in_progress = 1;
|
||||
|
||||
|
@ -968,16 +1002,20 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
|
|||
__func__);
|
||||
}
|
||||
|
||||
static void hsmmc_command_incomplete(struct omap_hsmmc_host *host, int err)
|
||||
static void hsmmc_command_incomplete(struct omap_hsmmc_host *host,
|
||||
int err, int end_cmd)
|
||||
{
|
||||
omap_hsmmc_reset_controller_fsm(host, SRC);
|
||||
host->cmd->error = err;
|
||||
if (end_cmd) {
|
||||
omap_hsmmc_reset_controller_fsm(host, SRC);
|
||||
if (host->cmd)
|
||||
host->cmd->error = err;
|
||||
}
|
||||
|
||||
if (host->data) {
|
||||
omap_hsmmc_reset_controller_fsm(host, SRD);
|
||||
omap_hsmmc_dma_cleanup(host, err);
|
||||
}
|
||||
|
||||
} else if (host->mrq && host->mrq->cmd)
|
||||
host->mrq->cmd->error = err;
|
||||
}
|
||||
|
||||
static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
|
||||
|
@ -988,23 +1026,25 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
|
|||
data = host->data;
|
||||
dev_vdbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
|
||||
|
||||
if (status & ERR) {
|
||||
if (status & ERR_EN) {
|
||||
omap_hsmmc_dbg_report_irq(host, status);
|
||||
if (status & (CMD_TIMEOUT | DATA_TIMEOUT))
|
||||
hsmmc_command_incomplete(host, -ETIMEDOUT);
|
||||
else if (status & (CMD_CRC | DATA_CRC))
|
||||
hsmmc_command_incomplete(host, -EILSEQ);
|
||||
|
||||
end_cmd = 1;
|
||||
if (status & (CTO_EN | CCRC_EN))
|
||||
end_cmd = 1;
|
||||
if (status & (CTO_EN | DTO_EN))
|
||||
hsmmc_command_incomplete(host, -ETIMEDOUT, end_cmd);
|
||||
else if (status & (CCRC_EN | DCRC_EN))
|
||||
hsmmc_command_incomplete(host, -EILSEQ, end_cmd);
|
||||
|
||||
if (host->data || host->response_busy) {
|
||||
end_trans = 1;
|
||||
end_trans = !end_cmd;
|
||||
host->response_busy = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (end_cmd || ((status & CC) && host->cmd))
|
||||
if (end_cmd || ((status & CC_EN) && host->cmd))
|
||||
omap_hsmmc_cmd_done(host, host->cmd);
|
||||
if ((end_trans || (status & TC)) && host->mrq)
|
||||
if ((end_trans || (status & TC_EN)) && host->mrq)
|
||||
omap_hsmmc_xfer_done(host, data);
|
||||
}
|
||||
|
||||
|
@ -1101,7 +1141,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
|
|||
|
||||
return 0;
|
||||
err:
|
||||
dev_dbg(mmc_dev(host->mmc), "Unable to switch operating voltage\n");
|
||||
dev_err(mmc_dev(host->mmc), "Unable to switch operating voltage\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1360,7 +1400,7 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
|
|||
if (host->use_dma) {
|
||||
ret = omap_hsmmc_start_dma_transfer(host, req);
|
||||
if (ret != 0) {
|
||||
dev_dbg(mmc_dev(host->mmc), "MMC start dma failure\n");
|
||||
dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -1678,7 +1718,7 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
|
|||
{
|
||||
struct omap_mmc_platform_data *pdata;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 bus_width;
|
||||
u32 bus_width, max_freq;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
|
@ -1705,6 +1745,12 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
|
|||
if (of_find_property(np, "ti,needs-special-reset", NULL))
|
||||
pdata->slots[0].features |= HSMMC_HAS_UPDATED_RESET;
|
||||
|
||||
if (!of_property_read_u32(np, "max-frequency", &max_freq))
|
||||
pdata->max_freq = max_freq;
|
||||
|
||||
if (of_find_property(np, "ti,needs-special-hs-handling", NULL))
|
||||
pdata->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
|
@ -1725,6 +1771,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
|
|||
const struct of_device_id *match;
|
||||
dma_cap_mask_t mask;
|
||||
unsigned tx_req, rx_req;
|
||||
struct pinctrl *pinctrl;
|
||||
|
||||
match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
|
||||
if (match) {
|
||||
|
@ -1821,7 +1868,6 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
|
|||
* MMC can still work without debounce clock.
|
||||
*/
|
||||
if (IS_ERR(host->dbclk)) {
|
||||
dev_warn(mmc_dev(host->mmc), "Failed to get debounce clk\n");
|
||||
host->dbclk = NULL;
|
||||
} else if (clk_prepare_enable(host->dbclk) != 0) {
|
||||
dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n");
|
||||
|
@ -1889,13 +1935,13 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
|
|||
ret = request_irq(host->irq, omap_hsmmc_irq, 0,
|
||||
mmc_hostname(mmc), host);
|
||||
if (ret) {
|
||||
dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
|
||||
dev_err(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
if (pdata->init != NULL) {
|
||||
if (pdata->init(&pdev->dev) != 0) {
|
||||
dev_dbg(mmc_dev(host->mmc),
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"Unable to configure MMC IRQs\n");
|
||||
goto err_irq_cd_init;
|
||||
}
|
||||
|
@ -1918,7 +1964,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
|
|||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
mmc_hostname(mmc), host);
|
||||
if (ret) {
|
||||
dev_dbg(mmc_dev(host->mmc),
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"Unable to grab MMC CD IRQ\n");
|
||||
goto err_irq_cd;
|
||||
}
|
||||
|
@ -1928,6 +1974,11 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
|
|||
|
||||
omap_hsmmc_disable_irq(host);
|
||||
|
||||
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
||||
if (IS_ERR(pinctrl))
|
||||
dev_warn(&pdev->dev,
|
||||
"pins are not configured from the driver\n");
|
||||
|
||||
omap_hsmmc_protect_card(host);
|
||||
|
||||
mmc_add_host(mmc);
|
||||
|
@ -2027,6 +2078,25 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int omap_hsmmc_prepare(struct device *dev)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||
|
||||
if (host->pdata->suspend)
|
||||
return host->pdata->suspend(dev, host->slot_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_hsmmc_complete(struct device *dev)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||
|
||||
if (host->pdata->resume)
|
||||
host->pdata->resume(dev, host->slot_id);
|
||||
|
||||
}
|
||||
|
||||
static int omap_hsmmc_suspend(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -2040,23 +2110,10 @@ static int omap_hsmmc_suspend(struct device *dev)
|
|||
|
||||
pm_runtime_get_sync(host->dev);
|
||||
host->suspended = 1;
|
||||
if (host->pdata->suspend) {
|
||||
ret = host->pdata->suspend(dev, host->slot_id);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "Unable to handle MMC board"
|
||||
" level suspend\n");
|
||||
host->suspended = 0;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = mmc_suspend_host(host->mmc);
|
||||
|
||||
if (ret) {
|
||||
host->suspended = 0;
|
||||
if (host->pdata->resume) {
|
||||
if (host->pdata->resume(dev, host->slot_id))
|
||||
dev_dbg(dev, "Unmask interrupt failed\n");
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -2093,12 +2150,6 @@ static int omap_hsmmc_resume(struct device *dev)
|
|||
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))
|
||||
omap_hsmmc_conf_bus_power(host);
|
||||
|
||||
if (host->pdata->resume) {
|
||||
ret = host->pdata->resume(dev, host->slot_id);
|
||||
if (ret)
|
||||
dev_dbg(dev, "Unmask interrupt failed\n");
|
||||
}
|
||||
|
||||
omap_hsmmc_protect_card(host);
|
||||
|
||||
/* Notify the core to resume the host */
|
||||
|
@ -2114,8 +2165,10 @@ static int omap_hsmmc_resume(struct device *dev)
|
|||
}
|
||||
|
||||
#else
|
||||
#define omap_hsmmc_prepare NULL
|
||||
#define omap_hsmmc_complete NULL
|
||||
#define omap_hsmmc_suspend NULL
|
||||
#define omap_hsmmc_resume NULL
|
||||
#define omap_hsmmc_resume NULL
|
||||
#endif
|
||||
|
||||
static int omap_hsmmc_runtime_suspend(struct device *dev)
|
||||
|
@ -2143,6 +2196,8 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
|
|||
static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
|
||||
.suspend = omap_hsmmc_suspend,
|
||||
.resume = omap_hsmmc_resume,
|
||||
.prepare = omap_hsmmc_prepare,
|
||||
.complete = omap_hsmmc_complete,
|
||||
.runtime_suspend = omap_hsmmc_runtime_suspend,
|
||||
.runtime_resume = omap_hsmmc_runtime_resume,
|
||||
};
|
||||
|
|
|
@ -19,20 +19,30 @@
|
|||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
struct sdhci_dove_priv {
|
||||
struct clk *clk;
|
||||
int gpio_cd;
|
||||
};
|
||||
|
||||
static irqreturn_t sdhci_dove_carddetect_irq(int irq, void *data)
|
||||
{
|
||||
struct sdhci_host *host = data;
|
||||
|
||||
tasklet_schedule(&host->card_tasklet);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)
|
||||
{
|
||||
u16 ret;
|
||||
|
@ -50,16 +60,25 @@ static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)
|
|||
|
||||
static u32 sdhci_dove_readl(struct sdhci_host *host, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_dove_priv *priv = pltfm_host->priv;
|
||||
u32 ret;
|
||||
|
||||
ret = readl(host->ioaddr + reg);
|
||||
|
||||
switch (reg) {
|
||||
case SDHCI_CAPABILITIES:
|
||||
ret = readl(host->ioaddr + reg);
|
||||
/* Mask the support for 3.0V */
|
||||
ret &= ~SDHCI_CAN_VDD_300;
|
||||
break;
|
||||
default:
|
||||
ret = readl(host->ioaddr + reg);
|
||||
case SDHCI_PRESENT_STATE:
|
||||
if (gpio_is_valid(priv->gpio_cd)) {
|
||||
if (gpio_get_value(priv->gpio_cd) == 0)
|
||||
ret |= SDHCI_CARD_PRESENT;
|
||||
else
|
||||
ret &= ~SDHCI_CARD_PRESENT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -92,25 +111,70 @@ static int __devinit sdhci_dove_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->clk = clk_get(&pdev->dev, NULL);
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_prepare_enable(priv->clk);
|
||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
|
||||
ret = sdhci_pltfm_register(pdev, &sdhci_dove_pdata);
|
||||
if (ret)
|
||||
goto sdhci_dove_register_fail;
|
||||
if (pdev->dev.of_node) {
|
||||
priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node,
|
||||
"cd-gpios", 0);
|
||||
} else {
|
||||
priv->gpio_cd = -EINVAL;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(priv->gpio_cd)) {
|
||||
ret = gpio_request(priv->gpio_cd, "sdhci-cd");
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "card detect gpio request failed: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
gpio_direction_input(priv->gpio_cd);
|
||||
}
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata);
|
||||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
goto err_sdhci_pltfm_init;
|
||||
}
|
||||
|
||||
host = platform_get_drvdata(pdev);
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = priv;
|
||||
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_prepare_enable(priv->clk);
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto err_sdhci_add;
|
||||
|
||||
/*
|
||||
* We must request the IRQ after sdhci_add_host(), as the tasklet only
|
||||
* gets setup in sdhci_add_host() and we oops.
|
||||
*/
|
||||
if (gpio_is_valid(priv->gpio_cd)) {
|
||||
ret = request_irq(gpio_to_irq(priv->gpio_cd),
|
||||
sdhci_dove_carddetect_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
||||
mmc_hostname(host->mmc), host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "card detect irq request failed: %d\n",
|
||||
ret);
|
||||
goto err_request_irq;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
sdhci_dove_register_fail:
|
||||
if (!IS_ERR(priv->clk)) {
|
||||
err_request_irq:
|
||||
sdhci_remove_host(host, 0);
|
||||
err_sdhci_add:
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_disable_unprepare(priv->clk);
|
||||
clk_put(priv->clk);
|
||||
}
|
||||
sdhci_pltfm_free(pdev);
|
||||
err_sdhci_pltfm_init:
|
||||
if (gpio_is_valid(priv->gpio_cd))
|
||||
gpio_free(priv->gpio_cd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -122,10 +186,14 @@ static int __devexit sdhci_dove_remove(struct platform_device *pdev)
|
|||
|
||||
sdhci_pltfm_unregister(pdev);
|
||||
|
||||
if (!IS_ERR(priv->clk)) {
|
||||
clk_disable_unprepare(priv->clk);
|
||||
clk_put(priv->clk);
|
||||
if (gpio_is_valid(priv->gpio_cd)) {
|
||||
free_irq(gpio_to_irq(priv->gpio_cd), host);
|
||||
gpio_free(priv->gpio_cd);
|
||||
}
|
||||
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -456,10 +456,10 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
|
||||
imx_data = kzalloc(sizeof(struct pltfm_imx_data), GFP_KERNEL);
|
||||
imx_data = devm_kzalloc(&pdev->dev, sizeof(*imx_data), GFP_KERNEL);
|
||||
if (!imx_data) {
|
||||
err = -ENOMEM;
|
||||
goto err_imx_data;
|
||||
goto free_sdhci;
|
||||
}
|
||||
|
||||
if (of_id)
|
||||
|
@ -470,19 +470,19 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
|
||||
if (IS_ERR(imx_data->clk_ipg)) {
|
||||
err = PTR_ERR(imx_data->clk_ipg);
|
||||
goto err_clk_get;
|
||||
goto free_sdhci;
|
||||
}
|
||||
|
||||
imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
|
||||
if (IS_ERR(imx_data->clk_ahb)) {
|
||||
err = PTR_ERR(imx_data->clk_ahb);
|
||||
goto err_clk_get;
|
||||
goto free_sdhci;
|
||||
}
|
||||
|
||||
imx_data->clk_per = devm_clk_get(&pdev->dev, "per");
|
||||
if (IS_ERR(imx_data->clk_per)) {
|
||||
err = PTR_ERR(imx_data->clk_per);
|
||||
goto err_clk_get;
|
||||
goto free_sdhci;
|
||||
}
|
||||
|
||||
pltfm_host->clk = imx_data->clk_per;
|
||||
|
@ -494,7 +494,7 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
imx_data->pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
||||
if (IS_ERR(imx_data->pinctrl)) {
|
||||
err = PTR_ERR(imx_data->pinctrl);
|
||||
goto pin_err;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||
|
@ -519,7 +519,7 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
if (!host->mmc->parent->platform_data) {
|
||||
dev_err(mmc_dev(host->mmc), "no board data!\n");
|
||||
err = -EINVAL;
|
||||
goto no_board_data;
|
||||
goto disable_clk;
|
||||
}
|
||||
imx_data->boarddata = *((struct esdhc_platform_data *)
|
||||
host->mmc->parent->platform_data);
|
||||
|
@ -527,7 +527,8 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
|
||||
/* write_protect */
|
||||
if (boarddata->wp_type == ESDHC_WP_GPIO) {
|
||||
err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP");
|
||||
err = devm_gpio_request_one(&pdev->dev, boarddata->wp_gpio,
|
||||
GPIOF_IN, "ESDHC_WP");
|
||||
if (err) {
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"no write-protect pin available!\n");
|
||||
|
@ -543,19 +544,21 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
|
||||
switch (boarddata->cd_type) {
|
||||
case ESDHC_CD_GPIO:
|
||||
err = gpio_request_one(boarddata->cd_gpio, GPIOF_IN, "ESDHC_CD");
|
||||
err = devm_gpio_request_one(&pdev->dev, boarddata->cd_gpio,
|
||||
GPIOF_IN, "ESDHC_CD");
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"no card-detect pin available!\n");
|
||||
goto no_card_detect_pin;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
err = request_irq(gpio_to_irq(boarddata->cd_gpio), cd_irq,
|
||||
err = devm_request_irq(&pdev->dev,
|
||||
gpio_to_irq(boarddata->cd_gpio), cd_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
||||
mmc_hostname(host->mmc), host);
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc), "request irq error\n");
|
||||
goto no_card_detect_irq;
|
||||
goto disable_clk;
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
|
@ -574,27 +577,15 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
|
||||
err = sdhci_add_host(host);
|
||||
if (err)
|
||||
goto err_add_host;
|
||||
goto disable_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
err_add_host:
|
||||
if (gpio_is_valid(boarddata->cd_gpio))
|
||||
free_irq(gpio_to_irq(boarddata->cd_gpio), host);
|
||||
no_card_detect_irq:
|
||||
if (gpio_is_valid(boarddata->cd_gpio))
|
||||
gpio_free(boarddata->cd_gpio);
|
||||
if (gpio_is_valid(boarddata->wp_gpio))
|
||||
gpio_free(boarddata->wp_gpio);
|
||||
no_card_detect_pin:
|
||||
no_board_data:
|
||||
pin_err:
|
||||
disable_clk:
|
||||
clk_disable_unprepare(imx_data->clk_per);
|
||||
clk_disable_unprepare(imx_data->clk_ipg);
|
||||
clk_disable_unprepare(imx_data->clk_ahb);
|
||||
err_clk_get:
|
||||
kfree(imx_data);
|
||||
err_imx_data:
|
||||
free_sdhci:
|
||||
sdhci_pltfm_free(pdev);
|
||||
return err;
|
||||
}
|
||||
|
@ -604,25 +595,14 @@ static int __devexit sdhci_esdhc_imx_remove(struct platform_device *pdev)
|
|||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct esdhc_platform_data *boarddata = &imx_data->boarddata;
|
||||
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
|
||||
|
||||
sdhci_remove_host(host, dead);
|
||||
|
||||
if (gpio_is_valid(boarddata->wp_gpio))
|
||||
gpio_free(boarddata->wp_gpio);
|
||||
|
||||
if (gpio_is_valid(boarddata->cd_gpio)) {
|
||||
free_irq(gpio_to_irq(boarddata->cd_gpio), host);
|
||||
gpio_free(boarddata->cd_gpio);
|
||||
}
|
||||
|
||||
clk_disable_unprepare(imx_data->clk_per);
|
||||
clk_disable_unprepare(imx_data->clk_ipg);
|
||||
clk_disable_unprepare(imx_data->clk_ahb);
|
||||
|
||||
kfree(imx_data);
|
||||
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "sdhci-esdhc.h"
|
||||
|
||||
#define VENDOR_V_22 0x12
|
||||
#define VENDOR_V_23 0x13
|
||||
static u32 esdhc_readl(struct sdhci_host *host, int reg)
|
||||
{
|
||||
u32 ret;
|
||||
|
@ -85,6 +86,18 @@ static u8 esdhc_readb(struct sdhci_host *host, int reg)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void esdhc_writel(struct sdhci_host *host, u32 val, int reg)
|
||||
{
|
||||
/*
|
||||
* Enable IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE]
|
||||
* when SYSCTL[RSTD]) is set for some special operations.
|
||||
* No any impact other operation.
|
||||
*/
|
||||
if (reg == SDHCI_INT_ENABLE)
|
||||
val |= SDHCI_INT_BLK_GAP;
|
||||
sdhci_be32bs_writel(host, val, reg);
|
||||
}
|
||||
|
||||
static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
|
||||
{
|
||||
if (reg == SDHCI_BLOCK_SIZE) {
|
||||
|
@ -121,6 +134,41 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
|
|||
sdhci_be32bs_writeb(host, val, reg);
|
||||
}
|
||||
|
||||
/*
|
||||
* For Abort or Suspend after Stop at Block Gap, ignore the ADMA
|
||||
* error(IRQSTAT[ADMAE]) if both Transfer Complete(IRQSTAT[TC])
|
||||
* and Block Gap Event(IRQSTAT[BGE]) are also set.
|
||||
* For Continue, apply soft reset for data(SYSCTL[RSTD]);
|
||||
* and re-issue the entire read transaction from beginning.
|
||||
*/
|
||||
static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask)
|
||||
{
|
||||
u32 tmp;
|
||||
bool applicable;
|
||||
dma_addr_t dmastart;
|
||||
dma_addr_t dmanow;
|
||||
|
||||
tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS);
|
||||
tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
|
||||
|
||||
applicable = (intmask & SDHCI_INT_DATA_END) &&
|
||||
(intmask & SDHCI_INT_BLK_GAP) &&
|
||||
(tmp == VENDOR_V_23);
|
||||
if (!applicable)
|
||||
return;
|
||||
|
||||
host->data->error = 0;
|
||||
dmastart = sg_dma_address(host->data->sg);
|
||||
dmanow = dmastart + host->data->bytes_xfered;
|
||||
/*
|
||||
* Force update to the next DMA block boundary.
|
||||
*/
|
||||
dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) +
|
||||
SDHCI_DEFAULT_BOUNDARY_SIZE;
|
||||
host->data->bytes_xfered = dmanow - dmastart;
|
||||
sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS);
|
||||
}
|
||||
|
||||
static int esdhc_of_enable_dma(struct sdhci_host *host)
|
||||
{
|
||||
setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
|
||||
|
@ -177,13 +225,16 @@ static void esdhc_of_platform_init(struct sdhci_host *host)
|
|||
vvn = (vvn & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
|
||||
if (vvn == VENDOR_V_22)
|
||||
host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23;
|
||||
|
||||
if (vvn > VENDOR_V_22)
|
||||
host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_esdhc_ops = {
|
||||
.read_l = esdhc_readl,
|
||||
.read_w = esdhc_readw,
|
||||
.read_b = esdhc_readb,
|
||||
.write_l = sdhci_be32bs_writel,
|
||||
.write_l = esdhc_writel,
|
||||
.write_w = esdhc_writew,
|
||||
.write_b = esdhc_writeb,
|
||||
.set_clock = esdhc_of_set_clock,
|
||||
|
@ -195,6 +246,7 @@ static struct sdhci_ops sdhci_esdhc_ops = {
|
|||
.platform_suspend = esdhc_of_suspend,
|
||||
.platform_resume = esdhc_of_resume,
|
||||
#endif
|
||||
.adma_workaround = esdhci_of_adma_workaround,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_esdhc_pdata = {
|
||||
|
|
|
@ -114,6 +114,7 @@ static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot)
|
|||
|
||||
SDHCI_TIMEOUT_CLK_UNIT |
|
||||
SDHCI_CAN_VDD_330 |
|
||||
SDHCI_CAN_DO_HISPD |
|
||||
SDHCI_CAN_DO_SDMA;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -78,6 +78,9 @@ void sdhci_get_of_property(struct platform_device *pdev)
|
|||
if (of_get_property(np, "broken-cd", NULL))
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||
|
||||
if (of_get_property(np, "no-1-8-v", NULL))
|
||||
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc"))
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_DMA;
|
||||
|
||||
|
@ -89,6 +92,12 @@ void sdhci_get_of_property(struct platform_device *pdev)
|
|||
clk = of_get_property(np, "clock-frequency", &size);
|
||||
if (clk && size == sizeof(*clk) && *clk)
|
||||
pltfm_host->clock = be32_to_cpup(clk);
|
||||
|
||||
if (of_find_property(np, "keep-power-in-suspend", NULL))
|
||||
host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
|
||||
|
||||
if (of_find_property(np, "enable-sdio-wakeup", NULL))
|
||||
host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -163,10 +163,18 @@ static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32 pxav3_get_max_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
return clk_get_rate(pltfm_host->clk);
|
||||
}
|
||||
|
||||
static struct sdhci_ops pxav3_sdhci_ops = {
|
||||
.platform_reset_exit = pxav3_set_private_registers,
|
||||
.set_uhs_signaling = pxav3_set_uhs_signaling,
|
||||
.platform_send_init_74_clocks = pxav3_gen_init_74_clocks,
|
||||
.get_max_clock = pxav3_get_max_clock,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
@ -249,7 +257,8 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
|
|||
|
||||
host->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
|
||||
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
|
||||
| SDHCI_QUIRK_32BIT_ADMA_SIZE;
|
||||
| SDHCI_QUIRK_32BIT_ADMA_SIZE
|
||||
| SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
|
||||
|
||||
/* enable 1/8V DDR capable */
|
||||
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||
|
@ -271,6 +280,8 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
|
|||
|
||||
if (pdata->quirks)
|
||||
host->quirks |= pdata->quirks;
|
||||
if (pdata->quirks2)
|
||||
host->quirks2 |= pdata->quirks2;
|
||||
if (pdata->host_caps)
|
||||
host->mmc->caps |= pdata->host_caps;
|
||||
if (pdata->host_caps2)
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/of_gpio.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
|
@ -57,6 +58,7 @@ struct sdhci_s3c {
|
|||
int ext_cd_irq;
|
||||
int ext_cd_gpio;
|
||||
int *gpios;
|
||||
struct pinctrl *pctrl;
|
||||
|
||||
struct clk *clk_io;
|
||||
struct clk *clk_bus[MAX_BUS_CLK];
|
||||
|
@ -373,7 +375,9 @@ static struct sdhci_ops sdhci_s3c_ops = {
|
|||
static void sdhci_s3c_notify_change(struct platform_device *dev, int state)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(dev);
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
struct sdhci_s3c *sc = sdhci_priv(host);
|
||||
#endif
|
||||
unsigned long flags;
|
||||
|
||||
if (host) {
|
||||
|
@ -413,7 +417,7 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc)
|
|||
struct s3c_sdhci_platdata *pdata = sc->pdata;
|
||||
struct device *dev = &sc->pdev->dev;
|
||||
|
||||
if (gpio_request(pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) {
|
||||
if (devm_gpio_request(dev, pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) {
|
||||
sc->ext_cd_gpio = pdata->ext_cd_gpio;
|
||||
sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio);
|
||||
if (sc->ext_cd_irq &&
|
||||
|
@ -456,12 +460,12 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev,
|
|||
return -ENOMEM;
|
||||
|
||||
/* get the card detection method */
|
||||
if (of_get_property(node, "broken-cd", 0)) {
|
||||
if (of_get_property(node, "broken-cd", NULL)) {
|
||||
pdata->cd_type = S3C_SDHCI_CD_NONE;
|
||||
goto setup_bus;
|
||||
}
|
||||
|
||||
if (of_get_property(node, "non-removable", 0)) {
|
||||
if (of_get_property(node, "non-removable", NULL)) {
|
||||
pdata->cd_type = S3C_SDHCI_CD_PERMANENT;
|
||||
goto setup_bus;
|
||||
}
|
||||
|
@ -484,8 +488,9 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_info(dev, "assuming no card detect line available\n");
|
||||
pdata->cd_type = S3C_SDHCI_CD_NONE;
|
||||
/* assuming internal card detect that will be configured by pinctrl */
|
||||
pdata->cd_type = S3C_SDHCI_CD_INTERNAL;
|
||||
goto setup_bus;
|
||||
|
||||
found_cd:
|
||||
if (pdata->cd_type == S3C_SDHCI_CD_GPIO) {
|
||||
|
@ -494,7 +499,7 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev,
|
|||
if (of_get_property(node, "cd-inverted", NULL))
|
||||
pdata->ext_cd_gpio_invert = 1;
|
||||
} else if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) {
|
||||
ret = gpio_request(gpio, "sdhci-cd");
|
||||
ret = devm_gpio_request(dev, gpio, "sdhci-cd");
|
||||
if (ret) {
|
||||
dev_err(dev, "card detect gpio request failed\n");
|
||||
return -EINVAL;
|
||||
|
@ -503,33 +508,28 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev,
|
|||
}
|
||||
|
||||
setup_bus:
|
||||
if (!IS_ERR(ourhost->pctrl))
|
||||
return 0;
|
||||
|
||||
/* get the gpios for command, clock and data lines */
|
||||
for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) {
|
||||
gpio = of_get_gpio(node, cnt);
|
||||
if (!gpio_is_valid(gpio)) {
|
||||
dev_err(dev, "invalid gpio[%d]\n", cnt);
|
||||
goto err_free_dt_cd_gpio;
|
||||
return -EINVAL;
|
||||
}
|
||||
ourhost->gpios[cnt] = gpio;
|
||||
}
|
||||
|
||||
for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) {
|
||||
ret = gpio_request(ourhost->gpios[cnt], "sdhci-gpio");
|
||||
ret = devm_gpio_request(dev, ourhost->gpios[cnt], "sdhci-gpio");
|
||||
if (ret) {
|
||||
dev_err(dev, "gpio[%d] request failed\n", cnt);
|
||||
goto err_free_dt_gpios;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_dt_gpios:
|
||||
while (--cnt >= 0)
|
||||
gpio_free(ourhost->gpios[cnt]);
|
||||
err_free_dt_cd_gpio:
|
||||
if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL)
|
||||
gpio_free(ourhost->ext_cd_gpio);
|
||||
return -EINVAL;
|
||||
}
|
||||
#else
|
||||
static int __devinit sdhci_s3c_parse_dt(struct device *dev,
|
||||
|
@ -586,13 +586,15 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
|
|||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
ret = -ENOMEM;
|
||||
goto err_pdata;
|
||||
goto err_pdata_io_clk;
|
||||
}
|
||||
|
||||
sc->pctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata);
|
||||
if (ret)
|
||||
goto err_pdata;
|
||||
goto err_pdata_io_clk;
|
||||
} else {
|
||||
memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata));
|
||||
sc->ext_cd_gpio = -1; /* invalid gpio number */
|
||||
|
@ -610,7 +612,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(sc->clk_io)) {
|
||||
dev_err(dev, "failed to get io clock\n");
|
||||
ret = PTR_ERR(sc->clk_io);
|
||||
goto err_io_clk;
|
||||
goto err_pdata_io_clk;
|
||||
}
|
||||
|
||||
/* enable the local io clock and keep it running for the moment. */
|
||||
|
@ -773,13 +775,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
|
|||
clk_disable_unprepare(sc->clk_io);
|
||||
clk_put(sc->clk_io);
|
||||
|
||||
err_io_clk:
|
||||
for (ptr = 0; ptr < NUM_GPIOS(sc->pdata->max_width); ptr++)
|
||||
gpio_free(sc->gpios[ptr]);
|
||||
if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL)
|
||||
gpio_free(sc->ext_cd_gpio);
|
||||
|
||||
err_pdata:
|
||||
err_pdata_io_clk:
|
||||
sdhci_free_host(host);
|
||||
|
||||
return ret;
|
||||
|
@ -798,9 +794,6 @@ static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
|
|||
if (sc->ext_cd_irq)
|
||||
free_irq(sc->ext_cd_irq, sc);
|
||||
|
||||
if (gpio_is_valid(sc->ext_cd_gpio))
|
||||
gpio_free(sc->ext_cd_gpio);
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL)
|
||||
clk_prepare_enable(sc->clk_io);
|
||||
|
@ -821,11 +814,6 @@ static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
|
|||
clk_disable_unprepare(sc->clk_io);
|
||||
clk_put(sc->clk_io);
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
for (ptr = 0; ptr < NUM_GPIOS(sc->pdata->max_width); ptr++)
|
||||
gpio_free(sc->gpios[ptr]);
|
||||
}
|
||||
|
||||
sdhci_free_host(host);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
|
|
|
@ -146,6 +146,11 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
|
|||
goto put_clk;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(sdhci->clk, 50000000);
|
||||
if (ret)
|
||||
dev_dbg(&pdev->dev, "Error setting desired clk, clk=%lu\n",
|
||||
clk_get_rate(sdhci->clk));
|
||||
|
||||
if (np) {
|
||||
sdhci->data = sdhci_probe_config_dt(pdev);
|
||||
if (IS_ERR(sdhci->data)) {
|
||||
|
@ -297,7 +302,7 @@ static int sdhci_suspend(struct device *dev)
|
|||
|
||||
ret = sdhci_suspend_host(host);
|
||||
if (!ret)
|
||||
clk_disable_unprepare(sdhci->clk);
|
||||
clk_disable(sdhci->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -308,7 +313,7 @@ static int sdhci_resume(struct device *dev)
|
|||
struct spear_sdhci *sdhci = dev_get_platdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(sdhci->clk);
|
||||
ret = clk_enable(sdhci->clk);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "Resume: Error enabling clock\n");
|
||||
return ret;
|
||||
|
|
|
@ -1618,7 +1618,7 @@ static int sdhci_do_3_3v_signal_voltage_switch(struct sdhci_host *host,
|
|||
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
||||
|
||||
if (host->vqmmc) {
|
||||
ret = regulator_set_voltage(host->vqmmc, 3300000, 3300000);
|
||||
ret = regulator_set_voltage(host->vqmmc, 2700000, 3600000);
|
||||
if (ret) {
|
||||
pr_warning("%s: Switching to 3.3V signalling voltage "
|
||||
" failed\n", mmc_hostname(host->mmc));
|
||||
|
@ -1662,7 +1662,7 @@ static int sdhci_do_1_8v_signal_voltage_switch(struct sdhci_host *host,
|
|||
*/
|
||||
if (host->vqmmc)
|
||||
ret = regulator_set_voltage(host->vqmmc,
|
||||
1800000, 1800000);
|
||||
1700000, 1950000);
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
|
@ -1994,30 +1994,11 @@ static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
|
|||
sdhci_runtime_pm_put(host);
|
||||
}
|
||||
|
||||
static const struct mmc_host_ops sdhci_ops = {
|
||||
.request = sdhci_request,
|
||||
.set_ios = sdhci_set_ios,
|
||||
.get_ro = sdhci_get_ro,
|
||||
.hw_reset = sdhci_hw_reset,
|
||||
.enable_sdio_irq = sdhci_enable_sdio_irq,
|
||||
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
|
||||
.execute_tuning = sdhci_execute_tuning,
|
||||
.enable_preset_value = sdhci_enable_preset_value,
|
||||
};
|
||||
|
||||
/*****************************************************************************\
|
||||
* *
|
||||
* Tasklets *
|
||||
* *
|
||||
\*****************************************************************************/
|
||||
|
||||
static void sdhci_tasklet_card(unsigned long param)
|
||||
static void sdhci_card_event(struct mmc_host *mmc)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
unsigned long flags;
|
||||
|
||||
host = (struct sdhci_host*)param;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
/* Check host->mrq first in case we are runtime suspended */
|
||||
|
@ -2036,6 +2017,31 @@ static void sdhci_tasklet_card(unsigned long param)
|
|||
}
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
static const struct mmc_host_ops sdhci_ops = {
|
||||
.request = sdhci_request,
|
||||
.set_ios = sdhci_set_ios,
|
||||
.get_ro = sdhci_get_ro,
|
||||
.hw_reset = sdhci_hw_reset,
|
||||
.enable_sdio_irq = sdhci_enable_sdio_irq,
|
||||
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
|
||||
.execute_tuning = sdhci_execute_tuning,
|
||||
.enable_preset_value = sdhci_enable_preset_value,
|
||||
.card_event = sdhci_card_event,
|
||||
};
|
||||
|
||||
/*****************************************************************************\
|
||||
* *
|
||||
* Tasklets *
|
||||
* *
|
||||
\*****************************************************************************/
|
||||
|
||||
static void sdhci_tasklet_card(unsigned long param)
|
||||
{
|
||||
struct sdhci_host *host = (struct sdhci_host*)param;
|
||||
|
||||
sdhci_card_event(host->mmc);
|
||||
|
||||
mmc_detect_change(host->mmc, msecs_to_jiffies(200));
|
||||
}
|
||||
|
@ -2282,6 +2288,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
|||
pr_err("%s: ADMA error\n", mmc_hostname(host->mmc));
|
||||
sdhci_show_adma_error(host);
|
||||
host->data->error = -EIO;
|
||||
if (host->ops->adma_workaround)
|
||||
host->ops->adma_workaround(host, intmask);
|
||||
}
|
||||
|
||||
if (host->data->error)
|
||||
|
@ -2858,10 +2866,16 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
mmc_hostname(mmc));
|
||||
host->vqmmc = NULL;
|
||||
}
|
||||
}
|
||||
else if (regulator_is_supported_voltage(host->vqmmc, 1800000, 1800000))
|
||||
} else {
|
||||
regulator_enable(host->vqmmc);
|
||||
else
|
||||
if (!regulator_is_supported_voltage(host->vqmmc, 1700000,
|
||||
1950000))
|
||||
caps[1] &= ~(SDHCI_SUPPORT_SDR104 |
|
||||
SDHCI_SUPPORT_SDR50 |
|
||||
SDHCI_SUPPORT_DDR50);
|
||||
}
|
||||
|
||||
if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V)
|
||||
caps[1] &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
|
||||
SDHCI_SUPPORT_DDR50);
|
||||
|
||||
|
@ -2919,21 +2933,18 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
mmc_hostname(mmc));
|
||||
host->vmmc = NULL;
|
||||
}
|
||||
} else
|
||||
regulator_enable(host->vmmc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
if (host->vmmc) {
|
||||
ret = regulator_is_supported_voltage(host->vmmc, 3300000,
|
||||
3300000);
|
||||
ret = regulator_is_supported_voltage(host->vmmc, 2700000,
|
||||
3600000);
|
||||
if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_330)))
|
||||
caps[0] &= ~SDHCI_CAN_VDD_330;
|
||||
ret = regulator_is_supported_voltage(host->vmmc, 3000000,
|
||||
3000000);
|
||||
if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_300)))
|
||||
caps[0] &= ~SDHCI_CAN_VDD_300;
|
||||
ret = regulator_is_supported_voltage(host->vmmc, 1800000,
|
||||
1800000);
|
||||
ret = regulator_is_supported_voltage(host->vmmc, 1700000,
|
||||
1950000);
|
||||
if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_180)))
|
||||
caps[0] &= ~SDHCI_CAN_VDD_180;
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@
|
|||
#define SDHCI_SIGNAL_ENABLE 0x38
|
||||
#define SDHCI_INT_RESPONSE 0x00000001
|
||||
#define SDHCI_INT_DATA_END 0x00000002
|
||||
#define SDHCI_INT_BLK_GAP 0x00000004
|
||||
#define SDHCI_INT_DMA_END 0x00000008
|
||||
#define SDHCI_INT_SPACE_AVAIL 0x00000010
|
||||
#define SDHCI_INT_DATA_AVAIL 0x00000020
|
||||
|
@ -146,7 +147,8 @@
|
|||
#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
|
||||
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
|
||||
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
|
||||
SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR)
|
||||
SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR | \
|
||||
SDHCI_INT_BLK_GAP)
|
||||
#define SDHCI_INT_ALL_MASK ((unsigned int)-1)
|
||||
|
||||
#define SDHCI_ACMD12_ERR 0x3C
|
||||
|
@ -278,6 +280,7 @@ struct sdhci_ops {
|
|||
void (*hw_reset)(struct sdhci_host *host);
|
||||
void (*platform_suspend)(struct sdhci_host *host);
|
||||
void (*platform_resume)(struct sdhci_host *host);
|
||||
void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
|
||||
void (*platform_init)(struct sdhci_host *host);
|
||||
};
|
||||
|
||||
|
|
|
@ -1306,7 +1306,6 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
|
|||
struct sh_mmcif_plat_data *pd = pdev->dev.platform_data;
|
||||
struct resource *res;
|
||||
void __iomem *reg;
|
||||
char clk_name[8];
|
||||
|
||||
irq[0] = platform_get_irq(pdev, 0);
|
||||
irq[1] = platform_get_irq(pdev, 1);
|
||||
|
@ -1356,11 +1355,10 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
|
|||
pm_runtime_enable(&pdev->dev);
|
||||
host->power = false;
|
||||
|
||||
snprintf(clk_name, sizeof(clk_name), "mmc%d", pdev->id);
|
||||
host->hclk = clk_get(&pdev->dev, clk_name);
|
||||
host->hclk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(host->hclk)) {
|
||||
ret = PTR_ERR(host->hclk);
|
||||
dev_err(&pdev->dev, "cannot get clock \"%s\": %d\n", clk_name, ret);
|
||||
dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
|
||||
goto eclkget;
|
||||
}
|
||||
ret = sh_mmcif_clk_update(host);
|
||||
|
|
|
@ -123,7 +123,6 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
struct tmio_mmc_data *mmc_data;
|
||||
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
||||
struct tmio_mmc_host *host;
|
||||
char clk_name[8];
|
||||
int irq, ret, i = 0;
|
||||
bool multiplexed_isr = true;
|
||||
|
||||
|
@ -144,11 +143,10 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id);
|
||||
priv->clk = clk_get(&pdev->dev, clk_name);
|
||||
priv->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->clk)) {
|
||||
dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
|
||||
ret = PTR_ERR(priv->clk);
|
||||
dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
|
||||
goto eclkget;
|
||||
}
|
||||
|
||||
|
@ -250,7 +248,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
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);
|
||||
host->mmc->f_max / 1000000);
|
||||
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -2362,6 +2362,7 @@ error4:
|
|||
error1:
|
||||
usb_free_urb(command_out_urb);
|
||||
error0:
|
||||
usb_put_dev(udev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -85,6 +85,7 @@ struct mmc_ext_csd {
|
|||
bool boot_ro_lockable;
|
||||
u8 raw_exception_status; /* 53 */
|
||||
u8 raw_partition_support; /* 160 */
|
||||
u8 raw_rpmb_size_mult; /* 168 */
|
||||
u8 raw_erased_mem_count; /* 181 */
|
||||
u8 raw_ext_csd_structure; /* 194 */
|
||||
u8 raw_card_type; /* 196 */
|
||||
|
@ -206,6 +207,7 @@ struct mmc_part {
|
|||
#define MMC_BLK_DATA_AREA_MAIN (1<<0)
|
||||
#define MMC_BLK_DATA_AREA_BOOT (1<<1)
|
||||
#define MMC_BLK_DATA_AREA_GP (1<<2)
|
||||
#define MMC_BLK_DATA_AREA_RPMB (1<<3)
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -170,6 +170,8 @@ extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
|
|||
extern unsigned int mmc_calc_max_discard(struct mmc_card *card);
|
||||
|
||||
extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);
|
||||
extern int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount,
|
||||
bool is_rel_write);
|
||||
extern int mmc_hw_reset(struct mmc_host *host);
|
||||
extern int mmc_hw_reset_check(struct mmc_host *host);
|
||||
extern int mmc_can_reset(struct mmc_card *card);
|
||||
|
|
|
@ -229,8 +229,9 @@ struct dw_mci_board {
|
|||
u32 quirks; /* Workaround / Quirk flags */
|
||||
unsigned int bus_hz; /* Clock speed at the cclk_in pad */
|
||||
|
||||
unsigned int caps; /* Capabilities */
|
||||
unsigned int caps2; /* More capabilities */
|
||||
u32 caps; /* Capabilities */
|
||||
u32 caps2; /* More capabilities */
|
||||
u32 pm_caps; /* PM capabilities */
|
||||
/*
|
||||
* Override fifo depth. If 0, autodetect it from the FIFOTH register,
|
||||
* but note that this may not be reliable after a bootloader has used
|
||||
|
|
|
@ -53,12 +53,12 @@ struct mmc_ios {
|
|||
#define MMC_TIMING_LEGACY 0
|
||||
#define MMC_TIMING_MMC_HS 1
|
||||
#define MMC_TIMING_SD_HS 2
|
||||
#define MMC_TIMING_UHS_SDR12 MMC_TIMING_LEGACY
|
||||
#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
|
||||
#define MMC_TIMING_UHS_SDR50 3
|
||||
#define MMC_TIMING_UHS_SDR104 4
|
||||
#define MMC_TIMING_UHS_DDR50 5
|
||||
#define MMC_TIMING_MMC_HS200 6
|
||||
#define MMC_TIMING_UHS_SDR12 3
|
||||
#define MMC_TIMING_UHS_SDR25 4
|
||||
#define MMC_TIMING_UHS_SDR50 5
|
||||
#define MMC_TIMING_UHS_SDR104 6
|
||||
#define MMC_TIMING_UHS_DDR50 7
|
||||
#define MMC_TIMING_MMC_HS200 8
|
||||
|
||||
#define MMC_SDR_MODE 0
|
||||
#define MMC_1_2V_DDR_MODE 1
|
||||
|
@ -136,6 +136,7 @@ struct mmc_host_ops {
|
|||
void (*enable_preset_value)(struct mmc_host *host, bool enable);
|
||||
int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
|
||||
void (*hw_reset)(struct mmc_host *host);
|
||||
void (*card_event)(struct mmc_host *host);
|
||||
};
|
||||
|
||||
struct mmc_card;
|
||||
|
@ -211,7 +212,7 @@ struct mmc_host {
|
|||
#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */
|
||||
#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */
|
||||
|
||||
unsigned long caps; /* Host capabilities */
|
||||
u32 caps; /* Host capabilities */
|
||||
|
||||
#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */
|
||||
#define MMC_CAP_MMC_HIGHSPEED (1 << 1) /* Can do MMC high-speed timing */
|
||||
|
@ -241,7 +242,7 @@ struct mmc_host {
|
|||
#define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */
|
||||
#define MMC_CAP_HW_RESET (1 << 31) /* Hardware reset */
|
||||
|
||||
unsigned int caps2; /* More host capabilities */
|
||||
u32 caps2; /* More host capabilities */
|
||||
|
||||
#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */
|
||||
#define MMC_CAP2_CACHE_CTRL (1 << 1) /* Allow cache control */
|
||||
|
|
|
@ -286,6 +286,7 @@ struct _mmc_csd {
|
|||
#define EXT_CSD_BKOPS_START 164 /* W */
|
||||
#define EXT_CSD_SANITIZE_START 165 /* W */
|
||||
#define EXT_CSD_WR_REL_PARAM 166 /* RO */
|
||||
#define EXT_CSD_RPMB_MULT 168 /* RO */
|
||||
#define EXT_CSD_BOOT_WP 173 /* R/W */
|
||||
#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
|
||||
#define EXT_CSD_PART_CONFIG 179 /* R/W */
|
||||
|
@ -339,6 +340,7 @@ struct _mmc_csd {
|
|||
|
||||
#define EXT_CSD_PART_CONFIG_ACC_MASK (0x7)
|
||||
#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1)
|
||||
#define EXT_CSD_PART_CONFIG_ACC_RPMB (0x3)
|
||||
#define EXT_CSD_PART_CONFIG_ACC_GP0 (0x4)
|
||||
|
||||
#define EXT_CSD_PART_SUPPORT_PART_EN (0x1)
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MMC_MXS_MMC_H__
|
||||
#define __LINUX_MMC_MXS_MMC_H__
|
||||
|
||||
struct mxs_mmc_platform_data {
|
||||
int wp_gpio; /* write protect pin */
|
||||
unsigned int flags;
|
||||
#define SLOTF_4_BIT_CAPABLE (1 << 0)
|
||||
#define SLOTF_8_BIT_CAPABLE (1 << 1)
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MMC_MXS_MMC_H__ */
|
|
@ -92,6 +92,8 @@ struct sdhci_host {
|
|||
|
||||
#define SDHCI_QUIRK2_HOST_OFF_CARD_ON (1<<0)
|
||||
#define SDHCI_QUIRK2_HOST_NO_CMD23 (1<<1)
|
||||
/* The system physically doesn't support 1.8v, even if the host does */
|
||||
#define SDHCI_QUIRK2_NO_1_8_V (1<<2)
|
||||
|
||||
int irq; /* Device IRQ */
|
||||
void __iomem *ioaddr; /* Mapped address */
|
||||
|
@ -158,8 +160,8 @@ struct sdhci_host {
|
|||
|
||||
struct timer_list timer; /* Timer for timeouts */
|
||||
|
||||
unsigned int caps; /* Alternative CAPABILITY_0 */
|
||||
unsigned int caps1; /* Alternative CAPABILITY_1 */
|
||||
u32 caps; /* Alternative CAPABILITY_0 */
|
||||
u32 caps1; /* Alternative CAPABILITY_1 */
|
||||
|
||||
unsigned int ocr_avail_sdio; /* OCR bit masks */
|
||||
unsigned int ocr_avail_sd;
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
* @max_speed: the maximum speed supported
|
||||
* @host_caps: Standard MMC host capabilities bit field.
|
||||
* @quirks: quirks of platfrom
|
||||
* @quirks2: quirks2 of platfrom
|
||||
* @pm_caps: pm_caps of platfrom
|
||||
*/
|
||||
struct sdhci_pxa_platdata {
|
||||
|
@ -48,9 +49,10 @@ struct sdhci_pxa_platdata {
|
|||
unsigned int ext_cd_gpio;
|
||||
bool ext_cd_gpio_invert;
|
||||
unsigned int max_speed;
|
||||
unsigned int host_caps;
|
||||
unsigned int host_caps2;
|
||||
u32 host_caps;
|
||||
u32 host_caps2;
|
||||
unsigned int quirks;
|
||||
unsigned int quirks2;
|
||||
unsigned int pm_caps;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue