Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (83 commits) mmc: fix compile error when CONFIG_BLOCK is not enabled mmc: core: Cleanup eMMC4.5 conditionals mmc: omap_hsmmc: if multiblock reads are broken, disable them mmc: core: add workaround for controllers with broken multiblock reads mmc: core: Prevent too long response times for suspend mmc: recognise SDIO cards with SDIO_CCCR_REV 3.00 mmc: sd: Handle SD3.0 cards not supporting UHS-I bus speed mode mmc: core: support HPI send command mmc: core: Add cache control for eMMC4.5 device mmc: core: Modify the timeout value for writing power class mmc: core: new discard feature support at eMMC v4.5 mmc: core: mmc sanitize feature support for v4.5 mmc: dw_mmc: modify DATA register offset mmc: sdhci-pci: add flag for devices that can support runtime PM mmc: omap_hsmmc: ensure pbias configuration is always done mmc: core: Add Power Off Notify Feature eMMC 4.5 mmc: sdhci-s3c: fix potential NULL dereference mmc: replace printk with appropriate display macro mmc: core: Add default timeout value for CMD6 mmc: sdhci-pci: add runtime pm support ...
This commit is contained in:
commit
46b51ea209
|
@ -0,0 +1,27 @@
|
||||||
|
* NVIDIA Tegra Secure Digital Host Controller
|
||||||
|
|
||||||
|
This controller on Tegra family SoCs provides an interface for MMC, SD,
|
||||||
|
and SDIO types of memory cards.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : Should be "nvidia,<chip>-sdhci"
|
||||||
|
- reg : Should contain SD/MMC registers location and length
|
||||||
|
- interrupts : Should contain SD/MMC interrupt
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- cd-gpios : Specify GPIOs for card detection
|
||||||
|
- wp-gpios : Specify GPIOs for write protection
|
||||||
|
- power-gpios : Specify GPIOs for power control
|
||||||
|
- support-8bit : Boolean, indicates if 8-bit mode should be used.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
sdhci@c8000200 {
|
||||||
|
compatible = "nvidia,tegra20-sdhci";
|
||||||
|
reg = <0xc8000200 0x200>;
|
||||||
|
interrupts = <47>;
|
||||||
|
cd-gpios = <&gpio 69 0>; /* gpio PI5 */
|
||||||
|
wp-gpios = <&gpio 57 0>; /* gpio PH1 */
|
||||||
|
power-gpios = <&gpio 155 0>; /* gpio PT3 */
|
||||||
|
support-8bit;
|
||||||
|
};
|
|
@ -21,6 +21,11 @@ o fail_make_request
|
||||||
/sys/block/<device>/make-it-fail or
|
/sys/block/<device>/make-it-fail or
|
||||||
/sys/block/<device>/<partition>/make-it-fail. (generic_make_request())
|
/sys/block/<device>/<partition>/make-it-fail. (generic_make_request())
|
||||||
|
|
||||||
|
o fail_mmc_request
|
||||||
|
|
||||||
|
injects MMC data errors on devices permitted by setting
|
||||||
|
debugfs entries under /sys/kernel/debug/mmc0/fail_mmc_request
|
||||||
|
|
||||||
Configure fault-injection capabilities behavior
|
Configure fault-injection capabilities behavior
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
|
|
||||||
|
@ -115,7 +120,8 @@ use the boot option:
|
||||||
|
|
||||||
failslab=
|
failslab=
|
||||||
fail_page_alloc=
|
fail_page_alloc=
|
||||||
fail_make_request=<interval>,<probability>,<space>,<times>
|
fail_make_request=
|
||||||
|
mmc_core.fail_request=<interval>,<probability>,<space>,<times>
|
||||||
|
|
||||||
How to add new fault injection capability
|
How to add new fault injection capability
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
|
|
@ -319,7 +319,7 @@ void __init at91_add_device_mci(short mmc_id, struct mci_platform_data *data)
|
||||||
if (!data)
|
if (!data)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
|
for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
|
||||||
if (data->slot[i].bus_width) {
|
if (data->slot[i].bus_width) {
|
||||||
/* input/irq */
|
/* input/irq */
|
||||||
if (data->slot[i].detect_pin) {
|
if (data->slot[i].detect_pin) {
|
||||||
|
|
|
@ -175,12 +175,6 @@ static struct resource resources_sdc1[] = {
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
.name = "cmd_irq",
|
.name = "cmd_irq",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
.start = INT_SDC1_1,
|
|
||||||
.end = INT_SDC1_1,
|
|
||||||
.flags = IORESOURCE_IRQ,
|
|
||||||
.name = "pio_irq",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
||||||
.name = "status_irq"
|
.name = "status_irq"
|
||||||
|
@ -203,12 +197,6 @@ static struct resource resources_sdc2[] = {
|
||||||
.end = INT_SDC2_0,
|
.end = INT_SDC2_0,
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
.name = "cmd_irq",
|
.name = "cmd_irq",
|
||||||
},
|
|
||||||
{
|
|
||||||
.start = INT_SDC2_1,
|
|
||||||
.end = INT_SDC2_1,
|
|
||||||
.flags = IORESOURCE_IRQ,
|
|
||||||
.name = "pio_irq",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
||||||
|
@ -232,12 +220,6 @@ static struct resource resources_sdc3[] = {
|
||||||
.end = INT_SDC3_0,
|
.end = INT_SDC3_0,
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
.name = "cmd_irq",
|
.name = "cmd_irq",
|
||||||
},
|
|
||||||
{
|
|
||||||
.start = INT_SDC3_1,
|
|
||||||
.end = INT_SDC3_1,
|
|
||||||
.flags = IORESOURCE_IRQ,
|
|
||||||
.name = "pio_irq",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
||||||
|
@ -261,12 +243,6 @@ static struct resource resources_sdc4[] = {
|
||||||
.end = INT_SDC4_0,
|
.end = INT_SDC4_0,
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
.name = "cmd_irq",
|
.name = "cmd_irq",
|
||||||
},
|
|
||||||
{
|
|
||||||
.start = INT_SDC4_1,
|
|
||||||
.end = INT_SDC4_1,
|
|
||||||
.flags = IORESOURCE_IRQ,
|
|
||||||
.name = "pio_irq",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
||||||
|
|
|
@ -139,12 +139,6 @@ static struct resource resources_sdc1[] = {
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
.name = "cmd_irq",
|
.name = "cmd_irq",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
.start = INT_SDC1_1,
|
|
||||||
.end = INT_SDC1_1,
|
|
||||||
.flags = IORESOURCE_IRQ,
|
|
||||||
.name = "pio_irq",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
||||||
.name = "status_irq"
|
.name = "status_irq"
|
||||||
|
@ -167,12 +161,6 @@ static struct resource resources_sdc2[] = {
|
||||||
.end = INT_SDC2_0,
|
.end = INT_SDC2_0,
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
.name = "cmd_irq",
|
.name = "cmd_irq",
|
||||||
},
|
|
||||||
{
|
|
||||||
.start = INT_SDC2_1,
|
|
||||||
.end = INT_SDC2_1,
|
|
||||||
.flags = IORESOURCE_IRQ,
|
|
||||||
.name = "pio_irq",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
||||||
|
@ -196,12 +184,6 @@ static struct resource resources_sdc3[] = {
|
||||||
.end = INT_SDC3_0,
|
.end = INT_SDC3_0,
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
.name = "cmd_irq",
|
.name = "cmd_irq",
|
||||||
},
|
|
||||||
{
|
|
||||||
.start = INT_SDC3_1,
|
|
||||||
.end = INT_SDC3_1,
|
|
||||||
.flags = IORESOURCE_IRQ,
|
|
||||||
.name = "pio_irq",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
||||||
|
@ -225,12 +207,6 @@ static struct resource resources_sdc4[] = {
|
||||||
.end = INT_SDC4_0,
|
.end = INT_SDC4_0,
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
.name = "cmd_irq",
|
.name = "cmd_irq",
|
||||||
},
|
|
||||||
{
|
|
||||||
.start = INT_SDC4_1,
|
|
||||||
.end = INT_SDC4_1,
|
|
||||||
.flags = IORESOURCE_IRQ,
|
|
||||||
.name = "pio_irq",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
|
||||||
|
|
|
@ -8,13 +8,6 @@
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/mmc/card.h>
|
||||||
#include <linux/mmc/sdio_func.h>
|
#include <linux/mmc/sdio_func.h>
|
||||||
|
|
||||||
struct embedded_sdio_data {
|
|
||||||
struct sdio_cis cis;
|
|
||||||
struct sdio_cccr cccr;
|
|
||||||
struct sdio_embedded_func *funcs;
|
|
||||||
int num_funcs;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct msm_mmc_gpio {
|
struct msm_mmc_gpio {
|
||||||
unsigned no;
|
unsigned no;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
@ -29,9 +22,9 @@ struct msm_mmc_platform_data {
|
||||||
unsigned int ocr_mask; /* available voltages */
|
unsigned int ocr_mask; /* available voltages */
|
||||||
u32 (*translate_vdd)(struct device *, unsigned int);
|
u32 (*translate_vdd)(struct device *, unsigned int);
|
||||||
unsigned int (*status)(struct device *);
|
unsigned int (*status)(struct device *);
|
||||||
struct embedded_sdio_data *embedded_sdio;
|
|
||||||
int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id);
|
int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id);
|
||||||
struct msm_mmc_gpio_data *gpio_data;
|
struct msm_mmc_gpio_data *gpio_data;
|
||||||
|
void (*init_card)(struct mmc_card *card);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -355,14 +355,17 @@ static struct resource sdhi0_resources[] = {
|
||||||
.flags = IORESOURCE_MEM,
|
.flags = IORESOURCE_MEM,
|
||||||
},
|
},
|
||||||
[1] = {
|
[1] = {
|
||||||
|
.name = SH_MOBILE_SDHI_IRQ_CARD_DETECT,
|
||||||
.start = gic_spi(83),
|
.start = gic_spi(83),
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
},
|
},
|
||||||
[2] = {
|
[2] = {
|
||||||
|
.name = SH_MOBILE_SDHI_IRQ_SDCARD,
|
||||||
.start = gic_spi(84),
|
.start = gic_spi(84),
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
},
|
},
|
||||||
[3] = {
|
[3] = {
|
||||||
|
.name = SH_MOBILE_SDHI_IRQ_SDIO,
|
||||||
.start = gic_spi(85),
|
.start = gic_spi(85),
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
},
|
},
|
||||||
|
@ -398,14 +401,17 @@ static struct resource sdhi1_resources[] = {
|
||||||
.flags = IORESOURCE_MEM,
|
.flags = IORESOURCE_MEM,
|
||||||
},
|
},
|
||||||
[1] = {
|
[1] = {
|
||||||
|
.name = SH_MOBILE_SDHI_IRQ_CARD_DETECT,
|
||||||
.start = gic_spi(87),
|
.start = gic_spi(87),
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
},
|
},
|
||||||
[2] = {
|
[2] = {
|
||||||
|
.name = SH_MOBILE_SDHI_IRQ_SDCARD,
|
||||||
.start = gic_spi(88),
|
.start = gic_spi(88),
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
},
|
},
|
||||||
[3] = {
|
[3] = {
|
||||||
|
.name = SH_MOBILE_SDHI_IRQ_SDIO,
|
||||||
.start = gic_spi(89),
|
.start = gic_spi(89),
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1072,14 +1072,17 @@ static struct resource sdhi1_resources[] = {
|
||||||
.flags = IORESOURCE_MEM,
|
.flags = IORESOURCE_MEM,
|
||||||
},
|
},
|
||||||
[1] = {
|
[1] = {
|
||||||
|
.name = SH_MOBILE_SDHI_IRQ_CARD_DETECT,
|
||||||
.start = evt2irq(0x0e80), /* SDHI1_SDHI1I0 */
|
.start = evt2irq(0x0e80), /* SDHI1_SDHI1I0 */
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
},
|
},
|
||||||
[2] = {
|
[2] = {
|
||||||
|
.name = SH_MOBILE_SDHI_IRQ_SDCARD,
|
||||||
.start = evt2irq(0x0ea0), /* SDHI1_SDHI1I1 */
|
.start = evt2irq(0x0ea0), /* SDHI1_SDHI1I1 */
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
},
|
},
|
||||||
[3] = {
|
[3] = {
|
||||||
|
.name = SH_MOBILE_SDHI_IRQ_SDIO,
|
||||||
.start = evt2irq(0x0ec0), /* SDHI1_SDHI1I2 */
|
.start = evt2irq(0x0ec0), /* SDHI1_SDHI1I2 */
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
},
|
},
|
||||||
|
@ -1123,14 +1126,17 @@ static struct resource sdhi2_resources[] = {
|
||||||
.flags = IORESOURCE_MEM,
|
.flags = IORESOURCE_MEM,
|
||||||
},
|
},
|
||||||
[1] = {
|
[1] = {
|
||||||
|
.name = SH_MOBILE_SDHI_IRQ_CARD_DETECT,
|
||||||
.start = evt2irq(0x1200), /* SDHI2_SDHI2I0 */
|
.start = evt2irq(0x1200), /* SDHI2_SDHI2I0 */
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
},
|
},
|
||||||
[2] = {
|
[2] = {
|
||||||
|
.name = SH_MOBILE_SDHI_IRQ_SDCARD,
|
||||||
.start = evt2irq(0x1220), /* SDHI2_SDHI2I1 */
|
.start = evt2irq(0x1220), /* SDHI2_SDHI2I1 */
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
},
|
},
|
||||||
[3] = {
|
[3] = {
|
||||||
|
.name = SH_MOBILE_SDHI_IRQ_SDIO,
|
||||||
.start = evt2irq(0x1240), /* SDHI2_SDHI2I2 */
|
.start = evt2irq(0x1240), /* SDHI2_SDHI2I2 */
|
||||||
.flags = IORESOURCE_IRQ,
|
.flags = IORESOURCE_IRQ,
|
||||||
},
|
},
|
||||||
|
|
|
@ -31,7 +31,24 @@
|
||||||
|
|
||||||
#define OMAP_MMC_MAX_SLOTS 2
|
#define OMAP_MMC_MAX_SLOTS 2
|
||||||
|
|
||||||
#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(1)
|
/*
|
||||||
|
* struct omap_mmc_dev_attr.flags possibilities
|
||||||
|
*
|
||||||
|
* OMAP_HSMMC_SUPPORTS_DUAL_VOLT: Some HSMMC controller instances can
|
||||||
|
* operate with either 1.8Vdc or 3.0Vdc card voltages; this flag
|
||||||
|
* should be set if this is the case. See for example Section 22.5.3
|
||||||
|
* "MMC/SD/SDIO1 Bus Voltage Selection" of the OMAP34xx Multimedia
|
||||||
|
* Device Silicon Revision 3.1.x Revision ZR (July 2011) (SWPU223R).
|
||||||
|
*
|
||||||
|
* OMAP_HSMMC_BROKEN_MULTIBLOCK_READ: Multiple-block read transfers
|
||||||
|
* don't work correctly on some MMC controller instances on some
|
||||||
|
* OMAP3 SoCs; this flag should be set if this is the case. See
|
||||||
|
* for example Advisory 2.1.1.128 "MMC: Multiple Block Read
|
||||||
|
* Operation Issue" in _OMAP3530/3525/3515/3503 Silicon Errata_
|
||||||
|
* Revision F (October 2010) (SPRZ278F).
|
||||||
|
*/
|
||||||
|
#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0)
|
||||||
|
#define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1)
|
||||||
|
|
||||||
struct omap_mmc_dev_attr {
|
struct omap_mmc_dev_attr {
|
||||||
u8 flags;
|
u8 flags;
|
||||||
|
|
|
@ -94,6 +94,11 @@ struct mmc_blk_data {
|
||||||
unsigned int read_only;
|
unsigned int read_only;
|
||||||
unsigned int part_type;
|
unsigned int part_type;
|
||||||
unsigned int name_idx;
|
unsigned int name_idx;
|
||||||
|
unsigned int reset_done;
|
||||||
|
#define MMC_BLK_READ BIT(0)
|
||||||
|
#define MMC_BLK_WRITE BIT(1)
|
||||||
|
#define MMC_BLK_DISCARD BIT(2)
|
||||||
|
#define MMC_BLK_SECDISCARD BIT(3)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only set in main mmc_blk_data associated
|
* Only set in main mmc_blk_data associated
|
||||||
|
@ -109,11 +114,11 @@ static DEFINE_MUTEX(open_lock);
|
||||||
enum mmc_blk_status {
|
enum mmc_blk_status {
|
||||||
MMC_BLK_SUCCESS = 0,
|
MMC_BLK_SUCCESS = 0,
|
||||||
MMC_BLK_PARTIAL,
|
MMC_BLK_PARTIAL,
|
||||||
MMC_BLK_RETRY,
|
|
||||||
MMC_BLK_RETRY_SINGLE,
|
|
||||||
MMC_BLK_DATA_ERR,
|
|
||||||
MMC_BLK_CMD_ERR,
|
MMC_BLK_CMD_ERR,
|
||||||
|
MMC_BLK_RETRY,
|
||||||
MMC_BLK_ABORT,
|
MMC_BLK_ABORT,
|
||||||
|
MMC_BLK_DATA_ERR,
|
||||||
|
MMC_BLK_ECC_ERR,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_param(perdev_minors, int, 0444);
|
module_param(perdev_minors, int, 0444);
|
||||||
|
@ -291,7 +296,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||||
struct mmc_card *card;
|
struct mmc_card *card;
|
||||||
struct mmc_command cmd = {0};
|
struct mmc_command cmd = {0};
|
||||||
struct mmc_data data = {0};
|
struct mmc_data data = {0};
|
||||||
struct mmc_request mrq = {0};
|
struct mmc_request mrq = {NULL};
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -442,19 +447,24 @@ static inline int mmc_blk_part_switch(struct mmc_card *card,
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct mmc_blk_data *main_md = mmc_get_drvdata(card);
|
struct mmc_blk_data *main_md = mmc_get_drvdata(card);
|
||||||
|
|
||||||
if (main_md->part_curr == md->part_type)
|
if (main_md->part_curr == md->part_type)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (mmc_card_mmc(card)) {
|
if (mmc_card_mmc(card)) {
|
||||||
card->ext_csd.part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
|
u8 part_config = card->ext_csd.part_config;
|
||||||
card->ext_csd.part_config |= md->part_type;
|
|
||||||
|
part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
|
||||||
|
part_config |= md->part_type;
|
||||||
|
|
||||||
ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
EXT_CSD_PART_CONFIG, card->ext_csd.part_config,
|
EXT_CSD_PART_CONFIG, part_config,
|
||||||
card->ext_csd.part_time);
|
card->ext_csd.part_time);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
card->ext_csd.part_config = part_config;
|
||||||
|
}
|
||||||
|
|
||||||
main_md->part_curr = md->part_type;
|
main_md->part_curr = md->part_type;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -466,7 +476,7 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
|
||||||
u32 result;
|
u32 result;
|
||||||
__be32 *blocks;
|
__be32 *blocks;
|
||||||
|
|
||||||
struct mmc_request mrq = {0};
|
struct mmc_request mrq = {NULL};
|
||||||
struct mmc_command cmd = {0};
|
struct mmc_command cmd = {0};
|
||||||
struct mmc_data data = {0};
|
struct mmc_data data = {0};
|
||||||
unsigned int timeout_us;
|
unsigned int timeout_us;
|
||||||
|
@ -616,7 +626,7 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error,
|
||||||
* Otherwise we don't understand what happened, so abort.
|
* Otherwise we don't understand what happened, so abort.
|
||||||
*/
|
*/
|
||||||
static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
|
static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
|
||||||
struct mmc_blk_request *brq)
|
struct mmc_blk_request *brq, int *ecc_err)
|
||||||
{
|
{
|
||||||
bool prev_cmd_status_valid = true;
|
bool prev_cmd_status_valid = true;
|
||||||
u32 status, stop_status = 0;
|
u32 status, stop_status = 0;
|
||||||
|
@ -641,6 +651,12 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
|
||||||
if (err)
|
if (err)
|
||||||
return ERR_ABORT;
|
return ERR_ABORT;
|
||||||
|
|
||||||
|
/* Flag ECC errors */
|
||||||
|
if ((status & R1_CARD_ECC_FAILED) ||
|
||||||
|
(brq->stop.resp[0] & R1_CARD_ECC_FAILED) ||
|
||||||
|
(brq->cmd.resp[0] & R1_CARD_ECC_FAILED))
|
||||||
|
*ecc_err = 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check the current card state. If it is in some data transfer
|
* Check the current card state. If it is in some data transfer
|
||||||
* mode, tell it to stop (and hopefully transition back to TRAN.)
|
* mode, tell it to stop (and hopefully transition back to TRAN.)
|
||||||
|
@ -658,6 +674,8 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
|
||||||
*/
|
*/
|
||||||
if (err)
|
if (err)
|
||||||
return ERR_ABORT;
|
return ERR_ABORT;
|
||||||
|
if (stop_status & R1_CARD_ECC_FAILED)
|
||||||
|
*ecc_err = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for set block count errors */
|
/* Check for set block count errors */
|
||||||
|
@ -670,6 +688,10 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
|
||||||
return mmc_blk_cmd_error(req, "r/w cmd", brq->cmd.error,
|
return mmc_blk_cmd_error(req, "r/w cmd", brq->cmd.error,
|
||||||
prev_cmd_status_valid, status);
|
prev_cmd_status_valid, status);
|
||||||
|
|
||||||
|
/* Data errors */
|
||||||
|
if (!brq->stop.error)
|
||||||
|
return ERR_CONTINUE;
|
||||||
|
|
||||||
/* Now for stop errors. These aren't fatal to the transfer. */
|
/* Now for stop errors. These aren't fatal to the transfer. */
|
||||||
pr_err("%s: error %d sending stop command, original cmd response %#x, card status %#x\n",
|
pr_err("%s: error %d sending stop command, original cmd response %#x, card status %#x\n",
|
||||||
req->rq_disk->disk_name, brq->stop.error,
|
req->rq_disk->disk_name, brq->stop.error,
|
||||||
|
@ -686,12 +708,45 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
|
||||||
return ERR_CONTINUE;
|
return ERR_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host,
|
||||||
|
int type)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (md->reset_done & type)
|
||||||
|
return -EEXIST;
|
||||||
|
|
||||||
|
md->reset_done |= type;
|
||||||
|
err = mmc_hw_reset(host);
|
||||||
|
/* Ensure we switch back to the correct partition */
|
||||||
|
if (err != -EOPNOTSUPP) {
|
||||||
|
struct mmc_blk_data *main_md = mmc_get_drvdata(host->card);
|
||||||
|
int part_err;
|
||||||
|
|
||||||
|
main_md->part_curr = main_md->part_type;
|
||||||
|
part_err = mmc_blk_part_switch(host->card, md);
|
||||||
|
if (part_err) {
|
||||||
|
/*
|
||||||
|
* We have failed to get back into the correct
|
||||||
|
* partition, so we need to abort the whole request.
|
||||||
|
*/
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mmc_blk_reset_success(struct mmc_blk_data *md, int type)
|
||||||
|
{
|
||||||
|
md->reset_done &= ~type;
|
||||||
|
}
|
||||||
|
|
||||||
static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
|
static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
|
||||||
{
|
{
|
||||||
struct mmc_blk_data *md = mq->data;
|
struct mmc_blk_data *md = mq->data;
|
||||||
struct mmc_card *card = md->queue.card;
|
struct mmc_card *card = md->queue.card;
|
||||||
unsigned int from, nr, arg;
|
unsigned int from, nr, arg;
|
||||||
int err = 0;
|
int err = 0, type = MMC_BLK_DISCARD;
|
||||||
|
|
||||||
if (!mmc_can_erase(card)) {
|
if (!mmc_can_erase(card)) {
|
||||||
err = -EOPNOTSUPP;
|
err = -EOPNOTSUPP;
|
||||||
|
@ -701,11 +756,13 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
|
||||||
from = blk_rq_pos(req);
|
from = blk_rq_pos(req);
|
||||||
nr = blk_rq_sectors(req);
|
nr = blk_rq_sectors(req);
|
||||||
|
|
||||||
if (mmc_can_trim(card))
|
if (mmc_can_discard(card))
|
||||||
|
arg = MMC_DISCARD_ARG;
|
||||||
|
else if (mmc_can_trim(card))
|
||||||
arg = MMC_TRIM_ARG;
|
arg = MMC_TRIM_ARG;
|
||||||
else
|
else
|
||||||
arg = MMC_ERASE_ARG;
|
arg = MMC_ERASE_ARG;
|
||||||
|
retry:
|
||||||
if (card->quirks & MMC_QUIRK_INAND_CMD38) {
|
if (card->quirks & MMC_QUIRK_INAND_CMD38) {
|
||||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
INAND_CMD38_ARG_EXT_CSD,
|
INAND_CMD38_ARG_EXT_CSD,
|
||||||
|
@ -718,6 +775,10 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
|
||||||
}
|
}
|
||||||
err = mmc_erase(card, from, nr, arg);
|
err = mmc_erase(card, from, nr, arg);
|
||||||
out:
|
out:
|
||||||
|
if (err == -EIO && !mmc_blk_reset(md, card->host, type))
|
||||||
|
goto retry;
|
||||||
|
if (!err)
|
||||||
|
mmc_blk_reset_success(md, type);
|
||||||
spin_lock_irq(&md->lock);
|
spin_lock_irq(&md->lock);
|
||||||
__blk_end_request(req, err, blk_rq_bytes(req));
|
__blk_end_request(req, err, blk_rq_bytes(req));
|
||||||
spin_unlock_irq(&md->lock);
|
spin_unlock_irq(&md->lock);
|
||||||
|
@ -731,13 +792,20 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
|
||||||
struct mmc_blk_data *md = mq->data;
|
struct mmc_blk_data *md = mq->data;
|
||||||
struct mmc_card *card = md->queue.card;
|
struct mmc_card *card = md->queue.card;
|
||||||
unsigned int from, nr, arg;
|
unsigned int from, nr, arg;
|
||||||
int err = 0;
|
int err = 0, type = MMC_BLK_SECDISCARD;
|
||||||
|
|
||||||
if (!mmc_can_secure_erase_trim(card)) {
|
if (!(mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))) {
|
||||||
err = -EOPNOTSUPP;
|
err = -EOPNOTSUPP;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The sanitize operation is supported at v4.5 only */
|
||||||
|
if (mmc_can_sanitize(card)) {
|
||||||
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
|
EXT_CSD_SANITIZE_START, 1, 0);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
from = blk_rq_pos(req);
|
from = blk_rq_pos(req);
|
||||||
nr = blk_rq_sectors(req);
|
nr = blk_rq_sectors(req);
|
||||||
|
|
||||||
|
@ -745,7 +813,7 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
|
||||||
arg = MMC_SECURE_TRIM1_ARG;
|
arg = MMC_SECURE_TRIM1_ARG;
|
||||||
else
|
else
|
||||||
arg = MMC_SECURE_ERASE_ARG;
|
arg = MMC_SECURE_ERASE_ARG;
|
||||||
|
retry:
|
||||||
if (card->quirks & MMC_QUIRK_INAND_CMD38) {
|
if (card->quirks & MMC_QUIRK_INAND_CMD38) {
|
||||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
INAND_CMD38_ARG_EXT_CSD,
|
INAND_CMD38_ARG_EXT_CSD,
|
||||||
|
@ -769,6 +837,10 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
|
||||||
err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG);
|
err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
|
if (err == -EIO && !mmc_blk_reset(md, card->host, type))
|
||||||
|
goto retry;
|
||||||
|
if (!err)
|
||||||
|
mmc_blk_reset_success(md, type);
|
||||||
spin_lock_irq(&md->lock);
|
spin_lock_irq(&md->lock);
|
||||||
__blk_end_request(req, err, blk_rq_bytes(req));
|
__blk_end_request(req, err, blk_rq_bytes(req));
|
||||||
spin_unlock_irq(&md->lock);
|
spin_unlock_irq(&md->lock);
|
||||||
|
@ -779,16 +851,18 @@ out:
|
||||||
static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
|
static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
|
||||||
{
|
{
|
||||||
struct mmc_blk_data *md = mq->data;
|
struct mmc_blk_data *md = mq->data;
|
||||||
|
struct mmc_card *card = md->queue.card;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ret = mmc_flush_cache(card);
|
||||||
|
if (ret)
|
||||||
|
ret = -EIO;
|
||||||
|
|
||||||
/*
|
|
||||||
* No-op, only service this because we need REQ_FUA for reliable
|
|
||||||
* writes.
|
|
||||||
*/
|
|
||||||
spin_lock_irq(&md->lock);
|
spin_lock_irq(&md->lock);
|
||||||
__blk_end_request_all(req, 0);
|
__blk_end_request_all(req, ret);
|
||||||
spin_unlock_irq(&md->lock);
|
spin_unlock_irq(&md->lock);
|
||||||
|
|
||||||
return 1;
|
return ret ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -825,11 +899,11 @@ static inline void mmc_apply_rel_rw(struct mmc_blk_request *brq,
|
||||||
static int mmc_blk_err_check(struct mmc_card *card,
|
static int mmc_blk_err_check(struct mmc_card *card,
|
||||||
struct mmc_async_req *areq)
|
struct mmc_async_req *areq)
|
||||||
{
|
{
|
||||||
enum mmc_blk_status ret = MMC_BLK_SUCCESS;
|
|
||||||
struct mmc_queue_req *mq_mrq = container_of(areq, struct mmc_queue_req,
|
struct mmc_queue_req *mq_mrq = container_of(areq, struct mmc_queue_req,
|
||||||
mmc_active);
|
mmc_active);
|
||||||
struct mmc_blk_request *brq = &mq_mrq->brq;
|
struct mmc_blk_request *brq = &mq_mrq->brq;
|
||||||
struct request *req = mq_mrq->req;
|
struct request *req = mq_mrq->req;
|
||||||
|
int ecc_err = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sbc.error indicates a problem with the set block count
|
* sbc.error indicates a problem with the set block count
|
||||||
|
@ -841,8 +915,9 @@ static int mmc_blk_err_check(struct mmc_card *card,
|
||||||
* stop.error indicates a problem with the stop command. Data
|
* stop.error indicates a problem with the stop command. Data
|
||||||
* may have been transferred, or may still be transferring.
|
* may have been transferred, or may still be transferring.
|
||||||
*/
|
*/
|
||||||
if (brq->sbc.error || brq->cmd.error || brq->stop.error) {
|
if (brq->sbc.error || brq->cmd.error || brq->stop.error ||
|
||||||
switch (mmc_blk_cmd_recovery(card, req, brq)) {
|
brq->data.error) {
|
||||||
|
switch (mmc_blk_cmd_recovery(card, req, brq, &ecc_err)) {
|
||||||
case ERR_RETRY:
|
case ERR_RETRY:
|
||||||
return MMC_BLK_RETRY;
|
return MMC_BLK_RETRY;
|
||||||
case ERR_ABORT:
|
case ERR_ABORT:
|
||||||
|
@ -873,7 +948,7 @@ static int mmc_blk_err_check(struct mmc_card *card,
|
||||||
do {
|
do {
|
||||||
int err = get_card_status(card, &status, 5);
|
int err = get_card_status(card, &status, 5);
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(KERN_ERR "%s: error %d requesting status\n",
|
pr_err("%s: error %d requesting status\n",
|
||||||
req->rq_disk->disk_name, err);
|
req->rq_disk->disk_name, err);
|
||||||
return MMC_BLK_CMD_ERR;
|
return MMC_BLK_CMD_ERR;
|
||||||
}
|
}
|
||||||
|
@ -894,23 +969,21 @@ static int mmc_blk_err_check(struct mmc_card *card,
|
||||||
brq->cmd.resp[0], brq->stop.resp[0]);
|
brq->cmd.resp[0], brq->stop.resp[0]);
|
||||||
|
|
||||||
if (rq_data_dir(req) == READ) {
|
if (rq_data_dir(req) == READ) {
|
||||||
if (brq->data.blocks > 1) {
|
if (ecc_err)
|
||||||
/* Redo read one sector at a time */
|
return MMC_BLK_ECC_ERR;
|
||||||
pr_warning("%s: retrying using single block read\n",
|
|
||||||
req->rq_disk->disk_name);
|
|
||||||
return MMC_BLK_RETRY_SINGLE;
|
|
||||||
}
|
|
||||||
return MMC_BLK_DATA_ERR;
|
return MMC_BLK_DATA_ERR;
|
||||||
} else {
|
} else {
|
||||||
return MMC_BLK_CMD_ERR;
|
return MMC_BLK_CMD_ERR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == MMC_BLK_SUCCESS &&
|
if (!brq->data.bytes_xfered)
|
||||||
blk_rq_bytes(req) != brq->data.bytes_xfered)
|
return MMC_BLK_RETRY;
|
||||||
ret = MMC_BLK_PARTIAL;
|
|
||||||
|
|
||||||
return ret;
|
if (blk_rq_bytes(req) != brq->data.bytes_xfered)
|
||||||
|
return MMC_BLK_PARTIAL;
|
||||||
|
|
||||||
|
return MMC_BLK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||||
|
@ -957,13 +1030,20 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||||
if (brq->data.blocks > card->host->max_blk_count)
|
if (brq->data.blocks > card->host->max_blk_count)
|
||||||
brq->data.blocks = card->host->max_blk_count;
|
brq->data.blocks = card->host->max_blk_count;
|
||||||
|
|
||||||
/*
|
if (brq->data.blocks > 1) {
|
||||||
* After a read error, we redo the request one sector at a time
|
/*
|
||||||
* in order to accurately determine which sectors can be read
|
* After a read error, we redo the request one sector
|
||||||
* successfully.
|
* at a time in order to accurately determine which
|
||||||
*/
|
* sectors can be read successfully.
|
||||||
if (disable_multi && brq->data.blocks > 1)
|
*/
|
||||||
brq->data.blocks = 1;
|
if (disable_multi)
|
||||||
|
brq->data.blocks = 1;
|
||||||
|
|
||||||
|
/* Some controllers can't do multiblock reads due to hw bugs */
|
||||||
|
if (card->host->caps2 & MMC_CAP2_NO_MULTI_READ &&
|
||||||
|
rq_data_dir(req) == READ)
|
||||||
|
brq->data.blocks = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (brq->data.blocks > 1 || do_rel_wr) {
|
if (brq->data.blocks > 1 || do_rel_wr) {
|
||||||
/* SPI multiblock writes terminate using a special
|
/* SPI multiblock writes terminate using a special
|
||||||
|
@ -1049,12 +1129,41 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||||
mmc_queue_bounce_pre(mqrq);
|
mmc_queue_bounce_pre(mqrq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mmc_blk_cmd_err(struct mmc_blk_data *md, struct mmc_card *card,
|
||||||
|
struct mmc_blk_request *brq, struct request *req,
|
||||||
|
int ret)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If this is an SD card and we're writing, we can first
|
||||||
|
* mark the known good sectors as ok.
|
||||||
|
*
|
||||||
|
* If the card is not SD, we can still ok written sectors
|
||||||
|
* as reported by the controller (which might be less than
|
||||||
|
* the real number of written sectors, but never more).
|
||||||
|
*/
|
||||||
|
if (mmc_card_sd(card)) {
|
||||||
|
u32 blocks;
|
||||||
|
|
||||||
|
blocks = mmc_sd_num_wr_blocks(card);
|
||||||
|
if (blocks != (u32)-1) {
|
||||||
|
spin_lock_irq(&md->lock);
|
||||||
|
ret = __blk_end_request(req, 0, blocks << 9);
|
||||||
|
spin_unlock_irq(&md->lock);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spin_lock_irq(&md->lock);
|
||||||
|
ret = __blk_end_request(req, 0, brq->data.bytes_xfered);
|
||||||
|
spin_unlock_irq(&md->lock);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
||||||
{
|
{
|
||||||
struct mmc_blk_data *md = mq->data;
|
struct mmc_blk_data *md = mq->data;
|
||||||
struct mmc_card *card = md->queue.card;
|
struct mmc_card *card = md->queue.card;
|
||||||
struct mmc_blk_request *brq = &mq->mqrq_cur->brq;
|
struct mmc_blk_request *brq = &mq->mqrq_cur->brq;
|
||||||
int ret = 1, disable_multi = 0, retry = 0;
|
int ret = 1, disable_multi = 0, retry = 0, type;
|
||||||
enum mmc_blk_status status;
|
enum mmc_blk_status status;
|
||||||
struct mmc_queue_req *mq_rq;
|
struct mmc_queue_req *mq_rq;
|
||||||
struct request *req;
|
struct request *req;
|
||||||
|
@ -1076,6 +1185,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
||||||
mq_rq = container_of(areq, struct mmc_queue_req, mmc_active);
|
mq_rq = container_of(areq, struct mmc_queue_req, mmc_active);
|
||||||
brq = &mq_rq->brq;
|
brq = &mq_rq->brq;
|
||||||
req = mq_rq->req;
|
req = mq_rq->req;
|
||||||
|
type = rq_data_dir(req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE;
|
||||||
mmc_queue_bounce_post(mq_rq);
|
mmc_queue_bounce_post(mq_rq);
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
|
@ -1084,18 +1194,18 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
||||||
/*
|
/*
|
||||||
* A block was successfully transferred.
|
* A block was successfully transferred.
|
||||||
*/
|
*/
|
||||||
|
mmc_blk_reset_success(md, type);
|
||||||
spin_lock_irq(&md->lock);
|
spin_lock_irq(&md->lock);
|
||||||
ret = __blk_end_request(req, 0,
|
ret = __blk_end_request(req, 0,
|
||||||
brq->data.bytes_xfered);
|
brq->data.bytes_xfered);
|
||||||
spin_unlock_irq(&md->lock);
|
spin_unlock_irq(&md->lock);
|
||||||
|
/*
|
||||||
|
* If the blk_end_request function returns non-zero even
|
||||||
|
* though all data has been transferred and no errors
|
||||||
|
* were returned by the host controller, it's a bug.
|
||||||
|
*/
|
||||||
if (status == MMC_BLK_SUCCESS && ret) {
|
if (status == MMC_BLK_SUCCESS && ret) {
|
||||||
/*
|
pr_err("%s BUG rq_tot %d d_xfer %d\n",
|
||||||
* The blk_end_request has returned non zero
|
|
||||||
* even though all data is transfered and no
|
|
||||||
* erros returned by host.
|
|
||||||
* If this happen it's a bug.
|
|
||||||
*/
|
|
||||||
printk(KERN_ERR "%s BUG rq_tot %d d_xfer %d\n",
|
|
||||||
__func__, blk_rq_bytes(req),
|
__func__, blk_rq_bytes(req),
|
||||||
brq->data.bytes_xfered);
|
brq->data.bytes_xfered);
|
||||||
rqc = NULL;
|
rqc = NULL;
|
||||||
|
@ -1103,16 +1213,36 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MMC_BLK_CMD_ERR:
|
case MMC_BLK_CMD_ERR:
|
||||||
goto cmd_err;
|
ret = mmc_blk_cmd_err(md, card, brq, req, ret);
|
||||||
case MMC_BLK_RETRY_SINGLE:
|
if (!mmc_blk_reset(md, card->host, type))
|
||||||
disable_multi = 1;
|
break;
|
||||||
break;
|
goto cmd_abort;
|
||||||
case MMC_BLK_RETRY:
|
case MMC_BLK_RETRY:
|
||||||
if (retry++ < 5)
|
if (retry++ < 5)
|
||||||
break;
|
break;
|
||||||
|
/* Fall through */
|
||||||
case MMC_BLK_ABORT:
|
case MMC_BLK_ABORT:
|
||||||
|
if (!mmc_blk_reset(md, card->host, type))
|
||||||
|
break;
|
||||||
goto cmd_abort;
|
goto cmd_abort;
|
||||||
case MMC_BLK_DATA_ERR:
|
case MMC_BLK_DATA_ERR: {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = mmc_blk_reset(md, card->host, type);
|
||||||
|
if (!err)
|
||||||
|
break;
|
||||||
|
if (err == -ENODEV)
|
||||||
|
goto cmd_abort;
|
||||||
|
/* Fall through */
|
||||||
|
}
|
||||||
|
case MMC_BLK_ECC_ERR:
|
||||||
|
if (brq->data.blocks > 1) {
|
||||||
|
/* Redo read one sector at a time */
|
||||||
|
pr_warning("%s: retrying using single block read\n",
|
||||||
|
req->rq_disk->disk_name);
|
||||||
|
disable_multi = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* After an error, we redo I/O one sector at a
|
* After an error, we redo I/O one sector at a
|
||||||
* time, so we only reach here after trying to
|
* time, so we only reach here after trying to
|
||||||
|
@ -1129,7 +1259,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
/*
|
/*
|
||||||
* In case of a none complete request
|
* In case of a incomplete request
|
||||||
* prepare it again and resend.
|
* prepare it again and resend.
|
||||||
*/
|
*/
|
||||||
mmc_blk_rw_rq_prep(mq_rq, card, disable_multi, mq);
|
mmc_blk_rw_rq_prep(mq_rq, card, disable_multi, mq);
|
||||||
|
@ -1139,30 +1269,6 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
cmd_err:
|
|
||||||
/*
|
|
||||||
* If this is an SD card and we're writing, we can first
|
|
||||||
* mark the known good sectors as ok.
|
|
||||||
*
|
|
||||||
* If the card is not SD, we can still ok written sectors
|
|
||||||
* as reported by the controller (which might be less than
|
|
||||||
* the real number of written sectors, but never more).
|
|
||||||
*/
|
|
||||||
if (mmc_card_sd(card)) {
|
|
||||||
u32 blocks;
|
|
||||||
|
|
||||||
blocks = mmc_sd_num_wr_blocks(card);
|
|
||||||
if (blocks != (u32)-1) {
|
|
||||||
spin_lock_irq(&md->lock);
|
|
||||||
ret = __blk_end_request(req, 0, blocks << 9);
|
|
||||||
spin_unlock_irq(&md->lock);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
spin_lock_irq(&md->lock);
|
|
||||||
ret = __blk_end_request(req, 0, brq->data.bytes_xfered);
|
|
||||||
spin_unlock_irq(&md->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_abort:
|
cmd_abort:
|
||||||
spin_lock_irq(&md->lock);
|
spin_lock_irq(&md->lock);
|
||||||
while (ret)
|
while (ret)
|
||||||
|
@ -1190,6 +1296,11 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
||||||
|
|
||||||
ret = mmc_blk_part_switch(card, md);
|
ret = mmc_blk_part_switch(card, md);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
if (req) {
|
||||||
|
spin_lock_irq(&md->lock);
|
||||||
|
__blk_end_request_all(req, -EIO);
|
||||||
|
spin_unlock_irq(&md->lock);
|
||||||
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -1374,32 +1485,35 @@ static int mmc_blk_alloc_part(struct mmc_card *card,
|
||||||
|
|
||||||
string_get_size((u64)get_capacity(part_md->disk) << 9, STRING_UNITS_2,
|
string_get_size((u64)get_capacity(part_md->disk) << 9, STRING_UNITS_2,
|
||||||
cap_str, sizeof(cap_str));
|
cap_str, sizeof(cap_str));
|
||||||
printk(KERN_INFO "%s: %s %s partition %u %s\n",
|
pr_info("%s: %s %s partition %u %s\n",
|
||||||
part_md->disk->disk_name, mmc_card_id(card),
|
part_md->disk->disk_name, mmc_card_id(card),
|
||||||
mmc_card_name(card), part_md->part_type, cap_str);
|
mmc_card_name(card), part_md->part_type, cap_str);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* MMC Physical partitions consist of two boot partitions and
|
||||||
|
* up to four general purpose partitions.
|
||||||
|
* For each partition enabled in EXT_CSD a block device will be allocatedi
|
||||||
|
* to provide access to the partition.
|
||||||
|
*/
|
||||||
|
|
||||||
static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
|
static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int idx, ret = 0;
|
||||||
|
|
||||||
if (!mmc_card_mmc(card))
|
if (!mmc_card_mmc(card))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (card->ext_csd.boot_size) {
|
for (idx = 0; idx < card->nr_parts; idx++) {
|
||||||
ret = mmc_blk_alloc_part(card, md, EXT_CSD_PART_CONFIG_ACC_BOOT0,
|
if (card->part[idx].size) {
|
||||||
card->ext_csd.boot_size >> 9,
|
ret = mmc_blk_alloc_part(card, md,
|
||||||
true,
|
card->part[idx].part_cfg,
|
||||||
"boot0");
|
card->part[idx].size >> 9,
|
||||||
if (ret)
|
card->part[idx].force_ro,
|
||||||
return ret;
|
card->part[idx].name);
|
||||||
ret = mmc_blk_alloc_part(card, md, EXT_CSD_PART_CONFIG_ACC_BOOT1,
|
if (ret)
|
||||||
card->ext_csd.boot_size >> 9,
|
return ret;
|
||||||
true,
|
}
|
||||||
"boot1");
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1415,7 +1529,7 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
|
||||||
mmc_release_host(card->host);
|
mmc_release_host(card->host);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(KERN_ERR "%s: unable to set block size to 512: %d\n",
|
pr_err("%s: unable to set block size to 512: %d\n",
|
||||||
md->disk->disk_name, err);
|
md->disk->disk_name, err);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -1517,7 +1631,7 @@ static int mmc_blk_probe(struct mmc_card *card)
|
||||||
|
|
||||||
string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,
|
string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,
|
||||||
cap_str, sizeof(cap_str));
|
cap_str, sizeof(cap_str));
|
||||||
printk(KERN_INFO "%s: %s %s %s %s\n",
|
pr_info("%s: %s %s %s %s\n",
|
||||||
md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
|
md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
|
||||||
cap_str, md->read_only ? "(ro)" : "");
|
cap_str, md->read_only ? "(ro)" : "");
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
|
||||||
#define RESULT_OK 0
|
#define RESULT_OK 0
|
||||||
#define RESULT_FAIL 1
|
#define RESULT_FAIL 1
|
||||||
|
@ -250,7 +251,7 @@ static int mmc_test_wait_busy(struct mmc_test_card *test)
|
||||||
if (!busy && mmc_test_busy(&cmd)) {
|
if (!busy && mmc_test_busy(&cmd)) {
|
||||||
busy = 1;
|
busy = 1;
|
||||||
if (test->card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
|
if (test->card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
|
||||||
printk(KERN_INFO "%s: Warning: Host did not "
|
pr_info("%s: Warning: Host did not "
|
||||||
"wait for busy state to end.\n",
|
"wait for busy state to end.\n",
|
||||||
mmc_hostname(test->card->host));
|
mmc_hostname(test->card->host));
|
||||||
}
|
}
|
||||||
|
@ -552,7 +553,7 @@ static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes,
|
||||||
rate = mmc_test_rate(bytes, &ts);
|
rate = mmc_test_rate(bytes, &ts);
|
||||||
iops = mmc_test_rate(100, &ts); /* I/O ops per sec x 100 */
|
iops = mmc_test_rate(100, &ts); /* I/O ops per sec x 100 */
|
||||||
|
|
||||||
printk(KERN_INFO "%s: Transfer of %u sectors (%u%s KiB) took %lu.%09lu "
|
pr_info("%s: Transfer of %u sectors (%u%s KiB) took %lu.%09lu "
|
||||||
"seconds (%u kB/s, %u KiB/s, %u.%02u IOPS)\n",
|
"seconds (%u kB/s, %u KiB/s, %u.%02u IOPS)\n",
|
||||||
mmc_hostname(test->card->host), sectors, sectors >> 1,
|
mmc_hostname(test->card->host), sectors, sectors >> 1,
|
||||||
(sectors & 1 ? ".5" : ""), (unsigned long)ts.tv_sec,
|
(sectors & 1 ? ".5" : ""), (unsigned long)ts.tv_sec,
|
||||||
|
@ -578,7 +579,7 @@ static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes,
|
||||||
rate = mmc_test_rate(tot, &ts);
|
rate = mmc_test_rate(tot, &ts);
|
||||||
iops = mmc_test_rate(count * 100, &ts); /* I/O ops per sec x 100 */
|
iops = mmc_test_rate(count * 100, &ts); /* I/O ops per sec x 100 */
|
||||||
|
|
||||||
printk(KERN_INFO "%s: Transfer of %u x %u sectors (%u x %u%s KiB) took "
|
pr_info("%s: Transfer of %u x %u sectors (%u x %u%s KiB) took "
|
||||||
"%lu.%09lu seconds (%u kB/s, %u KiB/s, "
|
"%lu.%09lu seconds (%u kB/s, %u KiB/s, "
|
||||||
"%u.%02u IOPS, sg_len %d)\n",
|
"%u.%02u IOPS, sg_len %d)\n",
|
||||||
mmc_hostname(test->card->host), count, sectors, count,
|
mmc_hostname(test->card->host), count, sectors, count,
|
||||||
|
@ -1408,7 +1409,7 @@ static int mmc_test_multi_read_high(struct mmc_test_card *test)
|
||||||
|
|
||||||
static int mmc_test_no_highmem(struct mmc_test_card *test)
|
static int mmc_test_no_highmem(struct mmc_test_card *test)
|
||||||
{
|
{
|
||||||
printk(KERN_INFO "%s: Highmem not configured - test skipped\n",
|
pr_info("%s: Highmem not configured - test skipped\n",
|
||||||
mmc_hostname(test->card->host));
|
mmc_hostname(test->card->host));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1435,7 +1436,7 @@ static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz,
|
||||||
t->max_seg_sz, &t->sg_len, min_sg_len);
|
t->max_seg_sz, &t->sg_len, min_sg_len);
|
||||||
}
|
}
|
||||||
if (err)
|
if (err)
|
||||||
printk(KERN_INFO "%s: Failed to map sg list\n",
|
pr_info("%s: Failed to map sg list\n",
|
||||||
mmc_hostname(test->card->host));
|
mmc_hostname(test->card->host));
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -2135,7 +2136,7 @@ static int mmc_test_rw_multiple(struct mmc_test_card *test,
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
err:
|
err:
|
||||||
printk(KERN_INFO "[%s] error\n", __func__);
|
pr_info("[%s] error\n", __func__);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2149,7 +2150,7 @@ static int mmc_test_rw_multiple_size(struct mmc_test_card *test,
|
||||||
|
|
||||||
if (rw->do_nonblock_req &&
|
if (rw->do_nonblock_req &&
|
||||||
((!pre_req && post_req) || (pre_req && !post_req))) {
|
((!pre_req && post_req) || (pre_req && !post_req))) {
|
||||||
printk(KERN_INFO "error: only one of pre/post is defined\n");
|
pr_info("error: only one of pre/post is defined\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2328,6 +2329,31 @@ static int mmc_test_profile_sglen_r_nonblock_perf(struct mmc_test_card *test)
|
||||||
return mmc_test_rw_multiple_sg_len(test, &test_data);
|
return mmc_test_rw_multiple_sg_len(test, &test_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* eMMC hardware reset.
|
||||||
|
*/
|
||||||
|
static int mmc_test_hw_reset(struct mmc_test_card *test)
|
||||||
|
{
|
||||||
|
struct mmc_card *card = test->card;
|
||||||
|
struct mmc_host *host = card->host;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = mmc_hw_reset_check(host);
|
||||||
|
if (!err)
|
||||||
|
return RESULT_OK;
|
||||||
|
|
||||||
|
if (err == -ENOSYS)
|
||||||
|
return RESULT_FAIL;
|
||||||
|
|
||||||
|
if (err != -EOPNOTSUPP)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (!mmc_can_reset(card))
|
||||||
|
return RESULT_UNSUP_CARD;
|
||||||
|
|
||||||
|
return RESULT_UNSUP_HOST;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct mmc_test_case mmc_test_cases[] = {
|
static const struct mmc_test_case mmc_test_cases[] = {
|
||||||
{
|
{
|
||||||
.name = "Basic write (no data verification)",
|
.name = "Basic write (no data verification)",
|
||||||
|
@ -2650,6 +2676,11 @@ static const struct mmc_test_case mmc_test_cases[] = {
|
||||||
.run = mmc_test_profile_sglen_r_nonblock_perf,
|
.run = mmc_test_profile_sglen_r_nonblock_perf,
|
||||||
.cleanup = mmc_test_area_cleanup,
|
.cleanup = mmc_test_area_cleanup,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "eMMC hardware reset",
|
||||||
|
.run = mmc_test_hw_reset,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static DEFINE_MUTEX(mmc_test_lock);
|
static DEFINE_MUTEX(mmc_test_lock);
|
||||||
|
@ -2660,7 +2691,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
||||||
{
|
{
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
printk(KERN_INFO "%s: Starting tests of card %s...\n",
|
pr_info("%s: Starting tests of card %s...\n",
|
||||||
mmc_hostname(test->card->host), mmc_card_id(test->card));
|
mmc_hostname(test->card->host), mmc_card_id(test->card));
|
||||||
|
|
||||||
mmc_claim_host(test->card->host);
|
mmc_claim_host(test->card->host);
|
||||||
|
@ -2671,14 +2702,14 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
||||||
if (testcase && ((i + 1) != testcase))
|
if (testcase && ((i + 1) != testcase))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
printk(KERN_INFO "%s: Test case %d. %s...\n",
|
pr_info("%s: Test case %d. %s...\n",
|
||||||
mmc_hostname(test->card->host), i + 1,
|
mmc_hostname(test->card->host), i + 1,
|
||||||
mmc_test_cases[i].name);
|
mmc_test_cases[i].name);
|
||||||
|
|
||||||
if (mmc_test_cases[i].prepare) {
|
if (mmc_test_cases[i].prepare) {
|
||||||
ret = mmc_test_cases[i].prepare(test);
|
ret = mmc_test_cases[i].prepare(test);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
printk(KERN_INFO "%s: Result: Prepare "
|
pr_info("%s: Result: Prepare "
|
||||||
"stage failed! (%d)\n",
|
"stage failed! (%d)\n",
|
||||||
mmc_hostname(test->card->host),
|
mmc_hostname(test->card->host),
|
||||||
ret);
|
ret);
|
||||||
|
@ -2708,25 +2739,25 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
||||||
ret = mmc_test_cases[i].run(test);
|
ret = mmc_test_cases[i].run(test);
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case RESULT_OK:
|
case RESULT_OK:
|
||||||
printk(KERN_INFO "%s: Result: OK\n",
|
pr_info("%s: Result: OK\n",
|
||||||
mmc_hostname(test->card->host));
|
mmc_hostname(test->card->host));
|
||||||
break;
|
break;
|
||||||
case RESULT_FAIL:
|
case RESULT_FAIL:
|
||||||
printk(KERN_INFO "%s: Result: FAILED\n",
|
pr_info("%s: Result: FAILED\n",
|
||||||
mmc_hostname(test->card->host));
|
mmc_hostname(test->card->host));
|
||||||
break;
|
break;
|
||||||
case RESULT_UNSUP_HOST:
|
case RESULT_UNSUP_HOST:
|
||||||
printk(KERN_INFO "%s: Result: UNSUPPORTED "
|
pr_info("%s: Result: UNSUPPORTED "
|
||||||
"(by host)\n",
|
"(by host)\n",
|
||||||
mmc_hostname(test->card->host));
|
mmc_hostname(test->card->host));
|
||||||
break;
|
break;
|
||||||
case RESULT_UNSUP_CARD:
|
case RESULT_UNSUP_CARD:
|
||||||
printk(KERN_INFO "%s: Result: UNSUPPORTED "
|
pr_info("%s: Result: UNSUPPORTED "
|
||||||
"(by card)\n",
|
"(by card)\n",
|
||||||
mmc_hostname(test->card->host));
|
mmc_hostname(test->card->host));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printk(KERN_INFO "%s: Result: ERROR (%d)\n",
|
pr_info("%s: Result: ERROR (%d)\n",
|
||||||
mmc_hostname(test->card->host), ret);
|
mmc_hostname(test->card->host), ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2737,7 +2768,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
||||||
if (mmc_test_cases[i].cleanup) {
|
if (mmc_test_cases[i].cleanup) {
|
||||||
ret = mmc_test_cases[i].cleanup(test);
|
ret = mmc_test_cases[i].cleanup(test);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
printk(KERN_INFO "%s: Warning: Cleanup "
|
pr_info("%s: Warning: Cleanup "
|
||||||
"stage failed! (%d)\n",
|
"stage failed! (%d)\n",
|
||||||
mmc_hostname(test->card->host),
|
mmc_hostname(test->card->host),
|
||||||
ret);
|
ret);
|
||||||
|
@ -2747,7 +2778,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
||||||
|
|
||||||
mmc_release_host(test->card->host);
|
mmc_release_host(test->card->host);
|
||||||
|
|
||||||
printk(KERN_INFO "%s: Tests completed.\n",
|
pr_info("%s: Tests completed.\n",
|
||||||
mmc_hostname(test->card->host));
|
mmc_hostname(test->card->host));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ static void mmc_request(struct request_queue *q)
|
||||||
wake_up_process(mq->thread);
|
wake_up_process(mq->thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct scatterlist *mmc_alloc_sg(int sg_len, int *err)
|
static struct scatterlist *mmc_alloc_sg(int sg_len, int *err)
|
||||||
{
|
{
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ static void mmc_queue_setup_discard(struct request_queue *q,
|
||||||
/* granularity must not be greater than max. discard */
|
/* granularity must not be greater than max. discard */
|
||||||
if (card->pref_erase > max_discard)
|
if (card->pref_erase > max_discard)
|
||||||
q->limits.discard_granularity = 0;
|
q->limits.discard_granularity = 0;
|
||||||
if (mmc_can_secure_erase_trim(card))
|
if (mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))
|
||||||
queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD, q);
|
queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD, q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,13 +197,13 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
|
||||||
if (bouncesz > 512) {
|
if (bouncesz > 512) {
|
||||||
mqrq_cur->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
|
mqrq_cur->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
|
||||||
if (!mqrq_cur->bounce_buf) {
|
if (!mqrq_cur->bounce_buf) {
|
||||||
printk(KERN_WARNING "%s: unable to "
|
pr_warning("%s: unable to "
|
||||||
"allocate bounce cur buffer\n",
|
"allocate bounce cur buffer\n",
|
||||||
mmc_card_name(card));
|
mmc_card_name(card));
|
||||||
}
|
}
|
||||||
mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
|
mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
|
||||||
if (!mqrq_prev->bounce_buf) {
|
if (!mqrq_prev->bounce_buf) {
|
||||||
printk(KERN_WARNING "%s: unable to "
|
pr_warning("%s: unable to "
|
||||||
"allocate bounce prev buffer\n",
|
"allocate bounce prev buffer\n",
|
||||||
mmc_card_name(card));
|
mmc_card_name(card));
|
||||||
kfree(mqrq_cur->bounce_buf);
|
kfree(mqrq_cur->bounce_buf);
|
||||||
|
|
|
@ -1082,7 +1082,7 @@ static int sdio_uart_probe(struct sdio_func *func,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (func->class == SDIO_CLASS_UART) {
|
if (func->class == SDIO_CLASS_UART) {
|
||||||
printk(KERN_WARNING "%s: need info on UART class basic setup\n",
|
pr_warning("%s: need info on UART class basic setup\n",
|
||||||
sdio_func_id(func));
|
sdio_func_id(func));
|
||||||
kfree(port);
|
kfree(port);
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
|
@ -1101,23 +1101,23 @@ static int sdio_uart_probe(struct sdio_func *func,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!tpl) {
|
if (!tpl) {
|
||||||
printk(KERN_WARNING
|
pr_warning(
|
||||||
"%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
|
"%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
|
||||||
sdio_func_id(func));
|
sdio_func_id(func));
|
||||||
kfree(port);
|
kfree(port);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
printk(KERN_DEBUG "%s: Register ID = 0x%02x, Exp ID = 0x%02x\n",
|
pr_debug("%s: Register ID = 0x%02x, Exp ID = 0x%02x\n",
|
||||||
sdio_func_id(func), tpl->data[2], tpl->data[3]);
|
sdio_func_id(func), tpl->data[2], tpl->data[3]);
|
||||||
port->regs_offset = (tpl->data[4] << 0) |
|
port->regs_offset = (tpl->data[4] << 0) |
|
||||||
(tpl->data[5] << 8) |
|
(tpl->data[5] << 8) |
|
||||||
(tpl->data[6] << 16);
|
(tpl->data[6] << 16);
|
||||||
printk(KERN_DEBUG "%s: regs offset = 0x%x\n",
|
pr_debug("%s: regs offset = 0x%x\n",
|
||||||
sdio_func_id(func), port->regs_offset);
|
sdio_func_id(func), port->regs_offset);
|
||||||
port->uartclk = tpl->data[7] * 115200;
|
port->uartclk = tpl->data[7] * 115200;
|
||||||
if (port->uartclk == 0)
|
if (port->uartclk == 0)
|
||||||
port->uartclk = 115200;
|
port->uartclk = 115200;
|
||||||
printk(KERN_DEBUG "%s: clk %d baudcode %u 4800-div %u\n",
|
pr_debug("%s: clk %d baudcode %u 4800-div %u\n",
|
||||||
sdio_func_id(func), port->uartclk,
|
sdio_func_id(func), port->uartclk,
|
||||||
tpl->data[7], tpl->data[8] | (tpl->data[9] << 8));
|
tpl->data[7], tpl->data[8] | (tpl->data[9] << 8));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -295,7 +295,7 @@ int mmc_add_card(struct mmc_card *card)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mmc_host_is_spi(card->host)) {
|
if (mmc_host_is_spi(card->host)) {
|
||||||
printk(KERN_INFO "%s: new %s%s%s card on SPI\n",
|
pr_info("%s: new %s%s%s card on SPI\n",
|
||||||
mmc_hostname(card->host),
|
mmc_hostname(card->host),
|
||||||
mmc_card_highspeed(card) ? "high speed " : "",
|
mmc_card_highspeed(card) ? "high speed " : "",
|
||||||
mmc_card_ddr_mode(card) ? "DDR " : "",
|
mmc_card_ddr_mode(card) ? "DDR " : "",
|
||||||
|
@ -334,10 +334,10 @@ void mmc_remove_card(struct mmc_card *card)
|
||||||
|
|
||||||
if (mmc_card_present(card)) {
|
if (mmc_card_present(card)) {
|
||||||
if (mmc_host_is_spi(card->host)) {
|
if (mmc_host_is_spi(card->host)) {
|
||||||
printk(KERN_INFO "%s: SPI card removed\n",
|
pr_info("%s: SPI card removed\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
} else {
|
} else {
|
||||||
printk(KERN_INFO "%s: card %04x removed\n",
|
pr_info("%s: card %04x removed\n",
|
||||||
mmc_hostname(card->host), card->rca);
|
mmc_hostname(card->host), card->rca);
|
||||||
}
|
}
|
||||||
device_del(&card->dev);
|
device_del(&card->dev);
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/suspend.h>
|
#include <linux/suspend.h>
|
||||||
|
#include <linux/fault-inject.h>
|
||||||
|
#include <linux/random.h>
|
||||||
|
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/mmc/card.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
|
@ -83,6 +85,43 @@ static void mmc_flush_scheduled_work(void)
|
||||||
flush_workqueue(workqueue);
|
flush_workqueue(workqueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_FAIL_MMC_REQUEST
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal function. Inject random data errors.
|
||||||
|
* If mmc_data is NULL no errors are injected.
|
||||||
|
*/
|
||||||
|
static void mmc_should_fail_request(struct mmc_host *host,
|
||||||
|
struct mmc_request *mrq)
|
||||||
|
{
|
||||||
|
struct mmc_command *cmd = mrq->cmd;
|
||||||
|
struct mmc_data *data = mrq->data;
|
||||||
|
static const int data_errors[] = {
|
||||||
|
-ETIMEDOUT,
|
||||||
|
-EILSEQ,
|
||||||
|
-EIO,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (cmd->error || data->error ||
|
||||||
|
!should_fail(&host->fail_mmc_request, data->blksz * data->blocks))
|
||||||
|
return;
|
||||||
|
|
||||||
|
data->error = data_errors[random32() % ARRAY_SIZE(data_errors)];
|
||||||
|
data->bytes_xfered = (random32() % (data->bytes_xfered >> 9)) << 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* CONFIG_FAIL_MMC_REQUEST */
|
||||||
|
|
||||||
|
static inline void mmc_should_fail_request(struct mmc_host *host,
|
||||||
|
struct mmc_request *mrq)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_FAIL_MMC_REQUEST */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mmc_request_done - finish processing an MMC request
|
* mmc_request_done - finish processing an MMC request
|
||||||
* @host: MMC host which completed request
|
* @host: MMC host which completed request
|
||||||
|
@ -102,13 +141,15 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err && cmd->retries) {
|
if (err && cmd->retries) {
|
||||||
pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
|
/*
|
||||||
mmc_hostname(host), cmd->opcode, err);
|
* Request starter must handle retries - see
|
||||||
|
* mmc_wait_for_req_done().
|
||||||
cmd->retries--;
|
*/
|
||||||
cmd->error = 0;
|
if (mrq->done)
|
||||||
host->ops->request(host, mrq);
|
mrq->done(mrq);
|
||||||
} else {
|
} else {
|
||||||
|
mmc_should_fail_request(host, mrq);
|
||||||
|
|
||||||
led_trigger_event(host->led, LED_OFF);
|
led_trigger_event(host->led, LED_OFF);
|
||||||
|
|
||||||
pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
|
pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
|
||||||
|
@ -212,7 +253,21 @@ static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
|
||||||
static void mmc_wait_for_req_done(struct mmc_host *host,
|
static void mmc_wait_for_req_done(struct mmc_host *host,
|
||||||
struct mmc_request *mrq)
|
struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
wait_for_completion(&mrq->completion);
|
struct mmc_command *cmd;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
wait_for_completion(&mrq->completion);
|
||||||
|
|
||||||
|
cmd = mrq->cmd;
|
||||||
|
if (!cmd->error || !cmd->retries)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
|
||||||
|
mmc_hostname(host), cmd->opcode, cmd->error);
|
||||||
|
cmd->retries--;
|
||||||
|
cmd->error = 0;
|
||||||
|
host->ops->request(host, mrq);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -279,8 +334,14 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
|
||||||
mmc_wait_for_req_done(host, host->areq->mrq);
|
mmc_wait_for_req_done(host, host->areq->mrq);
|
||||||
err = host->areq->err_check(host->card, host->areq);
|
err = host->areq->err_check(host->card, host->areq);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
/* post process the completed failed request */
|
||||||
mmc_post_req(host, host->areq->mrq, 0);
|
mmc_post_req(host, host->areq->mrq, 0);
|
||||||
if (areq)
|
if (areq)
|
||||||
|
/*
|
||||||
|
* Cancel the new prepared request, because
|
||||||
|
* it can't run until the failed
|
||||||
|
* request has been properly handled.
|
||||||
|
*/
|
||||||
mmc_post_req(host, areq->mrq, -EINVAL);
|
mmc_post_req(host, areq->mrq, -EINVAL);
|
||||||
|
|
||||||
host->areq = NULL;
|
host->areq = NULL;
|
||||||
|
@ -318,6 +379,63 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(mmc_wait_for_req);
|
EXPORT_SYMBOL(mmc_wait_for_req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mmc_interrupt_hpi - Issue for High priority Interrupt
|
||||||
|
* @card: the MMC card associated with the HPI transfer
|
||||||
|
*
|
||||||
|
* Issued High Priority Interrupt, and check for card status
|
||||||
|
* util out-of prg-state.
|
||||||
|
*/
|
||||||
|
int mmc_interrupt_hpi(struct mmc_card *card)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
u32 status;
|
||||||
|
|
||||||
|
BUG_ON(!card);
|
||||||
|
|
||||||
|
if (!card->ext_csd.hpi_en) {
|
||||||
|
pr_info("%s: HPI enable bit unset\n", mmc_hostname(card->host));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mmc_claim_host(card->host);
|
||||||
|
err = mmc_send_status(card, &status);
|
||||||
|
if (err) {
|
||||||
|
pr_err("%s: Get card status fail\n", mmc_hostname(card->host));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the card status is in PRG-state, we can send the HPI command.
|
||||||
|
*/
|
||||||
|
if (R1_CURRENT_STATE(status) == R1_STATE_PRG) {
|
||||||
|
do {
|
||||||
|
/*
|
||||||
|
* We don't know when the HPI command will finish
|
||||||
|
* processing, so we need to resend HPI until out
|
||||||
|
* of prg-state, and keep checking the card status
|
||||||
|
* with SEND_STATUS. If a timeout error occurs when
|
||||||
|
* sending the HPI command, we are already out of
|
||||||
|
* prg-state.
|
||||||
|
*/
|
||||||
|
err = mmc_send_hpi_cmd(card, &status);
|
||||||
|
if (err)
|
||||||
|
pr_debug("%s: abort HPI (%d error)\n",
|
||||||
|
mmc_hostname(card->host), err);
|
||||||
|
|
||||||
|
err = mmc_send_status(card, &status);
|
||||||
|
if (err)
|
||||||
|
break;
|
||||||
|
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
|
||||||
|
} else
|
||||||
|
pr_debug("%s: Left prg-state\n", mmc_hostname(card->host));
|
||||||
|
|
||||||
|
out:
|
||||||
|
mmc_release_host(card->host);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(mmc_interrupt_hpi);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mmc_wait_for_cmd - start a command and wait for completion
|
* mmc_wait_for_cmd - start a command and wait for completion
|
||||||
* @host: MMC host to start command
|
* @host: MMC host to start command
|
||||||
|
@ -330,7 +448,7 @@ EXPORT_SYMBOL(mmc_wait_for_req);
|
||||||
*/
|
*/
|
||||||
int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
|
int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
|
||||||
{
|
{
|
||||||
struct mmc_request mrq = {0};
|
struct mmc_request mrq = {NULL};
|
||||||
|
|
||||||
WARN_ON(!host->claimed);
|
WARN_ON(!host->claimed);
|
||||||
|
|
||||||
|
@ -1119,13 +1237,11 @@ static void mmc_power_up(struct mmc_host *host)
|
||||||
bit = fls(host->ocr_avail) - 1;
|
bit = fls(host->ocr_avail) - 1;
|
||||||
|
|
||||||
host->ios.vdd = bit;
|
host->ios.vdd = bit;
|
||||||
if (mmc_host_is_spi(host)) {
|
if (mmc_host_is_spi(host))
|
||||||
host->ios.chip_select = MMC_CS_HIGH;
|
host->ios.chip_select = MMC_CS_HIGH;
|
||||||
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
|
else
|
||||||
} else {
|
|
||||||
host->ios.chip_select = MMC_CS_DONTCARE;
|
host->ios.chip_select = MMC_CS_DONTCARE;
|
||||||
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
|
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
|
||||||
}
|
|
||||||
host->ios.power_mode = MMC_POWER_UP;
|
host->ios.power_mode = MMC_POWER_UP;
|
||||||
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
||||||
host->ios.timing = MMC_TIMING_LEGACY;
|
host->ios.timing = MMC_TIMING_LEGACY;
|
||||||
|
@ -1151,13 +1267,45 @@ static void mmc_power_up(struct mmc_host *host)
|
||||||
mmc_host_clk_release(host);
|
mmc_host_clk_release(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mmc_power_off(struct mmc_host *host)
|
void mmc_power_off(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
|
struct mmc_card *card;
|
||||||
|
unsigned int notify_type;
|
||||||
|
unsigned int timeout;
|
||||||
|
int err;
|
||||||
|
|
||||||
mmc_host_clk_hold(host);
|
mmc_host_clk_hold(host);
|
||||||
|
|
||||||
|
card = host->card;
|
||||||
host->ios.clock = 0;
|
host->ios.clock = 0;
|
||||||
host->ios.vdd = 0;
|
host->ios.vdd = 0;
|
||||||
|
|
||||||
|
if (card && mmc_card_mmc(card) &&
|
||||||
|
(card->poweroff_notify_state == MMC_POWERED_ON)) {
|
||||||
|
|
||||||
|
if (host->power_notify_type == MMC_HOST_PW_NOTIFY_SHORT) {
|
||||||
|
notify_type = EXT_CSD_POWER_OFF_SHORT;
|
||||||
|
timeout = card->ext_csd.generic_cmd6_time;
|
||||||
|
card->poweroff_notify_state = MMC_POWEROFF_SHORT;
|
||||||
|
} else {
|
||||||
|
notify_type = EXT_CSD_POWER_OFF_LONG;
|
||||||
|
timeout = card->ext_csd.power_off_longtime;
|
||||||
|
card->poweroff_notify_state = MMC_POWEROFF_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
|
EXT_CSD_POWER_OFF_NOTIFICATION,
|
||||||
|
notify_type, timeout);
|
||||||
|
|
||||||
|
if (err && err != -EBADMSG)
|
||||||
|
pr_err("Device failed to respond within %d poweroff "
|
||||||
|
"time. Forcefully powering down the device\n",
|
||||||
|
timeout);
|
||||||
|
|
||||||
|
/* Set the card state to no notification after the poweroff */
|
||||||
|
card->poweroff_notify_state = MMC_NO_POWER_NOTIFICATION;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset ocr mask to be the highest possible voltage supported for
|
* Reset ocr mask to be the highest possible voltage supported for
|
||||||
* this mmc host. This value will be used at next power up.
|
* this mmc host. This value will be used at next power up.
|
||||||
|
@ -1173,6 +1321,13 @@ static void mmc_power_off(struct mmc_host *host)
|
||||||
host->ios.timing = MMC_TIMING_LEGACY;
|
host->ios.timing = MMC_TIMING_LEGACY;
|
||||||
mmc_set_ios(host);
|
mmc_set_ios(host);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some configurations, such as the 802.11 SDIO card in the OLPC
|
||||||
|
* XO-1.5, require a short delay after poweroff before the card
|
||||||
|
* can be successfully turned on again.
|
||||||
|
*/
|
||||||
|
mmc_delay(1);
|
||||||
|
|
||||||
mmc_host_clk_release(host);
|
mmc_host_clk_release(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1241,8 +1396,7 @@ void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove the current bus handler from a host. Assumes that there are
|
* Remove the current bus handler from a host.
|
||||||
* no interesting cards left, so the bus is powered down.
|
|
||||||
*/
|
*/
|
||||||
void mmc_detach_bus(struct mmc_host *host)
|
void mmc_detach_bus(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
|
@ -1259,8 +1413,6 @@ void mmc_detach_bus(struct mmc_host *host)
|
||||||
|
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
mmc_power_off(host);
|
|
||||||
|
|
||||||
mmc_bus_put(host);
|
mmc_bus_put(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1478,9 +1630,9 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
||||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||||
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(KERN_ERR "mmc_erase: group start error %d, "
|
pr_err("mmc_erase: group start error %d, "
|
||||||
"status %#x\n", err, cmd.resp[0]);
|
"status %#x\n", err, cmd.resp[0]);
|
||||||
err = -EINVAL;
|
err = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1493,9 +1645,9 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
||||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||||
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(KERN_ERR "mmc_erase: group end error %d, status %#x\n",
|
pr_err("mmc_erase: group end error %d, status %#x\n",
|
||||||
err, cmd.resp[0]);
|
err, cmd.resp[0]);
|
||||||
err = -EINVAL;
|
err = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1506,7 +1658,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
||||||
cmd.cmd_timeout_ms = mmc_erase_timeout(card, arg, qty);
|
cmd.cmd_timeout_ms = mmc_erase_timeout(card, arg, qty);
|
||||||
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(KERN_ERR "mmc_erase: erase error %d, status %#x\n",
|
pr_err("mmc_erase: erase error %d, status %#x\n",
|
||||||
err, cmd.resp[0]);
|
err, cmd.resp[0]);
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1523,7 +1675,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
||||||
/* Do not retry else we can't see errors */
|
/* Do not retry else we can't see errors */
|
||||||
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
||||||
if (err || (cmd.resp[0] & 0xFDF92000)) {
|
if (err || (cmd.resp[0] & 0xFDF92000)) {
|
||||||
printk(KERN_ERR "error %d requesting status %#x\n",
|
pr_err("error %d requesting status %#x\n",
|
||||||
err, cmd.resp[0]);
|
err, cmd.resp[0]);
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1614,10 +1766,32 @@ int mmc_can_trim(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN)
|
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN)
|
||||||
return 1;
|
return 1;
|
||||||
|
if (mmc_can_discard(card))
|
||||||
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(mmc_can_trim);
|
EXPORT_SYMBOL(mmc_can_trim);
|
||||||
|
|
||||||
|
int mmc_can_discard(struct mmc_card *card)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* As there's no way to detect the discard support bit at v4.5
|
||||||
|
* use the s/w feature support filed.
|
||||||
|
*/
|
||||||
|
if (card->ext_csd.feature_support & MMC_DISCARD_FEATURE)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(mmc_can_discard);
|
||||||
|
|
||||||
|
int mmc_can_sanitize(struct mmc_card *card)
|
||||||
|
{
|
||||||
|
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_SANITIZE)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(mmc_can_sanitize);
|
||||||
|
|
||||||
int mmc_can_secure_erase_trim(struct mmc_card *card)
|
int mmc_can_secure_erase_trim(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN)
|
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN)
|
||||||
|
@ -1727,6 +1901,94 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(mmc_set_blocklen);
|
EXPORT_SYMBOL(mmc_set_blocklen);
|
||||||
|
|
||||||
|
static void mmc_hw_reset_for_init(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
|
||||||
|
return;
|
||||||
|
mmc_host_clk_hold(host);
|
||||||
|
host->ops->hw_reset(host);
|
||||||
|
mmc_host_clk_release(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mmc_can_reset(struct mmc_card *card)
|
||||||
|
{
|
||||||
|
u8 rst_n_function;
|
||||||
|
|
||||||
|
if (!mmc_card_mmc(card))
|
||||||
|
return 0;
|
||||||
|
rst_n_function = card->ext_csd.rst_n_function;
|
||||||
|
if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(mmc_can_reset);
|
||||||
|
|
||||||
|
static int mmc_do_hw_reset(struct mmc_host *host, int check)
|
||||||
|
{
|
||||||
|
struct mmc_card *card = host->card;
|
||||||
|
|
||||||
|
if (!host->bus_ops->power_restore)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (!card)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!mmc_can_reset(card))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
mmc_host_clk_hold(host);
|
||||||
|
mmc_set_clock(host, host->f_init);
|
||||||
|
|
||||||
|
host->ops->hw_reset(host);
|
||||||
|
|
||||||
|
/* If the reset has happened, then a status command will fail */
|
||||||
|
if (check) {
|
||||||
|
struct mmc_command cmd = {0};
|
||||||
|
int err;
|
||||||
|
|
||||||
|
cmd.opcode = MMC_SEND_STATUS;
|
||||||
|
if (!mmc_host_is_spi(card->host))
|
||||||
|
cmd.arg = card->rca << 16;
|
||||||
|
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||||
|
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
||||||
|
if (!err) {
|
||||||
|
mmc_host_clk_release(host);
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_DDR);
|
||||||
|
if (mmc_host_is_spi(host)) {
|
||||||
|
host->ios.chip_select = MMC_CS_HIGH;
|
||||||
|
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
|
||||||
|
} else {
|
||||||
|
host->ios.chip_select = MMC_CS_DONTCARE;
|
||||||
|
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
|
||||||
|
}
|
||||||
|
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
||||||
|
host->ios.timing = MMC_TIMING_LEGACY;
|
||||||
|
mmc_set_ios(host);
|
||||||
|
|
||||||
|
mmc_host_clk_release(host);
|
||||||
|
|
||||||
|
return host->bus_ops->power_restore(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mmc_hw_reset(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
return mmc_do_hw_reset(host, 0);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(mmc_hw_reset);
|
||||||
|
|
||||||
|
int mmc_hw_reset_check(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
return mmc_do_hw_reset(host, 1);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(mmc_hw_reset_check);
|
||||||
|
|
||||||
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
|
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
|
||||||
{
|
{
|
||||||
host->f_init = freq;
|
host->f_init = freq;
|
||||||
|
@ -1737,6 +1999,12 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
|
||||||
#endif
|
#endif
|
||||||
mmc_power_up(host);
|
mmc_power_up(host);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some eMMCs (with VCCQ always on) may not be reset after power up, so
|
||||||
|
* do a hardware reset if possible.
|
||||||
|
*/
|
||||||
|
mmc_hw_reset_for_init(host);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sdio_reset sends CMD52 to reset card. Since we do not know
|
* sdio_reset sends CMD52 to reset card. Since we do not know
|
||||||
* if the card is being re-initialized, just send it. CMD52
|
* if the card is being re-initialized, just send it. CMD52
|
||||||
|
@ -1845,6 +2113,7 @@ void mmc_stop_host(struct mmc_host *host)
|
||||||
|
|
||||||
mmc_claim_host(host);
|
mmc_claim_host(host);
|
||||||
mmc_detach_bus(host);
|
mmc_detach_bus(host);
|
||||||
|
mmc_power_off(host);
|
||||||
mmc_release_host(host);
|
mmc_release_host(host);
|
||||||
mmc_bus_put(host);
|
mmc_bus_put(host);
|
||||||
return;
|
return;
|
||||||
|
@ -1946,6 +2215,65 @@ int mmc_card_can_sleep(struct mmc_host *host)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(mmc_card_can_sleep);
|
EXPORT_SYMBOL(mmc_card_can_sleep);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flush the cache to the non-volatile storage.
|
||||||
|
*/
|
||||||
|
int mmc_flush_cache(struct mmc_card *card)
|
||||||
|
{
|
||||||
|
struct mmc_host *host = card->host;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (!(host->caps2 & MMC_CAP2_CACHE_CTRL))
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (mmc_card_mmc(card) &&
|
||||||
|
(card->ext_csd.cache_size > 0) &&
|
||||||
|
(card->ext_csd.cache_ctrl & 1)) {
|
||||||
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
|
EXT_CSD_FLUSH_CACHE, 1, 0);
|
||||||
|
if (err)
|
||||||
|
pr_err("%s: cache flush error %d\n",
|
||||||
|
mmc_hostname(card->host), err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(mmc_flush_cache);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Turn the cache ON/OFF.
|
||||||
|
* Turning the cache OFF shall trigger flushing of the data
|
||||||
|
* to the non-volatile storage.
|
||||||
|
*/
|
||||||
|
int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
|
||||||
|
{
|
||||||
|
struct mmc_card *card = host->card;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (!(host->caps2 & MMC_CAP2_CACHE_CTRL) ||
|
||||||
|
mmc_card_is_removable(host))
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (card && mmc_card_mmc(card) &&
|
||||||
|
(card->ext_csd.cache_size > 0)) {
|
||||||
|
enable = !!enable;
|
||||||
|
|
||||||
|
if (card->ext_csd.cache_ctrl ^ enable)
|
||||||
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
|
EXT_CSD_CACHE_CTRL, enable, 0);
|
||||||
|
if (err)
|
||||||
|
pr_err("%s: cache %s error %d\n",
|
||||||
|
mmc_hostname(card->host),
|
||||||
|
enable ? "on" : "off",
|
||||||
|
err);
|
||||||
|
else
|
||||||
|
card->ext_csd.cache_ctrl = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(mmc_cache_ctrl);
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1960,23 +2288,39 @@ int mmc_suspend_host(struct mmc_host *host)
|
||||||
cancel_delayed_work(&host->disable);
|
cancel_delayed_work(&host->disable);
|
||||||
cancel_delayed_work(&host->detect);
|
cancel_delayed_work(&host->detect);
|
||||||
mmc_flush_scheduled_work();
|
mmc_flush_scheduled_work();
|
||||||
|
err = mmc_cache_ctrl(host, 0);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
mmc_bus_get(host);
|
mmc_bus_get(host);
|
||||||
if (host->bus_ops && !host->bus_dead) {
|
if (host->bus_ops && !host->bus_dead) {
|
||||||
if (host->bus_ops->suspend)
|
|
||||||
err = host->bus_ops->suspend(host);
|
/*
|
||||||
if (err == -ENOSYS || !host->bus_ops->resume) {
|
* A long response time is not acceptable for device drivers
|
||||||
/*
|
* when doing suspend. Prevent mmc_claim_host in the suspend
|
||||||
* We simply "remove" the card in this case.
|
* sequence, to potentially wait "forever" by trying to
|
||||||
* It will be redetected on resume.
|
* pre-claim the host.
|
||||||
*/
|
*/
|
||||||
if (host->bus_ops->remove)
|
if (mmc_try_claim_host(host)) {
|
||||||
host->bus_ops->remove(host);
|
if (host->bus_ops->suspend)
|
||||||
mmc_claim_host(host);
|
err = host->bus_ops->suspend(host);
|
||||||
mmc_detach_bus(host);
|
if (err == -ENOSYS || !host->bus_ops->resume) {
|
||||||
mmc_release_host(host);
|
/*
|
||||||
host->pm_flags = 0;
|
* We simply "remove" the card in this case.
|
||||||
err = 0;
|
* It will be redetected on resume.
|
||||||
|
*/
|
||||||
|
if (host->bus_ops->remove)
|
||||||
|
host->bus_ops->remove(host);
|
||||||
|
mmc_claim_host(host);
|
||||||
|
mmc_detach_bus(host);
|
||||||
|
mmc_power_off(host);
|
||||||
|
mmc_release_host(host);
|
||||||
|
host->pm_flags = 0;
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
mmc_do_release_host(host);
|
||||||
|
} else {
|
||||||
|
err = -EBUSY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mmc_bus_put(host);
|
mmc_bus_put(host);
|
||||||
|
@ -1984,6 +2328,7 @@ int mmc_suspend_host(struct mmc_host *host)
|
||||||
if (!err && !mmc_card_keep_power(host))
|
if (!err && !mmc_card_keep_power(host))
|
||||||
mmc_power_off(host);
|
mmc_power_off(host);
|
||||||
|
|
||||||
|
out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2018,7 +2363,7 @@ int mmc_resume_host(struct mmc_host *host)
|
||||||
BUG_ON(!host->bus_ops->resume);
|
BUG_ON(!host->bus_ops->resume);
|
||||||
err = host->bus_ops->resume(host);
|
err = host->bus_ops->resume(host);
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(KERN_WARNING "%s: error %d during resume "
|
pr_warning("%s: error %d during resume "
|
||||||
"(card was removed?)\n",
|
"(card was removed?)\n",
|
||||||
mmc_hostname(host), err);
|
mmc_hostname(host), err);
|
||||||
err = 0;
|
err = 0;
|
||||||
|
@ -2049,6 +2394,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
host->rescan_disable = 1;
|
host->rescan_disable = 1;
|
||||||
|
host->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
cancel_delayed_work_sync(&host->detect);
|
cancel_delayed_work_sync(&host->detect);
|
||||||
|
|
||||||
|
@ -2061,6 +2407,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
|
||||||
host->bus_ops->remove(host);
|
host->bus_ops->remove(host);
|
||||||
|
|
||||||
mmc_detach_bus(host);
|
mmc_detach_bus(host);
|
||||||
|
mmc_power_off(host);
|
||||||
mmc_release_host(host);
|
mmc_release_host(host);
|
||||||
host->pm_flags = 0;
|
host->pm_flags = 0;
|
||||||
break;
|
break;
|
||||||
|
@ -2071,6 +2418,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
host->rescan_disable = 0;
|
host->rescan_disable = 0;
|
||||||
|
host->power_notify_type = MMC_HOST_PW_NOTIFY_LONG;
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
mmc_detect_change(host, 0);
|
mmc_detect_change(host, 0);
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage,
|
||||||
bool cmd11);
|
bool cmd11);
|
||||||
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
|
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
|
||||||
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
|
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
|
||||||
|
void mmc_power_off(struct mmc_host *host);
|
||||||
|
|
||||||
static inline void mmc_delay(unsigned int ms)
|
static inline void mmc_delay(unsigned int ms)
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,11 +7,13 @@
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
#include <linux/moduleparam.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/stat.h>
|
#include <linux/stat.h>
|
||||||
|
#include <linux/fault-inject.h>
|
||||||
|
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/mmc/card.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
|
@ -19,6 +21,14 @@
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "mmc_ops.h"
|
#include "mmc_ops.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_FAIL_MMC_REQUEST
|
||||||
|
|
||||||
|
static DECLARE_FAULT_ATTR(fail_default_attr);
|
||||||
|
static char *fail_request;
|
||||||
|
module_param(fail_request, charp, 0);
|
||||||
|
|
||||||
|
#endif /* CONFIG_FAIL_MMC_REQUEST */
|
||||||
|
|
||||||
/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */
|
/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */
|
||||||
static int mmc_ios_show(struct seq_file *s, void *data)
|
static int mmc_ios_show(struct seq_file *s, void *data)
|
||||||
{
|
{
|
||||||
|
@ -113,6 +123,15 @@ static int mmc_ios_show(struct seq_file *s, void *data)
|
||||||
case MMC_TIMING_SD_HS:
|
case MMC_TIMING_SD_HS:
|
||||||
str = "sd high-speed";
|
str = "sd high-speed";
|
||||||
break;
|
break;
|
||||||
|
case MMC_TIMING_UHS_SDR50:
|
||||||
|
str = "sd uhs SDR50";
|
||||||
|
break;
|
||||||
|
case MMC_TIMING_UHS_SDR104:
|
||||||
|
str = "sd uhs SDR104";
|
||||||
|
break;
|
||||||
|
case MMC_TIMING_UHS_DDR50:
|
||||||
|
str = "sd uhs DDR50";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
str = "invalid";
|
str = "invalid";
|
||||||
break;
|
break;
|
||||||
|
@ -187,6 +206,15 @@ void mmc_add_host_debugfs(struct mmc_host *host)
|
||||||
if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
|
if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
|
||||||
root, &host->clk_delay))
|
root, &host->clk_delay))
|
||||||
goto err_node;
|
goto err_node;
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_FAIL_MMC_REQUEST
|
||||||
|
if (fail_request)
|
||||||
|
setup_fault_attr(&fail_default_attr, fail_request);
|
||||||
|
host->fail_mmc_request = fail_default_attr;
|
||||||
|
if (IS_ERR(fault_create_debugfs_attr("fail_mmc_request",
|
||||||
|
root,
|
||||||
|
&host->fail_mmc_request)))
|
||||||
|
goto err_node;
|
||||||
#endif
|
#endif
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -301,6 +301,17 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
||||||
host->max_blk_size = 512;
|
host->max_blk_size = 512;
|
||||||
host->max_blk_count = PAGE_CACHE_SIZE / 512;
|
host->max_blk_count = PAGE_CACHE_SIZE / 512;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable runtime power management by default. This flag was added due
|
||||||
|
* to runtime power management causing disruption for some users, but
|
||||||
|
* the power on/off code has been improved since then.
|
||||||
|
*
|
||||||
|
* We'll enable this flag by default as an experiment, and if no
|
||||||
|
* problems are reported, we will follow up later and remove the flag
|
||||||
|
* altogether.
|
||||||
|
*/
|
||||||
|
host->caps = MMC_CAP_POWER_OFF_CARD;
|
||||||
|
|
||||||
return host;
|
return host;
|
||||||
|
|
||||||
free:
|
free:
|
||||||
|
|
|
@ -101,7 +101,7 @@ static int mmc_decode_cid(struct mmc_card *card)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
printk(KERN_ERR "%s: card has unknown MMCA version %d\n",
|
pr_err("%s: card has unknown MMCA version %d\n",
|
||||||
mmc_hostname(card->host), card->csd.mmca_vsn);
|
mmc_hostname(card->host), card->csd.mmca_vsn);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ static int mmc_decode_csd(struct mmc_card *card)
|
||||||
*/
|
*/
|
||||||
csd->structure = UNSTUFF_BITS(resp, 126, 2);
|
csd->structure = UNSTUFF_BITS(resp, 126, 2);
|
||||||
if (csd->structure == 0) {
|
if (csd->structure == 0) {
|
||||||
printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
|
pr_err("%s: unrecognised CSD structure version %d\n",
|
||||||
mmc_hostname(card->host), csd->structure);
|
mmc_hostname(card->host), csd->structure);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,7 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
|
||||||
*/
|
*/
|
||||||
ext_csd = kmalloc(512, GFP_KERNEL);
|
ext_csd = kmalloc(512, GFP_KERNEL);
|
||||||
if (!ext_csd) {
|
if (!ext_csd) {
|
||||||
printk(KERN_ERR "%s: could not allocate a buffer to "
|
pr_err("%s: could not allocate a buffer to "
|
||||||
"receive the ext_csd.\n", mmc_hostname(card->host));
|
"receive the ext_csd.\n", mmc_hostname(card->host));
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
@ -217,12 +217,12 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
|
||||||
* stored in their CSD.
|
* stored in their CSD.
|
||||||
*/
|
*/
|
||||||
if (card->csd.capacity == (4096 * 512)) {
|
if (card->csd.capacity == (4096 * 512)) {
|
||||||
printk(KERN_ERR "%s: unable to read EXT_CSD "
|
pr_err("%s: unable to read EXT_CSD "
|
||||||
"on a possible high capacity card. "
|
"on a possible high capacity card. "
|
||||||
"Card will be ignored.\n",
|
"Card will be ignored.\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
} else {
|
} else {
|
||||||
printk(KERN_WARNING "%s: unable to read "
|
pr_warning("%s: unable to read "
|
||||||
"EXT_CSD, performance might "
|
"EXT_CSD, performance might "
|
||||||
"suffer.\n",
|
"suffer.\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
|
@ -239,7 +239,9 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
|
||||||
*/
|
*/
|
||||||
static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0, idx;
|
||||||
|
unsigned int part_size;
|
||||||
|
u8 hc_erase_grp_sz = 0, hc_wp_grp_sz = 0;
|
||||||
|
|
||||||
BUG_ON(!card);
|
BUG_ON(!card);
|
||||||
|
|
||||||
|
@ -250,7 +252,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||||
card->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE];
|
card->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE];
|
||||||
if (card->csd.structure == 3) {
|
if (card->csd.structure == 3) {
|
||||||
if (card->ext_csd.raw_ext_csd_structure > 2) {
|
if (card->ext_csd.raw_ext_csd_structure > 2) {
|
||||||
printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
|
pr_err("%s: unrecognised EXT_CSD structure "
|
||||||
"version %d\n", mmc_hostname(card->host),
|
"version %d\n", mmc_hostname(card->host),
|
||||||
card->ext_csd.raw_ext_csd_structure);
|
card->ext_csd.raw_ext_csd_structure);
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
|
@ -260,7 +262,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||||
|
|
||||||
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
|
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
|
||||||
if (card->ext_csd.rev > 6) {
|
if (card->ext_csd.rev > 6) {
|
||||||
printk(KERN_ERR "%s: unrecognised EXT_CSD revision %d\n",
|
pr_err("%s: unrecognised EXT_CSD revision %d\n",
|
||||||
mmc_hostname(card->host), card->ext_csd.rev);
|
mmc_hostname(card->host), card->ext_csd.rev);
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -306,7 +308,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* MMC v4 spec says this cannot happen */
|
/* MMC v4 spec says this cannot happen */
|
||||||
printk(KERN_WARNING "%s: card is mmc v4 but doesn't "
|
pr_warning("%s: card is mmc v4 but doesn't "
|
||||||
"support any high-speed modes.\n",
|
"support any high-speed modes.\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
}
|
}
|
||||||
|
@ -340,7 +342,14 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||||
* There are two boot regions of equal size, defined in
|
* There are two boot regions of equal size, defined in
|
||||||
* multiples of 128K.
|
* multiples of 128K.
|
||||||
*/
|
*/
|
||||||
card->ext_csd.boot_size = ext_csd[EXT_CSD_BOOT_MULT] << 17;
|
if (ext_csd[EXT_CSD_BOOT_MULT] && mmc_boot_partition_access(card->host)) {
|
||||||
|
for (idx = 0; idx < MMC_NUM_BOOT_PARTITION; idx++) {
|
||||||
|
part_size = ext_csd[EXT_CSD_BOOT_MULT] << 17;
|
||||||
|
mmc_part_add(card, part_size,
|
||||||
|
EXT_CSD_PART_CONFIG_ACC_BOOT0 + idx,
|
||||||
|
"boot%d", idx, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
card->ext_csd.raw_hc_erase_gap_size =
|
card->ext_csd.raw_hc_erase_gap_size =
|
||||||
|
@ -359,11 +368,12 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||||
* card has the Enhanced area enabled. If so, export enhanced
|
* card has the Enhanced area enabled. If so, export enhanced
|
||||||
* area offset and size to user by adding sysfs interface.
|
* area offset and size to user by adding sysfs interface.
|
||||||
*/
|
*/
|
||||||
|
card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT];
|
||||||
if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) &&
|
if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) &&
|
||||||
(ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) {
|
(ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) {
|
||||||
u8 hc_erase_grp_sz =
|
hc_erase_grp_sz =
|
||||||
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
|
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
|
||||||
u8 hc_wp_grp_sz =
|
hc_wp_grp_sz =
|
||||||
ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
|
ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
|
||||||
|
|
||||||
card->ext_csd.enhanced_area_en = 1;
|
card->ext_csd.enhanced_area_en = 1;
|
||||||
|
@ -392,6 +402,41 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||||
card->ext_csd.enhanced_area_offset = -EINVAL;
|
card->ext_csd.enhanced_area_offset = -EINVAL;
|
||||||
card->ext_csd.enhanced_area_size = -EINVAL;
|
card->ext_csd.enhanced_area_size = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* General purpose partition feature support --
|
||||||
|
* If ext_csd has the size of general purpose partitions,
|
||||||
|
* set size, part_cfg, partition name in mmc_part.
|
||||||
|
*/
|
||||||
|
if (ext_csd[EXT_CSD_PARTITION_SUPPORT] &
|
||||||
|
EXT_CSD_PART_SUPPORT_PART_EN) {
|
||||||
|
if (card->ext_csd.enhanced_area_en != 1) {
|
||||||
|
hc_erase_grp_sz =
|
||||||
|
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
|
||||||
|
hc_wp_grp_sz =
|
||||||
|
ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
|
||||||
|
|
||||||
|
card->ext_csd.enhanced_area_en = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (idx = 0; idx < MMC_NUM_GP_PARTITION; idx++) {
|
||||||
|
if (!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3] &&
|
||||||
|
!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] &&
|
||||||
|
!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2])
|
||||||
|
continue;
|
||||||
|
part_size =
|
||||||
|
(ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2]
|
||||||
|
<< 16) +
|
||||||
|
(ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1]
|
||||||
|
<< 8) +
|
||||||
|
ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3];
|
||||||
|
part_size *= (size_t)(hc_erase_grp_sz *
|
||||||
|
hc_wp_grp_sz);
|
||||||
|
mmc_part_add(card, part_size << 19,
|
||||||
|
EXT_CSD_PART_CONFIG_ACC_GP0 + idx,
|
||||||
|
"gp%d", idx, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
card->ext_csd.sec_trim_mult =
|
card->ext_csd.sec_trim_mult =
|
||||||
ext_csd[EXT_CSD_SEC_TRIM_MULT];
|
ext_csd[EXT_CSD_SEC_TRIM_MULT];
|
||||||
card->ext_csd.sec_erase_mult =
|
card->ext_csd.sec_erase_mult =
|
||||||
|
@ -402,14 +447,48 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||||
ext_csd[EXT_CSD_TRIM_MULT];
|
ext_csd[EXT_CSD_TRIM_MULT];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (card->ext_csd.rev >= 5)
|
if (card->ext_csd.rev >= 5) {
|
||||||
card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
|
/* check whether the eMMC card supports HPI */
|
||||||
|
if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
|
||||||
|
card->ext_csd.hpi = 1;
|
||||||
|
if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2)
|
||||||
|
card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION;
|
||||||
|
else
|
||||||
|
card->ext_csd.hpi_cmd = MMC_SEND_STATUS;
|
||||||
|
/*
|
||||||
|
* Indicate the maximum timeout to close
|
||||||
|
* a command interrupted by HPI
|
||||||
|
*/
|
||||||
|
card->ext_csd.out_of_int_time =
|
||||||
|
ext_csd[EXT_CSD_OUT_OF_INTERRUPT_TIME] * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
card->ext_csd.raw_erased_mem_count = ext_csd[EXT_CSD_ERASED_MEM_CONT];
|
||||||
if (ext_csd[EXT_CSD_ERASED_MEM_CONT])
|
if (ext_csd[EXT_CSD_ERASED_MEM_CONT])
|
||||||
card->erased_byte = 0xFF;
|
card->erased_byte = 0xFF;
|
||||||
else
|
else
|
||||||
card->erased_byte = 0x0;
|
card->erased_byte = 0x0;
|
||||||
|
|
||||||
|
/* eMMC v4.5 or later */
|
||||||
|
if (card->ext_csd.rev >= 6) {
|
||||||
|
card->ext_csd.feature_support |= MMC_DISCARD_FEATURE;
|
||||||
|
|
||||||
|
card->ext_csd.generic_cmd6_time = 10 *
|
||||||
|
ext_csd[EXT_CSD_GENERIC_CMD6_TIME];
|
||||||
|
card->ext_csd.power_off_longtime = 10 *
|
||||||
|
ext_csd[EXT_CSD_POWER_OFF_LONG_TIME];
|
||||||
|
|
||||||
|
card->ext_csd.cache_size =
|
||||||
|
ext_csd[EXT_CSD_CACHE_SIZE + 0] << 0 |
|
||||||
|
ext_csd[EXT_CSD_CACHE_SIZE + 1] << 8 |
|
||||||
|
ext_csd[EXT_CSD_CACHE_SIZE + 2] << 16 |
|
||||||
|
ext_csd[EXT_CSD_CACHE_SIZE + 3] << 24;
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -529,6 +608,86 @@ static struct device_type mmc_type = {
|
||||||
.groups = mmc_attr_groups,
|
.groups = mmc_attr_groups,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Select the PowerClass for the current bus width
|
||||||
|
* If power class is defined for 4/8 bit bus in the
|
||||||
|
* extended CSD register, select it by executing the
|
||||||
|
* mmc_switch command.
|
||||||
|
*/
|
||||||
|
static int mmc_select_powerclass(struct mmc_card *card,
|
||||||
|
unsigned int bus_width, u8 *ext_csd)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
unsigned int pwrclass_val;
|
||||||
|
unsigned int index = 0;
|
||||||
|
struct mmc_host *host;
|
||||||
|
|
||||||
|
BUG_ON(!card);
|
||||||
|
|
||||||
|
host = card->host;
|
||||||
|
BUG_ON(!host);
|
||||||
|
|
||||||
|
if (ext_csd == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Power class selection is supported for versions >= 4.0 */
|
||||||
|
if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Power class values are defined only for 4/8 bit bus */
|
||||||
|
if (bus_width == EXT_CSD_BUS_WIDTH_1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (1 << host->ios.vdd) {
|
||||||
|
case MMC_VDD_165_195:
|
||||||
|
if (host->ios.clock <= 26000000)
|
||||||
|
index = EXT_CSD_PWR_CL_26_195;
|
||||||
|
else if (host->ios.clock <= 52000000)
|
||||||
|
index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
|
||||||
|
EXT_CSD_PWR_CL_52_195 :
|
||||||
|
EXT_CSD_PWR_CL_DDR_52_195;
|
||||||
|
else if (host->ios.clock <= 200000000)
|
||||||
|
index = EXT_CSD_PWR_CL_200_195;
|
||||||
|
break;
|
||||||
|
case MMC_VDD_32_33:
|
||||||
|
case MMC_VDD_33_34:
|
||||||
|
case MMC_VDD_34_35:
|
||||||
|
case MMC_VDD_35_36:
|
||||||
|
if (host->ios.clock <= 26000000)
|
||||||
|
index = EXT_CSD_PWR_CL_26_360;
|
||||||
|
else if (host->ios.clock <= 52000000)
|
||||||
|
index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
|
||||||
|
EXT_CSD_PWR_CL_52_360 :
|
||||||
|
EXT_CSD_PWR_CL_DDR_52_360;
|
||||||
|
else if (host->ios.clock <= 200000000)
|
||||||
|
index = EXT_CSD_PWR_CL_200_360;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pr_warning("%s: Voltage range not supported "
|
||||||
|
"for power class.\n", mmc_hostname(host));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pwrclass_val = ext_csd[index];
|
||||||
|
|
||||||
|
if (bus_width & (EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_BUS_WIDTH_8))
|
||||||
|
pwrclass_val = (pwrclass_val & EXT_CSD_PWR_CL_8BIT_MASK) >>
|
||||||
|
EXT_CSD_PWR_CL_8BIT_SHIFT;
|
||||||
|
else
|
||||||
|
pwrclass_val = (pwrclass_val & EXT_CSD_PWR_CL_4BIT_MASK) >>
|
||||||
|
EXT_CSD_PWR_CL_4BIT_SHIFT;
|
||||||
|
|
||||||
|
/* If the power class is different from the default value */
|
||||||
|
if (pwrclass_val > 0) {
|
||||||
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
|
EXT_CSD_POWER_CLASS,
|
||||||
|
pwrclass_val,
|
||||||
|
card->ext_csd.generic_cmd6_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle the detection and initialisation of a card.
|
* Handle the detection and initialisation of a card.
|
||||||
*
|
*
|
||||||
|
@ -548,11 +707,16 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
BUG_ON(!host);
|
BUG_ON(!host);
|
||||||
WARN_ON(!host->claimed);
|
WARN_ON(!host->claimed);
|
||||||
|
|
||||||
|
/* Set correct bus mode for MMC before attempting init */
|
||||||
|
if (!mmc_host_is_spi(host))
|
||||||
|
mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since we're changing the OCR value, we seem to
|
* Since we're changing the OCR value, we seem to
|
||||||
* need to tell some cards to go back to the idle
|
* need to tell some cards to go back to the idle
|
||||||
* state. We wait 1ms to give cards time to
|
* state. We wait 1ms to give cards time to
|
||||||
* respond.
|
* respond.
|
||||||
|
* mmc_go_idle is needed for eMMC that are asleep
|
||||||
*/
|
*/
|
||||||
mmc_go_idle(host);
|
mmc_go_idle(host);
|
||||||
|
|
||||||
|
@ -668,7 +832,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
*/
|
*/
|
||||||
if (card->ext_csd.enhanced_area_en) {
|
if (card->ext_csd.enhanced_area_en) {
|
||||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
EXT_CSD_ERASE_GROUP_DEF, 1, 0);
|
EXT_CSD_ERASE_GROUP_DEF, 1,
|
||||||
|
card->ext_csd.generic_cmd6_time);
|
||||||
|
|
||||||
if (err && err != -EBADMSG)
|
if (err && err != -EBADMSG)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
@ -705,18 +870,36 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
goto free_card;
|
goto free_card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the host supports the power_off_notify capability then
|
||||||
|
* set the notification byte in the ext_csd register of device
|
||||||
|
*/
|
||||||
|
if ((host->caps2 & MMC_CAP2_POWEROFF_NOTIFY) &&
|
||||||
|
(card->poweroff_notify_state == MMC_NO_POWER_NOTIFICATION)) {
|
||||||
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
|
EXT_CSD_POWER_OFF_NOTIFICATION,
|
||||||
|
EXT_CSD_POWER_ON,
|
||||||
|
card->ext_csd.generic_cmd6_time);
|
||||||
|
if (err && err != -EBADMSG)
|
||||||
|
goto free_card;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!err)
|
||||||
|
card->poweroff_notify_state = MMC_POWERED_ON;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Activate high speed (if supported)
|
* Activate high speed (if supported)
|
||||||
*/
|
*/
|
||||||
if ((card->ext_csd.hs_max_dtr != 0) &&
|
if ((card->ext_csd.hs_max_dtr != 0) &&
|
||||||
(host->caps & MMC_CAP_MMC_HIGHSPEED)) {
|
(host->caps & MMC_CAP_MMC_HIGHSPEED)) {
|
||||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
EXT_CSD_HS_TIMING, 1, 0);
|
EXT_CSD_HS_TIMING, 1,
|
||||||
|
card->ext_csd.generic_cmd6_time);
|
||||||
if (err && err != -EBADMSG)
|
if (err && err != -EBADMSG)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(KERN_WARNING "%s: switch to highspeed failed\n",
|
pr_warning("%s: switch to highspeed failed\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
err = 0;
|
err = 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -725,6 +908,22 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable HPI feature (if supported)
|
||||||
|
*/
|
||||||
|
if (card->ext_csd.hpi) {
|
||||||
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
|
EXT_CSD_HPI_MGMT, 1, 0);
|
||||||
|
if (err && err != -EBADMSG)
|
||||||
|
goto free_card;
|
||||||
|
if (err) {
|
||||||
|
pr_warning("%s: Enabling HPI failed\n",
|
||||||
|
mmc_hostname(card->host));
|
||||||
|
err = 0;
|
||||||
|
} else
|
||||||
|
card->ext_csd.hpi_en = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compute bus speed.
|
* Compute bus speed.
|
||||||
*/
|
*/
|
||||||
|
@ -780,10 +979,18 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
bus_width = bus_widths[idx];
|
bus_width = bus_widths[idx];
|
||||||
if (bus_width == MMC_BUS_WIDTH_1)
|
if (bus_width == MMC_BUS_WIDTH_1)
|
||||||
ddr = 0; /* no DDR for 1-bit width */
|
ddr = 0; /* no DDR for 1-bit width */
|
||||||
|
err = mmc_select_powerclass(card, ext_csd_bits[idx][0],
|
||||||
|
ext_csd);
|
||||||
|
if (err)
|
||||||
|
pr_err("%s: power class selection to "
|
||||||
|
"bus width %d failed\n",
|
||||||
|
mmc_hostname(card->host),
|
||||||
|
1 << bus_width);
|
||||||
|
|
||||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
EXT_CSD_BUS_WIDTH,
|
EXT_CSD_BUS_WIDTH,
|
||||||
ext_csd_bits[idx][0],
|
ext_csd_bits[idx][0],
|
||||||
0);
|
card->ext_csd.generic_cmd6_time);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
mmc_set_bus_width(card->host, bus_width);
|
mmc_set_bus_width(card->host, bus_width);
|
||||||
|
|
||||||
|
@ -803,13 +1010,21 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!err && ddr) {
|
if (!err && ddr) {
|
||||||
|
err = mmc_select_powerclass(card, ext_csd_bits[idx][1],
|
||||||
|
ext_csd);
|
||||||
|
if (err)
|
||||||
|
pr_err("%s: power class selection to "
|
||||||
|
"bus width %d ddr %d failed\n",
|
||||||
|
mmc_hostname(card->host),
|
||||||
|
1 << bus_width, ddr);
|
||||||
|
|
||||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
EXT_CSD_BUS_WIDTH,
|
EXT_CSD_BUS_WIDTH,
|
||||||
ext_csd_bits[idx][1],
|
ext_csd_bits[idx][1],
|
||||||
0);
|
card->ext_csd.generic_cmd6_time);
|
||||||
}
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
|
pr_warning("%s: switch to bus width %d ddr %d "
|
||||||
"failed\n", mmc_hostname(card->host),
|
"failed\n", mmc_hostname(card->host),
|
||||||
1 << bus_width, ddr);
|
1 << bus_width, ddr);
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
@ -840,6 +1055,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If cache size is higher than 0, this indicates
|
||||||
|
* the existence of cache and it can be turned on.
|
||||||
|
*/
|
||||||
|
if ((host->caps2 & MMC_CAP2_CACHE_CTRL) &&
|
||||||
|
card->ext_csd.cache_size > 0) {
|
||||||
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
|
EXT_CSD_CACHE_CTRL, 1, 0);
|
||||||
|
if (err && err != -EBADMSG)
|
||||||
|
goto free_card;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only if no error, cache is turned on successfully.
|
||||||
|
*/
|
||||||
|
card->ext_csd.cache_ctrl = err ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!oldcard)
|
if (!oldcard)
|
||||||
host->card = card;
|
host->card = card;
|
||||||
|
|
||||||
|
@ -891,6 +1123,7 @@ static void mmc_detect(struct mmc_host *host)
|
||||||
|
|
||||||
mmc_claim_host(host);
|
mmc_claim_host(host);
|
||||||
mmc_detach_bus(host);
|
mmc_detach_bus(host);
|
||||||
|
mmc_power_off(host);
|
||||||
mmc_release_host(host);
|
mmc_release_host(host);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -900,16 +1133,20 @@ static void mmc_detect(struct mmc_host *host)
|
||||||
*/
|
*/
|
||||||
static int mmc_suspend(struct mmc_host *host)
|
static int mmc_suspend(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
BUG_ON(!host);
|
BUG_ON(!host);
|
||||||
BUG_ON(!host->card);
|
BUG_ON(!host->card);
|
||||||
|
|
||||||
mmc_claim_host(host);
|
mmc_claim_host(host);
|
||||||
if (!mmc_host_is_spi(host))
|
if (mmc_card_can_sleep(host))
|
||||||
|
err = mmc_card_sleep(host);
|
||||||
|
else if (!mmc_host_is_spi(host))
|
||||||
mmc_deselect_cards(host);
|
mmc_deselect_cards(host);
|
||||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||||
mmc_release_host(host);
|
mmc_release_host(host);
|
||||||
|
|
||||||
return 0;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1016,6 +1253,10 @@ int mmc_attach_mmc(struct mmc_host *host)
|
||||||
BUG_ON(!host);
|
BUG_ON(!host);
|
||||||
WARN_ON(!host->claimed);
|
WARN_ON(!host->claimed);
|
||||||
|
|
||||||
|
/* Set correct bus mode for MMC before attempting attach */
|
||||||
|
if (!mmc_host_is_spi(host))
|
||||||
|
mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
|
||||||
|
|
||||||
err = mmc_send_op_cond(host, 0, &ocr);
|
err = mmc_send_op_cond(host, 0, &ocr);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
@ -1038,7 +1279,7 @@ int mmc_attach_mmc(struct mmc_host *host)
|
||||||
* support.
|
* support.
|
||||||
*/
|
*/
|
||||||
if (ocr & 0x7F) {
|
if (ocr & 0x7F) {
|
||||||
printk(KERN_WARNING "%s: card claims to support voltages "
|
pr_warning("%s: card claims to support voltages "
|
||||||
"below the defined range. These will be ignored.\n",
|
"below the defined range. These will be ignored.\n",
|
||||||
mmc_hostname(host));
|
mmc_hostname(host));
|
||||||
ocr &= ~0x7F;
|
ocr &= ~0x7F;
|
||||||
|
@ -1077,7 +1318,7 @@ remove_card:
|
||||||
err:
|
err:
|
||||||
mmc_detach_bus(host);
|
mmc_detach_bus(host);
|
||||||
|
|
||||||
printk(KERN_ERR "%s: error %d whilst initialising MMC card\n",
|
pr_err("%s: error %d whilst initialising MMC card\n",
|
||||||
mmc_hostname(host), err);
|
mmc_hostname(host), err);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -233,7 +233,7 @@ static int
|
||||||
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
|
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
|
||||||
u32 opcode, void *buf, unsigned len)
|
u32 opcode, void *buf, unsigned len)
|
||||||
{
|
{
|
||||||
struct mmc_request mrq = {0};
|
struct mmc_request mrq = {NULL};
|
||||||
struct mmc_command cmd = {0};
|
struct mmc_command cmd = {0};
|
||||||
struct mmc_data data = {0};
|
struct mmc_data data = {0};
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
@ -414,7 +414,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||||
return -EBADMSG;
|
return -EBADMSG;
|
||||||
} else {
|
} else {
|
||||||
if (status & 0xFDFFA000)
|
if (status & 0xFDFFA000)
|
||||||
printk(KERN_WARNING "%s: unexpected status %#x after "
|
pr_warning("%s: unexpected status %#x after "
|
||||||
"switch", mmc_hostname(card->host), status);
|
"switch", mmc_hostname(card->host), status);
|
||||||
if (status & R1_SWITCH_ERROR)
|
if (status & R1_SWITCH_ERROR)
|
||||||
return -EBADMSG;
|
return -EBADMSG;
|
||||||
|
@ -454,7 +454,7 @@ static int
|
||||||
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
|
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
|
||||||
u8 len)
|
u8 len)
|
||||||
{
|
{
|
||||||
struct mmc_request mrq = {0};
|
struct mmc_request mrq = {NULL};
|
||||||
struct mmc_command cmd = {0};
|
struct mmc_command cmd = {0};
|
||||||
struct mmc_data data = {0};
|
struct mmc_data data = {0};
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
@ -476,7 +476,7 @@ mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
|
||||||
else if (len == 4)
|
else if (len == 4)
|
||||||
test_buf = testdata_4bit;
|
test_buf = testdata_4bit;
|
||||||
else {
|
else {
|
||||||
printk(KERN_ERR "%s: Invalid bus_width %d\n",
|
pr_err("%s: Invalid bus_width %d\n",
|
||||||
mmc_hostname(host), len);
|
mmc_hostname(host), len);
|
||||||
kfree(data_buf);
|
kfree(data_buf);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -547,3 +547,34 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width)
|
||||||
err = mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width);
|
err = mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
|
||||||
|
{
|
||||||
|
struct mmc_command cmd = {0};
|
||||||
|
unsigned int opcode;
|
||||||
|
unsigned int flags;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
opcode = card->ext_csd.hpi_cmd;
|
||||||
|
if (opcode == MMC_STOP_TRANSMISSION)
|
||||||
|
flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||||
|
else if (opcode == MMC_SEND_STATUS)
|
||||||
|
flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||||
|
|
||||||
|
cmd.opcode = opcode;
|
||||||
|
cmd.arg = card->rca << 16 | 1;
|
||||||
|
cmd.flags = flags;
|
||||||
|
cmd.cmd_timeout_ms = card->ext_csd.out_of_int_time;
|
||||||
|
|
||||||
|
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
||||||
|
if (err) {
|
||||||
|
pr_warn("%s: error %d interrupting operation. "
|
||||||
|
"HPI command response %#x\n", mmc_hostname(card->host),
|
||||||
|
err, cmd.resp[0]);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (status)
|
||||||
|
*status = cmd.resp[0];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
|
||||||
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
|
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
|
||||||
int mmc_card_sleepawake(struct mmc_host *host, int sleep);
|
int mmc_card_sleepawake(struct mmc_host *host, int sleep);
|
||||||
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
|
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
|
||||||
|
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,14 @@
|
||||||
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
|
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef SDIO_VENDOR_ID_STE
|
||||||
|
#define SDIO_VENDOR_ID_STE 0x0020
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SDIO_DEVICE_ID_STE_CW1200
|
||||||
|
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This hook just adds a quirk for all sdio devices
|
* This hook just adds a quirk for all sdio devices
|
||||||
*/
|
*/
|
||||||
|
@ -46,6 +54,9 @@ static const struct mmc_fixup mmc_fixup_methods[] = {
|
||||||
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
|
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
|
||||||
add_quirk, MMC_QUIRK_DISABLE_CD),
|
add_quirk, MMC_QUIRK_DISABLE_CD),
|
||||||
|
|
||||||
|
SDIO_FIXUP(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200,
|
||||||
|
add_quirk, MMC_QUIRK_BROKEN_BYTE_MODE_512),
|
||||||
|
|
||||||
END_FIXUP
|
END_FIXUP
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -163,7 +163,7 @@ static int mmc_decode_csd(struct mmc_card *card)
|
||||||
csd->erase_size = 1;
|
csd->erase_size = 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
|
pr_err("%s: unrecognised CSD structure version %d\n",
|
||||||
mmc_hostname(card->host), csd_struct);
|
mmc_hostname(card->host), csd_struct);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,7 @@ static int mmc_decode_scr(struct mmc_card *card)
|
||||||
|
|
||||||
scr_struct = UNSTUFF_BITS(resp, 60, 4);
|
scr_struct = UNSTUFF_BITS(resp, 60, 4);
|
||||||
if (scr_struct != 0) {
|
if (scr_struct != 0) {
|
||||||
printk(KERN_ERR "%s: unrecognised SCR structure version %d\n",
|
pr_err("%s: unrecognised SCR structure version %d\n",
|
||||||
mmc_hostname(card->host), scr_struct);
|
mmc_hostname(card->host), scr_struct);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -218,7 +218,7 @@ static int mmc_read_ssr(struct mmc_card *card)
|
||||||
u32 *ssr;
|
u32 *ssr;
|
||||||
|
|
||||||
if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
|
if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
|
||||||
printk(KERN_WARNING "%s: card lacks mandatory SD Status "
|
pr_warning("%s: card lacks mandatory SD Status "
|
||||||
"function.\n", mmc_hostname(card->host));
|
"function.\n", mmc_hostname(card->host));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -229,7 +229,7 @@ static int mmc_read_ssr(struct mmc_card *card)
|
||||||
|
|
||||||
err = mmc_app_sd_status(card, ssr);
|
err = mmc_app_sd_status(card, ssr);
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(KERN_WARNING "%s: problem reading SD Status "
|
pr_warning("%s: problem reading SD Status "
|
||||||
"register.\n", mmc_hostname(card->host));
|
"register.\n", mmc_hostname(card->host));
|
||||||
err = 0;
|
err = 0;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -253,7 +253,7 @@ static int mmc_read_ssr(struct mmc_card *card)
|
||||||
card->ssr.erase_offset = eo * 1000;
|
card->ssr.erase_offset = eo * 1000;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
printk(KERN_WARNING "%s: SD Status: Invalid Allocation Unit "
|
pr_warning("%s: SD Status: Invalid Allocation Unit "
|
||||||
"size.\n", mmc_hostname(card->host));
|
"size.\n", mmc_hostname(card->host));
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
|
@ -273,7 +273,7 @@ static int mmc_read_switch(struct mmc_card *card)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!(card->csd.cmdclass & CCC_SWITCH)) {
|
if (!(card->csd.cmdclass & CCC_SWITCH)) {
|
||||||
printk(KERN_WARNING "%s: card lacks mandatory switch "
|
pr_warning("%s: card lacks mandatory switch "
|
||||||
"function, performance might suffer.\n",
|
"function, performance might suffer.\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -283,7 +283,7 @@ static int mmc_read_switch(struct mmc_card *card)
|
||||||
|
|
||||||
status = kmalloc(64, GFP_KERNEL);
|
status = kmalloc(64, GFP_KERNEL);
|
||||||
if (!status) {
|
if (!status) {
|
||||||
printk(KERN_ERR "%s: could not allocate a buffer for "
|
pr_err("%s: could not allocate a buffer for "
|
||||||
"switch capabilities.\n",
|
"switch capabilities.\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -299,13 +299,16 @@ static int mmc_read_switch(struct mmc_card *card)
|
||||||
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
|
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
printk(KERN_WARNING "%s: problem reading Bus Speed modes.\n",
|
pr_warning("%s: problem reading Bus Speed modes.\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
err = 0;
|
err = 0;
|
||||||
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status[13] & UHS_SDR50_BUS_SPEED)
|
||||||
|
card->sw_caps.hs_max_dtr = 50000000;
|
||||||
|
|
||||||
if (card->scr.sda_spec3) {
|
if (card->scr.sda_spec3) {
|
||||||
card->sw_caps.sd3_bus_mode = status[13];
|
card->sw_caps.sd3_bus_mode = status[13];
|
||||||
|
|
||||||
|
@ -319,7 +322,7 @@ static int mmc_read_switch(struct mmc_card *card)
|
||||||
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
|
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
printk(KERN_WARNING "%s: problem reading "
|
pr_warning("%s: problem reading "
|
||||||
"Driver Strength.\n",
|
"Driver Strength.\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
err = 0;
|
err = 0;
|
||||||
|
@ -339,7 +342,7 @@ static int mmc_read_switch(struct mmc_card *card)
|
||||||
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
|
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
printk(KERN_WARNING "%s: problem reading "
|
pr_warning("%s: problem reading "
|
||||||
"Current Limit.\n",
|
"Current Limit.\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
err = 0;
|
err = 0;
|
||||||
|
@ -348,9 +351,6 @@ static int mmc_read_switch(struct mmc_card *card)
|
||||||
}
|
}
|
||||||
|
|
||||||
card->sw_caps.sd3_curr_limit = status[7];
|
card->sw_caps.sd3_curr_limit = status[7];
|
||||||
} else {
|
|
||||||
if (status[13] & 0x02)
|
|
||||||
card->sw_caps.hs_max_dtr = 50000000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -383,7 +383,7 @@ int mmc_sd_switch_hs(struct mmc_card *card)
|
||||||
|
|
||||||
status = kmalloc(64, GFP_KERNEL);
|
status = kmalloc(64, GFP_KERNEL);
|
||||||
if (!status) {
|
if (!status) {
|
||||||
printk(KERN_ERR "%s: could not allocate a buffer for "
|
pr_err("%s: could not allocate a buffer for "
|
||||||
"switch capabilities.\n", mmc_hostname(card->host));
|
"switch capabilities.\n", mmc_hostname(card->host));
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
@ -393,7 +393,7 @@ int mmc_sd_switch_hs(struct mmc_card *card)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if ((status[16] & 0xF) != 1) {
|
if ((status[16] & 0xF) != 1) {
|
||||||
printk(KERN_WARNING "%s: Problem switching card "
|
pr_warning("%s: Problem switching card "
|
||||||
"into high-speed mode!\n",
|
"into high-speed mode!\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
err = 0;
|
err = 0;
|
||||||
|
@ -459,7 +459,7 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if ((status[15] & 0xF) != drive_strength) {
|
if ((status[15] & 0xF) != drive_strength) {
|
||||||
printk(KERN_WARNING "%s: Problem setting drive strength!\n",
|
pr_warning("%s: Problem setting drive strength!\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -538,7 +538,7 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if ((status[16] & 0xF) != card->sd_bus_speed)
|
if ((status[16] & 0xF) != card->sd_bus_speed)
|
||||||
printk(KERN_WARNING "%s: Problem setting bus speed mode!\n",
|
pr_warning("%s: Problem setting bus speed mode!\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
else {
|
else {
|
||||||
mmc_set_timing(card->host, timing);
|
mmc_set_timing(card->host, timing);
|
||||||
|
@ -600,7 +600,7 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (((status[15] >> 4) & 0x0F) != current_limit)
|
if (((status[15] >> 4) & 0x0F) != current_limit)
|
||||||
printk(KERN_WARNING "%s: Problem setting current limit!\n",
|
pr_warning("%s: Problem setting current limit!\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -622,7 +622,7 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
|
||||||
|
|
||||||
status = kmalloc(64, GFP_KERNEL);
|
status = kmalloc(64, GFP_KERNEL);
|
||||||
if (!status) {
|
if (!status) {
|
||||||
printk(KERN_ERR "%s: could not allocate a buffer for "
|
pr_err("%s: could not allocate a buffer for "
|
||||||
"switch capabilities.\n", mmc_hostname(card->host));
|
"switch capabilities.\n", mmc_hostname(card->host));
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
@ -852,7 +852,7 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
|
||||||
ro = host->ops->get_ro(host);
|
ro = host->ops->get_ro(host);
|
||||||
|
|
||||||
if (ro < 0) {
|
if (ro < 0) {
|
||||||
printk(KERN_WARNING "%s: host does not "
|
pr_warning("%s: host does not "
|
||||||
"support reading read-only "
|
"support reading read-only "
|
||||||
"switch. assuming write-enable.\n",
|
"switch. assuming write-enable.\n",
|
||||||
mmc_hostname(host));
|
mmc_hostname(host));
|
||||||
|
@ -929,8 +929,6 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||||
err = mmc_send_relative_addr(host, &card->rca);
|
err = mmc_send_relative_addr(host, &card->rca);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!oldcard) {
|
if (!oldcard) {
|
||||||
|
@ -1043,6 +1041,7 @@ static void mmc_sd_detect(struct mmc_host *host)
|
||||||
|
|
||||||
mmc_claim_host(host);
|
mmc_claim_host(host);
|
||||||
mmc_detach_bus(host);
|
mmc_detach_bus(host);
|
||||||
|
mmc_power_off(host);
|
||||||
mmc_release_host(host);
|
mmc_release_host(host);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1167,7 +1166,7 @@ int mmc_attach_sd(struct mmc_host *host)
|
||||||
* support.
|
* support.
|
||||||
*/
|
*/
|
||||||
if (ocr & 0x7F) {
|
if (ocr & 0x7F) {
|
||||||
printk(KERN_WARNING "%s: card claims to support voltages "
|
pr_warning("%s: card claims to support voltages "
|
||||||
"below the defined range. These will be ignored.\n",
|
"below the defined range. These will be ignored.\n",
|
||||||
mmc_hostname(host));
|
mmc_hostname(host));
|
||||||
ocr &= ~0x7F;
|
ocr &= ~0x7F;
|
||||||
|
@ -1175,7 +1174,7 @@ int mmc_attach_sd(struct mmc_host *host)
|
||||||
|
|
||||||
if ((ocr & MMC_VDD_165_195) &&
|
if ((ocr & MMC_VDD_165_195) &&
|
||||||
!(host->ocr_avail_sd & MMC_VDD_165_195)) {
|
!(host->ocr_avail_sd & MMC_VDD_165_195)) {
|
||||||
printk(KERN_WARNING "%s: SD card claims to support the "
|
pr_warning("%s: SD card claims to support the "
|
||||||
"incompletely defined 'low voltage range'. This "
|
"incompletely defined 'low voltage range'. This "
|
||||||
"will be ignored.\n", mmc_hostname(host));
|
"will be ignored.\n", mmc_hostname(host));
|
||||||
ocr &= ~MMC_VDD_165_195;
|
ocr &= ~MMC_VDD_165_195;
|
||||||
|
@ -1214,7 +1213,7 @@ remove_card:
|
||||||
err:
|
err:
|
||||||
mmc_detach_bus(host);
|
mmc_detach_bus(host);
|
||||||
|
|
||||||
printk(KERN_ERR "%s: error %d whilst initialising SD card\n",
|
pr_err("%s: error %d whilst initialising SD card\n",
|
||||||
mmc_hostname(host), err);
|
mmc_hostname(host), err);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -67,7 +67,7 @@ EXPORT_SYMBOL_GPL(mmc_app_cmd);
|
||||||
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
|
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
|
||||||
struct mmc_command *cmd, int retries)
|
struct mmc_command *cmd, int retries)
|
||||||
{
|
{
|
||||||
struct mmc_request mrq = {0};
|
struct mmc_request mrq = {NULL};
|
||||||
|
|
||||||
int i, err;
|
int i, err;
|
||||||
|
|
||||||
|
@ -244,7 +244,7 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
|
||||||
int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
|
int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct mmc_request mrq = {0};
|
struct mmc_request mrq = {NULL};
|
||||||
struct mmc_command cmd = {0};
|
struct mmc_command cmd = {0};
|
||||||
struct mmc_data data = {0};
|
struct mmc_data data = {0};
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
@ -303,7 +303,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
|
||||||
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
|
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
|
||||||
u8 value, u8 *resp)
|
u8 value, u8 *resp)
|
||||||
{
|
{
|
||||||
struct mmc_request mrq = {0};
|
struct mmc_request mrq = {NULL};
|
||||||
struct mmc_command cmd = {0};
|
struct mmc_command cmd = {0};
|
||||||
struct mmc_data data = {0};
|
struct mmc_data data = {0};
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
@ -348,7 +348,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
|
||||||
int mmc_app_sd_status(struct mmc_card *card, void *ssr)
|
int mmc_app_sd_status(struct mmc_card *card, void *ssr)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct mmc_request mrq = {0};
|
struct mmc_request mrq = {NULL};
|
||||||
struct mmc_command cmd = {0};
|
struct mmc_command cmd = {0};
|
||||||
struct mmc_data data = {0};
|
struct mmc_data data = {0};
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
|
|
@ -111,8 +111,8 @@ static int sdio_read_cccr(struct mmc_card *card)
|
||||||
|
|
||||||
cccr_vsn = data & 0x0f;
|
cccr_vsn = data & 0x0f;
|
||||||
|
|
||||||
if (cccr_vsn > SDIO_CCCR_REV_1_20) {
|
if (cccr_vsn > SDIO_CCCR_REV_3_00) {
|
||||||
printk(KERN_ERR "%s: unrecognised CCCR structure version %d\n",
|
pr_err("%s: unrecognised CCCR structure version %d\n",
|
||||||
mmc_hostname(card->host), cccr_vsn);
|
mmc_hostname(card->host), cccr_vsn);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -408,8 +408,6 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
||||||
*/
|
*/
|
||||||
if (oldcard)
|
if (oldcard)
|
||||||
oldcard->rca = card->rca;
|
oldcard->rca = card->rca;
|
||||||
|
|
||||||
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -597,6 +595,7 @@ out:
|
||||||
|
|
||||||
mmc_claim_host(host);
|
mmc_claim_host(host);
|
||||||
mmc_detach_bus(host);
|
mmc_detach_bus(host);
|
||||||
|
mmc_power_off(host);
|
||||||
mmc_release_host(host);
|
mmc_release_host(host);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -778,7 +777,7 @@ int mmc_attach_sdio(struct mmc_host *host)
|
||||||
* support.
|
* support.
|
||||||
*/
|
*/
|
||||||
if (ocr & 0x7F) {
|
if (ocr & 0x7F) {
|
||||||
printk(KERN_WARNING "%s: card claims to support voltages "
|
pr_warning("%s: card claims to support voltages "
|
||||||
"below the defined range. These will be ignored.\n",
|
"below the defined range. These will be ignored.\n",
|
||||||
mmc_hostname(host));
|
mmc_hostname(host));
|
||||||
ocr &= ~0x7F;
|
ocr &= ~0x7F;
|
||||||
|
@ -875,7 +874,7 @@ remove:
|
||||||
err:
|
err:
|
||||||
mmc_detach_bus(host);
|
mmc_detach_bus(host);
|
||||||
|
|
||||||
printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n",
|
pr_err("%s: error %d whilst initialising SDIO card\n",
|
||||||
mmc_hostname(host), err);
|
mmc_hostname(host), err);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -173,7 +173,7 @@ static int sdio_bus_remove(struct device *dev)
|
||||||
drv->remove(func);
|
drv->remove(func);
|
||||||
|
|
||||||
if (func->irq_handler) {
|
if (func->irq_handler) {
|
||||||
printk(KERN_WARNING "WARNING: driver %s did not remove "
|
pr_warning("WARNING: driver %s did not remove "
|
||||||
"its interrupt handler!\n", drv->name);
|
"its interrupt handler!\n", drv->name);
|
||||||
sdio_claim_host(func);
|
sdio_claim_host(func);
|
||||||
sdio_release_irq(func);
|
sdio_release_irq(func);
|
||||||
|
|
|
@ -132,7 +132,7 @@ static int cis_tpl_parse(struct mmc_card *card, struct sdio_func *func,
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
if (ret && ret != -EILSEQ && ret != -ENOENT) {
|
if (ret && ret != -EILSEQ && ret != -ENOENT) {
|
||||||
printk(KERN_ERR "%s: bad %s tuple 0x%02x (%u bytes)\n",
|
pr_err("%s: bad %s tuple 0x%02x (%u bytes)\n",
|
||||||
mmc_hostname(card->host), tpl_descr, code, size);
|
mmc_hostname(card->host), tpl_descr, code, size);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -313,7 +313,7 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
|
||||||
|
|
||||||
if (ret == -ENOENT) {
|
if (ret == -ENOENT) {
|
||||||
/* warn about unknown tuples */
|
/* warn about unknown tuples */
|
||||||
printk(KERN_WARNING "%s: queuing unknown"
|
pr_warning("%s: queuing unknown"
|
||||||
" CIS tuple 0x%02x (%u bytes)\n",
|
" CIS tuple 0x%02x (%u bytes)\n",
|
||||||
mmc_hostname(card->host),
|
mmc_hostname(card->host),
|
||||||
tpl_code, tpl_link);
|
tpl_code, tpl_link);
|
||||||
|
|
|
@ -45,7 +45,7 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
|
||||||
|
|
||||||
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
|
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n",
|
pr_debug("%s: error %d reading SDIO_CCCR_INTx\n",
|
||||||
mmc_card_id(card), ret);
|
mmc_card_id(card), ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
|
||||||
if (pending & (1 << i)) {
|
if (pending & (1 << i)) {
|
||||||
func = card->sdio_func[i - 1];
|
func = card->sdio_func[i - 1];
|
||||||
if (!func) {
|
if (!func) {
|
||||||
printk(KERN_WARNING "%s: pending IRQ for "
|
pr_warning("%s: pending IRQ for "
|
||||||
"non-existent function\n",
|
"non-existent function\n",
|
||||||
mmc_card_id(card));
|
mmc_card_id(card));
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
@ -63,7 +63,7 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
|
||||||
func->irq_handler(func);
|
func->irq_handler(func);
|
||||||
count++;
|
count++;
|
||||||
} else {
|
} else {
|
||||||
printk(KERN_WARNING "%s: pending IRQ with no handler\n",
|
pr_warning("%s: pending IRQ with no handler\n",
|
||||||
sdio_func_id(func));
|
sdio_func_id(func));
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
|
||||||
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
|
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
|
||||||
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
|
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
|
||||||
{
|
{
|
||||||
struct mmc_request mrq = {0};
|
struct mmc_request mrq = {NULL};
|
||||||
struct mmc_command cmd = {0};
|
struct mmc_command cmd = {0};
|
||||||
struct mmc_data data = {0};
|
struct mmc_data data = {0};
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
@ -144,8 +144,11 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
|
||||||
cmd.arg |= fn << 28;
|
cmd.arg |= fn << 28;
|
||||||
cmd.arg |= incr_addr ? 0x04000000 : 0x00000000;
|
cmd.arg |= incr_addr ? 0x04000000 : 0x00000000;
|
||||||
cmd.arg |= addr << 9;
|
cmd.arg |= addr << 9;
|
||||||
if (blocks == 1 && blksz <= 512)
|
if (blocks == 1 && blksz < 512)
|
||||||
cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */
|
cmd.arg |= blksz; /* byte mode */
|
||||||
|
else if (blocks == 1 && blksz == 512 &&
|
||||||
|
!(mmc_card_broken_byte_mode_512(card)))
|
||||||
|
cmd.arg |= 0; /* byte mode, 0==512 */
|
||||||
else
|
else
|
||||||
cmd.arg |= 0x08000000 | blocks; /* block mode */
|
cmd.arg |= 0x08000000 | blocks; /* block mode */
|
||||||
cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
|
cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
|
||||||
|
|
|
@ -130,13 +130,13 @@ config MMC_SDHCI_CNS3XXX
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
|
|
||||||
config MMC_SDHCI_ESDHC_IMX
|
config MMC_SDHCI_ESDHC_IMX
|
||||||
tristate "SDHCI platform support for the Freescale eSDHC i.MX controller"
|
tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller"
|
||||||
depends on ARCH_MX25 || ARCH_MX35 || ARCH_MX5
|
depends on ARCH_MXC
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
select MMC_SDHCI_IO_ACCESSORS
|
select MMC_SDHCI_IO_ACCESSORS
|
||||||
help
|
help
|
||||||
This selects the Freescale eSDHC controller support on the platform
|
This selects the Freescale eSDHC/uSDHC controller support
|
||||||
bus, found on platforms like mx35/51.
|
found on i.MX25, i.MX35 i.MX5x and i.MX6x.
|
||||||
|
|
||||||
If you have a controller with this interface, say Y or M here.
|
If you have a controller with this interface, say Y or M here.
|
||||||
|
|
||||||
|
@ -326,11 +326,11 @@ config MMC_MSM
|
||||||
support for SDIO devices.
|
support for SDIO devices.
|
||||||
|
|
||||||
config MMC_MXC
|
config MMC_MXC
|
||||||
tristate "Freescale i.MX2/3 Multimedia Card Interface support"
|
tristate "Freescale i.MX21/27/31 Multimedia Card Interface support"
|
||||||
depends on MACH_MX21 || MACH_MX27 || ARCH_MX31
|
depends on ARCH_MXC
|
||||||
help
|
help
|
||||||
This selects the Freescale i.MX2/3 Multimedia card Interface.
|
This selects the Freescale i.MX21, i.MX27 and i.MX31 Multimedia card
|
||||||
If you have a i.MX platform with a Multimedia Card slot,
|
Interface. If you have a i.MX platform with a Multimedia Card slot,
|
||||||
say Y or M here.
|
say Y or M here.
|
||||||
|
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
|
|
|
@ -869,7 +869,11 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
|
||||||
static irqreturn_t at91_mmc_det_irq(int irq, void *_host)
|
static irqreturn_t at91_mmc_det_irq(int irq, void *_host)
|
||||||
{
|
{
|
||||||
struct at91mci_host *host = _host;
|
struct at91mci_host *host = _host;
|
||||||
int present = !gpio_get_value(irq_to_gpio(irq));
|
int present;
|
||||||
|
|
||||||
|
/* entering this ISR means that we have configured det_pin:
|
||||||
|
* we can use its value in board structure */
|
||||||
|
present = !gpio_get_value(host->board->det_pin);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* we expect this irq on both insert and remove,
|
* we expect this irq on both insert and remove,
|
||||||
|
|
|
@ -17,112 +17,126 @@
|
||||||
#define __DRIVERS_MMC_ATMEL_MCI_H__
|
#define __DRIVERS_MMC_ATMEL_MCI_H__
|
||||||
|
|
||||||
/* MCI Register Definitions */
|
/* MCI Register Definitions */
|
||||||
#define MCI_CR 0x0000 /* Control */
|
#define ATMCI_CR 0x0000 /* Control */
|
||||||
# define MCI_CR_MCIEN ( 1 << 0) /* MCI Enable */
|
# define ATMCI_CR_MCIEN ( 1 << 0) /* MCI Enable */
|
||||||
# define MCI_CR_MCIDIS ( 1 << 1) /* MCI Disable */
|
# define ATMCI_CR_MCIDIS ( 1 << 1) /* MCI Disable */
|
||||||
# define MCI_CR_PWSEN ( 1 << 2) /* Power Save Enable */
|
# define ATMCI_CR_PWSEN ( 1 << 2) /* Power Save Enable */
|
||||||
# define MCI_CR_PWSDIS ( 1 << 3) /* Power Save Disable */
|
# define ATMCI_CR_PWSDIS ( 1 << 3) /* Power Save Disable */
|
||||||
# define MCI_CR_SWRST ( 1 << 7) /* Software Reset */
|
# define ATMCI_CR_SWRST ( 1 << 7) /* Software Reset */
|
||||||
#define MCI_MR 0x0004 /* Mode */
|
#define ATMCI_MR 0x0004 /* Mode */
|
||||||
# define MCI_MR_CLKDIV(x) ((x) << 0) /* Clock Divider */
|
# define ATMCI_MR_CLKDIV(x) ((x) << 0) /* Clock Divider */
|
||||||
# define MCI_MR_PWSDIV(x) ((x) << 8) /* Power Saving Divider */
|
# define ATMCI_MR_PWSDIV(x) ((x) << 8) /* Power Saving Divider */
|
||||||
# define MCI_MR_RDPROOF ( 1 << 11) /* Read Proof */
|
# define ATMCI_MR_RDPROOF ( 1 << 11) /* Read Proof */
|
||||||
# define MCI_MR_WRPROOF ( 1 << 12) /* Write Proof */
|
# define ATMCI_MR_WRPROOF ( 1 << 12) /* Write Proof */
|
||||||
# define MCI_MR_PDCFBYTE ( 1 << 13) /* Force Byte Transfer */
|
# define ATMCI_MR_PDCFBYTE ( 1 << 13) /* Force Byte Transfer */
|
||||||
# define MCI_MR_PDCPADV ( 1 << 14) /* Padding Value */
|
# define ATMCI_MR_PDCPADV ( 1 << 14) /* Padding Value */
|
||||||
# define MCI_MR_PDCMODE ( 1 << 15) /* PDC-oriented Mode */
|
# define ATMCI_MR_PDCMODE ( 1 << 15) /* PDC-oriented Mode */
|
||||||
#define MCI_DTOR 0x0008 /* Data Timeout */
|
#define ATMCI_DTOR 0x0008 /* Data Timeout */
|
||||||
# define MCI_DTOCYC(x) ((x) << 0) /* Data Timeout Cycles */
|
# define ATMCI_DTOCYC(x) ((x) << 0) /* Data Timeout Cycles */
|
||||||
# define MCI_DTOMUL(x) ((x) << 4) /* Data Timeout Multiplier */
|
# define ATMCI_DTOMUL(x) ((x) << 4) /* Data Timeout Multiplier */
|
||||||
#define MCI_SDCR 0x000c /* SD Card / SDIO */
|
#define ATMCI_SDCR 0x000c /* SD Card / SDIO */
|
||||||
# define MCI_SDCSEL_SLOT_A ( 0 << 0) /* Select SD slot A */
|
# define ATMCI_SDCSEL_SLOT_A ( 0 << 0) /* Select SD slot A */
|
||||||
# define MCI_SDCSEL_SLOT_B ( 1 << 0) /* Select SD slot A */
|
# define ATMCI_SDCSEL_SLOT_B ( 1 << 0) /* Select SD slot A */
|
||||||
# define MCI_SDCSEL_MASK ( 3 << 0)
|
# define ATMCI_SDCSEL_MASK ( 3 << 0)
|
||||||
# define MCI_SDCBUS_1BIT ( 0 << 6) /* 1-bit data bus */
|
# define ATMCI_SDCBUS_1BIT ( 0 << 6) /* 1-bit data bus */
|
||||||
# define MCI_SDCBUS_4BIT ( 2 << 6) /* 4-bit data bus */
|
# define ATMCI_SDCBUS_4BIT ( 2 << 6) /* 4-bit data bus */
|
||||||
# define MCI_SDCBUS_8BIT ( 3 << 6) /* 8-bit data bus[2] */
|
# define ATMCI_SDCBUS_8BIT ( 3 << 6) /* 8-bit data bus[2] */
|
||||||
# define MCI_SDCBUS_MASK ( 3 << 6)
|
# define ATMCI_SDCBUS_MASK ( 3 << 6)
|
||||||
#define MCI_ARGR 0x0010 /* Command Argument */
|
#define ATMCI_ARGR 0x0010 /* Command Argument */
|
||||||
#define MCI_CMDR 0x0014 /* Command */
|
#define ATMCI_CMDR 0x0014 /* Command */
|
||||||
# define MCI_CMDR_CMDNB(x) ((x) << 0) /* Command Opcode */
|
# define ATMCI_CMDR_CMDNB(x) ((x) << 0) /* Command Opcode */
|
||||||
# define MCI_CMDR_RSPTYP_NONE ( 0 << 6) /* No response */
|
# define ATMCI_CMDR_RSPTYP_NONE ( 0 << 6) /* No response */
|
||||||
# define MCI_CMDR_RSPTYP_48BIT ( 1 << 6) /* 48-bit response */
|
# define ATMCI_CMDR_RSPTYP_48BIT ( 1 << 6) /* 48-bit response */
|
||||||
# define MCI_CMDR_RSPTYP_136BIT ( 2 << 6) /* 136-bit response */
|
# define ATMCI_CMDR_RSPTYP_136BIT ( 2 << 6) /* 136-bit response */
|
||||||
# define MCI_CMDR_SPCMD_INIT ( 1 << 8) /* Initialization command */
|
# define ATMCI_CMDR_SPCMD_INIT ( 1 << 8) /* Initialization command */
|
||||||
# define MCI_CMDR_SPCMD_SYNC ( 2 << 8) /* Synchronized command */
|
# define ATMCI_CMDR_SPCMD_SYNC ( 2 << 8) /* Synchronized command */
|
||||||
# define MCI_CMDR_SPCMD_INT ( 4 << 8) /* Interrupt command */
|
# define ATMCI_CMDR_SPCMD_INT ( 4 << 8) /* Interrupt command */
|
||||||
# define MCI_CMDR_SPCMD_INTRESP ( 5 << 8) /* Interrupt response */
|
# define ATMCI_CMDR_SPCMD_INTRESP ( 5 << 8) /* Interrupt response */
|
||||||
# define MCI_CMDR_OPDCMD ( 1 << 11) /* Open Drain */
|
# define ATMCI_CMDR_OPDCMD ( 1 << 11) /* Open Drain */
|
||||||
# define MCI_CMDR_MAXLAT_5CYC ( 0 << 12) /* Max latency 5 cycles */
|
# define ATMCI_CMDR_MAXLAT_5CYC ( 0 << 12) /* Max latency 5 cycles */
|
||||||
# define MCI_CMDR_MAXLAT_64CYC ( 1 << 12) /* Max latency 64 cycles */
|
# define ATMCI_CMDR_MAXLAT_64CYC ( 1 << 12) /* Max latency 64 cycles */
|
||||||
# define MCI_CMDR_START_XFER ( 1 << 16) /* Start data transfer */
|
# define ATMCI_CMDR_START_XFER ( 1 << 16) /* Start data transfer */
|
||||||
# define MCI_CMDR_STOP_XFER ( 2 << 16) /* Stop data transfer */
|
# define ATMCI_CMDR_STOP_XFER ( 2 << 16) /* Stop data transfer */
|
||||||
# define MCI_CMDR_TRDIR_WRITE ( 0 << 18) /* Write data */
|
# define ATMCI_CMDR_TRDIR_WRITE ( 0 << 18) /* Write data */
|
||||||
# define MCI_CMDR_TRDIR_READ ( 1 << 18) /* Read data */
|
# define ATMCI_CMDR_TRDIR_READ ( 1 << 18) /* Read data */
|
||||||
# define MCI_CMDR_BLOCK ( 0 << 19) /* Single-block transfer */
|
# define ATMCI_CMDR_BLOCK ( 0 << 19) /* Single-block transfer */
|
||||||
# define MCI_CMDR_MULTI_BLOCK ( 1 << 19) /* Multi-block transfer */
|
# define ATMCI_CMDR_MULTI_BLOCK ( 1 << 19) /* Multi-block transfer */
|
||||||
# define MCI_CMDR_STREAM ( 2 << 19) /* MMC Stream transfer */
|
# define ATMCI_CMDR_STREAM ( 2 << 19) /* MMC Stream transfer */
|
||||||
# define MCI_CMDR_SDIO_BYTE ( 4 << 19) /* SDIO Byte transfer */
|
# define ATMCI_CMDR_SDIO_BYTE ( 4 << 19) /* SDIO Byte transfer */
|
||||||
# define MCI_CMDR_SDIO_BLOCK ( 5 << 19) /* SDIO Block transfer */
|
# define ATMCI_CMDR_SDIO_BLOCK ( 5 << 19) /* SDIO Block transfer */
|
||||||
# define MCI_CMDR_SDIO_SUSPEND ( 1 << 24) /* SDIO Suspend Command */
|
# define ATMCI_CMDR_SDIO_SUSPEND ( 1 << 24) /* SDIO Suspend Command */
|
||||||
# define MCI_CMDR_SDIO_RESUME ( 2 << 24) /* SDIO Resume Command */
|
# define ATMCI_CMDR_SDIO_RESUME ( 2 << 24) /* SDIO Resume Command */
|
||||||
#define MCI_BLKR 0x0018 /* Block */
|
#define ATMCI_BLKR 0x0018 /* Block */
|
||||||
# define MCI_BCNT(x) ((x) << 0) /* Data Block Count */
|
# define ATMCI_BCNT(x) ((x) << 0) /* Data Block Count */
|
||||||
# define MCI_BLKLEN(x) ((x) << 16) /* Data Block Length */
|
# define ATMCI_BLKLEN(x) ((x) << 16) /* Data Block Length */
|
||||||
#define MCI_CSTOR 0x001c /* Completion Signal Timeout[2] */
|
#define ATMCI_CSTOR 0x001c /* Completion Signal Timeout[2] */
|
||||||
# define MCI_CSTOCYC(x) ((x) << 0) /* CST cycles */
|
# define ATMCI_CSTOCYC(x) ((x) << 0) /* CST cycles */
|
||||||
# define MCI_CSTOMUL(x) ((x) << 4) /* CST multiplier */
|
# define ATMCI_CSTOMUL(x) ((x) << 4) /* CST multiplier */
|
||||||
#define MCI_RSPR 0x0020 /* Response 0 */
|
#define ATMCI_RSPR 0x0020 /* Response 0 */
|
||||||
#define MCI_RSPR1 0x0024 /* Response 1 */
|
#define ATMCI_RSPR1 0x0024 /* Response 1 */
|
||||||
#define MCI_RSPR2 0x0028 /* Response 2 */
|
#define ATMCI_RSPR2 0x0028 /* Response 2 */
|
||||||
#define MCI_RSPR3 0x002c /* Response 3 */
|
#define ATMCI_RSPR3 0x002c /* Response 3 */
|
||||||
#define MCI_RDR 0x0030 /* Receive Data */
|
#define ATMCI_RDR 0x0030 /* Receive Data */
|
||||||
#define MCI_TDR 0x0034 /* Transmit Data */
|
#define ATMCI_TDR 0x0034 /* Transmit Data */
|
||||||
#define MCI_SR 0x0040 /* Status */
|
#define ATMCI_SR 0x0040 /* Status */
|
||||||
#define MCI_IER 0x0044 /* Interrupt Enable */
|
#define ATMCI_IER 0x0044 /* Interrupt Enable */
|
||||||
#define MCI_IDR 0x0048 /* Interrupt Disable */
|
#define ATMCI_IDR 0x0048 /* Interrupt Disable */
|
||||||
#define MCI_IMR 0x004c /* Interrupt Mask */
|
#define ATMCI_IMR 0x004c /* Interrupt Mask */
|
||||||
# define MCI_CMDRDY ( 1 << 0) /* Command Ready */
|
# define ATMCI_CMDRDY ( 1 << 0) /* Command Ready */
|
||||||
# define MCI_RXRDY ( 1 << 1) /* Receiver Ready */
|
# define ATMCI_RXRDY ( 1 << 1) /* Receiver Ready */
|
||||||
# define MCI_TXRDY ( 1 << 2) /* Transmitter Ready */
|
# define ATMCI_TXRDY ( 1 << 2) /* Transmitter Ready */
|
||||||
# define MCI_BLKE ( 1 << 3) /* Data Block Ended */
|
# define ATMCI_BLKE ( 1 << 3) /* Data Block Ended */
|
||||||
# define MCI_DTIP ( 1 << 4) /* Data Transfer In Progress */
|
# define ATMCI_DTIP ( 1 << 4) /* Data Transfer In Progress */
|
||||||
# define MCI_NOTBUSY ( 1 << 5) /* Data Not Busy */
|
# define ATMCI_NOTBUSY ( 1 << 5) /* Data Not Busy */
|
||||||
# define MCI_SDIOIRQA ( 1 << 8) /* SDIO IRQ in slot A */
|
# define ATMCI_ENDRX ( 1 << 6) /* End of RX Buffer */
|
||||||
# define MCI_SDIOIRQB ( 1 << 9) /* SDIO IRQ in slot B */
|
# define ATMCI_ENDTX ( 1 << 7) /* End of TX Buffer */
|
||||||
# define MCI_RINDE ( 1 << 16) /* Response Index Error */
|
# define ATMCI_SDIOIRQA ( 1 << 8) /* SDIO IRQ in slot A */
|
||||||
# define MCI_RDIRE ( 1 << 17) /* Response Direction Error */
|
# define ATMCI_SDIOIRQB ( 1 << 9) /* SDIO IRQ in slot B */
|
||||||
# define MCI_RCRCE ( 1 << 18) /* Response CRC Error */
|
# define ATMCI_SDIOWAIT ( 1 << 12) /* SDIO Read Wait Operation Status */
|
||||||
# define MCI_RENDE ( 1 << 19) /* Response End Bit Error */
|
# define ATMCI_CSRCV ( 1 << 13) /* CE-ATA Completion Signal Received */
|
||||||
# define MCI_RTOE ( 1 << 20) /* Response Time-Out Error */
|
# define ATMCI_RXBUFF ( 1 << 14) /* RX Buffer Full */
|
||||||
# define MCI_DCRCE ( 1 << 21) /* Data CRC Error */
|
# define ATMCI_TXBUFE ( 1 << 15) /* TX Buffer Empty */
|
||||||
# define MCI_DTOE ( 1 << 22) /* Data Time-Out Error */
|
# define ATMCI_RINDE ( 1 << 16) /* Response Index Error */
|
||||||
# define MCI_OVRE ( 1 << 30) /* RX Overrun Error */
|
# define ATMCI_RDIRE ( 1 << 17) /* Response Direction Error */
|
||||||
# define MCI_UNRE ( 1 << 31) /* TX Underrun Error */
|
# define ATMCI_RCRCE ( 1 << 18) /* Response CRC Error */
|
||||||
#define MCI_DMA 0x0050 /* DMA Configuration[2] */
|
# define ATMCI_RENDE ( 1 << 19) /* Response End Bit Error */
|
||||||
# define MCI_DMA_OFFSET(x) ((x) << 0) /* DMA Write Buffer Offset */
|
# define ATMCI_RTOE ( 1 << 20) /* Response Time-Out Error */
|
||||||
# define MCI_DMA_CHKSIZE(x) ((x) << 4) /* DMA Channel Read and Write Chunk Size */
|
# define ATMCI_DCRCE ( 1 << 21) /* Data CRC Error */
|
||||||
# define MCI_DMAEN ( 1 << 8) /* DMA Hardware Handshaking Enable */
|
# define ATMCI_DTOE ( 1 << 22) /* Data Time-Out Error */
|
||||||
#define MCI_CFG 0x0054 /* Configuration[2] */
|
# define ATMCI_CSTOE ( 1 << 23) /* Completion Signal Time-out Error */
|
||||||
# define MCI_CFG_FIFOMODE_1DATA ( 1 << 0) /* MCI Internal FIFO control mode */
|
# define ATMCI_BLKOVRE ( 1 << 24) /* DMA Block Overrun Error */
|
||||||
# define MCI_CFG_FERRCTRL_COR ( 1 << 4) /* Flow Error flag reset control mode */
|
# define ATMCI_DMADONE ( 1 << 25) /* DMA Transfer Done */
|
||||||
# define MCI_CFG_HSMODE ( 1 << 8) /* High Speed Mode */
|
# define ATMCI_FIFOEMPTY ( 1 << 26) /* FIFO Empty Flag */
|
||||||
# define MCI_CFG_LSYNC ( 1 << 12) /* Synchronize on the last block */
|
# define ATMCI_XFRDONE ( 1 << 27) /* Transfer Done Flag */
|
||||||
#define MCI_WPMR 0x00e4 /* Write Protection Mode[2] */
|
# define ATMCI_ACKRCV ( 1 << 28) /* Boot Operation Acknowledge Received */
|
||||||
# define MCI_WP_EN ( 1 << 0) /* WP Enable */
|
# define ATMCI_ACKRCVE ( 1 << 29) /* Boot Operation Acknowledge Error */
|
||||||
# define MCI_WP_KEY (0x4d4349 << 8) /* WP Key */
|
# define ATMCI_OVRE ( 1 << 30) /* RX Overrun Error */
|
||||||
#define MCI_WPSR 0x00e8 /* Write Protection Status[2] */
|
# define ATMCI_UNRE ( 1 << 31) /* TX Underrun Error */
|
||||||
# define MCI_GET_WP_VS(x) ((x) & 0x0f)
|
#define ATMCI_DMA 0x0050 /* DMA Configuration[2] */
|
||||||
# define MCI_GET_WP_VSRC(x) (((x) >> 8) & 0xffff)
|
# define ATMCI_DMA_OFFSET(x) ((x) << 0) /* DMA Write Buffer Offset */
|
||||||
#define MCI_FIFO_APERTURE 0x0200 /* FIFO Aperture[2] */
|
# define ATMCI_DMA_CHKSIZE(x) ((x) << 4) /* DMA Channel Read and Write Chunk Size */
|
||||||
|
# define ATMCI_DMAEN ( 1 << 8) /* DMA Hardware Handshaking Enable */
|
||||||
|
#define ATMCI_CFG 0x0054 /* Configuration[2] */
|
||||||
|
# define ATMCI_CFG_FIFOMODE_1DATA ( 1 << 0) /* MCI Internal FIFO control mode */
|
||||||
|
# define ATMCI_CFG_FERRCTRL_COR ( 1 << 4) /* Flow Error flag reset control mode */
|
||||||
|
# define ATMCI_CFG_HSMODE ( 1 << 8) /* High Speed Mode */
|
||||||
|
# define ATMCI_CFG_LSYNC ( 1 << 12) /* Synchronize on the last block */
|
||||||
|
#define ATMCI_WPMR 0x00e4 /* Write Protection Mode[2] */
|
||||||
|
# define ATMCI_WP_EN ( 1 << 0) /* WP Enable */
|
||||||
|
# define ATMCI_WP_KEY (0x4d4349 << 8) /* WP Key */
|
||||||
|
#define ATMCI_WPSR 0x00e8 /* Write Protection Status[2] */
|
||||||
|
# define ATMCI_GET_WP_VS(x) ((x) & 0x0f)
|
||||||
|
# define ATMCI_GET_WP_VSRC(x) (((x) >> 8) & 0xffff)
|
||||||
|
#define ATMCI_VERSION 0x00FC /* Version */
|
||||||
|
#define ATMCI_FIFO_APERTURE 0x0200 /* FIFO Aperture[2] */
|
||||||
|
|
||||||
/* This is not including the FIFO Aperture on MCI2 */
|
/* This is not including the FIFO Aperture on MCI2 */
|
||||||
#define MCI_REGS_SIZE 0x100
|
#define ATMCI_REGS_SIZE 0x100
|
||||||
|
|
||||||
/* Register access macros */
|
/* Register access macros */
|
||||||
#define mci_readl(port,reg) \
|
#define atmci_readl(port,reg) \
|
||||||
__raw_readl((port)->regs + MCI_##reg)
|
__raw_readl((port)->regs + reg)
|
||||||
#define mci_writel(port,reg,value) \
|
#define atmci_writel(port,reg,value) \
|
||||||
__raw_writel((value), (port)->regs + MCI_##reg)
|
__raw_writel((value), (port)->regs + reg)
|
||||||
|
|
||||||
#endif /* __DRIVERS_MMC_ATMEL_MCI_H__ */
|
#endif /* __DRIVERS_MMC_ATMEL_MCI_H__ */
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -55,7 +55,7 @@
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#define DBG(fmt, idx, args...) \
|
#define DBG(fmt, idx, args...) \
|
||||||
printk(KERN_DEBUG "au1xmmc(%d): DEBUG: " fmt, idx, ##args)
|
pr_debug("au1xmmc(%d): DEBUG: " fmt, idx, ##args)
|
||||||
#else
|
#else
|
||||||
#define DBG(fmt, idx, args...) do {} while (0)
|
#define DBG(fmt, idx, args...) do {} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
@ -268,7 +268,7 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
|
||||||
mmccmd |= SD_CMD_RT_3;
|
mmccmd |= SD_CMD_RT_3;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printk(KERN_INFO "au1xmmc: unhandled response type %02x\n",
|
pr_info("au1xmmc: unhandled response type %02x\n",
|
||||||
mmc_resp_type(cmd));
|
mmc_resp_type(cmd));
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -1031,7 +1031,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
|
||||||
#ifdef CONFIG_SOC_AU1200
|
#ifdef CONFIG_SOC_AU1200
|
||||||
ret = au1xmmc_dbdma_init(host);
|
ret = au1xmmc_dbdma_init(host);
|
||||||
if (ret)
|
if (ret)
|
||||||
printk(KERN_INFO DRIVER_NAME ": DBDMA init failed; using PIO\n");
|
pr_info(DRIVER_NAME ": DBDMA init failed; using PIO\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_LEDS_CLASS
|
#ifdef CONFIG_LEDS_CLASS
|
||||||
|
@ -1056,7 +1056,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
platform_set_drvdata(pdev, host);
|
platform_set_drvdata(pdev, host);
|
||||||
|
|
||||||
printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X"
|
pr_info(DRIVER_NAME ": MMC Controller %d set up at %8.8X"
|
||||||
" (mode=%s)\n", pdev->id, host->iobase,
|
" (mode=%s)\n", pdev->id, host->iobase,
|
||||||
host->flags & HOST_F_DMA ? "dma" : "pio");
|
host->flags & HOST_F_DMA ? "dma" : "pio");
|
||||||
|
|
||||||
|
@ -1188,7 +1188,7 @@ static int __init au1xmmc_init(void)
|
||||||
*/
|
*/
|
||||||
memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev);
|
memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev);
|
||||||
if (!memid)
|
if (!memid)
|
||||||
printk(KERN_ERR "au1xmmc: cannot add memory dbdma dev\n");
|
pr_err("au1xmmc: cannot add memory dbdma dev\n");
|
||||||
#endif
|
#endif
|
||||||
return platform_driver_register(&au1xmmc_driver);
|
return platform_driver_register(&au1xmmc_driver);
|
||||||
}
|
}
|
||||||
|
|
|
@ -764,11 +764,29 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
|
||||||
return present;
|
return present;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
|
||||||
|
{
|
||||||
|
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||||
|
struct dw_mci *host = slot->host;
|
||||||
|
u32 int_mask;
|
||||||
|
|
||||||
|
/* Enable/disable Slot Specific SDIO interrupt */
|
||||||
|
int_mask = mci_readl(host, INTMASK);
|
||||||
|
if (enb) {
|
||||||
|
mci_writel(host, INTMASK,
|
||||||
|
(int_mask | (1 << SDMMC_INT_SDIO(slot->id))));
|
||||||
|
} else {
|
||||||
|
mci_writel(host, INTMASK,
|
||||||
|
(int_mask & ~(1 << SDMMC_INT_SDIO(slot->id))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const struct mmc_host_ops dw_mci_ops = {
|
static const struct mmc_host_ops dw_mci_ops = {
|
||||||
.request = dw_mci_request,
|
.request = dw_mci_request,
|
||||||
.set_ios = dw_mci_set_ios,
|
.set_ios = dw_mci_set_ios,
|
||||||
.get_ro = dw_mci_get_ro,
|
.get_ro = dw_mci_get_ro,
|
||||||
.get_cd = dw_mci_get_cd,
|
.get_cd = dw_mci_get_cd,
|
||||||
|
.enable_sdio_irq = dw_mci_enable_sdio_irq,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
|
static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
|
||||||
|
@ -1025,7 +1043,8 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
|
||||||
buf += len;
|
buf += len;
|
||||||
cnt -= len;
|
cnt -= len;
|
||||||
if (!sg_next(host->sg) || host->part_buf_count == 2) {
|
if (!sg_next(host->sg) || host->part_buf_count == 2) {
|
||||||
mci_writew(host, DATA, host->part_buf16);
|
mci_writew(host, DATA(host->data_offset),
|
||||||
|
host->part_buf16);
|
||||||
host->part_buf_count = 0;
|
host->part_buf_count = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1042,21 +1061,23 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
|
||||||
cnt -= len;
|
cnt -= len;
|
||||||
/* push data from aligned buffer into fifo */
|
/* push data from aligned buffer into fifo */
|
||||||
for (i = 0; i < items; ++i)
|
for (i = 0; i < items; ++i)
|
||||||
mci_writew(host, DATA, aligned_buf[i]);
|
mci_writew(host, DATA(host->data_offset),
|
||||||
|
aligned_buf[i]);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
u16 *pdata = buf;
|
u16 *pdata = buf;
|
||||||
for (; cnt >= 2; cnt -= 2)
|
for (; cnt >= 2; cnt -= 2)
|
||||||
mci_writew(host, DATA, *pdata++);
|
mci_writew(host, DATA(host->data_offset), *pdata++);
|
||||||
buf = pdata;
|
buf = pdata;
|
||||||
}
|
}
|
||||||
/* put anything remaining in the part_buf */
|
/* put anything remaining in the part_buf */
|
||||||
if (cnt) {
|
if (cnt) {
|
||||||
dw_mci_set_part_bytes(host, buf, cnt);
|
dw_mci_set_part_bytes(host, buf, cnt);
|
||||||
if (!sg_next(host->sg))
|
if (!sg_next(host->sg))
|
||||||
mci_writew(host, DATA, host->part_buf16);
|
mci_writew(host, DATA(host->data_offset),
|
||||||
|
host->part_buf16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1071,7 +1092,8 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
|
||||||
int items = len >> 1;
|
int items = len >> 1;
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < items; ++i)
|
for (i = 0; i < items; ++i)
|
||||||
aligned_buf[i] = mci_readw(host, DATA);
|
aligned_buf[i] = mci_readw(host,
|
||||||
|
DATA(host->data_offset));
|
||||||
/* memcpy from aligned buffer into output buffer */
|
/* memcpy from aligned buffer into output buffer */
|
||||||
memcpy(buf, aligned_buf, len);
|
memcpy(buf, aligned_buf, len);
|
||||||
buf += len;
|
buf += len;
|
||||||
|
@ -1082,11 +1104,11 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
|
||||||
{
|
{
|
||||||
u16 *pdata = buf;
|
u16 *pdata = buf;
|
||||||
for (; cnt >= 2; cnt -= 2)
|
for (; cnt >= 2; cnt -= 2)
|
||||||
*pdata++ = mci_readw(host, DATA);
|
*pdata++ = mci_readw(host, DATA(host->data_offset));
|
||||||
buf = pdata;
|
buf = pdata;
|
||||||
}
|
}
|
||||||
if (cnt) {
|
if (cnt) {
|
||||||
host->part_buf16 = mci_readw(host, DATA);
|
host->part_buf16 = mci_readw(host, DATA(host->data_offset));
|
||||||
dw_mci_pull_final_bytes(host, buf, cnt);
|
dw_mci_pull_final_bytes(host, buf, cnt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1099,7 +1121,8 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
|
||||||
buf += len;
|
buf += len;
|
||||||
cnt -= len;
|
cnt -= len;
|
||||||
if (!sg_next(host->sg) || host->part_buf_count == 4) {
|
if (!sg_next(host->sg) || host->part_buf_count == 4) {
|
||||||
mci_writel(host, DATA, host->part_buf32);
|
mci_writel(host, DATA(host->data_offset),
|
||||||
|
host->part_buf32);
|
||||||
host->part_buf_count = 0;
|
host->part_buf_count = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1116,21 +1139,23 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
|
||||||
cnt -= len;
|
cnt -= len;
|
||||||
/* push data from aligned buffer into fifo */
|
/* push data from aligned buffer into fifo */
|
||||||
for (i = 0; i < items; ++i)
|
for (i = 0; i < items; ++i)
|
||||||
mci_writel(host, DATA, aligned_buf[i]);
|
mci_writel(host, DATA(host->data_offset),
|
||||||
|
aligned_buf[i]);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
u32 *pdata = buf;
|
u32 *pdata = buf;
|
||||||
for (; cnt >= 4; cnt -= 4)
|
for (; cnt >= 4; cnt -= 4)
|
||||||
mci_writel(host, DATA, *pdata++);
|
mci_writel(host, DATA(host->data_offset), *pdata++);
|
||||||
buf = pdata;
|
buf = pdata;
|
||||||
}
|
}
|
||||||
/* put anything remaining in the part_buf */
|
/* put anything remaining in the part_buf */
|
||||||
if (cnt) {
|
if (cnt) {
|
||||||
dw_mci_set_part_bytes(host, buf, cnt);
|
dw_mci_set_part_bytes(host, buf, cnt);
|
||||||
if (!sg_next(host->sg))
|
if (!sg_next(host->sg))
|
||||||
mci_writel(host, DATA, host->part_buf32);
|
mci_writel(host, DATA(host->data_offset),
|
||||||
|
host->part_buf32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1145,7 +1170,8 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
|
||||||
int items = len >> 2;
|
int items = len >> 2;
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < items; ++i)
|
for (i = 0; i < items; ++i)
|
||||||
aligned_buf[i] = mci_readl(host, DATA);
|
aligned_buf[i] = mci_readl(host,
|
||||||
|
DATA(host->data_offset));
|
||||||
/* memcpy from aligned buffer into output buffer */
|
/* memcpy from aligned buffer into output buffer */
|
||||||
memcpy(buf, aligned_buf, len);
|
memcpy(buf, aligned_buf, len);
|
||||||
buf += len;
|
buf += len;
|
||||||
|
@ -1156,11 +1182,11 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
|
||||||
{
|
{
|
||||||
u32 *pdata = buf;
|
u32 *pdata = buf;
|
||||||
for (; cnt >= 4; cnt -= 4)
|
for (; cnt >= 4; cnt -= 4)
|
||||||
*pdata++ = mci_readl(host, DATA);
|
*pdata++ = mci_readl(host, DATA(host->data_offset));
|
||||||
buf = pdata;
|
buf = pdata;
|
||||||
}
|
}
|
||||||
if (cnt) {
|
if (cnt) {
|
||||||
host->part_buf32 = mci_readl(host, DATA);
|
host->part_buf32 = mci_readl(host, DATA(host->data_offset));
|
||||||
dw_mci_pull_final_bytes(host, buf, cnt);
|
dw_mci_pull_final_bytes(host, buf, cnt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1173,7 +1199,8 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
|
||||||
buf += len;
|
buf += len;
|
||||||
cnt -= len;
|
cnt -= len;
|
||||||
if (!sg_next(host->sg) || host->part_buf_count == 8) {
|
if (!sg_next(host->sg) || host->part_buf_count == 8) {
|
||||||
mci_writew(host, DATA, host->part_buf);
|
mci_writew(host, DATA(host->data_offset),
|
||||||
|
host->part_buf);
|
||||||
host->part_buf_count = 0;
|
host->part_buf_count = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1190,21 +1217,23 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
|
||||||
cnt -= len;
|
cnt -= len;
|
||||||
/* push data from aligned buffer into fifo */
|
/* push data from aligned buffer into fifo */
|
||||||
for (i = 0; i < items; ++i)
|
for (i = 0; i < items; ++i)
|
||||||
mci_writeq(host, DATA, aligned_buf[i]);
|
mci_writeq(host, DATA(host->data_offset),
|
||||||
|
aligned_buf[i]);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
u64 *pdata = buf;
|
u64 *pdata = buf;
|
||||||
for (; cnt >= 8; cnt -= 8)
|
for (; cnt >= 8; cnt -= 8)
|
||||||
mci_writeq(host, DATA, *pdata++);
|
mci_writeq(host, DATA(host->data_offset), *pdata++);
|
||||||
buf = pdata;
|
buf = pdata;
|
||||||
}
|
}
|
||||||
/* put anything remaining in the part_buf */
|
/* put anything remaining in the part_buf */
|
||||||
if (cnt) {
|
if (cnt) {
|
||||||
dw_mci_set_part_bytes(host, buf, cnt);
|
dw_mci_set_part_bytes(host, buf, cnt);
|
||||||
if (!sg_next(host->sg))
|
if (!sg_next(host->sg))
|
||||||
mci_writeq(host, DATA, host->part_buf);
|
mci_writeq(host, DATA(host->data_offset),
|
||||||
|
host->part_buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1219,7 +1248,8 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
|
||||||
int items = len >> 3;
|
int items = len >> 3;
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < items; ++i)
|
for (i = 0; i < items; ++i)
|
||||||
aligned_buf[i] = mci_readq(host, DATA);
|
aligned_buf[i] = mci_readq(host,
|
||||||
|
DATA(host->data_offset));
|
||||||
/* memcpy from aligned buffer into output buffer */
|
/* memcpy from aligned buffer into output buffer */
|
||||||
memcpy(buf, aligned_buf, len);
|
memcpy(buf, aligned_buf, len);
|
||||||
buf += len;
|
buf += len;
|
||||||
|
@ -1230,11 +1260,11 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
|
||||||
{
|
{
|
||||||
u64 *pdata = buf;
|
u64 *pdata = buf;
|
||||||
for (; cnt >= 8; cnt -= 8)
|
for (; cnt >= 8; cnt -= 8)
|
||||||
*pdata++ = mci_readq(host, DATA);
|
*pdata++ = mci_readq(host, DATA(host->data_offset));
|
||||||
buf = pdata;
|
buf = pdata;
|
||||||
}
|
}
|
||||||
if (cnt) {
|
if (cnt) {
|
||||||
host->part_buf = mci_readq(host, DATA);
|
host->part_buf = mci_readq(host, DATA(host->data_offset));
|
||||||
dw_mci_pull_final_bytes(host, buf, cnt);
|
dw_mci_pull_final_bytes(host, buf, cnt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1406,6 +1436,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
||||||
struct dw_mci *host = dev_id;
|
struct dw_mci *host = dev_id;
|
||||||
u32 status, pending;
|
u32 status, pending;
|
||||||
unsigned int pass_count = 0;
|
unsigned int pass_count = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
status = mci_readl(host, RINTSTS);
|
status = mci_readl(host, RINTSTS);
|
||||||
|
@ -1477,6 +1508,15 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
||||||
queue_work(dw_mci_card_workqueue, &host->card_work);
|
queue_work(dw_mci_card_workqueue, &host->card_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle SDIO Interrupts */
|
||||||
|
for (i = 0; i < host->num_slots; i++) {
|
||||||
|
struct dw_mci_slot *slot = host->slot[i];
|
||||||
|
if (pending & SDMMC_INT_SDIO(i)) {
|
||||||
|
mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i));
|
||||||
|
mmc_signal_sdio_irq(slot->mmc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} while (pass_count++ < 5);
|
} while (pass_count++ < 5);
|
||||||
|
|
||||||
#ifdef CONFIG_MMC_DW_IDMAC
|
#ifdef CONFIG_MMC_DW_IDMAC
|
||||||
|
@ -1673,7 +1713,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
||||||
|
|
||||||
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
|
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
|
||||||
if (IS_ERR(host->vmmc)) {
|
if (IS_ERR(host->vmmc)) {
|
||||||
printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc));
|
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
|
||||||
host->vmmc = NULL;
|
host->vmmc = NULL;
|
||||||
} else
|
} else
|
||||||
regulator_enable(host->vmmc);
|
regulator_enable(host->vmmc);
|
||||||
|
@ -1923,6 +1963,18 @@ static int dw_mci_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In 2.40a spec, Data offset is changed.
|
||||||
|
* Need to check the version-id and set data-offset for DATA register.
|
||||||
|
*/
|
||||||
|
host->verid = SDMMC_GET_VERID(mci_readl(host, VERID));
|
||||||
|
dev_info(&pdev->dev, "Version ID is %04x\n", host->verid);
|
||||||
|
|
||||||
|
if (host->verid < DW_MMC_240A)
|
||||||
|
host->data_offset = DATA_OFFSET;
|
||||||
|
else
|
||||||
|
host->data_offset = DATA_240A_OFFSET;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enable interrupts for command done, data over, data empty, card det,
|
* Enable interrupts for command done, data over, data empty, card det,
|
||||||
* receive ready and error such as transmit, receive timeout, crc error
|
* receive ready and error such as transmit, receive timeout, crc error
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#ifndef _DW_MMC_H_
|
#ifndef _DW_MMC_H_
|
||||||
#define _DW_MMC_H_
|
#define _DW_MMC_H_
|
||||||
|
|
||||||
|
#define DW_MMC_240A 0x240a
|
||||||
|
|
||||||
#define SDMMC_CTRL 0x000
|
#define SDMMC_CTRL 0x000
|
||||||
#define SDMMC_PWREN 0x004
|
#define SDMMC_PWREN 0x004
|
||||||
#define SDMMC_CLKDIV 0x008
|
#define SDMMC_CLKDIV 0x008
|
||||||
|
@ -51,7 +53,14 @@
|
||||||
#define SDMMC_IDINTEN 0x090
|
#define SDMMC_IDINTEN 0x090
|
||||||
#define SDMMC_DSCADDR 0x094
|
#define SDMMC_DSCADDR 0x094
|
||||||
#define SDMMC_BUFADDR 0x098
|
#define SDMMC_BUFADDR 0x098
|
||||||
#define SDMMC_DATA 0x100
|
#define SDMMC_DATA(x) (x)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Data offset is difference according to Version
|
||||||
|
* Lower than 2.40a : data register offest is 0x100
|
||||||
|
*/
|
||||||
|
#define DATA_OFFSET 0x100
|
||||||
|
#define DATA_240A_OFFSET 0x200
|
||||||
|
|
||||||
/* shift bit field */
|
/* shift bit field */
|
||||||
#define _SBF(f, v) ((v) << (f))
|
#define _SBF(f, v) ((v) << (f))
|
||||||
|
@ -82,7 +91,7 @@
|
||||||
#define SDMMC_CTYPE_4BIT BIT(0)
|
#define SDMMC_CTYPE_4BIT BIT(0)
|
||||||
#define SDMMC_CTYPE_1BIT 0
|
#define SDMMC_CTYPE_1BIT 0
|
||||||
/* Interrupt status & mask register defines */
|
/* Interrupt status & mask register defines */
|
||||||
#define SDMMC_INT_SDIO BIT(16)
|
#define SDMMC_INT_SDIO(n) BIT(16 + (n))
|
||||||
#define SDMMC_INT_EBE BIT(15)
|
#define SDMMC_INT_EBE BIT(15)
|
||||||
#define SDMMC_INT_ACD BIT(14)
|
#define SDMMC_INT_ACD BIT(14)
|
||||||
#define SDMMC_INT_SBE BIT(13)
|
#define SDMMC_INT_SBE BIT(13)
|
||||||
|
@ -130,6 +139,8 @@
|
||||||
#define SDMMC_IDMAC_ENABLE BIT(7)
|
#define SDMMC_IDMAC_ENABLE BIT(7)
|
||||||
#define SDMMC_IDMAC_FB BIT(1)
|
#define SDMMC_IDMAC_FB BIT(1)
|
||||||
#define SDMMC_IDMAC_SWRESET BIT(0)
|
#define SDMMC_IDMAC_SWRESET BIT(0)
|
||||||
|
/* Version ID register define */
|
||||||
|
#define SDMMC_GET_VERID(x) ((x) & 0xFFFF)
|
||||||
|
|
||||||
/* Register access macros */
|
/* Register access macros */
|
||||||
#define mci_readl(dev, reg) \
|
#define mci_readl(dev, reg) \
|
||||||
|
|
|
@ -942,7 +942,7 @@ static int __init imxmci_probe(struct platform_device *pdev)
|
||||||
int ret = 0, irq;
|
int ret = 0, irq;
|
||||||
u16 rev_no;
|
u16 rev_no;
|
||||||
|
|
||||||
printk(KERN_INFO "i.MX mmc driver\n");
|
pr_info("i.MX mmc driver\n");
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/bio.h>
|
#include <linux/bio.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/crc7.h>
|
#include <linux/crc7.h>
|
||||||
|
|
|
@ -466,7 +466,7 @@ static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data)
|
||||||
struct mmci_host_next *next = &host->next_data;
|
struct mmci_host_next *next = &host->next_data;
|
||||||
|
|
||||||
if (data->host_cookie && data->host_cookie != next->cookie) {
|
if (data->host_cookie && data->host_cookie != next->cookie) {
|
||||||
printk(KERN_WARNING "[%s] invalid cookie: data->host_cookie %d"
|
pr_warning("[%s] invalid cookie: data->host_cookie %d"
|
||||||
" host->next_data.cookie %d\n",
|
" host->next_data.cookie %d\n",
|
||||||
__func__, data->host_cookie, host->next_data.cookie);
|
__func__, data->host_cookie, host->next_data.cookie);
|
||||||
data->host_cookie = 0;
|
data->host_cookie = 0;
|
||||||
|
@ -531,7 +531,7 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
|
||||||
if (chan) {
|
if (chan) {
|
||||||
if (err)
|
if (err)
|
||||||
dmaengine_terminate_all(chan);
|
dmaengine_terminate_all(chan);
|
||||||
if (err || data->host_cookie)
|
if (data->host_cookie)
|
||||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
||||||
data->sg_len, dir);
|
data->sg_len, dir);
|
||||||
mrq->data->host_cookie = 0;
|
mrq->data->host_cookie = 0;
|
||||||
|
|
|
@ -213,7 +213,8 @@ msmsdcc_dma_exec_func(struct msm_dmov_cmd *cmd)
|
||||||
msmsdcc_writel(host, host->cmd_timeout, MMCIDATATIMER);
|
msmsdcc_writel(host, host->cmd_timeout, MMCIDATATIMER);
|
||||||
msmsdcc_writel(host, (unsigned int)host->curr.xfer_size,
|
msmsdcc_writel(host, (unsigned int)host->curr.xfer_size,
|
||||||
MMCIDATALENGTH);
|
MMCIDATALENGTH);
|
||||||
msmsdcc_writel(host, host->cmd_pio_irqmask, MMCIMASK1);
|
msmsdcc_writel(host, (msmsdcc_readl(host, MMCIMASK0) &
|
||||||
|
(~MCI_IRQ_PIO)) | host->cmd_pio_irqmask, MMCIMASK0);
|
||||||
msmsdcc_writel(host, host->cmd_datactrl, MMCIDATACTRL);
|
msmsdcc_writel(host, host->cmd_datactrl, MMCIDATACTRL);
|
||||||
|
|
||||||
if (host->cmd_cmd) {
|
if (host->cmd_cmd) {
|
||||||
|
@ -388,7 +389,7 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data)
|
||||||
n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
|
n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
|
||||||
host->dma.num_ents, host->dma.dir);
|
host->dma.num_ents, host->dma.dir);
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
printk(KERN_ERR "%s: Unable to map in all sg elements\n",
|
pr_err("%s: Unable to map in all sg elements\n",
|
||||||
mmc_hostname(host->mmc));
|
mmc_hostname(host->mmc));
|
||||||
host->dma.sg = NULL;
|
host->dma.sg = NULL;
|
||||||
host->dma.num_ents = 0;
|
host->dma.num_ents = 0;
|
||||||
|
@ -474,7 +475,7 @@ msmsdcc_start_command_deferred(struct msmsdcc_host *host,
|
||||||
*c |= MCI_CSPM_MCIABORT;
|
*c |= MCI_CSPM_MCIABORT;
|
||||||
|
|
||||||
if (host->curr.cmd != NULL) {
|
if (host->curr.cmd != NULL) {
|
||||||
printk(KERN_ERR "%s: Overlapping command requests\n",
|
pr_err("%s: Overlapping command requests\n",
|
||||||
mmc_hostname(host->mmc));
|
mmc_hostname(host->mmc));
|
||||||
}
|
}
|
||||||
host->curr.cmd = cmd;
|
host->curr.cmd = cmd;
|
||||||
|
@ -543,7 +544,9 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data,
|
||||||
|
|
||||||
msmsdcc_writel(host, host->curr.xfer_size, MMCIDATALENGTH);
|
msmsdcc_writel(host, host->curr.xfer_size, MMCIDATALENGTH);
|
||||||
|
|
||||||
msmsdcc_writel(host, pio_irqmask, MMCIMASK1);
|
msmsdcc_writel(host, (msmsdcc_readl(host, MMCIMASK0) &
|
||||||
|
(~MCI_IRQ_PIO)) | pio_irqmask, MMCIMASK0);
|
||||||
|
|
||||||
msmsdcc_writel(host, datactrl, MMCIDATACTRL);
|
msmsdcc_writel(host, datactrl, MMCIDATACTRL);
|
||||||
|
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
|
@ -659,8 +662,13 @@ msmsdcc_pio_irq(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct msmsdcc_host *host = dev_id;
|
struct msmsdcc_host *host = dev_id;
|
||||||
uint32_t status;
|
uint32_t status;
|
||||||
|
u32 mci_mask0;
|
||||||
|
|
||||||
status = msmsdcc_readl(host, MMCISTATUS);
|
status = msmsdcc_readl(host, MMCISTATUS);
|
||||||
|
mci_mask0 = msmsdcc_readl(host, MMCIMASK0);
|
||||||
|
|
||||||
|
if (((mci_mask0 & status) & MCI_IRQ_PIO) == 0)
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -719,10 +727,12 @@ msmsdcc_pio_irq(int irq, void *dev_id)
|
||||||
} while (1);
|
} while (1);
|
||||||
|
|
||||||
if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE)
|
if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE)
|
||||||
msmsdcc_writel(host, MCI_RXDATAAVLBLMASK, MMCIMASK1);
|
msmsdcc_writel(host, (mci_mask0 & (~MCI_IRQ_PIO)) |
|
||||||
|
MCI_RXDATAAVLBLMASK, MMCIMASK0);
|
||||||
|
|
||||||
if (!host->curr.xfer_remain)
|
if (!host->curr.xfer_remain)
|
||||||
msmsdcc_writel(host, 0, MMCIMASK1);
|
msmsdcc_writel(host, (mci_mask0 & (~MCI_IRQ_PIO)) | 0,
|
||||||
|
MMCIMASK0);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -854,6 +864,8 @@ msmsdcc_irq(int irq, void *dev_id)
|
||||||
do {
|
do {
|
||||||
status = msmsdcc_readl(host, MMCISTATUS);
|
status = msmsdcc_readl(host, MMCISTATUS);
|
||||||
status &= msmsdcc_readl(host, MMCIMASK0);
|
status &= msmsdcc_readl(host, MMCIMASK0);
|
||||||
|
if ((status & (~MCI_IRQ_PIO)) == 0)
|
||||||
|
break;
|
||||||
msmsdcc_writel(host, status, MMCICLEAR);
|
msmsdcc_writel(host, status, MMCICLEAR);
|
||||||
|
|
||||||
if (status & MCI_SDIOINTR)
|
if (status & MCI_SDIOINTR)
|
||||||
|
@ -939,7 +951,7 @@ static void msmsdcc_setup_gpio(struct msmsdcc_host *host, bool enable)
|
||||||
struct msm_mmc_gpio_data *curr;
|
struct msm_mmc_gpio_data *curr;
|
||||||
int i, rc = 0;
|
int i, rc = 0;
|
||||||
|
|
||||||
if (!host->plat->gpio_data && host->gpio_config_status == enable)
|
if (!host->plat->gpio_data || host->gpio_config_status == enable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
curr = host->plat->gpio_data;
|
curr = host->plat->gpio_data;
|
||||||
|
@ -1052,10 +1064,19 @@ static void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void msmsdcc_init_card(struct mmc_host *mmc, struct mmc_card *card)
|
||||||
|
{
|
||||||
|
struct msmsdcc_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
|
if (host->plat->init_card)
|
||||||
|
host->plat->init_card(card);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct mmc_host_ops msmsdcc_ops = {
|
static const struct mmc_host_ops msmsdcc_ops = {
|
||||||
.request = msmsdcc_request,
|
.request = msmsdcc_request,
|
||||||
.set_ios = msmsdcc_set_ios,
|
.set_ios = msmsdcc_set_ios,
|
||||||
.enable_sdio_irq = msmsdcc_enable_sdio_irq,
|
.enable_sdio_irq = msmsdcc_enable_sdio_irq,
|
||||||
|
.init_card = msmsdcc_init_card,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1092,7 +1113,7 @@ msmsdcc_platform_status_irq(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct msmsdcc_host *host = dev_id;
|
struct msmsdcc_host *host = dev_id;
|
||||||
|
|
||||||
printk(KERN_DEBUG "%s: %d\n", __func__, irq);
|
pr_debug("%s: %d\n", __func__, irq);
|
||||||
msmsdcc_check_status((unsigned long) host);
|
msmsdcc_check_status((unsigned long) host);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -1102,7 +1123,7 @@ msmsdcc_status_notify_cb(int card_present, void *dev_id)
|
||||||
{
|
{
|
||||||
struct msmsdcc_host *host = dev_id;
|
struct msmsdcc_host *host = dev_id;
|
||||||
|
|
||||||
printk(KERN_DEBUG "%s: card_present %d\n", mmc_hostname(host->mmc),
|
pr_debug("%s: card_present %d\n", mmc_hostname(host->mmc),
|
||||||
card_present);
|
card_present);
|
||||||
msmsdcc_check_status((unsigned long) host);
|
msmsdcc_check_status((unsigned long) host);
|
||||||
}
|
}
|
||||||
|
@ -1150,7 +1171,6 @@ msmsdcc_probe(struct platform_device *pdev)
|
||||||
struct msmsdcc_host *host;
|
struct msmsdcc_host *host;
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct resource *cmd_irqres = NULL;
|
struct resource *cmd_irqres = NULL;
|
||||||
struct resource *pio_irqres = NULL;
|
|
||||||
struct resource *stat_irqres = NULL;
|
struct resource *stat_irqres = NULL;
|
||||||
struct resource *memres = NULL;
|
struct resource *memres = NULL;
|
||||||
struct resource *dmares = NULL;
|
struct resource *dmares = NULL;
|
||||||
|
@ -1175,12 +1195,10 @@ msmsdcc_probe(struct platform_device *pdev)
|
||||||
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||||
cmd_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
|
cmd_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
|
||||||
"cmd_irq");
|
"cmd_irq");
|
||||||
pio_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
|
|
||||||
"pio_irq");
|
|
||||||
stat_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
|
stat_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
|
||||||
"status_irq");
|
"status_irq");
|
||||||
|
|
||||||
if (!cmd_irqres || !pio_irqres || !memres) {
|
if (!cmd_irqres || !memres) {
|
||||||
pr_err("%s: Invalid resource\n", __func__);
|
pr_err("%s: Invalid resource\n", __func__);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
@ -1200,17 +1218,20 @@ msmsdcc_probe(struct platform_device *pdev)
|
||||||
host->plat = plat;
|
host->plat = plat;
|
||||||
host->mmc = mmc;
|
host->mmc = mmc;
|
||||||
host->curr.cmd = NULL;
|
host->curr.cmd = NULL;
|
||||||
|
init_timer(&host->busclk_timer);
|
||||||
|
host->busclk_timer.data = (unsigned long) host;
|
||||||
|
host->busclk_timer.function = msmsdcc_busclk_expired;
|
||||||
|
|
||||||
|
|
||||||
host->cmdpoll = 1;
|
host->cmdpoll = 1;
|
||||||
|
|
||||||
host->base = ioremap(memres->start, PAGE_SIZE);
|
host->base = ioremap(memres->start, PAGE_SIZE);
|
||||||
if (!host->base) {
|
if (!host->base) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto host_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
host->cmd_irqres = cmd_irqres;
|
host->cmd_irqres = cmd_irqres;
|
||||||
host->pio_irqres = pio_irqres;
|
|
||||||
host->memres = memres;
|
host->memres = memres;
|
||||||
host->dmares = dmares;
|
host->dmares = dmares;
|
||||||
spin_lock_init(&host->lock);
|
spin_lock_init(&host->lock);
|
||||||
|
@ -1221,13 +1242,19 @@ msmsdcc_probe(struct platform_device *pdev)
|
||||||
/*
|
/*
|
||||||
* Setup DMA
|
* Setup DMA
|
||||||
*/
|
*/
|
||||||
msmsdcc_init_dma(host);
|
if (host->dmares) {
|
||||||
|
ret = msmsdcc_init_dma(host);
|
||||||
|
if (ret)
|
||||||
|
goto ioremap_free;
|
||||||
|
} else {
|
||||||
|
host->dma.channel = -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get our clocks */
|
/* Get our clocks */
|
||||||
host->pclk = clk_get(&pdev->dev, "sdc_pclk");
|
host->pclk = clk_get(&pdev->dev, "sdc_pclk");
|
||||||
if (IS_ERR(host->pclk)) {
|
if (IS_ERR(host->pclk)) {
|
||||||
ret = PTR_ERR(host->pclk);
|
ret = PTR_ERR(host->pclk);
|
||||||
goto host_free;
|
goto dma_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
host->clk = clk_get(&pdev->dev, "sdc_clk");
|
host->clk = clk_get(&pdev->dev, "sdc_clk");
|
||||||
|
@ -1236,17 +1263,17 @@ msmsdcc_probe(struct platform_device *pdev)
|
||||||
goto pclk_put;
|
goto pclk_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = clk_set_rate(host->clk, msmsdcc_fmin);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s: Clock rate set failed (%d)\n", __func__, ret);
|
||||||
|
goto clk_put;
|
||||||
|
}
|
||||||
|
|
||||||
/* Enable clocks */
|
/* Enable clocks */
|
||||||
ret = msmsdcc_enable_clocks(host);
|
ret = msmsdcc_enable_clocks(host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto clk_put;
|
goto clk_put;
|
||||||
|
|
||||||
ret = clk_set_rate(host->clk, msmsdcc_fmin);
|
|
||||||
if (ret) {
|
|
||||||
pr_err("%s: Clock rate set failed (%d)\n", __func__, ret);
|
|
||||||
goto clk_disable;
|
|
||||||
}
|
|
||||||
|
|
||||||
host->pclk_rate = clk_get_rate(host->pclk);
|
host->pclk_rate = clk_get_rate(host->pclk);
|
||||||
host->clk_rate = clk_get_rate(host->clk);
|
host->clk_rate = clk_get_rate(host->clk);
|
||||||
|
|
||||||
|
@ -1316,16 +1343,12 @@ msmsdcc_probe(struct platform_device *pdev)
|
||||||
host->eject = !host->oldstat;
|
host->eject = !host->oldstat;
|
||||||
}
|
}
|
||||||
|
|
||||||
init_timer(&host->busclk_timer);
|
|
||||||
host->busclk_timer.data = (unsigned long) host;
|
|
||||||
host->busclk_timer.function = msmsdcc_busclk_expired;
|
|
||||||
|
|
||||||
ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED,
|
ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED,
|
||||||
DRIVER_NAME " (cmd)", host);
|
DRIVER_NAME " (cmd)", host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto stat_irq_free;
|
goto stat_irq_free;
|
||||||
|
|
||||||
ret = request_irq(pio_irqres->start, msmsdcc_pio_irq, IRQF_SHARED,
|
ret = request_irq(cmd_irqres->start, msmsdcc_pio_irq, IRQF_SHARED,
|
||||||
DRIVER_NAME " (pio)", host);
|
DRIVER_NAME " (pio)", host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto cmd_irq_free;
|
goto cmd_irq_free;
|
||||||
|
@ -1368,6 +1391,13 @@ msmsdcc_probe(struct platform_device *pdev)
|
||||||
clk_put(host->clk);
|
clk_put(host->clk);
|
||||||
pclk_put:
|
pclk_put:
|
||||||
clk_put(host->pclk);
|
clk_put(host->pclk);
|
||||||
|
dma_free:
|
||||||
|
if (host->dmares)
|
||||||
|
dma_free_coherent(NULL, sizeof(struct msmsdcc_nc_dmadata),
|
||||||
|
host->dma.nc, host->dma.nc_busaddr);
|
||||||
|
ioremap_free:
|
||||||
|
tasklet_kill(&host->dma_tlet);
|
||||||
|
iounmap(host->base);
|
||||||
host_free:
|
host_free:
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
out:
|
out:
|
||||||
|
|
|
@ -140,6 +140,11 @@
|
||||||
MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \
|
MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \
|
||||||
MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK|MCI_PROGDONEMASK)
|
MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK|MCI_PROGDONEMASK)
|
||||||
|
|
||||||
|
#define MCI_IRQ_PIO \
|
||||||
|
(MCI_RXDATAAVLBLMASK | MCI_TXDATAAVLBLMASK | MCI_RXFIFOEMPTYMASK | \
|
||||||
|
MCI_TXFIFOEMPTYMASK | MCI_RXFIFOFULLMASK | MCI_TXFIFOFULLMASK | \
|
||||||
|
MCI_RXFIFOHALFFULLMASK | MCI_TXFIFOHALFEMPTYMASK | \
|
||||||
|
MCI_RXACTIVEMASK | MCI_TXACTIVEMASK)
|
||||||
/*
|
/*
|
||||||
* The size of the FIFO in bytes.
|
* The size of the FIFO in bytes.
|
||||||
*/
|
*/
|
||||||
|
@ -202,7 +207,6 @@ struct msmsdcc_stats {
|
||||||
|
|
||||||
struct msmsdcc_host {
|
struct msmsdcc_host {
|
||||||
struct resource *cmd_irqres;
|
struct resource *cmd_irqres;
|
||||||
struct resource *pio_irqres;
|
|
||||||
struct resource *memres;
|
struct resource *memres;
|
||||||
struct resource *dmares;
|
struct resource *dmares;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
|
|
@ -117,7 +117,7 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
|
||||||
host->pio_size = data->blocks * data->blksz;
|
host->pio_size = data->blocks * data->blksz;
|
||||||
host->pio_ptr = sg_virt(data->sg);
|
host->pio_ptr = sg_virt(data->sg);
|
||||||
if (!nodma)
|
if (!nodma)
|
||||||
printk(KERN_DEBUG "%s: fallback to PIO for data "
|
pr_debug("%s: fallback to PIO for data "
|
||||||
"at 0x%p size %d\n",
|
"at 0x%p size %d\n",
|
||||||
mmc_hostname(host->mmc),
|
mmc_hostname(host->mmc),
|
||||||
host->pio_ptr, host->pio_size);
|
host->pio_ptr, host->pio_size);
|
||||||
|
@ -471,7 +471,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
|
||||||
if (mrq->data)
|
if (mrq->data)
|
||||||
err_status = mvsd_finish_data(host, mrq->data, err_status);
|
err_status = mvsd_finish_data(host, mrq->data, err_status);
|
||||||
if (err_status) {
|
if (err_status) {
|
||||||
printk(KERN_ERR "%s: unhandled error status %#04x\n",
|
pr_err("%s: unhandled error status %#04x\n",
|
||||||
mmc_hostname(host->mmc), err_status);
|
mmc_hostname(host->mmc), err_status);
|
||||||
cmd->error = -ENOMSG;
|
cmd->error = -ENOMSG;
|
||||||
}
|
}
|
||||||
|
@ -489,7 +489,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
|
||||||
if (irq_handled)
|
if (irq_handled)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
printk(KERN_ERR "%s: unhandled interrupt status=0x%04x en=0x%04x "
|
pr_err("%s: unhandled interrupt status=0x%04x en=0x%04x "
|
||||||
"pio=%d\n", mmc_hostname(host->mmc), intr_status,
|
"pio=%d\n", mmc_hostname(host->mmc), intr_status,
|
||||||
host->intr_en, host->pio_size);
|
host->intr_en, host->pio_size);
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
|
@ -505,9 +505,9 @@ static void mvsd_timeout_timer(unsigned long data)
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
mrq = host->mrq;
|
mrq = host->mrq;
|
||||||
if (mrq) {
|
if (mrq) {
|
||||||
printk(KERN_ERR "%s: Timeout waiting for hardware interrupt.\n",
|
pr_err("%s: Timeout waiting for hardware interrupt.\n",
|
||||||
mmc_hostname(host->mmc));
|
mmc_hostname(host->mmc));
|
||||||
printk(KERN_ERR "%s: hw_state=0x%04x, intr_status=0x%04x "
|
pr_err("%s: hw_state=0x%04x, intr_status=0x%04x "
|
||||||
"intr_en=0x%04x\n", mmc_hostname(host->mmc),
|
"intr_en=0x%04x\n", mmc_hostname(host->mmc),
|
||||||
mvsd_read(MVSD_HW_STATE),
|
mvsd_read(MVSD_HW_STATE),
|
||||||
mvsd_read(MVSD_NOR_INTR_STATUS),
|
mvsd_read(MVSD_NOR_INTR_STATUS),
|
||||||
|
@ -762,7 +762,7 @@ static int __init mvsd_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ret = request_irq(irq, mvsd_irq, 0, DRIVER_NAME, host);
|
ret = request_irq(irq, mvsd_irq, 0, DRIVER_NAME, host);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
printk(KERN_ERR "%s: cannot assign irq %d\n", DRIVER_NAME, irq);
|
pr_err("%s: cannot assign irq %d\n", DRIVER_NAME, irq);
|
||||||
goto out;
|
goto out;
|
||||||
} else
|
} else
|
||||||
host->irq = irq;
|
host->irq = irq;
|
||||||
|
@ -802,7 +802,7 @@ static int __init mvsd_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
printk(KERN_NOTICE "%s: %s driver initialized, ",
|
pr_notice("%s: %s driver initialized, ",
|
||||||
mmc_hostname(mmc), DRIVER_NAME);
|
mmc_hostname(mmc), DRIVER_NAME);
|
||||||
if (host->gpio_card_detect)
|
if (host->gpio_card_detect)
|
||||||
printk("using GPIO %d for card detection\n",
|
printk("using GPIO %d for card detection\n",
|
||||||
|
|
|
@ -842,7 +842,7 @@ static int mxcmci_probe(struct platform_device *pdev)
|
||||||
int ret = 0, irq;
|
int ret = 0, irq;
|
||||||
dma_cap_mask_t mask;
|
dma_cap_mask_t mask;
|
||||||
|
|
||||||
printk(KERN_INFO "i.MX SDHC driver\n");
|
pr_info("i.MX SDHC driver\n");
|
||||||
|
|
||||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include <linux/mmc/sdio.h>
|
#include <linux/mmc/sdio.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
|
||||||
#include <mach/mxs.h>
|
#include <mach/mxs.h>
|
||||||
#include <mach/common.h>
|
#include <mach/common.h>
|
||||||
|
|
|
@ -450,15 +450,14 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
|
||||||
* framework is fixed, we need a workaround like this
|
* framework is fixed, we need a workaround like this
|
||||||
* (which is safe for MMC, but not in general).
|
* (which is safe for MMC, but not in general).
|
||||||
*/
|
*/
|
||||||
if (regulator_is_enabled(host->vcc) > 0) {
|
if (regulator_is_enabled(host->vcc) > 0 ||
|
||||||
regulator_enable(host->vcc);
|
(host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
|
||||||
regulator_disable(host->vcc);
|
int vdd = ffs(mmc_slot(host).ocr_mask) - 1;
|
||||||
}
|
|
||||||
if (host->vcc_aux) {
|
mmc_slot(host).set_power(host->dev, host->slot_id,
|
||||||
if (regulator_is_enabled(reg) > 0) {
|
1, vdd);
|
||||||
regulator_enable(reg);
|
mmc_slot(host).set_power(host->dev, host->slot_id,
|
||||||
regulator_disable(reg);
|
0, 0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1264,14 +1263,14 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
|
||||||
host->reqs_blocked = 0;
|
host->reqs_blocked = 0;
|
||||||
if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) {
|
if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) {
|
||||||
if (host->protect_card) {
|
if (host->protect_card) {
|
||||||
printk(KERN_INFO "%s: cover is closed, "
|
pr_info("%s: cover is closed, "
|
||||||
"card is now accessible\n",
|
"card is now accessible\n",
|
||||||
mmc_hostname(host->mmc));
|
mmc_hostname(host->mmc));
|
||||||
host->protect_card = 0;
|
host->protect_card = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!host->protect_card) {
|
if (!host->protect_card) {
|
||||||
printk(KERN_INFO "%s: cover is open, "
|
pr_info"%s: cover is open, "
|
||||||
"card is now inaccessible\n",
|
"card is now inaccessible\n",
|
||||||
mmc_hostname(host->mmc));
|
mmc_hostname(host->mmc));
|
||||||
host->protect_card = 1;
|
host->protect_card = 1;
|
||||||
|
@ -1422,7 +1421,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
|
||||||
|
|
||||||
if (!next && data->host_cookie &&
|
if (!next && data->host_cookie &&
|
||||||
data->host_cookie != host->next_data.cookie) {
|
data->host_cookie != host->next_data.cookie) {
|
||||||
printk(KERN_WARNING "[%s] invalid cookie: data->host_cookie %d"
|
pr_warning("[%s] invalid cookie: data->host_cookie %d"
|
||||||
" host->next_data.cookie %d\n",
|
" host->next_data.cookie %d\n",
|
||||||
__func__, data->host_cookie, host->next_data.cookie);
|
__func__, data->host_cookie, host->next_data.cookie);
|
||||||
data->host_cookie = 0;
|
data->host_cookie = 0;
|
||||||
|
@ -1943,6 +1942,10 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
|
||||||
omap_hsmmc_context_save(host);
|
omap_hsmmc_context_save(host);
|
||||||
|
|
||||||
mmc->caps |= MMC_CAP_DISABLE;
|
mmc->caps |= MMC_CAP_DISABLE;
|
||||||
|
if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) {
|
||||||
|
dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n");
|
||||||
|
mmc->caps2 |= MMC_CAP2_NO_MULTI_READ;
|
||||||
|
}
|
||||||
|
|
||||||
pm_runtime_enable(host->dev);
|
pm_runtime_enable(host->dev);
|
||||||
pm_runtime_get_sync(host->dev);
|
pm_runtime_get_sync(host->dev);
|
||||||
|
@ -2015,7 +2018,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Request IRQ for MMC operations */
|
/* Request IRQ for MMC operations */
|
||||||
ret = request_irq(host->irq, omap_hsmmc_irq, IRQF_DISABLED,
|
ret = request_irq(host->irq, omap_hsmmc_irq, 0,
|
||||||
mmc_hostname(mmc), host);
|
mmc_hostname(mmc), host);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
|
dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
|
||||||
|
@ -2043,8 +2046,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
|
||||||
if ((mmc_slot(host).card_detect_irq)) {
|
if ((mmc_slot(host).card_detect_irq)) {
|
||||||
ret = request_irq(mmc_slot(host).card_detect_irq,
|
ret = request_irq(mmc_slot(host).card_detect_irq,
|
||||||
omap_hsmmc_cd_handler,
|
omap_hsmmc_cd_handler,
|
||||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
|
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||||
| IRQF_DISABLED,
|
|
||||||
mmc_hostname(mmc), host);
|
mmc_hostname(mmc), host);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_dbg(mmc_dev(host->mmc),
|
dev_dbg(mmc_dev(host->mmc),
|
||||||
|
|
|
@ -558,7 +558,7 @@ static void pxamci_dma_irq(int dma, void *devid)
|
||||||
if (dcsr & DCSR_ENDINTR) {
|
if (dcsr & DCSR_ENDINTR) {
|
||||||
writel(BUF_PART_FULL, host->base + MMC_PRTBUF);
|
writel(BUF_PART_FULL, host->base + MMC_PRTBUF);
|
||||||
} else {
|
} else {
|
||||||
printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
|
pr_err("%s: DMA error on channel %d (DCSR=%#x)\n",
|
||||||
mmc_hostname(host->mmc), dma, dcsr);
|
mmc_hostname(host->mmc), dma, dcsr);
|
||||||
host->data->error = -EIO;
|
host->data->error = -EIO;
|
||||||
pxamci_data_done(host, 0);
|
pxamci_data_done(host, 0);
|
||||||
|
|
|
@ -247,7 +247,7 @@ static void s3cmci_check_sdio_irq(struct s3cmci_host *host)
|
||||||
{
|
{
|
||||||
if (host->sdio_irqen) {
|
if (host->sdio_irqen) {
|
||||||
if (gpio_get_value(S3C2410_GPE(8)) == 0) {
|
if (gpio_get_value(S3C2410_GPE(8)) == 0) {
|
||||||
printk(KERN_DEBUG "%s: signalling irq\n", __func__);
|
pr_debug("%s: signalling irq\n", __func__);
|
||||||
mmc_signal_sdio_irq(host->mmc);
|
mmc_signal_sdio_irq(host->mmc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -344,7 +344,7 @@ static void s3cmci_disable_irq(struct s3cmci_host *host, bool transfer)
|
||||||
|
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
|
|
||||||
//printk(KERN_DEBUG "%s: transfer %d\n", __func__, transfer);
|
/* pr_debug("%s: transfer %d\n", __func__, transfer); */
|
||||||
|
|
||||||
host->irq_disabled = transfer;
|
host->irq_disabled = transfer;
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,15 @@
|
||||||
/* VENDOR SPEC register */
|
/* VENDOR SPEC register */
|
||||||
#define SDHCI_VENDOR_SPEC 0xC0
|
#define SDHCI_VENDOR_SPEC 0xC0
|
||||||
#define SDHCI_VENDOR_SPEC_SDIO_QUIRK 0x00000002
|
#define SDHCI_VENDOR_SPEC_SDIO_QUIRK 0x00000002
|
||||||
|
#define SDHCI_MIX_CTRL 0x48
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is an INT DMA ERR mis-match between eSDHC and STD SDHC SPEC:
|
||||||
|
* Bit25 is used in STD SPEC, and is reserved in fsl eSDHC design,
|
||||||
|
* but bit28 is used as the INT DMA ERR in fsl eSDHC design.
|
||||||
|
* Define this macro DMA error INT for fsl eSDHC
|
||||||
|
*/
|
||||||
|
#define SDHCI_INT_VENDOR_SPEC_DMA_ERR 0x10000000
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The CMDTYPE of the CMD register (offset 0xE) should be set to
|
* The CMDTYPE of the CMD register (offset 0xE) should be set to
|
||||||
|
@ -51,6 +60,7 @@ enum imx_esdhc_type {
|
||||||
IMX35_ESDHC,
|
IMX35_ESDHC,
|
||||||
IMX51_ESDHC,
|
IMX51_ESDHC,
|
||||||
IMX53_ESDHC,
|
IMX53_ESDHC,
|
||||||
|
IMX6Q_USDHC,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pltfm_imx_data {
|
struct pltfm_imx_data {
|
||||||
|
@ -73,6 +83,9 @@ static struct platform_device_id imx_esdhc_devtype[] = {
|
||||||
}, {
|
}, {
|
||||||
.name = "sdhci-esdhc-imx53",
|
.name = "sdhci-esdhc-imx53",
|
||||||
.driver_data = IMX53_ESDHC,
|
.driver_data = IMX53_ESDHC,
|
||||||
|
}, {
|
||||||
|
.name = "sdhci-usdhc-imx6q",
|
||||||
|
.driver_data = IMX6Q_USDHC,
|
||||||
}, {
|
}, {
|
||||||
/* sentinel */
|
/* sentinel */
|
||||||
}
|
}
|
||||||
|
@ -84,6 +97,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
|
||||||
{ .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], },
|
{ .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], },
|
||||||
{ .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], },
|
{ .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], },
|
||||||
{ .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], },
|
{ .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], },
|
||||||
|
{ .compatible = "fsl,imx6q-usdhc", .data = &imx_esdhc_devtype[IMX6Q_USDHC], },
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
|
MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
|
||||||
|
@ -108,6 +122,11 @@ static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
|
||||||
return data->devtype == IMX53_ESDHC;
|
return data->devtype == IMX53_ESDHC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int is_imx6q_usdhc(struct pltfm_imx_data *data)
|
||||||
|
{
|
||||||
|
return data->devtype == IMX6Q_USDHC;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
|
static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
|
||||||
{
|
{
|
||||||
void __iomem *base = host->ioaddr + (reg & ~0x3);
|
void __iomem *base = host->ioaddr + (reg & ~0x3);
|
||||||
|
@ -135,6 +154,27 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
|
||||||
val |= SDHCI_CARD_PRESENT;
|
val |= SDHCI_CARD_PRESENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unlikely(reg == SDHCI_CAPABILITIES)) {
|
||||||
|
/* In FSL esdhc IC module, only bit20 is used to indicate the
|
||||||
|
* ADMA2 capability of esdhc, but this bit is messed up on
|
||||||
|
* some SOCs (e.g. on MX25, MX35 this bit is set, but they
|
||||||
|
* don't actually support ADMA2). So set the BROKEN_ADMA
|
||||||
|
* uirk on MX25/35 platforms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (val & SDHCI_CAN_DO_ADMA1) {
|
||||||
|
val &= ~SDHCI_CAN_DO_ADMA1;
|
||||||
|
val |= SDHCI_CAN_DO_ADMA2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(reg == SDHCI_INT_STATUS)) {
|
||||||
|
if (val & SDHCI_INT_VENDOR_SPEC_DMA_ERR) {
|
||||||
|
val &= ~SDHCI_INT_VENDOR_SPEC_DMA_ERR;
|
||||||
|
val |= SDHCI_INT_ADMA_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,13 +219,28 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
|
||||||
writel(v, host->ioaddr + SDHCI_VENDOR_SPEC);
|
writel(v, host->ioaddr + SDHCI_VENDOR_SPEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
|
||||||
|
if (val & SDHCI_INT_ADMA_ERROR) {
|
||||||
|
val &= ~SDHCI_INT_ADMA_ERROR;
|
||||||
|
val |= SDHCI_INT_VENDOR_SPEC_DMA_ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
writel(val, host->ioaddr + reg);
|
writel(val, host->ioaddr + reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
|
static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
|
||||||
{
|
{
|
||||||
if (unlikely(reg == SDHCI_HOST_VERSION))
|
if (unlikely(reg == SDHCI_HOST_VERSION)) {
|
||||||
reg ^= 2;
|
u16 val = readw(host->ioaddr + (reg ^ 2));
|
||||||
|
/*
|
||||||
|
* uSDHC supports SDHCI v3.0, but it's encoded as value
|
||||||
|
* 0x3 in host controller version register, which violates
|
||||||
|
* SDHCI_SPEC_300 definition. Work it around here.
|
||||||
|
*/
|
||||||
|
if ((val & SDHCI_SPEC_VER_MASK) == 3)
|
||||||
|
return --val;
|
||||||
|
}
|
||||||
|
|
||||||
return readw(host->ioaddr + reg);
|
return readw(host->ioaddr + reg);
|
||||||
}
|
}
|
||||||
|
@ -216,8 +271,17 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
||||||
if ((host->cmd->opcode == MMC_STOP_TRANSMISSION)
|
if ((host->cmd->opcode == MMC_STOP_TRANSMISSION)
|
||||||
&& (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
|
&& (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
|
||||||
val |= SDHCI_CMD_ABORTCMD;
|
val |= SDHCI_CMD_ABORTCMD;
|
||||||
writel(val << 16 | imx_data->scratchpad,
|
|
||||||
host->ioaddr + SDHCI_TRANSFER_MODE);
|
if (is_imx6q_usdhc(imx_data)) {
|
||||||
|
u32 m = readl(host->ioaddr + SDHCI_MIX_CTRL);
|
||||||
|
m = imx_data->scratchpad | (m & 0xffff0000);
|
||||||
|
writel(m, host->ioaddr + SDHCI_MIX_CTRL);
|
||||||
|
writel(val << 16,
|
||||||
|
host->ioaddr + SDHCI_TRANSFER_MODE);
|
||||||
|
} else {
|
||||||
|
writel(val << 16 | imx_data->scratchpad,
|
||||||
|
host->ioaddr + SDHCI_TRANSFER_MODE);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
case SDHCI_BLOCK_SIZE:
|
case SDHCI_BLOCK_SIZE:
|
||||||
val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
|
val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
|
||||||
|
@ -311,9 +375,10 @@ static struct sdhci_ops sdhci_esdhc_ops = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
|
static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
|
||||||
.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA
|
.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_HISPD_BIT
|
||||||
|
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
|
||||||
|
| SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC
|
||||||
| SDHCI_QUIRK_BROKEN_CARD_DETECTION,
|
| SDHCI_QUIRK_BROKEN_CARD_DETECTION,
|
||||||
/* ADMA has issues. Might be fixable */
|
|
||||||
.ops = &sdhci_esdhc_ops,
|
.ops = &sdhci_esdhc_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -405,7 +470,8 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data))
|
if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data))
|
||||||
/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
|
/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
|
||||||
host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK;
|
host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK
|
||||||
|
| SDHCI_QUIRK_BROKEN_ADMA;
|
||||||
|
|
||||||
if (is_imx53_esdhc(imx_data))
|
if (is_imx53_esdhc(imx_data))
|
||||||
imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT;
|
imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Freescale eSDHC controller driver.
|
* Freescale eSDHC controller driver.
|
||||||
*
|
*
|
||||||
* Copyright (c) 2007 Freescale Semiconductor, Inc.
|
* Copyright (c) 2007, 2010 Freescale Semiconductor, Inc.
|
||||||
* Copyright (c) 2009 MontaVista Software, Inc.
|
* Copyright (c) 2009 MontaVista Software, Inc.
|
||||||
*
|
*
|
||||||
* Authors: Xiaobo Xie <X.Xie@freescale.com>
|
* Authors: Xiaobo Xie <X.Xie@freescale.com>
|
||||||
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
#include "sdhci-esdhc.h"
|
#include "sdhci-esdhc.h"
|
||||||
|
@ -22,11 +23,21 @@
|
||||||
static u16 esdhc_readw(struct sdhci_host *host, int reg)
|
static u16 esdhc_readw(struct sdhci_host *host, int reg)
|
||||||
{
|
{
|
||||||
u16 ret;
|
u16 ret;
|
||||||
|
int base = reg & ~0x3;
|
||||||
|
int shift = (reg & 0x2) * 8;
|
||||||
|
|
||||||
if (unlikely(reg == SDHCI_HOST_VERSION))
|
if (unlikely(reg == SDHCI_HOST_VERSION))
|
||||||
ret = in_be16(host->ioaddr + reg);
|
ret = in_be32(host->ioaddr + base) & 0xffff;
|
||||||
else
|
else
|
||||||
ret = sdhci_be32bs_readw(host, reg);
|
ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 esdhc_readb(struct sdhci_host *host, int reg)
|
||||||
|
{
|
||||||
|
int base = reg & ~0x3;
|
||||||
|
int shift = (reg & 0x3) * 8;
|
||||||
|
u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +85,7 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
|
||||||
static struct sdhci_ops sdhci_esdhc_ops = {
|
static struct sdhci_ops sdhci_esdhc_ops = {
|
||||||
.read_l = sdhci_be32bs_readl,
|
.read_l = sdhci_be32bs_readl,
|
||||||
.read_w = esdhc_readw,
|
.read_w = esdhc_readw,
|
||||||
.read_b = sdhci_be32bs_readb,
|
.read_b = esdhc_readb,
|
||||||
.write_l = sdhci_be32bs_writel,
|
.write_l = sdhci_be32bs_writel,
|
||||||
.write_w = esdhc_writew,
|
.write_w = esdhc_writew,
|
||||||
.write_b = esdhc_writeb,
|
.write_b = esdhc_writeb,
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/highmem.h>
|
#include <linux/highmem.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -21,6 +22,9 @@
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/sfi.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#include "sdhci.h"
|
#include "sdhci.h"
|
||||||
|
|
||||||
|
@ -43,6 +47,7 @@ struct sdhci_pci_slot;
|
||||||
|
|
||||||
struct sdhci_pci_fixes {
|
struct sdhci_pci_fixes {
|
||||||
unsigned int quirks;
|
unsigned int quirks;
|
||||||
|
bool allow_runtime_pm;
|
||||||
|
|
||||||
int (*probe) (struct sdhci_pci_chip *);
|
int (*probe) (struct sdhci_pci_chip *);
|
||||||
|
|
||||||
|
@ -59,12 +64,16 @@ struct sdhci_pci_slot {
|
||||||
struct sdhci_host *host;
|
struct sdhci_host *host;
|
||||||
|
|
||||||
int pci_bar;
|
int pci_bar;
|
||||||
|
int rst_n_gpio;
|
||||||
|
int cd_gpio;
|
||||||
|
int cd_irq;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sdhci_pci_chip {
|
struct sdhci_pci_chip {
|
||||||
struct pci_dev *pdev;
|
struct pci_dev *pdev;
|
||||||
|
|
||||||
unsigned int quirks;
|
unsigned int quirks;
|
||||||
|
bool allow_runtime_pm;
|
||||||
const struct sdhci_pci_fixes *fixes;
|
const struct sdhci_pci_fixes *fixes;
|
||||||
|
|
||||||
int num_slots; /* Slots on controller */
|
int num_slots; /* Slots on controller */
|
||||||
|
@ -163,12 +172,129 @@ static int mrst_hc_probe(struct sdhci_pci_chip *chip)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Medfield eMMC hardware reset GPIOs */
|
||||||
|
static int mfd_emmc0_rst_gpio = -EINVAL;
|
||||||
|
static int mfd_emmc1_rst_gpio = -EINVAL;
|
||||||
|
|
||||||
|
static int mfd_emmc_gpio_parse(struct sfi_table_header *table)
|
||||||
|
{
|
||||||
|
struct sfi_table_simple *sb = (struct sfi_table_simple *)table;
|
||||||
|
struct sfi_gpio_table_entry *entry;
|
||||||
|
int i, num;
|
||||||
|
|
||||||
|
num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry);
|
||||||
|
entry = (struct sfi_gpio_table_entry *)sb->pentry;
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++, entry++) {
|
||||||
|
if (!strncmp(entry->pin_name, "emmc0_rst", SFI_NAME_LEN))
|
||||||
|
mfd_emmc0_rst_gpio = entry->pin_no;
|
||||||
|
else if (!strncmp(entry->pin_name, "emmc1_rst", SFI_NAME_LEN))
|
||||||
|
mfd_emmc1_rst_gpio = entry->pin_no;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
|
||||||
|
static irqreturn_t mfd_sd_cd(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct sdhci_pci_slot *slot = dev_id;
|
||||||
|
struct sdhci_host *host = slot->host;
|
||||||
|
|
||||||
|
mmc_detect_change(host->mmc, msecs_to_jiffies(200));
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MFLD_SD_CD_PIN 69
|
||||||
|
|
||||||
|
static int mfd_sd_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
|
{
|
||||||
|
int err, irq, gpio = MFLD_SD_CD_PIN;
|
||||||
|
|
||||||
|
slot->cd_gpio = -EINVAL;
|
||||||
|
slot->cd_irq = -EINVAL;
|
||||||
|
|
||||||
|
err = gpio_request(gpio, "sd_cd");
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
err = gpio_direction_input(gpio);
|
||||||
|
if (err < 0)
|
||||||
|
goto out_free;
|
||||||
|
|
||||||
|
irq = gpio_to_irq(gpio);
|
||||||
|
if (irq < 0)
|
||||||
|
goto out_free;
|
||||||
|
|
||||||
|
err = request_irq(irq, mfd_sd_cd, IRQF_TRIGGER_RISING |
|
||||||
|
IRQF_TRIGGER_FALLING, "sd_cd", slot);
|
||||||
|
if (err)
|
||||||
|
goto out_free;
|
||||||
|
|
||||||
|
slot->cd_gpio = gpio;
|
||||||
|
slot->cd_irq = irq;
|
||||||
|
slot->host->quirks2 |= SDHCI_QUIRK2_OWN_CARD_DETECTION;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_free:
|
||||||
|
gpio_free(gpio);
|
||||||
|
out:
|
||||||
|
dev_warn(&slot->chip->pdev->dev, "failed to setup card detect wake up\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mfd_sd_remove_slot(struct sdhci_pci_slot *slot, int dead)
|
||||||
|
{
|
||||||
|
if (slot->cd_irq >= 0)
|
||||||
|
free_irq(slot->cd_irq, slot);
|
||||||
|
gpio_free(slot->cd_gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define mfd_sd_probe_slot NULL
|
||||||
|
#define mfd_sd_remove_slot NULL
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
{
|
{
|
||||||
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA;
|
const char *name = NULL;
|
||||||
|
int gpio = -EINVAL;
|
||||||
|
|
||||||
|
sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, mfd_emmc_gpio_parse);
|
||||||
|
|
||||||
|
switch (slot->chip->pdev->device) {
|
||||||
|
case PCI_DEVICE_ID_INTEL_MFD_EMMC0:
|
||||||
|
gpio = mfd_emmc0_rst_gpio;
|
||||||
|
name = "eMMC0_reset";
|
||||||
|
break;
|
||||||
|
case PCI_DEVICE_ID_INTEL_MFD_EMMC1:
|
||||||
|
gpio = mfd_emmc1_rst_gpio;
|
||||||
|
name = "eMMC1_reset";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gpio_request(gpio, name)) {
|
||||||
|
gpio_direction_output(gpio, 1);
|
||||||
|
slot->rst_n_gpio = gpio;
|
||||||
|
slot->host->mmc->caps |= MMC_CAP_HW_RESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE;
|
||||||
|
|
||||||
|
slot->host->mmc->caps2 = MMC_CAP2_BOOTPART_NOACC;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mfd_emmc_remove_slot(struct sdhci_pci_slot *slot, int dead)
|
||||||
|
{
|
||||||
|
gpio_free(slot->rst_n_gpio);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = {
|
static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = {
|
||||||
.quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT,
|
.quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT,
|
||||||
.probe_slot = mrst_hc_probe_slot,
|
.probe_slot = mrst_hc_probe_slot,
|
||||||
|
@ -181,15 +307,21 @@ static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = {
|
||||||
|
|
||||||
static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = {
|
static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = {
|
||||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||||
|
.allow_runtime_pm = true,
|
||||||
|
.probe_slot = mfd_sd_probe_slot,
|
||||||
|
.remove_slot = mfd_sd_remove_slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = {
|
static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = {
|
||||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||||
|
.allow_runtime_pm = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = {
|
static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = {
|
||||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||||
|
.allow_runtime_pm = true,
|
||||||
.probe_slot = mfd_emmc_probe_slot,
|
.probe_slot = mfd_emmc_probe_slot,
|
||||||
|
.remove_slot = mfd_emmc_remove_slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* O2Micro extra registers */
|
/* O2Micro extra registers */
|
||||||
|
@ -832,9 +964,25 @@ static int sdhci_pci_8bit_width(struct sdhci_host *host, int width)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdhci_pci_hw_reset(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
struct sdhci_pci_slot *slot = sdhci_priv(host);
|
||||||
|
int rst_n_gpio = slot->rst_n_gpio;
|
||||||
|
|
||||||
|
if (!gpio_is_valid(rst_n_gpio))
|
||||||
|
return;
|
||||||
|
gpio_set_value_cansleep(rst_n_gpio, 0);
|
||||||
|
/* For eMMC, minimum is 1us but give it 10us for good measure */
|
||||||
|
udelay(10);
|
||||||
|
gpio_set_value_cansleep(rst_n_gpio, 1);
|
||||||
|
/* For eMMC, minimum is 200us but give it 300us for good measure */
|
||||||
|
usleep_range(300, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
static struct sdhci_ops sdhci_pci_ops = {
|
static struct sdhci_ops sdhci_pci_ops = {
|
||||||
.enable_dma = sdhci_pci_enable_dma,
|
.enable_dma = sdhci_pci_enable_dma,
|
||||||
.platform_8bit_width = sdhci_pci_8bit_width,
|
.platform_8bit_width = sdhci_pci_8bit_width,
|
||||||
|
.hw_reset = sdhci_pci_hw_reset,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************\
|
/*****************************************************************************\
|
||||||
|
@ -944,6 +1092,95 @@ static int sdhci_pci_resume(struct pci_dev *pdev)
|
||||||
|
|
||||||
#endif /* CONFIG_PM */
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
|
||||||
|
static int sdhci_pci_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
|
||||||
|
struct sdhci_pci_chip *chip;
|
||||||
|
struct sdhci_pci_slot *slot;
|
||||||
|
pm_message_t state = { .event = PM_EVENT_SUSPEND };
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
chip = pci_get_drvdata(pdev);
|
||||||
|
if (!chip)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < chip->num_slots; i++) {
|
||||||
|
slot = chip->slots[i];
|
||||||
|
if (!slot)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = sdhci_runtime_suspend_host(slot->host);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
for (i--; i >= 0; i--)
|
||||||
|
sdhci_runtime_resume_host(chip->slots[i]->host);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chip->fixes && chip->fixes->suspend) {
|
||||||
|
ret = chip->fixes->suspend(chip, state);
|
||||||
|
if (ret) {
|
||||||
|
for (i = chip->num_slots - 1; i >= 0; i--)
|
||||||
|
sdhci_runtime_resume_host(chip->slots[i]->host);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_pci_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
|
||||||
|
struct sdhci_pci_chip *chip;
|
||||||
|
struct sdhci_pci_slot *slot;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
chip = pci_get_drvdata(pdev);
|
||||||
|
if (!chip)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (chip->fixes && chip->fixes->resume) {
|
||||||
|
ret = chip->fixes->resume(chip);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < chip->num_slots; i++) {
|
||||||
|
slot = chip->slots[i];
|
||||||
|
if (!slot)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = sdhci_runtime_resume_host(slot->host);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_pci_runtime_idle(struct device *dev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define sdhci_pci_runtime_suspend NULL
|
||||||
|
#define sdhci_pci_runtime_resume NULL
|
||||||
|
#define sdhci_pci_runtime_idle NULL
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops sdhci_pci_pm_ops = {
|
||||||
|
.runtime_suspend = sdhci_pci_runtime_suspend,
|
||||||
|
.runtime_resume = sdhci_pci_runtime_resume,
|
||||||
|
.runtime_idle = sdhci_pci_runtime_idle,
|
||||||
|
};
|
||||||
|
|
||||||
/*****************************************************************************\
|
/*****************************************************************************\
|
||||||
* *
|
* *
|
||||||
* Device probing/removal *
|
* Device probing/removal *
|
||||||
|
@ -988,6 +1225,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
|
||||||
slot->chip = chip;
|
slot->chip = chip;
|
||||||
slot->host = host;
|
slot->host = host;
|
||||||
slot->pci_bar = bar;
|
slot->pci_bar = bar;
|
||||||
|
slot->rst_n_gpio = -EINVAL;
|
||||||
|
|
||||||
host->hw_name = "PCI";
|
host->hw_name = "PCI";
|
||||||
host->ops = &sdhci_pci_ops;
|
host->ops = &sdhci_pci_ops;
|
||||||
|
@ -1058,6 +1296,21 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
|
||||||
sdhci_free_host(slot->host);
|
sdhci_free_host(slot->host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __devinit sdhci_pci_runtime_pm_allow(struct device *dev)
|
||||||
|
{
|
||||||
|
pm_runtime_put_noidle(dev);
|
||||||
|
pm_runtime_allow(dev);
|
||||||
|
pm_runtime_set_autosuspend_delay(dev, 50);
|
||||||
|
pm_runtime_use_autosuspend(dev);
|
||||||
|
pm_suspend_ignore_children(dev, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __devexit sdhci_pci_runtime_pm_forbid(struct device *dev)
|
||||||
|
{
|
||||||
|
pm_runtime_forbid(dev);
|
||||||
|
pm_runtime_get_noresume(dev);
|
||||||
|
}
|
||||||
|
|
||||||
static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
|
static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
|
||||||
const struct pci_device_id *ent)
|
const struct pci_device_id *ent)
|
||||||
{
|
{
|
||||||
|
@ -1107,8 +1360,10 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
|
||||||
|
|
||||||
chip->pdev = pdev;
|
chip->pdev = pdev;
|
||||||
chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data;
|
chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data;
|
||||||
if (chip->fixes)
|
if (chip->fixes) {
|
||||||
chip->quirks = chip->fixes->quirks;
|
chip->quirks = chip->fixes->quirks;
|
||||||
|
chip->allow_runtime_pm = chip->fixes->allow_runtime_pm;
|
||||||
|
}
|
||||||
chip->num_slots = slots;
|
chip->num_slots = slots;
|
||||||
|
|
||||||
pci_set_drvdata(pdev, chip);
|
pci_set_drvdata(pdev, chip);
|
||||||
|
@ -1133,6 +1388,9 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
|
||||||
chip->slots[i] = slot;
|
chip->slots[i] = slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (chip->allow_runtime_pm)
|
||||||
|
sdhci_pci_runtime_pm_allow(&pdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
free:
|
free:
|
||||||
|
@ -1152,6 +1410,9 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
|
||||||
chip = pci_get_drvdata(pdev);
|
chip = pci_get_drvdata(pdev);
|
||||||
|
|
||||||
if (chip) {
|
if (chip) {
|
||||||
|
if (chip->allow_runtime_pm)
|
||||||
|
sdhci_pci_runtime_pm_forbid(&pdev->dev);
|
||||||
|
|
||||||
for (i = 0; i < chip->num_slots; i++)
|
for (i = 0; i < chip->num_slots; i++)
|
||||||
sdhci_pci_remove_slot(chip->slots[i]);
|
sdhci_pci_remove_slot(chip->slots[i]);
|
||||||
|
|
||||||
|
@ -1169,6 +1430,9 @@ static struct pci_driver sdhci_driver = {
|
||||||
.remove = __devexit_p(sdhci_pci_remove),
|
.remove = __devexit_p(sdhci_pci_remove),
|
||||||
.suspend = sdhci_pci_suspend,
|
.suspend = sdhci_pci_suspend,
|
||||||
.resume = sdhci_pci_resume,
|
.resume = sdhci_pci_resume,
|
||||||
|
.driver = {
|
||||||
|
.pm = &sdhci_pci_pm_ops
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************\
|
/*****************************************************************************\
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#ifdef CONFIG_PPC
|
#ifdef CONFIG_PPC
|
||||||
#include <asm/machdep.h>
|
#include <asm/machdep.h>
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/mmc/card.h>
|
||||||
|
@ -59,7 +60,7 @@ static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask)
|
||||||
* tune timing of read data/command when crc error happen
|
* tune timing of read data/command when crc error happen
|
||||||
* no performance impact
|
* no performance impact
|
||||||
*/
|
*/
|
||||||
if (pdata->clk_delay_sel == 1) {
|
if (pdata && pdata->clk_delay_sel == 1) {
|
||||||
tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP);
|
tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP);
|
||||||
|
|
||||||
tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT);
|
tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT);
|
||||||
|
@ -71,7 +72,7 @@ static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask)
|
||||||
writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP);
|
writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata->flags & PXA_FLAG_ENABLE_CLOCK_GATING) {
|
if (pdata && (pdata->flags & PXA_FLAG_ENABLE_CLOCK_GATING)) {
|
||||||
tmp = readw(host->ioaddr + SD_FIFO_PARAM);
|
tmp = readw(host->ioaddr + SD_FIFO_PARAM);
|
||||||
tmp &= ~CLK_GATE_SETTING_BITS;
|
tmp &= ~CLK_GATE_SETTING_BITS;
|
||||||
writew(tmp, host->ioaddr + SD_FIFO_PARAM);
|
writew(tmp, host->ioaddr + SD_FIFO_PARAM);
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <linux/platform_data/pxa_sdhci.h>
|
#include <linux/platform_data/pxa_sdhci.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include "sdhci.h"
|
#include "sdhci.h"
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
|
|
|
@ -203,17 +203,23 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||||
writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
|
writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reconfigure the hardware for new clock rate */
|
/* reprogram default hardware configuration */
|
||||||
|
writel(S3C64XX_SDHCI_CONTROL4_DRIVE_9mA,
|
||||||
|
host->ioaddr + S3C64XX_SDHCI_CONTROL4);
|
||||||
|
|
||||||
{
|
ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
|
||||||
struct mmc_ios ios;
|
ctrl |= (S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR |
|
||||||
|
S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK |
|
||||||
|
S3C_SDHCI_CTRL2_ENFBCLKRX |
|
||||||
|
S3C_SDHCI_CTRL2_DFCNT_NONE |
|
||||||
|
S3C_SDHCI_CTRL2_ENCLKOUTHOLD);
|
||||||
|
writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
|
||||||
|
|
||||||
ios.clock = clock;
|
/* reconfigure the controller for new clock rate */
|
||||||
|
ctrl = (S3C_SDHCI_CTRL3_FCSEL1 | S3C_SDHCI_CTRL3_FCSEL0);
|
||||||
if (ourhost->pdata->cfg_card)
|
if (clock < 25 * 1000000)
|
||||||
(ourhost->pdata->cfg_card)(ourhost->pdev, host->ioaddr,
|
ctrl |= (S3C_SDHCI_CTRL3_FCSEL3 | S3C_SDHCI_CTRL3_FCSEL2);
|
||||||
&ios, NULL);
|
writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL3);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -561,8 +567,10 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
err_req_regs:
|
err_req_regs:
|
||||||
for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
|
for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
|
||||||
clk_disable(sc->clk_bus[ptr]);
|
if (sc->clk_bus[ptr]) {
|
||||||
clk_put(sc->clk_bus[ptr]);
|
clk_disable(sc->clk_bus[ptr]);
|
||||||
|
clk_put(sc->clk_bus[ptr]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err_no_busclks:
|
err_no_busclks:
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/highmem.h>
|
#include <linux/highmem.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
@ -177,8 +178,6 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
|
||||||
sdhci->data->card_power_gpio);
|
sdhci->data->card_power_gpio);
|
||||||
goto err_pgpio_direction;
|
goto err_pgpio_direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpio_set_value(sdhci->data->card_power_gpio, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sdhci->data->card_int_gpio >= 0) {
|
if (sdhci->data->card_int_gpio >= 0) {
|
||||||
|
|
|
@ -17,9 +17,12 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/mmc/card.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
|
||||||
#include <asm/gpio.h>
|
#include <asm/gpio.h>
|
||||||
|
|
||||||
|
@ -75,10 +78,8 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
|
||||||
|
|
||||||
static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci)
|
static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(mmc_dev(sdhci->mmc));
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
|
||||||
struct tegra_sdhci_platform_data *plat;
|
struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
|
||||||
|
|
||||||
plat = pdev->dev.platform_data;
|
|
||||||
|
|
||||||
if (!gpio_is_valid(plat->wp_gpio))
|
if (!gpio_is_valid(plat->wp_gpio))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -96,12 +97,10 @@ static irqreturn_t carddetect_irq(int irq, void *data)
|
||||||
|
|
||||||
static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
|
static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct tegra_sdhci_platform_data *plat;
|
struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
|
||||||
u32 ctrl;
|
u32 ctrl;
|
||||||
|
|
||||||
plat = pdev->dev.platform_data;
|
|
||||||
|
|
||||||
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
||||||
if (plat->is_8bit && bus_width == MMC_BUS_WIDTH_8) {
|
if (plat->is_8bit && bus_width == MMC_BUS_WIDTH_8) {
|
||||||
ctrl &= ~SDHCI_CTRL_4BITBUS;
|
ctrl &= ~SDHCI_CTRL_4BITBUS;
|
||||||
|
@ -133,6 +132,36 @@ static struct sdhci_pltfm_data sdhci_tegra_pdata = {
|
||||||
.ops = &tegra_sdhci_ops,
|
.ops = &tegra_sdhci_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = {
|
||||||
|
{ .compatible = "nvidia,tegra20-sdhci", },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, sdhci_dt_ids);
|
||||||
|
|
||||||
|
static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata(
|
||||||
|
struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct tegra_sdhci_platform_data *plat;
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
|
||||||
|
if (!np)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
|
||||||
|
if (!plat) {
|
||||||
|
dev_err(&pdev->dev, "Can't allocate platform data\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
plat->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
|
||||||
|
plat->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
|
||||||
|
plat->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
|
||||||
|
if (of_find_property(np, "support-8bit", NULL))
|
||||||
|
plat->is_8bit = 1;
|
||||||
|
|
||||||
|
return plat;
|
||||||
|
}
|
||||||
|
|
||||||
static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
|
static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sdhci_pltfm_host *pltfm_host;
|
struct sdhci_pltfm_host *pltfm_host;
|
||||||
|
@ -149,12 +178,17 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
plat = pdev->dev.platform_data;
|
plat = pdev->dev.platform_data;
|
||||||
|
|
||||||
|
if (plat == NULL)
|
||||||
|
plat = sdhci_tegra_dt_parse_pdata(pdev);
|
||||||
|
|
||||||
if (plat == NULL) {
|
if (plat == NULL) {
|
||||||
dev_err(mmc_dev(host->mmc), "missing platform data\n");
|
dev_err(mmc_dev(host->mmc), "missing platform data\n");
|
||||||
rc = -ENXIO;
|
rc = -ENXIO;
|
||||||
goto err_no_plat;
|
goto err_no_plat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pltfm_host->priv = plat;
|
||||||
|
|
||||||
if (gpio_is_valid(plat->power_gpio)) {
|
if (gpio_is_valid(plat->power_gpio)) {
|
||||||
rc = gpio_request(plat->power_gpio, "sdhci_power");
|
rc = gpio_request(plat->power_gpio, "sdhci_power");
|
||||||
if (rc) {
|
if (rc) {
|
||||||
|
@ -249,13 +283,11 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct tegra_sdhci_platform_data *plat;
|
struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
|
||||||
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
|
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
|
||||||
|
|
||||||
sdhci_remove_host(host, dead);
|
sdhci_remove_host(host, dead);
|
||||||
|
|
||||||
plat = pdev->dev.platform_data;
|
|
||||||
|
|
||||||
if (gpio_is_valid(plat->wp_gpio)) {
|
if (gpio_is_valid(plat->wp_gpio)) {
|
||||||
tegra_gpio_disable(plat->wp_gpio);
|
tegra_gpio_disable(plat->wp_gpio);
|
||||||
gpio_free(plat->wp_gpio);
|
gpio_free(plat->wp_gpio);
|
||||||
|
@ -284,6 +316,7 @@ static struct platform_driver sdhci_tegra_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-tegra",
|
.name = "sdhci-tegra",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
.of_match_table = sdhci_tegra_dt_match,
|
||||||
},
|
},
|
||||||
.probe = sdhci_tegra_probe,
|
.probe = sdhci_tegra_probe,
|
||||||
.remove = __devexit_p(sdhci_tegra_remove),
|
.remove = __devexit_p(sdhci_tegra_remove),
|
||||||
|
|
|
@ -16,10 +16,12 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/highmem.h>
|
#include <linux/highmem.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
|
|
||||||
|
@ -41,6 +43,7 @@
|
||||||
#define MAX_TUNING_LOOP 40
|
#define MAX_TUNING_LOOP 40
|
||||||
|
|
||||||
static unsigned int debug_quirks = 0;
|
static unsigned int debug_quirks = 0;
|
||||||
|
static unsigned int debug_quirks2;
|
||||||
|
|
||||||
static void sdhci_finish_data(struct sdhci_host *);
|
static void sdhci_finish_data(struct sdhci_host *);
|
||||||
|
|
||||||
|
@ -49,53 +52,67 @@ static void sdhci_finish_command(struct sdhci_host *);
|
||||||
static int sdhci_execute_tuning(struct mmc_host *mmc);
|
static int sdhci_execute_tuning(struct mmc_host *mmc);
|
||||||
static void sdhci_tuning_timer(unsigned long data);
|
static void sdhci_tuning_timer(unsigned long data);
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
static int sdhci_runtime_pm_get(struct sdhci_host *host);
|
||||||
|
static int sdhci_runtime_pm_put(struct sdhci_host *host);
|
||||||
|
#else
|
||||||
|
static inline int sdhci_runtime_pm_get(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static inline int sdhci_runtime_pm_put(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void sdhci_dumpregs(struct sdhci_host *host)
|
static void sdhci_dumpregs(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
|
pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
|
||||||
mmc_hostname(host->mmc));
|
mmc_hostname(host->mmc));
|
||||||
|
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
|
pr_debug(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
|
||||||
sdhci_readl(host, SDHCI_DMA_ADDRESS),
|
sdhci_readl(host, SDHCI_DMA_ADDRESS),
|
||||||
sdhci_readw(host, SDHCI_HOST_VERSION));
|
sdhci_readw(host, SDHCI_HOST_VERSION));
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n",
|
pr_debug(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n",
|
||||||
sdhci_readw(host, SDHCI_BLOCK_SIZE),
|
sdhci_readw(host, SDHCI_BLOCK_SIZE),
|
||||||
sdhci_readw(host, SDHCI_BLOCK_COUNT));
|
sdhci_readw(host, SDHCI_BLOCK_COUNT));
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
|
pr_debug(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
|
||||||
sdhci_readl(host, SDHCI_ARGUMENT),
|
sdhci_readl(host, SDHCI_ARGUMENT),
|
||||||
sdhci_readw(host, SDHCI_TRANSFER_MODE));
|
sdhci_readw(host, SDHCI_TRANSFER_MODE));
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
|
pr_debug(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
|
||||||
sdhci_readl(host, SDHCI_PRESENT_STATE),
|
sdhci_readl(host, SDHCI_PRESENT_STATE),
|
||||||
sdhci_readb(host, SDHCI_HOST_CONTROL));
|
sdhci_readb(host, SDHCI_HOST_CONTROL));
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n",
|
pr_debug(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n",
|
||||||
sdhci_readb(host, SDHCI_POWER_CONTROL),
|
sdhci_readb(host, SDHCI_POWER_CONTROL),
|
||||||
sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
|
sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n",
|
pr_debug(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n",
|
||||||
sdhci_readb(host, SDHCI_WAKE_UP_CONTROL),
|
sdhci_readb(host, SDHCI_WAKE_UP_CONTROL),
|
||||||
sdhci_readw(host, SDHCI_CLOCK_CONTROL));
|
sdhci_readw(host, SDHCI_CLOCK_CONTROL));
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n",
|
pr_debug(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n",
|
||||||
sdhci_readb(host, SDHCI_TIMEOUT_CONTROL),
|
sdhci_readb(host, SDHCI_TIMEOUT_CONTROL),
|
||||||
sdhci_readl(host, SDHCI_INT_STATUS));
|
sdhci_readl(host, SDHCI_INT_STATUS));
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
|
pr_debug(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
|
||||||
sdhci_readl(host, SDHCI_INT_ENABLE),
|
sdhci_readl(host, SDHCI_INT_ENABLE),
|
||||||
sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
|
sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
|
pr_debug(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
|
||||||
sdhci_readw(host, SDHCI_ACMD12_ERR),
|
sdhci_readw(host, SDHCI_ACMD12_ERR),
|
||||||
sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
|
sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n",
|
pr_debug(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n",
|
||||||
sdhci_readl(host, SDHCI_CAPABILITIES),
|
sdhci_readl(host, SDHCI_CAPABILITIES),
|
||||||
sdhci_readl(host, SDHCI_CAPABILITIES_1));
|
sdhci_readl(host, SDHCI_CAPABILITIES_1));
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n",
|
pr_debug(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n",
|
||||||
sdhci_readw(host, SDHCI_COMMAND),
|
sdhci_readw(host, SDHCI_COMMAND),
|
||||||
sdhci_readl(host, SDHCI_MAX_CURRENT));
|
sdhci_readl(host, SDHCI_MAX_CURRENT));
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": Host ctl2: 0x%08x\n",
|
pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n",
|
||||||
sdhci_readw(host, SDHCI_HOST_CONTROL2));
|
sdhci_readw(host, SDHCI_HOST_CONTROL2));
|
||||||
|
|
||||||
if (host->flags & SDHCI_USE_ADMA)
|
if (host->flags & SDHCI_USE_ADMA)
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
|
pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
|
||||||
readl(host->ioaddr + SDHCI_ADMA_ERROR),
|
readl(host->ioaddr + SDHCI_ADMA_ERROR),
|
||||||
readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
|
readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
|
||||||
|
|
||||||
printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n");
|
pr_debug(DRIVER_NAME ": ===========================================\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************\
|
/*****************************************************************************\
|
||||||
|
@ -132,6 +149,9 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
|
||||||
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
|
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (host->quirks2 & SDHCI_QUIRK2_OWN_CARD_DETECTION)
|
||||||
|
return;
|
||||||
|
|
||||||
present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
|
present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
|
||||||
SDHCI_CARD_PRESENT;
|
SDHCI_CARD_PRESENT;
|
||||||
irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT;
|
irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT;
|
||||||
|
@ -180,7 +200,7 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||||
/* hw clears the bit when it's done */
|
/* hw clears the bit when it's done */
|
||||||
while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
|
while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
|
||||||
if (timeout == 0) {
|
if (timeout == 0) {
|
||||||
printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
|
pr_err("%s: Reset 0x%x never completed.\n",
|
||||||
mmc_hostname(host->mmc), (int)mask);
|
mmc_hostname(host->mmc), (int)mask);
|
||||||
sdhci_dumpregs(host);
|
sdhci_dumpregs(host);
|
||||||
return;
|
return;
|
||||||
|
@ -251,11 +271,14 @@ static void sdhci_led_control(struct led_classdev *led,
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
|
if (host->runtime_suspended)
|
||||||
|
goto out;
|
||||||
|
|
||||||
if (brightness == LED_OFF)
|
if (brightness == LED_OFF)
|
||||||
sdhci_deactivate_led(host);
|
sdhci_deactivate_led(host);
|
||||||
else
|
else
|
||||||
sdhci_activate_led(host);
|
sdhci_activate_led(host);
|
||||||
|
out:
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -654,7 +677,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count >= 0xF) {
|
if (count >= 0xF) {
|
||||||
printk(KERN_WARNING "%s: Too large timeout requested for CMD%d!\n",
|
pr_warning("%s: Too large timeout requested for CMD%d!\n",
|
||||||
mmc_hostname(host->mmc), cmd->opcode);
|
mmc_hostname(host->mmc), cmd->opcode);
|
||||||
count = 0xE;
|
count = 0xE;
|
||||||
}
|
}
|
||||||
|
@ -949,7 +972,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
||||||
|
|
||||||
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
|
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
|
||||||
if (timeout == 0) {
|
if (timeout == 0) {
|
||||||
printk(KERN_ERR "%s: Controller never released "
|
pr_err("%s: Controller never released "
|
||||||
"inhibit bit(s).\n", mmc_hostname(host->mmc));
|
"inhibit bit(s).\n", mmc_hostname(host->mmc));
|
||||||
sdhci_dumpregs(host);
|
sdhci_dumpregs(host);
|
||||||
cmd->error = -EIO;
|
cmd->error = -EIO;
|
||||||
|
@ -971,7 +994,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
||||||
sdhci_set_transfer_mode(host, cmd);
|
sdhci_set_transfer_mode(host, cmd);
|
||||||
|
|
||||||
if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
|
if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
|
||||||
printk(KERN_ERR "%s: Unsupported response type!\n",
|
pr_err("%s: Unsupported response type!\n",
|
||||||
mmc_hostname(host->mmc));
|
mmc_hostname(host->mmc));
|
||||||
cmd->error = -EINVAL;
|
cmd->error = -EINVAL;
|
||||||
tasklet_schedule(&host->finish_tasklet);
|
tasklet_schedule(&host->finish_tasklet);
|
||||||
|
@ -1121,7 +1144,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||||
while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
|
while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
|
||||||
& SDHCI_CLOCK_INT_STABLE)) {
|
& SDHCI_CLOCK_INT_STABLE)) {
|
||||||
if (timeout == 0) {
|
if (timeout == 0) {
|
||||||
printk(KERN_ERR "%s: Internal clock never "
|
pr_err("%s: Internal clock never "
|
||||||
"stabilised.\n", mmc_hostname(host->mmc));
|
"stabilised.\n", mmc_hostname(host->mmc));
|
||||||
sdhci_dumpregs(host);
|
sdhci_dumpregs(host);
|
||||||
return;
|
return;
|
||||||
|
@ -1209,6 +1232,8 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
|
|
||||||
|
sdhci_runtime_pm_get(host);
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
WARN_ON(host->mrq != NULL);
|
WARN_ON(host->mrq != NULL);
|
||||||
|
@ -1269,14 +1294,11 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
u8 ctrl;
|
u8 ctrl;
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
if (host->flags & SDHCI_DEVICE_DEAD)
|
if (host->flags & SDHCI_DEVICE_DEAD)
|
||||||
|
@ -1426,7 +1448,16 @@ out:
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int check_ro(struct sdhci_host *host)
|
static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
|
sdhci_runtime_pm_get(host);
|
||||||
|
sdhci_do_set_ios(host, ios);
|
||||||
|
sdhci_runtime_pm_put(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_check_ro(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int is_readonly;
|
int is_readonly;
|
||||||
|
@ -1450,19 +1481,16 @@ static int check_ro(struct sdhci_host *host)
|
||||||
|
|
||||||
#define SAMPLE_COUNT 5
|
#define SAMPLE_COUNT 5
|
||||||
|
|
||||||
static int sdhci_get_ro(struct mmc_host *mmc)
|
static int sdhci_do_get_ro(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host;
|
|
||||||
int i, ro_count;
|
int i, ro_count;
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
|
||||||
|
|
||||||
if (!(host->quirks & SDHCI_QUIRK_UNSTABLE_RO_DETECT))
|
if (!(host->quirks & SDHCI_QUIRK_UNSTABLE_RO_DETECT))
|
||||||
return check_ro(host);
|
return sdhci_check_ro(host);
|
||||||
|
|
||||||
ro_count = 0;
|
ro_count = 0;
|
||||||
for (i = 0; i < SAMPLE_COUNT; i++) {
|
for (i = 0; i < SAMPLE_COUNT; i++) {
|
||||||
if (check_ro(host)) {
|
if (sdhci_check_ro(host)) {
|
||||||
if (++ro_count > SAMPLE_COUNT / 2)
|
if (++ro_count > SAMPLE_COUNT / 2)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1471,38 +1499,64 @@ static int sdhci_get_ro(struct mmc_host *mmc)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
static void sdhci_hw_reset(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host;
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
if (host->ops && host->ops->hw_reset)
|
||||||
|
host->ops->hw_reset(host);
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
static int sdhci_get_ro(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
sdhci_runtime_pm_get(host);
|
||||||
|
ret = sdhci_do_get_ro(host);
|
||||||
|
sdhci_runtime_pm_put(host);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)
|
||||||
|
{
|
||||||
if (host->flags & SDHCI_DEVICE_DEAD)
|
if (host->flags & SDHCI_DEVICE_DEAD)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
host->flags |= SDHCI_SDIO_IRQ_ENABLED;
|
||||||
|
else
|
||||||
|
host->flags &= ~SDHCI_SDIO_IRQ_ENABLED;
|
||||||
|
|
||||||
|
/* SDIO IRQ will be enabled as appropriate in runtime resume */
|
||||||
|
if (host->runtime_suspended)
|
||||||
|
goto out;
|
||||||
|
|
||||||
if (enable)
|
if (enable)
|
||||||
sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);
|
sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);
|
||||||
else
|
else
|
||||||
sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);
|
sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);
|
||||||
out:
|
out:
|
||||||
mmiowb();
|
mmiowb();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
sdhci_enable_sdio_irq_nolock(host, enable);
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
|
||||||
struct mmc_ios *ios)
|
struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host;
|
|
||||||
u8 pwr;
|
u8 pwr;
|
||||||
u16 clk, ctrl;
|
u16 clk, ctrl;
|
||||||
u32 present_state;
|
u32 present_state;
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Signal Voltage Switching is only applicable for Host Controllers
|
* Signal Voltage Switching is only applicable for Host Controllers
|
||||||
* v3.00 and above.
|
* v3.00 and above.
|
||||||
|
@ -1528,7 +1582,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
||||||
if (!(ctrl & SDHCI_CTRL_VDD_180))
|
if (!(ctrl & SDHCI_CTRL_VDD_180))
|
||||||
return 0;
|
return 0;
|
||||||
else {
|
else {
|
||||||
printk(KERN_INFO DRIVER_NAME ": Switching to 3.3V "
|
pr_info(DRIVER_NAME ": Switching to 3.3V "
|
||||||
"signalling voltage failed\n");
|
"signalling voltage failed\n");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -1587,7 +1641,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
||||||
pwr |= SDHCI_POWER_ON;
|
pwr |= SDHCI_POWER_ON;
|
||||||
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
|
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
|
||||||
|
|
||||||
printk(KERN_INFO DRIVER_NAME ": Switching to 1.8V signalling "
|
pr_info(DRIVER_NAME ": Switching to 1.8V signalling "
|
||||||
"voltage failed, retrying with S18R set to 0\n");
|
"voltage failed, retrying with S18R set to 0\n");
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
} else
|
} else
|
||||||
|
@ -1595,6 +1649,20 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
||||||
|
struct mmc_ios *ios)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (host->version < SDHCI_SPEC_300)
|
||||||
|
return 0;
|
||||||
|
sdhci_runtime_pm_get(host);
|
||||||
|
err = sdhci_do_start_signal_voltage_switch(host, ios);
|
||||||
|
sdhci_runtime_pm_put(host);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int sdhci_execute_tuning(struct mmc_host *mmc)
|
static int sdhci_execute_tuning(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host;
|
struct sdhci_host *host;
|
||||||
|
@ -1606,6 +1674,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
|
|
||||||
|
sdhci_runtime_pm_get(host);
|
||||||
disable_irq(host->irq);
|
disable_irq(host->irq);
|
||||||
spin_lock(&host->lock);
|
spin_lock(&host->lock);
|
||||||
|
|
||||||
|
@ -1623,6 +1692,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
|
||||||
else {
|
else {
|
||||||
spin_unlock(&host->lock);
|
spin_unlock(&host->lock);
|
||||||
enable_irq(host->irq);
|
enable_irq(host->irq);
|
||||||
|
sdhci_runtime_pm_put(host);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1648,7 +1718,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
|
||||||
timeout = 150;
|
timeout = 150;
|
||||||
do {
|
do {
|
||||||
struct mmc_command cmd = {0};
|
struct mmc_command cmd = {0};
|
||||||
struct mmc_request mrq = {0};
|
struct mmc_request mrq = {NULL};
|
||||||
|
|
||||||
if (!tuning_loop_counter && !timeout)
|
if (!tuning_loop_counter && !timeout)
|
||||||
break;
|
break;
|
||||||
|
@ -1694,7 +1764,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
|
||||||
spin_lock(&host->lock);
|
spin_lock(&host->lock);
|
||||||
|
|
||||||
if (!host->tuning_done) {
|
if (!host->tuning_done) {
|
||||||
printk(KERN_INFO DRIVER_NAME ": Timeout waiting for "
|
pr_info(DRIVER_NAME ": Timeout waiting for "
|
||||||
"Buffer Read Ready interrupt during tuning "
|
"Buffer Read Ready interrupt during tuning "
|
||||||
"procedure, falling back to fixed sampling "
|
"procedure, falling back to fixed sampling "
|
||||||
"clock\n");
|
"clock\n");
|
||||||
|
@ -1724,7 +1794,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
|
||||||
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
||||||
} else {
|
} else {
|
||||||
if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
|
if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
|
||||||
printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
|
pr_info(DRIVER_NAME ": Tuning procedure"
|
||||||
" failed, falling back to fixed sampling"
|
" failed, falling back to fixed sampling"
|
||||||
" clock\n");
|
" clock\n");
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
|
@ -1766,18 +1836,16 @@ out:
|
||||||
sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
|
sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
|
||||||
spin_unlock(&host->lock);
|
spin_unlock(&host->lock);
|
||||||
enable_irq(host->irq);
|
enable_irq(host->irq);
|
||||||
|
sdhci_runtime_pm_put(host);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
|
static void sdhci_do_enable_preset_value(struct sdhci_host *host, bool enable)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host;
|
|
||||||
u16 ctrl;
|
u16 ctrl;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
|
||||||
|
|
||||||
/* Host Controller v3.00 defines preset value registers */
|
/* Host Controller v3.00 defines preset value registers */
|
||||||
if (host->version < SDHCI_SPEC_300)
|
if (host->version < SDHCI_SPEC_300)
|
||||||
return;
|
return;
|
||||||
|
@ -1793,18 +1861,30 @@ static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
|
||||||
if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
|
if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
|
||||||
ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
|
ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
|
||||||
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
||||||
|
host->flags |= SDHCI_PV_ENABLED;
|
||||||
} else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
|
} else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
|
||||||
ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
|
ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
|
||||||
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
||||||
|
host->flags &= ~SDHCI_PV_ENABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
|
sdhci_runtime_pm_get(host);
|
||||||
|
sdhci_do_enable_preset_value(host, enable);
|
||||||
|
sdhci_runtime_pm_put(host);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct mmc_host_ops sdhci_ops = {
|
static const struct mmc_host_ops sdhci_ops = {
|
||||||
.request = sdhci_request,
|
.request = sdhci_request,
|
||||||
.set_ios = sdhci_set_ios,
|
.set_ios = sdhci_set_ios,
|
||||||
.get_ro = sdhci_get_ro,
|
.get_ro = sdhci_get_ro,
|
||||||
|
.hw_reset = sdhci_hw_reset,
|
||||||
.enable_sdio_irq = sdhci_enable_sdio_irq,
|
.enable_sdio_irq = sdhci_enable_sdio_irq,
|
||||||
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
|
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
|
||||||
.execute_tuning = sdhci_execute_tuning,
|
.execute_tuning = sdhci_execute_tuning,
|
||||||
|
@ -1826,19 +1906,19 @@ static void sdhci_tasklet_card(unsigned long param)
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
|
/* Check host->mrq first in case we are runtime suspended */
|
||||||
if (host->mrq) {
|
if (host->mrq &&
|
||||||
printk(KERN_ERR "%s: Card removed during transfer!\n",
|
!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
|
||||||
mmc_hostname(host->mmc));
|
pr_err("%s: Card removed during transfer!\n",
|
||||||
printk(KERN_ERR "%s: Resetting controller.\n",
|
mmc_hostname(host->mmc));
|
||||||
mmc_hostname(host->mmc));
|
pr_err("%s: Resetting controller.\n",
|
||||||
|
mmc_hostname(host->mmc));
|
||||||
|
|
||||||
sdhci_reset(host, SDHCI_RESET_CMD);
|
sdhci_reset(host, SDHCI_RESET_CMD);
|
||||||
sdhci_reset(host, SDHCI_RESET_DATA);
|
sdhci_reset(host, SDHCI_RESET_DATA);
|
||||||
|
|
||||||
host->mrq->cmd->error = -ENOMEDIUM;
|
host->mrq->cmd->error = -ENOMEDIUM;
|
||||||
tasklet_schedule(&host->finish_tasklet);
|
tasklet_schedule(&host->finish_tasklet);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
@ -1854,14 +1934,16 @@ static void sdhci_tasklet_finish(unsigned long param)
|
||||||
|
|
||||||
host = (struct sdhci_host*)param;
|
host = (struct sdhci_host*)param;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this tasklet gets rescheduled while running, it will
|
* If this tasklet gets rescheduled while running, it will
|
||||||
* be run again afterwards but without any active request.
|
* be run again afterwards but without any active request.
|
||||||
*/
|
*/
|
||||||
if (!host->mrq)
|
if (!host->mrq) {
|
||||||
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
|
||||||
|
|
||||||
del_timer(&host->timer);
|
del_timer(&host->timer);
|
||||||
|
|
||||||
|
@ -1905,6 +1987,7 @@ static void sdhci_tasklet_finish(unsigned long param)
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
mmc_request_done(host->mmc, mrq);
|
mmc_request_done(host->mmc, mrq);
|
||||||
|
sdhci_runtime_pm_put(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_timeout_timer(unsigned long data)
|
static void sdhci_timeout_timer(unsigned long data)
|
||||||
|
@ -1917,7 +2000,7 @@ static void sdhci_timeout_timer(unsigned long data)
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
if (host->mrq) {
|
if (host->mrq) {
|
||||||
printk(KERN_ERR "%s: Timeout waiting for hardware "
|
pr_err("%s: Timeout waiting for hardware "
|
||||||
"interrupt.\n", mmc_hostname(host->mmc));
|
"interrupt.\n", mmc_hostname(host->mmc));
|
||||||
sdhci_dumpregs(host);
|
sdhci_dumpregs(host);
|
||||||
|
|
||||||
|
@ -1963,7 +2046,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
|
||||||
BUG_ON(intmask == 0);
|
BUG_ON(intmask == 0);
|
||||||
|
|
||||||
if (!host->cmd) {
|
if (!host->cmd) {
|
||||||
printk(KERN_ERR "%s: Got command interrupt 0x%08x even "
|
pr_err("%s: Got command interrupt 0x%08x even "
|
||||||
"though no command operation was in progress.\n",
|
"though no command operation was in progress.\n",
|
||||||
mmc_hostname(host->mmc), (unsigned)intmask);
|
mmc_hostname(host->mmc), (unsigned)intmask);
|
||||||
sdhci_dumpregs(host);
|
sdhci_dumpregs(host);
|
||||||
|
@ -2063,7 +2146,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printk(KERN_ERR "%s: Got data interrupt 0x%08x even "
|
pr_err("%s: Got data interrupt 0x%08x even "
|
||||||
"though no data operation was in progress.\n",
|
"though no data operation was in progress.\n",
|
||||||
mmc_hostname(host->mmc), (unsigned)intmask);
|
mmc_hostname(host->mmc), (unsigned)intmask);
|
||||||
sdhci_dumpregs(host);
|
sdhci_dumpregs(host);
|
||||||
|
@ -2080,7 +2163,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
||||||
!= MMC_BUS_TEST_R)
|
!= MMC_BUS_TEST_R)
|
||||||
host->data->error = -EILSEQ;
|
host->data->error = -EILSEQ;
|
||||||
else if (intmask & SDHCI_INT_ADMA_ERROR) {
|
else if (intmask & SDHCI_INT_ADMA_ERROR) {
|
||||||
printk(KERN_ERR "%s: ADMA error\n", mmc_hostname(host->mmc));
|
pr_err("%s: ADMA error\n", mmc_hostname(host->mmc));
|
||||||
sdhci_show_adma_error(host);
|
sdhci_show_adma_error(host);
|
||||||
host->data->error = -EIO;
|
host->data->error = -EIO;
|
||||||
}
|
}
|
||||||
|
@ -2136,12 +2219,19 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
||||||
static irqreturn_t sdhci_irq(int irq, void *dev_id)
|
static irqreturn_t sdhci_irq(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
irqreturn_t result;
|
irqreturn_t result;
|
||||||
struct sdhci_host* host = dev_id;
|
struct sdhci_host *host = dev_id;
|
||||||
u32 intmask;
|
u32 intmask;
|
||||||
int cardint = 0;
|
int cardint = 0;
|
||||||
|
|
||||||
spin_lock(&host->lock);
|
spin_lock(&host->lock);
|
||||||
|
|
||||||
|
if (host->runtime_suspended) {
|
||||||
|
spin_unlock(&host->lock);
|
||||||
|
pr_warning("%s: got irq while runtime suspended\n",
|
||||||
|
mmc_hostname(host->mmc));
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
|
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
|
||||||
|
|
||||||
if (!intmask || intmask == 0xffffffff) {
|
if (!intmask || intmask == 0xffffffff) {
|
||||||
|
@ -2194,7 +2284,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
|
||||||
intmask &= ~SDHCI_INT_ERROR;
|
intmask &= ~SDHCI_INT_ERROR;
|
||||||
|
|
||||||
if (intmask & SDHCI_INT_BUS_POWER) {
|
if (intmask & SDHCI_INT_BUS_POWER) {
|
||||||
printk(KERN_ERR "%s: Card is consuming too much power!\n",
|
pr_err("%s: Card is consuming too much power!\n",
|
||||||
mmc_hostname(host->mmc));
|
mmc_hostname(host->mmc));
|
||||||
sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
|
sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
|
||||||
}
|
}
|
||||||
|
@ -2207,7 +2297,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
|
||||||
intmask &= ~SDHCI_INT_CARD_INT;
|
intmask &= ~SDHCI_INT_CARD_INT;
|
||||||
|
|
||||||
if (intmask) {
|
if (intmask) {
|
||||||
printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
|
pr_err("%s: Unexpected interrupt 0x%08x.\n",
|
||||||
mmc_hostname(host->mmc), intmask);
|
mmc_hostname(host->mmc), intmask);
|
||||||
sdhci_dumpregs(host);
|
sdhci_dumpregs(host);
|
||||||
|
|
||||||
|
@ -2275,7 +2365,6 @@ int sdhci_resume_host(struct sdhci_host *host)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
||||||
if (host->ops->enable_dma)
|
if (host->ops->enable_dma)
|
||||||
host->ops->enable_dma(host);
|
host->ops->enable_dma(host);
|
||||||
|
@ -2314,6 +2403,90 @@ EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);
|
||||||
|
|
||||||
#endif /* CONFIG_PM */
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
|
||||||
|
static int sdhci_runtime_pm_get(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
return pm_runtime_get_sync(host->mmc->parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_runtime_pm_put(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
pm_runtime_mark_last_busy(host->mmc->parent);
|
||||||
|
return pm_runtime_put_autosuspend(host->mmc->parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sdhci_runtime_suspend_host(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/* Disable tuning since we are suspending */
|
||||||
|
if (host->version >= SDHCI_SPEC_300 &&
|
||||||
|
host->tuning_mode == SDHCI_TUNING_MODE_1) {
|
||||||
|
del_timer_sync(&host->tuning_timer);
|
||||||
|
host->flags &= ~SDHCI_NEEDS_RETUNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
|
||||||
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
|
synchronize_irq(host->irq);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
host->runtime_suspended = true;
|
||||||
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
|
||||||
|
|
||||||
|
int sdhci_runtime_resume_host(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int ret = 0, host_flags = host->flags;
|
||||||
|
|
||||||
|
if (host_flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
||||||
|
if (host->ops->enable_dma)
|
||||||
|
host->ops->enable_dma(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
sdhci_init(host, 0);
|
||||||
|
|
||||||
|
/* Force clock and power re-program */
|
||||||
|
host->pwr = 0;
|
||||||
|
host->clock = 0;
|
||||||
|
sdhci_do_set_ios(host, &host->mmc->ios);
|
||||||
|
|
||||||
|
sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios);
|
||||||
|
if (host_flags & SDHCI_PV_ENABLED)
|
||||||
|
sdhci_do_enable_preset_value(host, true);
|
||||||
|
|
||||||
|
/* Set the re-tuning expiration flag */
|
||||||
|
if ((host->version >= SDHCI_SPEC_300) && host->tuning_count &&
|
||||||
|
(host->tuning_mode == SDHCI_TUNING_MODE_1))
|
||||||
|
host->flags |= SDHCI_NEEDS_RETUNING;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
|
host->runtime_suspended = false;
|
||||||
|
|
||||||
|
/* Enable SDIO IRQ */
|
||||||
|
if ((host->flags & SDHCI_SDIO_IRQ_ENABLED))
|
||||||
|
sdhci_enable_sdio_irq_nolock(host, true);
|
||||||
|
|
||||||
|
/* Enable Card Detection */
|
||||||
|
sdhci_enable_card_detection(host);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/*****************************************************************************\
|
/*****************************************************************************\
|
||||||
* *
|
* *
|
||||||
* Device allocation/registration *
|
* Device allocation/registration *
|
||||||
|
@ -2356,6 +2529,8 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
|
|
||||||
if (debug_quirks)
|
if (debug_quirks)
|
||||||
host->quirks = debug_quirks;
|
host->quirks = debug_quirks;
|
||||||
|
if (debug_quirks2)
|
||||||
|
host->quirks2 = debug_quirks2;
|
||||||
|
|
||||||
sdhci_reset(host, SDHCI_RESET_ALL);
|
sdhci_reset(host, SDHCI_RESET_ALL);
|
||||||
|
|
||||||
|
@ -2363,7 +2538,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
host->version = (host->version & SDHCI_SPEC_VER_MASK)
|
host->version = (host->version & SDHCI_SPEC_VER_MASK)
|
||||||
>> SDHCI_SPEC_VER_SHIFT;
|
>> SDHCI_SPEC_VER_SHIFT;
|
||||||
if (host->version > SDHCI_SPEC_300) {
|
if (host->version > SDHCI_SPEC_300) {
|
||||||
printk(KERN_ERR "%s: Unknown controller version (%d). "
|
pr_err("%s: Unknown controller version (%d). "
|
||||||
"You may experience problems.\n", mmc_hostname(mmc),
|
"You may experience problems.\n", mmc_hostname(mmc),
|
||||||
host->version);
|
host->version);
|
||||||
}
|
}
|
||||||
|
@ -2400,7 +2575,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
||||||
if (host->ops->enable_dma) {
|
if (host->ops->enable_dma) {
|
||||||
if (host->ops->enable_dma(host)) {
|
if (host->ops->enable_dma(host)) {
|
||||||
printk(KERN_WARNING "%s: No suitable DMA "
|
pr_warning("%s: No suitable DMA "
|
||||||
"available. Falling back to PIO.\n",
|
"available. Falling back to PIO.\n",
|
||||||
mmc_hostname(mmc));
|
mmc_hostname(mmc));
|
||||||
host->flags &=
|
host->flags &=
|
||||||
|
@ -2420,7 +2595,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
if (!host->adma_desc || !host->align_buffer) {
|
if (!host->adma_desc || !host->align_buffer) {
|
||||||
kfree(host->adma_desc);
|
kfree(host->adma_desc);
|
||||||
kfree(host->align_buffer);
|
kfree(host->align_buffer);
|
||||||
printk(KERN_WARNING "%s: Unable to allocate ADMA "
|
pr_warning("%s: Unable to allocate ADMA "
|
||||||
"buffers. Falling back to standard DMA.\n",
|
"buffers. Falling back to standard DMA.\n",
|
||||||
mmc_hostname(mmc));
|
mmc_hostname(mmc));
|
||||||
host->flags &= ~SDHCI_USE_ADMA;
|
host->flags &= ~SDHCI_USE_ADMA;
|
||||||
|
@ -2448,8 +2623,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
if (host->max_clk == 0 || host->quirks &
|
if (host->max_clk == 0 || host->quirks &
|
||||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) {
|
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) {
|
||||||
if (!host->ops->get_max_clock) {
|
if (!host->ops->get_max_clock) {
|
||||||
printk(KERN_ERR
|
pr_err("%s: Hardware doesn't specify base clock "
|
||||||
"%s: Hardware doesn't specify base clock "
|
|
||||||
"frequency.\n", mmc_hostname(mmc));
|
"frequency.\n", mmc_hostname(mmc));
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -2495,8 +2669,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
host->timeout_clk = host->ops->get_timeout_clock(host);
|
host->timeout_clk = host->ops->get_timeout_clock(host);
|
||||||
} else if (!(host->quirks &
|
} else if (!(host->quirks &
|
||||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
|
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
|
||||||
printk(KERN_ERR
|
pr_err("%s: Hardware doesn't specify timeout clock "
|
||||||
"%s: Hardware doesn't specify timeout clock "
|
|
||||||
"frequency.\n", mmc_hostname(mmc));
|
"frequency.\n", mmc_hostname(mmc));
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -2566,6 +2739,15 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
if (caps[1] & SDHCI_DRIVER_TYPE_D)
|
if (caps[1] & SDHCI_DRIVER_TYPE_D)
|
||||||
mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
|
mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If Power Off Notify capability is enabled by the host,
|
||||||
|
* set notify to short power off notify timeout value.
|
||||||
|
*/
|
||||||
|
if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY)
|
||||||
|
mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
|
||||||
|
else
|
||||||
|
mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE;
|
||||||
|
|
||||||
/* Initial value for re-tuning timer count */
|
/* Initial value for re-tuning timer count */
|
||||||
host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
|
host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
|
||||||
SDHCI_RETUNING_TIMER_COUNT_SHIFT;
|
SDHCI_RETUNING_TIMER_COUNT_SHIFT;
|
||||||
|
@ -2655,7 +2837,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
mmc->ocr_avail_mmc &= host->ocr_avail_mmc;
|
mmc->ocr_avail_mmc &= host->ocr_avail_mmc;
|
||||||
|
|
||||||
if (mmc->ocr_avail == 0) {
|
if (mmc->ocr_avail == 0) {
|
||||||
printk(KERN_ERR "%s: Hardware doesn't report any "
|
pr_err("%s: Hardware doesn't report any "
|
||||||
"support voltages.\n", mmc_hostname(mmc));
|
"support voltages.\n", mmc_hostname(mmc));
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -2703,7 +2885,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
|
mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
|
||||||
SDHCI_MAX_BLOCK_SHIFT;
|
SDHCI_MAX_BLOCK_SHIFT;
|
||||||
if (mmc->max_blk_size >= 3) {
|
if (mmc->max_blk_size >= 3) {
|
||||||
printk(KERN_WARNING "%s: Invalid maximum block size, "
|
pr_warning("%s: Invalid maximum block size, "
|
||||||
"assuming 512 bytes\n", mmc_hostname(mmc));
|
"assuming 512 bytes\n", mmc_hostname(mmc));
|
||||||
mmc->max_blk_size = 0;
|
mmc->max_blk_size = 0;
|
||||||
}
|
}
|
||||||
|
@ -2742,7 +2924,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
|
|
||||||
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
|
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
|
||||||
if (IS_ERR(host->vmmc)) {
|
if (IS_ERR(host->vmmc)) {
|
||||||
printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc));
|
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
|
||||||
host->vmmc = NULL;
|
host->vmmc = NULL;
|
||||||
} else {
|
} else {
|
||||||
regulator_enable(host->vmmc);
|
regulator_enable(host->vmmc);
|
||||||
|
@ -2771,7 +2953,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
|
|
||||||
mmc_add_host(mmc);
|
mmc_add_host(mmc);
|
||||||
|
|
||||||
printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s\n",
|
pr_info("%s: SDHCI controller on %s [%s] using %s\n",
|
||||||
mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
|
mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
|
||||||
(host->flags & SDHCI_USE_ADMA) ? "ADMA" :
|
(host->flags & SDHCI_USE_ADMA) ? "ADMA" :
|
||||||
(host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
|
(host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
|
||||||
|
@ -2804,7 +2986,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
|
||||||
host->flags |= SDHCI_DEVICE_DEAD;
|
host->flags |= SDHCI_DEVICE_DEAD;
|
||||||
|
|
||||||
if (host->mrq) {
|
if (host->mrq) {
|
||||||
printk(KERN_ERR "%s: Controller removed during "
|
pr_err("%s: Controller removed during "
|
||||||
" transfer!\n", mmc_hostname(host->mmc));
|
" transfer!\n", mmc_hostname(host->mmc));
|
||||||
|
|
||||||
host->mrq->cmd->error = -ENOMEDIUM;
|
host->mrq->cmd->error = -ENOMEDIUM;
|
||||||
|
@ -2863,9 +3045,9 @@ EXPORT_SYMBOL_GPL(sdhci_free_host);
|
||||||
|
|
||||||
static int __init sdhci_drv_init(void)
|
static int __init sdhci_drv_init(void)
|
||||||
{
|
{
|
||||||
printk(KERN_INFO DRIVER_NAME
|
pr_info(DRIVER_NAME
|
||||||
": Secure Digital Host Controller Interface driver\n");
|
": Secure Digital Host Controller Interface driver\n");
|
||||||
printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
|
pr_info(DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2878,9 +3060,11 @@ module_init(sdhci_drv_init);
|
||||||
module_exit(sdhci_drv_exit);
|
module_exit(sdhci_drv_exit);
|
||||||
|
|
||||||
module_param(debug_quirks, uint, 0444);
|
module_param(debug_quirks, uint, 0444);
|
||||||
|
module_param(debug_quirks2, uint, 0444);
|
||||||
|
|
||||||
MODULE_AUTHOR("Pierre Ossman <pierre@ossman.eu>");
|
MODULE_AUTHOR("Pierre Ossman <pierre@ossman.eu>");
|
||||||
MODULE_DESCRIPTION("Secure Digital Host Controller Interface core driver");
|
MODULE_DESCRIPTION("Secure Digital Host Controller Interface core driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
|
MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
|
||||||
|
MODULE_PARM_DESC(debug_quirks2, "Force certain other quirks.");
|
||||||
|
|
|
@ -273,7 +273,7 @@ struct sdhci_ops {
|
||||||
void (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
|
void (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
|
||||||
void (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
|
void (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
|
||||||
int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
|
int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
|
||||||
|
void (*hw_reset)(struct sdhci_host *host);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
||||||
|
@ -379,4 +379,9 @@ extern int sdhci_resume_host(struct sdhci_host *host);
|
||||||
extern void sdhci_enable_irq_wakeups(struct sdhci_host *host);
|
extern void sdhci_enable_irq_wakeups(struct sdhci_host *host);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
extern int sdhci_runtime_suspend_host(struct sdhci_host *host);
|
||||||
|
extern int sdhci_runtime_resume_host(struct sdhci_host *host);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* __SDHCI_HW_H */
|
#endif /* __SDHCI_HW_H */
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/highmem.h>
|
#include <linux/highmem.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
|
||||||
#define DRIVER_NAME "sh_mmcif"
|
#define DRIVER_NAME "sh_mmcif"
|
||||||
#define DRIVER_VERSION "2010-04-28"
|
#define DRIVER_VERSION "2010-04-28"
|
||||||
|
@ -165,6 +166,8 @@ struct sh_mmcif_host {
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct mmc_data *data;
|
struct mmc_data *data;
|
||||||
struct platform_device *pd;
|
struct platform_device *pd;
|
||||||
|
struct sh_dmae_slave dma_slave_tx;
|
||||||
|
struct sh_dmae_slave dma_slave_rx;
|
||||||
struct clk *hclk;
|
struct clk *hclk;
|
||||||
unsigned int clk;
|
unsigned int clk;
|
||||||
int bus_width;
|
int bus_width;
|
||||||
|
@ -323,25 +326,35 @@ static bool sh_mmcif_filter(struct dma_chan *chan, void *arg)
|
||||||
static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
|
static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
|
||||||
struct sh_mmcif_plat_data *pdata)
|
struct sh_mmcif_plat_data *pdata)
|
||||||
{
|
{
|
||||||
|
struct sh_dmae_slave *tx, *rx;
|
||||||
host->dma_active = false;
|
host->dma_active = false;
|
||||||
|
|
||||||
/* We can only either use DMA for both Tx and Rx or not use it at all */
|
/* We can only either use DMA for both Tx and Rx or not use it at all */
|
||||||
if (pdata->dma) {
|
if (pdata->dma) {
|
||||||
|
dev_warn(&host->pd->dev,
|
||||||
|
"Update your platform to use embedded DMA slave IDs\n");
|
||||||
|
tx = &pdata->dma->chan_priv_tx;
|
||||||
|
rx = &pdata->dma->chan_priv_rx;
|
||||||
|
} else {
|
||||||
|
tx = &host->dma_slave_tx;
|
||||||
|
tx->slave_id = pdata->slave_id_tx;
|
||||||
|
rx = &host->dma_slave_rx;
|
||||||
|
rx->slave_id = pdata->slave_id_rx;
|
||||||
|
}
|
||||||
|
if (tx->slave_id > 0 && rx->slave_id > 0) {
|
||||||
dma_cap_mask_t mask;
|
dma_cap_mask_t mask;
|
||||||
|
|
||||||
dma_cap_zero(mask);
|
dma_cap_zero(mask);
|
||||||
dma_cap_set(DMA_SLAVE, mask);
|
dma_cap_set(DMA_SLAVE, mask);
|
||||||
|
|
||||||
host->chan_tx = dma_request_channel(mask, sh_mmcif_filter,
|
host->chan_tx = dma_request_channel(mask, sh_mmcif_filter, tx);
|
||||||
&pdata->dma->chan_priv_tx);
|
|
||||||
dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__,
|
dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__,
|
||||||
host->chan_tx);
|
host->chan_tx);
|
||||||
|
|
||||||
if (!host->chan_tx)
|
if (!host->chan_tx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
host->chan_rx = dma_request_channel(mask, sh_mmcif_filter,
|
host->chan_rx = dma_request_channel(mask, sh_mmcif_filter, rx);
|
||||||
&pdata->dma->chan_priv_rx);
|
|
||||||
dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__,
|
dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__,
|
||||||
host->chan_rx);
|
host->chan_rx);
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/sh_mobile_sdhi.h>
|
#include <linux/mmc/sh_mobile_sdhi.h>
|
||||||
|
@ -96,7 +97,8 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||||
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
||||||
struct tmio_mmc_host *host;
|
struct tmio_mmc_host *host;
|
||||||
char clk_name[8];
|
char clk_name[8];
|
||||||
int i, irq, ret;
|
int irq, ret, i = 0;
|
||||||
|
bool multiplexed_isr = true;
|
||||||
|
|
||||||
priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
|
priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
|
||||||
if (priv == NULL) {
|
if (priv == NULL) {
|
||||||
|
@ -153,27 +155,60 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto eprobe;
|
goto eprobe;
|
||||||
|
|
||||||
for (i = 0; i < 3; i++) {
|
/*
|
||||||
irq = platform_get_irq(pdev, i);
|
* Allow one or more specific (named) ISRs or
|
||||||
if (irq < 0) {
|
* one or more multiplexed (un-named) ISRs.
|
||||||
if (i) {
|
*/
|
||||||
continue;
|
|
||||||
} else {
|
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT);
|
||||||
ret = irq;
|
if (irq >= 0) {
|
||||||
goto eirq;
|
multiplexed_isr = false;
|
||||||
}
|
ret = request_irq(irq, tmio_mmc_card_detect_irq, 0,
|
||||||
}
|
|
||||||
ret = request_irq(irq, tmio_mmc_irq, 0,
|
|
||||||
dev_name(&pdev->dev), host);
|
dev_name(&pdev->dev), host);
|
||||||
if (ret) {
|
if (ret)
|
||||||
while (i--) {
|
goto eirq_card_detect;
|
||||||
irq = platform_get_irq(pdev, i);
|
|
||||||
if (irq >= 0)
|
|
||||||
free_irq(irq, host);
|
|
||||||
}
|
|
||||||
goto eirq;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO);
|
||||||
|
if (irq >= 0) {
|
||||||
|
multiplexed_isr = false;
|
||||||
|
ret = request_irq(irq, tmio_mmc_sdio_irq, 0,
|
||||||
|
dev_name(&pdev->dev), host);
|
||||||
|
if (ret)
|
||||||
|
goto eirq_sdio;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDCARD);
|
||||||
|
if (irq >= 0) {
|
||||||
|
multiplexed_isr = false;
|
||||||
|
ret = request_irq(irq, tmio_mmc_sdcard_irq, 0,
|
||||||
|
dev_name(&pdev->dev), host);
|
||||||
|
if (ret)
|
||||||
|
goto eirq_sdcard;
|
||||||
|
} else if (!multiplexed_isr) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"Principal SD-card IRQ is missing among named interrupts\n");
|
||||||
|
ret = irq;
|
||||||
|
goto eirq_sdcard;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multiplexed_isr) {
|
||||||
|
while (1) {
|
||||||
|
irq = platform_get_irq(pdev, i);
|
||||||
|
if (irq < 0)
|
||||||
|
break;
|
||||||
|
i++;
|
||||||
|
ret = request_irq(irq, tmio_mmc_irq, 0,
|
||||||
|
dev_name(&pdev->dev), host);
|
||||||
|
if (ret)
|
||||||
|
goto eirq_multiplexed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* There must be at least one IRQ source */
|
||||||
|
if (!i)
|
||||||
|
goto eirq_multiplexed;
|
||||||
|
}
|
||||||
|
|
||||||
dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
|
dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
|
||||||
mmc_hostname(host->mmc), (unsigned long)
|
mmc_hostname(host->mmc), (unsigned long)
|
||||||
(platform_get_resource(pdev,IORESOURCE_MEM, 0)->start),
|
(platform_get_resource(pdev,IORESOURCE_MEM, 0)->start),
|
||||||
|
@ -181,7 +216,20 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
eirq:
|
eirq_multiplexed:
|
||||||
|
while (i--) {
|
||||||
|
irq = platform_get_irq(pdev, i);
|
||||||
|
free_irq(irq, host);
|
||||||
|
}
|
||||||
|
eirq_sdcard:
|
||||||
|
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO);
|
||||||
|
if (irq >= 0)
|
||||||
|
free_irq(irq, host);
|
||||||
|
eirq_sdio:
|
||||||
|
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT);
|
||||||
|
if (irq >= 0)
|
||||||
|
free_irq(irq, host);
|
||||||
|
eirq_card_detect:
|
||||||
tmio_mmc_host_remove(host);
|
tmio_mmc_host_remove(host);
|
||||||
eprobe:
|
eprobe:
|
||||||
clk_disable(priv->clk);
|
clk_disable(priv->clk);
|
||||||
|
@ -197,16 +245,17 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
|
||||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||||
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
||||||
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
||||||
int i, irq;
|
int i = 0, irq;
|
||||||
|
|
||||||
p->pdata = NULL;
|
p->pdata = NULL;
|
||||||
|
|
||||||
tmio_mmc_host_remove(host);
|
tmio_mmc_host_remove(host);
|
||||||
|
|
||||||
for (i = 0; i < 3; i++) {
|
while (1) {
|
||||||
irq = platform_get_irq(pdev, i);
|
irq = platform_get_irq(pdev, i++);
|
||||||
if (irq >= 0)
|
if (irq < 0)
|
||||||
free_irq(irq, host);
|
break;
|
||||||
|
free_irq(irq, host);
|
||||||
}
|
}
|
||||||
|
|
||||||
clk_disable(priv->clk);
|
clk_disable(priv->clk);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/highmem.h>
|
#include <linux/highmem.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#define DRIVER_NAME "tifm_sd"
|
#define DRIVER_NAME "tifm_sd"
|
||||||
|
@ -631,7 +632,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (host->req) {
|
if (host->req) {
|
||||||
printk(KERN_ERR "%s : unfinished request detected\n",
|
pr_err("%s : unfinished request detected\n",
|
||||||
dev_name(&sock->dev));
|
dev_name(&sock->dev));
|
||||||
mrq->cmd->error = -ETIMEDOUT;
|
mrq->cmd->error = -ETIMEDOUT;
|
||||||
goto err_out;
|
goto err_out;
|
||||||
|
@ -671,7 +672,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
r_data->flags & MMC_DATA_WRITE
|
r_data->flags & MMC_DATA_WRITE
|
||||||
? PCI_DMA_TODEVICE
|
? PCI_DMA_TODEVICE
|
||||||
: PCI_DMA_FROMDEVICE)) {
|
: PCI_DMA_FROMDEVICE)) {
|
||||||
printk(KERN_ERR "%s : scatterlist map failed\n",
|
pr_err("%s : scatterlist map failed\n",
|
||||||
dev_name(&sock->dev));
|
dev_name(&sock->dev));
|
||||||
mrq->cmd->error = -ENOMEM;
|
mrq->cmd->error = -ENOMEM;
|
||||||
goto err_out;
|
goto err_out;
|
||||||
|
@ -683,7 +684,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
? PCI_DMA_TODEVICE
|
? PCI_DMA_TODEVICE
|
||||||
: PCI_DMA_FROMDEVICE);
|
: PCI_DMA_FROMDEVICE);
|
||||||
if (host->sg_len < 1) {
|
if (host->sg_len < 1) {
|
||||||
printk(KERN_ERR "%s : scatterlist map failed\n",
|
pr_err("%s : scatterlist map failed\n",
|
||||||
dev_name(&sock->dev));
|
dev_name(&sock->dev));
|
||||||
tifm_unmap_sg(sock, &host->bounce_buf, 1,
|
tifm_unmap_sg(sock, &host->bounce_buf, 1,
|
||||||
r_data->flags & MMC_DATA_WRITE
|
r_data->flags & MMC_DATA_WRITE
|
||||||
|
@ -747,7 +748,7 @@ static void tifm_sd_end_cmd(unsigned long data)
|
||||||
host->req = NULL;
|
host->req = NULL;
|
||||||
|
|
||||||
if (!mrq) {
|
if (!mrq) {
|
||||||
printk(KERN_ERR " %s : no request to complete?\n",
|
pr_err(" %s : no request to complete?\n",
|
||||||
dev_name(&sock->dev));
|
dev_name(&sock->dev));
|
||||||
spin_unlock_irqrestore(&sock->lock, flags);
|
spin_unlock_irqrestore(&sock->lock, flags);
|
||||||
return;
|
return;
|
||||||
|
@ -786,8 +787,7 @@ static void tifm_sd_abort(unsigned long data)
|
||||||
{
|
{
|
||||||
struct tifm_sd *host = (struct tifm_sd*)data;
|
struct tifm_sd *host = (struct tifm_sd*)data;
|
||||||
|
|
||||||
printk(KERN_ERR
|
pr_err("%s : card failed to respond for a long period of time "
|
||||||
"%s : card failed to respond for a long period of time "
|
|
||||||
"(%x, %x)\n",
|
"(%x, %x)\n",
|
||||||
dev_name(&host->dev->dev), host->req->cmd->opcode, host->cmd_flags);
|
dev_name(&host->dev->dev), host->req->cmd->opcode, host->cmd_flags);
|
||||||
|
|
||||||
|
@ -905,7 +905,7 @@ static int tifm_sd_initialize_host(struct tifm_sd *host)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
printk(KERN_ERR "%s : controller failed to reset\n",
|
pr_err("%s : controller failed to reset\n",
|
||||||
dev_name(&sock->dev));
|
dev_name(&sock->dev));
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -931,8 +931,7 @@ static int tifm_sd_initialize_host(struct tifm_sd *host)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
printk(KERN_ERR
|
pr_err("%s : card not ready - probe failed on initialization\n",
|
||||||
"%s : card not ready - probe failed on initialization\n",
|
|
||||||
dev_name(&sock->dev));
|
dev_name(&sock->dev));
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -953,7 +952,7 @@ static int tifm_sd_probe(struct tifm_dev *sock)
|
||||||
|
|
||||||
if (!(TIFM_SOCK_STATE_OCCUPIED
|
if (!(TIFM_SOCK_STATE_OCCUPIED
|
||||||
& readl(sock->addr + SOCK_PRESENT_STATE))) {
|
& readl(sock->addr + SOCK_PRESENT_STATE))) {
|
||||||
printk(KERN_WARNING "%s : card gone, unexpectedly\n",
|
pr_warning("%s : card gone, unexpectedly\n",
|
||||||
dev_name(&sock->dev));
|
dev_name(&sock->dev));
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,8 +88,8 @@ static int __devinit tmio_mmc_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto cell_disable;
|
goto cell_disable;
|
||||||
|
|
||||||
ret = request_irq(irq, tmio_mmc_irq, IRQF_DISABLED |
|
ret = request_irq(irq, tmio_mmc_irq, IRQF_TRIGGER_FALLING,
|
||||||
IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), host);
|
dev_name(&pdev->dev), host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto host_remove;
|
goto host_remove;
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,10 @@ struct tmio_mmc_host {
|
||||||
struct delayed_work delayed_reset_work;
|
struct delayed_work delayed_reset_work;
|
||||||
struct work_struct done;
|
struct work_struct done;
|
||||||
|
|
||||||
|
/* Cache IRQ mask */
|
||||||
|
u32 sdcard_irq_mask;
|
||||||
|
u32 sdio_irq_mask;
|
||||||
|
|
||||||
spinlock_t lock; /* protect host private data */
|
spinlock_t lock; /* protect host private data */
|
||||||
unsigned long last_req_ts;
|
unsigned long last_req_ts;
|
||||||
struct mutex ios_lock; /* protect set_ios() context */
|
struct mutex ios_lock; /* protect set_ios() context */
|
||||||
|
@ -93,6 +97,9 @@ void tmio_mmc_do_data_irq(struct tmio_mmc_host *host);
|
||||||
void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
|
void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
|
||||||
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
|
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
|
||||||
irqreturn_t tmio_mmc_irq(int irq, void *devid);
|
irqreturn_t tmio_mmc_irq(int irq, void *devid);
|
||||||
|
irqreturn_t tmio_mmc_sdcard_irq(int irq, void *devid);
|
||||||
|
irqreturn_t tmio_mmc_card_detect_irq(int irq, void *devid);
|
||||||
|
irqreturn_t tmio_mmc_sdio_irq(int irq, void *devid);
|
||||||
|
|
||||||
static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg,
|
static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg,
|
||||||
unsigned long *flags)
|
unsigned long *flags)
|
||||||
|
|
|
@ -48,14 +48,14 @@
|
||||||
|
|
||||||
void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i)
|
void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i)
|
||||||
{
|
{
|
||||||
u32 mask = sd_ctrl_read32(host, CTL_IRQ_MASK) & ~(i & TMIO_MASK_IRQ);
|
host->sdcard_irq_mask &= ~(i & TMIO_MASK_IRQ);
|
||||||
sd_ctrl_write32(host, CTL_IRQ_MASK, mask);
|
sd_ctrl_write32(host, CTL_IRQ_MASK, host->sdcard_irq_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i)
|
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i)
|
||||||
{
|
{
|
||||||
u32 mask = sd_ctrl_read32(host, CTL_IRQ_MASK) | (i & TMIO_MASK_IRQ);
|
host->sdcard_irq_mask |= (i & TMIO_MASK_IRQ);
|
||||||
sd_ctrl_write32(host, CTL_IRQ_MASK, mask);
|
sd_ctrl_write32(host, CTL_IRQ_MASK, host->sdcard_irq_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tmio_mmc_ack_mmc_irqs(struct tmio_mmc_host *host, u32 i)
|
static void tmio_mmc_ack_mmc_irqs(struct tmio_mmc_host *host, u32 i)
|
||||||
|
@ -92,7 +92,7 @@ static int tmio_mmc_next_sg(struct tmio_mmc_host *host)
|
||||||
static void pr_debug_status(u32 status)
|
static void pr_debug_status(u32 status)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
printk(KERN_DEBUG "status: %08x = ", status);
|
pr_debug("status: %08x = ", status);
|
||||||
STATUS_TO_TEXT(CARD_REMOVE, status, i);
|
STATUS_TO_TEXT(CARD_REMOVE, status, i);
|
||||||
STATUS_TO_TEXT(CARD_INSERT, status, i);
|
STATUS_TO_TEXT(CARD_INSERT, status, i);
|
||||||
STATUS_TO_TEXT(SIGSTATE, status, i);
|
STATUS_TO_TEXT(SIGSTATE, status, i);
|
||||||
|
@ -127,11 +127,13 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
host->sdio_irq_enabled = 1;
|
host->sdio_irq_enabled = 1;
|
||||||
|
host->sdio_irq_mask = TMIO_SDIO_MASK_ALL &
|
||||||
|
~TMIO_SDIO_STAT_IOIRQ;
|
||||||
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
|
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
|
||||||
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK,
|
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
|
||||||
(TMIO_SDIO_MASK_ALL & ~TMIO_SDIO_STAT_IOIRQ));
|
|
||||||
} else {
|
} else {
|
||||||
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, TMIO_SDIO_MASK_ALL);
|
host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
|
||||||
|
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
|
||||||
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000);
|
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000);
|
||||||
host->sdio_irq_enabled = 0;
|
host->sdio_irq_enabled = 0;
|
||||||
}
|
}
|
||||||
|
@ -543,45 +545,20 @@ out:
|
||||||
spin_unlock(&host->lock);
|
spin_unlock(&host->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
irqreturn_t tmio_mmc_irq(int irq, void *devid)
|
static void tmio_mmc_card_irq_status(struct tmio_mmc_host *host,
|
||||||
|
int *ireg, int *status)
|
||||||
|
{
|
||||||
|
*status = sd_ctrl_read32(host, CTL_STATUS);
|
||||||
|
*ireg = *status & TMIO_MASK_IRQ & ~host->sdcard_irq_mask;
|
||||||
|
|
||||||
|
pr_debug_status(*status);
|
||||||
|
pr_debug_status(*ireg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __tmio_mmc_card_detect_irq(struct tmio_mmc_host *host,
|
||||||
|
int ireg, int status)
|
||||||
{
|
{
|
||||||
struct tmio_mmc_host *host = devid;
|
|
||||||
struct mmc_host *mmc = host->mmc;
|
struct mmc_host *mmc = host->mmc;
|
||||||
struct tmio_mmc_data *pdata = host->pdata;
|
|
||||||
unsigned int ireg, irq_mask, status;
|
|
||||||
unsigned int sdio_ireg, sdio_irq_mask, sdio_status;
|
|
||||||
|
|
||||||
pr_debug("MMC IRQ begin\n");
|
|
||||||
|
|
||||||
status = sd_ctrl_read32(host, CTL_STATUS);
|
|
||||||
irq_mask = sd_ctrl_read32(host, CTL_IRQ_MASK);
|
|
||||||
ireg = status & TMIO_MASK_IRQ & ~irq_mask;
|
|
||||||
|
|
||||||
sdio_ireg = 0;
|
|
||||||
if (!ireg && pdata->flags & TMIO_MMC_SDIO_IRQ) {
|
|
||||||
sdio_status = sd_ctrl_read16(host, CTL_SDIO_STATUS);
|
|
||||||
sdio_irq_mask = sd_ctrl_read16(host, CTL_SDIO_IRQ_MASK);
|
|
||||||
sdio_ireg = sdio_status & TMIO_SDIO_MASK_ALL & ~sdio_irq_mask;
|
|
||||||
|
|
||||||
sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status & ~TMIO_SDIO_MASK_ALL);
|
|
||||||
|
|
||||||
if (sdio_ireg && !host->sdio_irq_enabled) {
|
|
||||||
pr_warning("tmio_mmc: Spurious SDIO IRQ, disabling! 0x%04x 0x%04x 0x%04x\n",
|
|
||||||
sdio_status, sdio_irq_mask, sdio_ireg);
|
|
||||||
tmio_mmc_enable_sdio_irq(mmc, 0);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mmc->caps & MMC_CAP_SDIO_IRQ &&
|
|
||||||
sdio_ireg & TMIO_SDIO_STAT_IOIRQ)
|
|
||||||
mmc_signal_sdio_irq(mmc);
|
|
||||||
|
|
||||||
if (sdio_ireg)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_debug_status(status);
|
|
||||||
pr_debug_status(ireg);
|
|
||||||
|
|
||||||
/* Card insert / remove attempts */
|
/* Card insert / remove attempts */
|
||||||
if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)) {
|
if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)) {
|
||||||
|
@ -591,43 +568,102 @@ irqreturn_t tmio_mmc_irq(int irq, void *devid)
|
||||||
((ireg & TMIO_STAT_CARD_INSERT) && !mmc->card)) &&
|
((ireg & TMIO_STAT_CARD_INSERT) && !mmc->card)) &&
|
||||||
!work_pending(&mmc->detect.work))
|
!work_pending(&mmc->detect.work))
|
||||||
mmc_detect_change(host->mmc, msecs_to_jiffies(100));
|
mmc_detect_change(host->mmc, msecs_to_jiffies(100));
|
||||||
goto out;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CRC and other errors */
|
return false;
|
||||||
/* if (ireg & TMIO_STAT_ERR_IRQ)
|
}
|
||||||
* handled |= tmio_error_irq(host, irq, stat);
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
irqreturn_t tmio_mmc_card_detect_irq(int irq, void *devid)
|
||||||
|
{
|
||||||
|
unsigned int ireg, status;
|
||||||
|
struct tmio_mmc_host *host = devid;
|
||||||
|
|
||||||
|
tmio_mmc_card_irq_status(host, &ireg, &status);
|
||||||
|
__tmio_mmc_card_detect_irq(host, ireg, status);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(tmio_mmc_card_detect_irq);
|
||||||
|
|
||||||
|
static bool __tmio_mmc_sdcard_irq(struct tmio_mmc_host *host,
|
||||||
|
int ireg, int status)
|
||||||
|
{
|
||||||
/* Command completion */
|
/* Command completion */
|
||||||
if (ireg & (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT)) {
|
if (ireg & (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT)) {
|
||||||
tmio_mmc_ack_mmc_irqs(host,
|
tmio_mmc_ack_mmc_irqs(host,
|
||||||
TMIO_STAT_CMDRESPEND |
|
TMIO_STAT_CMDRESPEND |
|
||||||
TMIO_STAT_CMDTIMEOUT);
|
TMIO_STAT_CMDTIMEOUT);
|
||||||
tmio_mmc_cmd_irq(host, status);
|
tmio_mmc_cmd_irq(host, status);
|
||||||
goto out;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Data transfer */
|
/* Data transfer */
|
||||||
if (ireg & (TMIO_STAT_RXRDY | TMIO_STAT_TXRQ)) {
|
if (ireg & (TMIO_STAT_RXRDY | TMIO_STAT_TXRQ)) {
|
||||||
tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_RXRDY | TMIO_STAT_TXRQ);
|
tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_RXRDY | TMIO_STAT_TXRQ);
|
||||||
tmio_mmc_pio_irq(host);
|
tmio_mmc_pio_irq(host);
|
||||||
goto out;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Data transfer completion */
|
/* Data transfer completion */
|
||||||
if (ireg & TMIO_STAT_DATAEND) {
|
if (ireg & TMIO_STAT_DATAEND) {
|
||||||
tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_DATAEND);
|
tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_DATAEND);
|
||||||
tmio_mmc_data_irq(host);
|
tmio_mmc_data_irq(host);
|
||||||
goto out;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_warning("tmio_mmc: Spurious irq, disabling! "
|
return false;
|
||||||
"0x%08x 0x%08x 0x%08x\n", status, irq_mask, ireg);
|
}
|
||||||
pr_debug_status(status);
|
|
||||||
tmio_mmc_disable_mmc_irqs(host, status & ~irq_mask);
|
irqreturn_t tmio_mmc_sdcard_irq(int irq, void *devid)
|
||||||
|
{
|
||||||
|
unsigned int ireg, status;
|
||||||
|
struct tmio_mmc_host *host = devid;
|
||||||
|
|
||||||
|
tmio_mmc_card_irq_status(host, &ireg, &status);
|
||||||
|
__tmio_mmc_sdcard_irq(host, ireg, status);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(tmio_mmc_sdcard_irq);
|
||||||
|
|
||||||
|
irqreturn_t tmio_mmc_sdio_irq(int irq, void *devid)
|
||||||
|
{
|
||||||
|
struct tmio_mmc_host *host = devid;
|
||||||
|
struct mmc_host *mmc = host->mmc;
|
||||||
|
struct tmio_mmc_data *pdata = host->pdata;
|
||||||
|
unsigned int ireg, status;
|
||||||
|
|
||||||
|
if (!(pdata->flags & TMIO_MMC_SDIO_IRQ))
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
|
status = sd_ctrl_read16(host, CTL_SDIO_STATUS);
|
||||||
|
ireg = status & TMIO_SDIO_MASK_ALL & ~host->sdcard_irq_mask;
|
||||||
|
|
||||||
|
sd_ctrl_write16(host, CTL_SDIO_STATUS, status & ~TMIO_SDIO_MASK_ALL);
|
||||||
|
|
||||||
|
if (mmc->caps & MMC_CAP_SDIO_IRQ && ireg & TMIO_SDIO_STAT_IOIRQ)
|
||||||
|
mmc_signal_sdio_irq(mmc);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(tmio_mmc_sdio_irq);
|
||||||
|
|
||||||
|
irqreturn_t tmio_mmc_irq(int irq, void *devid)
|
||||||
|
{
|
||||||
|
struct tmio_mmc_host *host = devid;
|
||||||
|
unsigned int ireg, status;
|
||||||
|
|
||||||
|
pr_debug("MMC IRQ begin\n");
|
||||||
|
|
||||||
|
tmio_mmc_card_irq_status(host, &ireg, &status);
|
||||||
|
if (__tmio_mmc_card_detect_irq(host, ireg, status))
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
if (__tmio_mmc_sdcard_irq(host, ireg, status))
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
|
tmio_mmc_sdio_irq(irq, devid);
|
||||||
|
|
||||||
out:
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(tmio_mmc_irq);
|
EXPORT_SYMBOL(tmio_mmc_irq);
|
||||||
|
@ -882,6 +918,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
||||||
tmio_mmc_clk_stop(_host);
|
tmio_mmc_clk_stop(_host);
|
||||||
tmio_mmc_reset(_host);
|
tmio_mmc_reset(_host);
|
||||||
|
|
||||||
|
_host->sdcard_irq_mask = sd_ctrl_read32(_host, CTL_IRQ_MASK);
|
||||||
tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
|
tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
|
||||||
if (pdata->flags & TMIO_MMC_SDIO_IRQ)
|
if (pdata->flags & TMIO_MMC_SDIO_IRQ)
|
||||||
tmio_mmc_enable_sdio_irq(mmc, 0);
|
tmio_mmc_enable_sdio_irq(mmc, 0);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/highmem.h>
|
#include <linux/highmem.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
@ -1191,7 +1192,7 @@ static void __devexit via_sd_remove(struct pci_dev *pcidev)
|
||||||
mmiowb();
|
mmiowb();
|
||||||
|
|
||||||
if (sdhost->mrq) {
|
if (sdhost->mrq) {
|
||||||
printk(KERN_ERR "%s: Controller removed during "
|
pr_err("%s: Controller removed during "
|
||||||
"transfer\n", mmc_hostname(sdhost->mmc));
|
"transfer\n", mmc_hostname(sdhost->mmc));
|
||||||
|
|
||||||
/* make sure all DMA is stopped */
|
/* make sure all DMA is stopped */
|
||||||
|
|
|
@ -194,7 +194,7 @@ static void wbsd_reset(struct wbsd_host *host)
|
||||||
{
|
{
|
||||||
u8 setup;
|
u8 setup;
|
||||||
|
|
||||||
printk(KERN_ERR "%s: Resetting chip\n", mmc_hostname(host->mmc));
|
pr_err("%s: Resetting chip\n", mmc_hostname(host->mmc));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Soft reset of chip (SD/MMC part).
|
* Soft reset of chip (SD/MMC part).
|
||||||
|
@ -721,7 +721,7 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
|
||||||
* Any leftover data?
|
* Any leftover data?
|
||||||
*/
|
*/
|
||||||
if (count) {
|
if (count) {
|
||||||
printk(KERN_ERR "%s: Incomplete DMA transfer. "
|
pr_err("%s: Incomplete DMA transfer. "
|
||||||
"%d bytes left.\n",
|
"%d bytes left.\n",
|
||||||
mmc_hostname(host->mmc), count);
|
mmc_hostname(host->mmc), count);
|
||||||
|
|
||||||
|
@ -803,7 +803,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
#ifdef CONFIG_MMC_DEBUG
|
#ifdef CONFIG_MMC_DEBUG
|
||||||
printk(KERN_WARNING "%s: Data command %d is not "
|
pr_warning("%s: Data command %d is not "
|
||||||
"supported by this controller.\n",
|
"supported by this controller.\n",
|
||||||
mmc_hostname(host->mmc), cmd->opcode);
|
mmc_hostname(host->mmc), cmd->opcode);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1029,7 +1029,7 @@ static void wbsd_tasklet_card(unsigned long param)
|
||||||
host->flags &= ~WBSD_FCARD_PRESENT;
|
host->flags &= ~WBSD_FCARD_PRESENT;
|
||||||
|
|
||||||
if (host->mrq) {
|
if (host->mrq) {
|
||||||
printk(KERN_ERR "%s: Card removed during transfer!\n",
|
pr_err("%s: Card removed during transfer!\n",
|
||||||
mmc_hostname(host->mmc));
|
mmc_hostname(host->mmc));
|
||||||
wbsd_reset(host);
|
wbsd_reset(host);
|
||||||
|
|
||||||
|
@ -1429,7 +1429,7 @@ free:
|
||||||
free_dma(dma);
|
free_dma(dma);
|
||||||
|
|
||||||
err:
|
err:
|
||||||
printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. "
|
pr_warning(DRIVER_NAME ": Unable to allocate DMA %d. "
|
||||||
"Falling back on FIFO.\n", dma);
|
"Falling back on FIFO.\n", dma);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1664,7 +1664,7 @@ static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
|
||||||
ret = wbsd_scan(host);
|
ret = wbsd_scan(host);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (pnp && (ret == -ENODEV)) {
|
if (pnp && (ret == -ENODEV)) {
|
||||||
printk(KERN_WARNING DRIVER_NAME
|
pr_warning(DRIVER_NAME
|
||||||
": Unable to confirm device presence. You may "
|
": Unable to confirm device presence. You may "
|
||||||
"experience lock-ups.\n");
|
"experience lock-ups.\n");
|
||||||
} else {
|
} else {
|
||||||
|
@ -1688,7 +1688,7 @@ static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
|
||||||
*/
|
*/
|
||||||
if (pnp) {
|
if (pnp) {
|
||||||
if ((host->config != 0) && !wbsd_chip_validate(host)) {
|
if ((host->config != 0) && !wbsd_chip_validate(host)) {
|
||||||
printk(KERN_WARNING DRIVER_NAME
|
pr_warning(DRIVER_NAME
|
||||||
": PnP active but chip not configured! "
|
": PnP active but chip not configured! "
|
||||||
"You probably have a buggy BIOS. "
|
"You probably have a buggy BIOS. "
|
||||||
"Configuring chip manually.\n");
|
"Configuring chip manually.\n");
|
||||||
|
@ -1720,7 +1720,7 @@ static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
|
||||||
|
|
||||||
mmc_add_host(mmc);
|
mmc_add_host(mmc);
|
||||||
|
|
||||||
printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc));
|
pr_info("%s: W83L51xD", mmc_hostname(mmc));
|
||||||
if (host->chip_id != 0)
|
if (host->chip_id != 0)
|
||||||
printk(" id %x", (int)host->chip_id);
|
printk(" id %x", (int)host->chip_id);
|
||||||
printk(" at 0x%x irq %d", (int)host->base, (int)host->irq);
|
printk(" at 0x%x irq %d", (int)host->base, (int)host->irq);
|
||||||
|
@ -1909,7 +1909,7 @@ static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
|
||||||
*/
|
*/
|
||||||
if (host->config != 0) {
|
if (host->config != 0) {
|
||||||
if (!wbsd_chip_validate(host)) {
|
if (!wbsd_chip_validate(host)) {
|
||||||
printk(KERN_WARNING DRIVER_NAME
|
pr_warning(DRIVER_NAME
|
||||||
": PnP active but chip not configured! "
|
": PnP active but chip not configured! "
|
||||||
"You probably have a buggy BIOS. "
|
"You probably have a buggy BIOS. "
|
||||||
"Configuring chip manually.\n");
|
"Configuring chip manually.\n");
|
||||||
|
@ -1973,9 +1973,9 @@ static int __init wbsd_drv_init(void)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
printk(KERN_INFO DRIVER_NAME
|
pr_info(DRIVER_NAME
|
||||||
": Winbond W83L51xD SD/MMC card interface driver\n");
|
": Winbond W83L51xD SD/MMC card interface driver\n");
|
||||||
printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
|
pr_info(DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
|
||||||
|
|
||||||
#ifdef CONFIG_PNP
|
#ifdef CONFIG_PNP
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef __LINUX_ATMEL_MCI_H
|
#ifndef __LINUX_ATMEL_MCI_H
|
||||||
#define __LINUX_ATMEL_MCI_H
|
#define __LINUX_ATMEL_MCI_H
|
||||||
|
|
||||||
#define ATMEL_MCI_MAX_NR_SLOTS 2
|
#define ATMCI_MAX_NR_SLOTS 2
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct mci_slot_pdata - board-specific per-slot configuration
|
* struct mci_slot_pdata - board-specific per-slot configuration
|
||||||
|
@ -33,7 +33,7 @@ struct mci_slot_pdata {
|
||||||
*/
|
*/
|
||||||
struct mci_platform_data {
|
struct mci_platform_data {
|
||||||
struct mci_dma_data *dma_slave;
|
struct mci_dma_data *dma_slave;
|
||||||
struct mci_slot_pdata slot[ATMEL_MCI_MAX_NR_SLOTS];
|
struct mci_slot_pdata slot[ATMCI_MAX_NR_SLOTS];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* __LINUX_ATMEL_MCI_H */
|
#endif /* __LINUX_ATMEL_MCI_H */
|
||||||
|
|
|
@ -33,4 +33,6 @@
|
||||||
|
|
||||||
#define ATMEL_PDC_PTSR 0x124 /* Transfer Status Register */
|
#define ATMEL_PDC_PTSR 0x124 /* Transfer Status Register */
|
||||||
|
|
||||||
|
#define ATMEL_PDC_SCND_BUF_OFF 0x10 /* Offset between first and second buffer registers */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -50,8 +50,12 @@ struct mmc_ext_csd {
|
||||||
u8 rel_sectors;
|
u8 rel_sectors;
|
||||||
u8 rel_param;
|
u8 rel_param;
|
||||||
u8 part_config;
|
u8 part_config;
|
||||||
|
u8 cache_ctrl;
|
||||||
|
u8 rst_n_function;
|
||||||
unsigned int part_time; /* Units: ms */
|
unsigned int part_time; /* Units: ms */
|
||||||
unsigned int sa_timeout; /* Units: 100ns */
|
unsigned int sa_timeout; /* Units: 100ns */
|
||||||
|
unsigned int generic_cmd6_time; /* Units: 10ms */
|
||||||
|
unsigned int power_off_longtime; /* Units: ms */
|
||||||
unsigned int hs_max_dtr;
|
unsigned int hs_max_dtr;
|
||||||
unsigned int sectors;
|
unsigned int sectors;
|
||||||
unsigned int card_type;
|
unsigned int card_type;
|
||||||
|
@ -63,11 +67,15 @@ struct mmc_ext_csd {
|
||||||
bool enhanced_area_en; /* enable bit */
|
bool enhanced_area_en; /* enable bit */
|
||||||
unsigned long long enhanced_area_offset; /* Units: Byte */
|
unsigned long long enhanced_area_offset; /* Units: Byte */
|
||||||
unsigned int enhanced_area_size; /* Units: KB */
|
unsigned int enhanced_area_size; /* Units: KB */
|
||||||
unsigned int boot_size; /* in bytes */
|
unsigned int cache_size; /* Units: KB */
|
||||||
|
bool hpi_en; /* HPI enablebit */
|
||||||
|
bool hpi; /* HPI support bit */
|
||||||
|
unsigned int hpi_cmd; /* cmd used as HPI */
|
||||||
u8 raw_partition_support; /* 160 */
|
u8 raw_partition_support; /* 160 */
|
||||||
u8 raw_erased_mem_count; /* 181 */
|
u8 raw_erased_mem_count; /* 181 */
|
||||||
u8 raw_ext_csd_structure; /* 194 */
|
u8 raw_ext_csd_structure; /* 194 */
|
||||||
u8 raw_card_type; /* 196 */
|
u8 raw_card_type; /* 196 */
|
||||||
|
u8 out_of_int_time; /* 198 */
|
||||||
u8 raw_s_a_timeout; /* 217 */
|
u8 raw_s_a_timeout; /* 217 */
|
||||||
u8 raw_hc_erase_gap_size; /* 221 */
|
u8 raw_hc_erase_gap_size; /* 221 */
|
||||||
u8 raw_erase_timeout_mult; /* 223 */
|
u8 raw_erase_timeout_mult; /* 223 */
|
||||||
|
@ -77,6 +85,9 @@ struct mmc_ext_csd {
|
||||||
u8 raw_sec_feature_support;/* 231 */
|
u8 raw_sec_feature_support;/* 231 */
|
||||||
u8 raw_trim_mult; /* 232 */
|
u8 raw_trim_mult; /* 232 */
|
||||||
u8 raw_sectors[4]; /* 212 - 4 bytes */
|
u8 raw_sectors[4]; /* 212 - 4 bytes */
|
||||||
|
|
||||||
|
unsigned int feature_support;
|
||||||
|
#define MMC_DISCARD_FEATURE BIT(0) /* CMD38 feature */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sd_scr {
|
struct sd_scr {
|
||||||
|
@ -157,6 +168,24 @@ struct sdio_func_tuple;
|
||||||
|
|
||||||
#define SDIO_MAX_FUNCS 7
|
#define SDIO_MAX_FUNCS 7
|
||||||
|
|
||||||
|
/* The number of MMC physical partitions. These consist of:
|
||||||
|
* boot partitions (2), general purpose partitions (4) in MMC v4.4.
|
||||||
|
*/
|
||||||
|
#define MMC_NUM_BOOT_PARTITION 2
|
||||||
|
#define MMC_NUM_GP_PARTITION 4
|
||||||
|
#define MMC_NUM_PHY_PARTITION 6
|
||||||
|
#define MAX_MMC_PART_NAME_LEN 20
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MMC Physical partitions
|
||||||
|
*/
|
||||||
|
struct mmc_part {
|
||||||
|
unsigned int size; /* partition size (in bytes) */
|
||||||
|
unsigned int part_cfg; /* partition type */
|
||||||
|
char name[MAX_MMC_PART_NAME_LEN];
|
||||||
|
bool force_ro; /* to make boot parts RO by default */
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MMC device
|
* MMC device
|
||||||
*/
|
*/
|
||||||
|
@ -188,6 +217,13 @@ struct mmc_card {
|
||||||
#define MMC_QUIRK_DISABLE_CD (1<<5) /* disconnect CD/DAT[3] resistor */
|
#define MMC_QUIRK_DISABLE_CD (1<<5) /* disconnect CD/DAT[3] resistor */
|
||||||
#define MMC_QUIRK_INAND_CMD38 (1<<6) /* iNAND devices have broken CMD38 */
|
#define MMC_QUIRK_INAND_CMD38 (1<<6) /* iNAND devices have broken CMD38 */
|
||||||
#define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */
|
#define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */
|
||||||
|
#define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8) /* Avoid sending 512 bytes in */
|
||||||
|
/* byte mode */
|
||||||
|
unsigned int poweroff_notify_state; /* eMMC4.5 notify feature */
|
||||||
|
#define MMC_NO_POWER_NOTIFICATION 0
|
||||||
|
#define MMC_POWERED_ON 1
|
||||||
|
#define MMC_POWEROFF_SHORT 2
|
||||||
|
#define MMC_POWEROFF_LONG 3
|
||||||
|
|
||||||
unsigned int erase_size; /* erase size in sectors */
|
unsigned int erase_size; /* erase size in sectors */
|
||||||
unsigned int erase_shift; /* if erase unit is power 2 */
|
unsigned int erase_shift; /* if erase unit is power 2 */
|
||||||
|
@ -216,8 +252,23 @@ struct mmc_card {
|
||||||
unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */
|
unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */
|
||||||
|
|
||||||
struct dentry *debugfs_root;
|
struct dentry *debugfs_root;
|
||||||
|
struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
|
||||||
|
unsigned int nr_parts;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function fill contents in mmc_part.
|
||||||
|
*/
|
||||||
|
static inline void mmc_part_add(struct mmc_card *card, unsigned int size,
|
||||||
|
unsigned int part_cfg, char *name, int idx, bool ro)
|
||||||
|
{
|
||||||
|
card->part[card->nr_parts].size = size;
|
||||||
|
card->part[card->nr_parts].part_cfg = part_cfg;
|
||||||
|
sprintf(card->part[card->nr_parts].name, name, idx);
|
||||||
|
card->part[card->nr_parts].force_ro = ro;
|
||||||
|
card->nr_parts++;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The world is not perfect and supplies us with broken mmc/sdio devices.
|
* The world is not perfect and supplies us with broken mmc/sdio devices.
|
||||||
* For at least some of these bugs we need a work-around.
|
* For at least some of these bugs we need a work-around.
|
||||||
|
@ -377,6 +428,11 @@ static inline int mmc_card_nonstd_func_interface(const struct mmc_card *c)
|
||||||
return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF;
|
return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int mmc_card_broken_byte_mode_512(const struct mmc_card *c)
|
||||||
|
{
|
||||||
|
return c->quirks & MMC_QUIRK_BROKEN_BYTE_MODE_512;
|
||||||
|
}
|
||||||
|
|
||||||
#define mmc_card_name(c) ((c)->cid.prod_name)
|
#define mmc_card_name(c) ((c)->cid.prod_name)
|
||||||
#define mmc_card_id(c) (dev_name(&(c)->dev))
|
#define mmc_card_id(c) (dev_name(&(c)->dev))
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,7 @@ struct mmc_async_req;
|
||||||
|
|
||||||
extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
|
extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
|
||||||
struct mmc_async_req *, int *);
|
struct mmc_async_req *, int *);
|
||||||
|
extern int mmc_interrupt_hpi(struct mmc_card *);
|
||||||
extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
|
extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
|
||||||
extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
|
extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
|
||||||
extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
|
extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
|
||||||
|
@ -146,6 +147,7 @@ extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
|
||||||
#define MMC_ERASE_ARG 0x00000000
|
#define MMC_ERASE_ARG 0x00000000
|
||||||
#define MMC_SECURE_ERASE_ARG 0x80000000
|
#define MMC_SECURE_ERASE_ARG 0x80000000
|
||||||
#define MMC_TRIM_ARG 0x00000001
|
#define MMC_TRIM_ARG 0x00000001
|
||||||
|
#define MMC_DISCARD_ARG 0x00000003
|
||||||
#define MMC_SECURE_TRIM1_ARG 0x80000001
|
#define MMC_SECURE_TRIM1_ARG 0x80000001
|
||||||
#define MMC_SECURE_TRIM2_ARG 0x80008000
|
#define MMC_SECURE_TRIM2_ARG 0x80008000
|
||||||
|
|
||||||
|
@ -156,12 +158,17 @@ extern int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
|
||||||
unsigned int arg);
|
unsigned int arg);
|
||||||
extern int mmc_can_erase(struct mmc_card *card);
|
extern int mmc_can_erase(struct mmc_card *card);
|
||||||
extern int mmc_can_trim(struct mmc_card *card);
|
extern int mmc_can_trim(struct mmc_card *card);
|
||||||
|
extern int mmc_can_discard(struct mmc_card *card);
|
||||||
|
extern int mmc_can_sanitize(struct mmc_card *card);
|
||||||
extern int mmc_can_secure_erase_trim(struct mmc_card *card);
|
extern int mmc_can_secure_erase_trim(struct mmc_card *card);
|
||||||
extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
|
extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
|
||||||
unsigned int nr);
|
unsigned int nr);
|
||||||
extern unsigned int mmc_calc_max_discard(struct mmc_card *card);
|
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_blocklen(struct mmc_card *card, unsigned int blocklen);
|
||||||
|
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);
|
||||||
|
|
||||||
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *);
|
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *);
|
||||||
extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int);
|
extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int);
|
||||||
|
@ -171,6 +178,8 @@ extern void mmc_release_host(struct mmc_host *host);
|
||||||
extern void mmc_do_release_host(struct mmc_host *host);
|
extern void mmc_do_release_host(struct mmc_host *host);
|
||||||
extern int mmc_try_claim_host(struct mmc_host *host);
|
extern int mmc_try_claim_host(struct mmc_host *host);
|
||||||
|
|
||||||
|
extern int mmc_flush_cache(struct mmc_card *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mmc_claim_host - exclusively claim a host
|
* mmc_claim_host - exclusively claim a host
|
||||||
* @host: mmc host to claim
|
* @host: mmc host to claim
|
||||||
|
|
|
@ -72,6 +72,8 @@ struct mmc_data;
|
||||||
* rate and timeout calculations.
|
* rate and timeout calculations.
|
||||||
* @current_speed: Configured rate of the controller.
|
* @current_speed: Configured rate of the controller.
|
||||||
* @num_slots: Number of slots available.
|
* @num_slots: Number of slots available.
|
||||||
|
* @verid: Denote Version ID.
|
||||||
|
* @data_offset: Set the offset of DATA register according to VERID.
|
||||||
* @pdev: Platform device associated with the MMC controller.
|
* @pdev: Platform device associated with the MMC controller.
|
||||||
* @pdata: Platform data associated with the MMC controller.
|
* @pdata: Platform data associated with the MMC controller.
|
||||||
* @slot: Slots sharing this MMC controller.
|
* @slot: Slots sharing this MMC controller.
|
||||||
|
@ -147,6 +149,8 @@ struct dw_mci {
|
||||||
u32 current_speed;
|
u32 current_speed;
|
||||||
u32 num_slots;
|
u32 num_slots;
|
||||||
u32 fifoth_val;
|
u32 fifoth_val;
|
||||||
|
u16 verid;
|
||||||
|
u16 data_offset;
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct dw_mci_board *pdata;
|
struct dw_mci_board *pdata;
|
||||||
struct dw_mci_slot *slot[MAX_MCI_SLOTS];
|
struct dw_mci_slot *slot[MAX_MCI_SLOTS];
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
|
#include <linux/fault-inject.h>
|
||||||
|
|
||||||
#include <linux/mmc/core.h>
|
#include <linux/mmc/core.h>
|
||||||
#include <linux/mmc/pm.h>
|
#include <linux/mmc/pm.h>
|
||||||
|
@ -108,6 +109,9 @@ struct mmc_host_ops {
|
||||||
* It is optional for the host to implement pre_req and post_req in
|
* It is optional for the host to implement pre_req and post_req in
|
||||||
* order to support double buffering of requests (prepare one
|
* order to support double buffering of requests (prepare one
|
||||||
* request while another request is active).
|
* request while another request is active).
|
||||||
|
* pre_req() must always be followed by a post_req().
|
||||||
|
* To undo a call made to pre_req(), call post_req() with
|
||||||
|
* a nonzero err condition.
|
||||||
*/
|
*/
|
||||||
void (*post_req)(struct mmc_host *host, struct mmc_request *req,
|
void (*post_req)(struct mmc_host *host, struct mmc_request *req,
|
||||||
int err);
|
int err);
|
||||||
|
@ -147,6 +151,7 @@ struct mmc_host_ops {
|
||||||
int (*execute_tuning)(struct mmc_host *host);
|
int (*execute_tuning)(struct mmc_host *host);
|
||||||
void (*enable_preset_value)(struct mmc_host *host, bool enable);
|
void (*enable_preset_value)(struct mmc_host *host, bool enable);
|
||||||
int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
|
int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
|
||||||
|
void (*hw_reset)(struct mmc_host *host);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mmc_card;
|
struct mmc_card;
|
||||||
|
@ -229,8 +234,20 @@ struct mmc_host {
|
||||||
#define MMC_CAP_MAX_CURRENT_600 (1 << 28) /* Host max current limit is 600mA */
|
#define MMC_CAP_MAX_CURRENT_600 (1 << 28) /* Host max current limit is 600mA */
|
||||||
#define MMC_CAP_MAX_CURRENT_800 (1 << 29) /* Host max current limit is 800mA */
|
#define MMC_CAP_MAX_CURRENT_800 (1 << 29) /* Host max current limit is 800mA */
|
||||||
#define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */
|
#define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */
|
||||||
|
#define MMC_CAP_HW_RESET (1 << 31) /* Hardware reset */
|
||||||
|
|
||||||
|
unsigned int 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 */
|
||||||
|
#define MMC_CAP2_POWEROFF_NOTIFY (1 << 2) /* Notify poweroff supported */
|
||||||
|
#define MMC_CAP2_NO_MULTI_READ (1 << 3) /* Multiblock reads don't work */
|
||||||
|
|
||||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||||
|
unsigned int power_notify_type;
|
||||||
|
#define MMC_HOST_PW_NOTIFY_NONE 0
|
||||||
|
#define MMC_HOST_PW_NOTIFY_SHORT 1
|
||||||
|
#define MMC_HOST_PW_NOTIFY_LONG 2
|
||||||
|
|
||||||
#ifdef CONFIG_MMC_CLKGATE
|
#ifdef CONFIG_MMC_CLKGATE
|
||||||
int clk_requests; /* internal reference counter */
|
int clk_requests; /* internal reference counter */
|
||||||
|
@ -302,6 +319,10 @@ struct mmc_host {
|
||||||
|
|
||||||
struct mmc_async_req *areq; /* active async req */
|
struct mmc_async_req *areq; /* active async req */
|
||||||
|
|
||||||
|
#ifdef CONFIG_FAIL_MMC_REQUEST
|
||||||
|
struct fault_attr fail_mmc_request;
|
||||||
|
#endif
|
||||||
|
|
||||||
unsigned long private[0] ____cacheline_aligned;
|
unsigned long private[0] ____cacheline_aligned;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -330,6 +351,8 @@ extern int mmc_power_restore_host(struct mmc_host *host);
|
||||||
extern void mmc_detect_change(struct mmc_host *, unsigned long delay);
|
extern void mmc_detect_change(struct mmc_host *, unsigned long delay);
|
||||||
extern void mmc_request_done(struct mmc_host *, struct mmc_request *);
|
extern void mmc_request_done(struct mmc_host *, struct mmc_request *);
|
||||||
|
|
||||||
|
extern int mmc_cache_ctrl(struct mmc_host *, u8);
|
||||||
|
|
||||||
static inline void mmc_signal_sdio_irq(struct mmc_host *host)
|
static inline void mmc_signal_sdio_irq(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
host->ops->enable_sdio_irq(host, 0);
|
host->ops->enable_sdio_irq(host, 0);
|
||||||
|
@ -394,4 +417,10 @@ static inline int mmc_host_cmd23(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
return host->caps & MMC_CAP_CMD23;
|
return host->caps & MMC_CAP_CMD23;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int mmc_boot_partition_access(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* LINUX_MMC_HOST_H */
|
#endif /* LINUX_MMC_HOST_H */
|
||||||
|
|
|
@ -270,18 +270,31 @@ struct _mmc_csd {
|
||||||
* EXT_CSD fields
|
* EXT_CSD fields
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define EXT_CSD_FLUSH_CACHE 32 /* W */
|
||||||
|
#define EXT_CSD_CACHE_CTRL 33 /* R/W */
|
||||||
|
#define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */
|
||||||
|
#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */
|
||||||
#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */
|
#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */
|
||||||
#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */
|
#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */
|
||||||
|
#define EXT_CSD_HPI_MGMT 161 /* R/W */
|
||||||
|
#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */
|
||||||
|
#define EXT_CSD_SANITIZE_START 165 /* W */
|
||||||
#define EXT_CSD_WR_REL_PARAM 166 /* RO */
|
#define EXT_CSD_WR_REL_PARAM 166 /* RO */
|
||||||
#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
|
#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
|
||||||
#define EXT_CSD_PART_CONFIG 179 /* R/W */
|
#define EXT_CSD_PART_CONFIG 179 /* R/W */
|
||||||
#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */
|
#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */
|
||||||
#define EXT_CSD_BUS_WIDTH 183 /* R/W */
|
#define EXT_CSD_BUS_WIDTH 183 /* R/W */
|
||||||
#define EXT_CSD_HS_TIMING 185 /* R/W */
|
#define EXT_CSD_HS_TIMING 185 /* R/W */
|
||||||
|
#define EXT_CSD_POWER_CLASS 187 /* R/W */
|
||||||
#define EXT_CSD_REV 192 /* RO */
|
#define EXT_CSD_REV 192 /* RO */
|
||||||
#define EXT_CSD_STRUCTURE 194 /* RO */
|
#define EXT_CSD_STRUCTURE 194 /* RO */
|
||||||
#define EXT_CSD_CARD_TYPE 196 /* RO */
|
#define EXT_CSD_CARD_TYPE 196 /* RO */
|
||||||
|
#define EXT_CSD_OUT_OF_INTERRUPT_TIME 198 /* RO */
|
||||||
#define EXT_CSD_PART_SWITCH_TIME 199 /* RO */
|
#define EXT_CSD_PART_SWITCH_TIME 199 /* RO */
|
||||||
|
#define EXT_CSD_PWR_CL_52_195 200 /* RO */
|
||||||
|
#define EXT_CSD_PWR_CL_26_195 201 /* RO */
|
||||||
|
#define EXT_CSD_PWR_CL_52_360 202 /* RO */
|
||||||
|
#define EXT_CSD_PWR_CL_26_360 203 /* RO */
|
||||||
#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
|
#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
|
||||||
#define EXT_CSD_S_A_TIMEOUT 217 /* RO */
|
#define EXT_CSD_S_A_TIMEOUT 217 /* RO */
|
||||||
#define EXT_CSD_REL_WR_SEC_C 222 /* RO */
|
#define EXT_CSD_REL_WR_SEC_C 222 /* RO */
|
||||||
|
@ -293,6 +306,14 @@ struct _mmc_csd {
|
||||||
#define EXT_CSD_SEC_ERASE_MULT 230 /* RO */
|
#define EXT_CSD_SEC_ERASE_MULT 230 /* RO */
|
||||||
#define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */
|
#define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */
|
||||||
#define EXT_CSD_TRIM_MULT 232 /* RO */
|
#define EXT_CSD_TRIM_MULT 232 /* RO */
|
||||||
|
#define EXT_CSD_PWR_CL_200_195 236 /* RO */
|
||||||
|
#define EXT_CSD_PWR_CL_200_360 237 /* RO */
|
||||||
|
#define EXT_CSD_PWR_CL_DDR_52_195 238 /* RO */
|
||||||
|
#define EXT_CSD_PWR_CL_DDR_52_360 239 /* RO */
|
||||||
|
#define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */
|
||||||
|
#define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */
|
||||||
|
#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */
|
||||||
|
#define EXT_CSD_HPI_FEATURES 503 /* RO */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* EXT_CSD field definitions
|
* EXT_CSD field definitions
|
||||||
|
@ -302,7 +323,9 @@ struct _mmc_csd {
|
||||||
|
|
||||||
#define EXT_CSD_PART_CONFIG_ACC_MASK (0x7)
|
#define EXT_CSD_PART_CONFIG_ACC_MASK (0x7)
|
||||||
#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1)
|
#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1)
|
||||||
#define EXT_CSD_PART_CONFIG_ACC_BOOT1 (0x2)
|
#define EXT_CSD_PART_CONFIG_ACC_GP0 (0x4)
|
||||||
|
|
||||||
|
#define EXT_CSD_PART_SUPPORT_PART_EN (0x1)
|
||||||
|
|
||||||
#define EXT_CSD_CMD_SET_NORMAL (1<<0)
|
#define EXT_CSD_CMD_SET_NORMAL (1<<0)
|
||||||
#define EXT_CSD_CMD_SET_SECURE (1<<1)
|
#define EXT_CSD_CMD_SET_SECURE (1<<1)
|
||||||
|
@ -327,7 +350,20 @@ struct _mmc_csd {
|
||||||
#define EXT_CSD_SEC_ER_EN BIT(0)
|
#define EXT_CSD_SEC_ER_EN BIT(0)
|
||||||
#define EXT_CSD_SEC_BD_BLK_EN BIT(2)
|
#define EXT_CSD_SEC_BD_BLK_EN BIT(2)
|
||||||
#define EXT_CSD_SEC_GB_CL_EN BIT(4)
|
#define EXT_CSD_SEC_GB_CL_EN BIT(4)
|
||||||
|
#define EXT_CSD_SEC_SANITIZE BIT(6) /* v4.5 only */
|
||||||
|
|
||||||
|
#define EXT_CSD_RST_N_EN_MASK 0x3
|
||||||
|
#define EXT_CSD_RST_N_ENABLED 1 /* RST_n is enabled on card */
|
||||||
|
|
||||||
|
#define EXT_CSD_NO_POWER_NOTIFICATION 0
|
||||||
|
#define EXT_CSD_POWER_ON 1
|
||||||
|
#define EXT_CSD_POWER_OFF_SHORT 2
|
||||||
|
#define EXT_CSD_POWER_OFF_LONG 3
|
||||||
|
|
||||||
|
#define EXT_CSD_PWR_CL_8BIT_MASK 0xF0 /* 8 bit PWR CLS */
|
||||||
|
#define EXT_CSD_PWR_CL_4BIT_MASK 0x0F /* 8 bit PWR CLS */
|
||||||
|
#define EXT_CSD_PWR_CL_8BIT_SHIFT 4
|
||||||
|
#define EXT_CSD_PWR_CL_4BIT_SHIFT 0
|
||||||
/*
|
/*
|
||||||
* MMC_SWITCH access modes
|
* MMC_SWITCH access modes
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -88,6 +88,10 @@ struct sdhci_host {
|
||||||
/* The read-only detection via SDHCI_PRESENT_STATE register is unstable */
|
/* The read-only detection via SDHCI_PRESENT_STATE register is unstable */
|
||||||
#define SDHCI_QUIRK_UNSTABLE_RO_DETECT (1<<31)
|
#define SDHCI_QUIRK_UNSTABLE_RO_DETECT (1<<31)
|
||||||
|
|
||||||
|
unsigned int quirks2; /* More deviations from spec. */
|
||||||
|
|
||||||
|
#define SDHCI_QUIRK2_OWN_CARD_DETECTION (1<<0)
|
||||||
|
|
||||||
int irq; /* Device IRQ */
|
int irq; /* Device IRQ */
|
||||||
void __iomem *ioaddr; /* Mapped address */
|
void __iomem *ioaddr; /* Mapped address */
|
||||||
|
|
||||||
|
@ -115,6 +119,8 @@ struct sdhci_host {
|
||||||
#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */
|
#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */
|
||||||
#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
|
#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
|
||||||
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
|
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
|
||||||
|
#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
|
||||||
|
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
|
||||||
|
|
||||||
unsigned int version; /* SDHCI spec. version */
|
unsigned int version; /* SDHCI spec. version */
|
||||||
|
|
||||||
|
@ -125,6 +131,8 @@ struct sdhci_host {
|
||||||
unsigned int clock; /* Current clock (MHz) */
|
unsigned int clock; /* Current clock (MHz) */
|
||||||
u8 pwr; /* Current voltage */
|
u8 pwr; /* Current voltage */
|
||||||
|
|
||||||
|
bool runtime_suspended; /* Host is runtime suspended */
|
||||||
|
|
||||||
struct mmc_request *mrq; /* Current request */
|
struct mmc_request *mrq; /* Current request */
|
||||||
struct mmc_command *cmd; /* Current command */
|
struct mmc_command *cmd; /* Current command */
|
||||||
struct mmc_data *data; /* Current data request */
|
struct mmc_data *data; /* Current data request */
|
||||||
|
|
|
@ -72,11 +72,13 @@
|
||||||
#define SDIO_CCCR_REV_1_00 0 /* CCCR/FBR Version 1.00 */
|
#define SDIO_CCCR_REV_1_00 0 /* CCCR/FBR Version 1.00 */
|
||||||
#define SDIO_CCCR_REV_1_10 1 /* CCCR/FBR Version 1.10 */
|
#define SDIO_CCCR_REV_1_10 1 /* CCCR/FBR Version 1.10 */
|
||||||
#define SDIO_CCCR_REV_1_20 2 /* CCCR/FBR Version 1.20 */
|
#define SDIO_CCCR_REV_1_20 2 /* CCCR/FBR Version 1.20 */
|
||||||
|
#define SDIO_CCCR_REV_3_00 3 /* CCCR/FBR Version 3.00 */
|
||||||
|
|
||||||
#define SDIO_SDIO_REV_1_00 0 /* SDIO Spec Version 1.00 */
|
#define SDIO_SDIO_REV_1_00 0 /* SDIO Spec Version 1.00 */
|
||||||
#define SDIO_SDIO_REV_1_10 1 /* SDIO Spec Version 1.10 */
|
#define SDIO_SDIO_REV_1_10 1 /* SDIO Spec Version 1.10 */
|
||||||
#define SDIO_SDIO_REV_1_20 2 /* SDIO Spec Version 1.20 */
|
#define SDIO_SDIO_REV_1_20 2 /* SDIO Spec Version 1.20 */
|
||||||
#define SDIO_SDIO_REV_2_00 3 /* SDIO Spec Version 2.00 */
|
#define SDIO_SDIO_REV_2_00 3 /* SDIO Spec Version 2.00 */
|
||||||
|
#define SDIO_SDIO_REV_3_00 4 /* SDIO Spec Version 3.00 */
|
||||||
|
|
||||||
#define SDIO_CCCR_SD 0x01
|
#define SDIO_CCCR_SD 0x01
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,9 @@ struct sh_mmcif_plat_data {
|
||||||
void (*set_pwr)(struct platform_device *pdev, int state);
|
void (*set_pwr)(struct platform_device *pdev, int state);
|
||||||
void (*down_pwr)(struct platform_device *pdev);
|
void (*down_pwr)(struct platform_device *pdev);
|
||||||
int (*get_cd)(struct platform_device *pdef);
|
int (*get_cd)(struct platform_device *pdef);
|
||||||
struct sh_mmcif_dma *dma;
|
struct sh_mmcif_dma *dma; /* Deprecated. Instead */
|
||||||
|
unsigned int slave_id_tx; /* use embedded slave_id_[tr]x */
|
||||||
|
unsigned int slave_id_rx;
|
||||||
u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */
|
u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */
|
||||||
unsigned long caps;
|
unsigned long caps;
|
||||||
u32 ocr;
|
u32 ocr;
|
||||||
|
|
|
@ -6,6 +6,10 @@
|
||||||
struct platform_device;
|
struct platform_device;
|
||||||
struct tmio_mmc_data;
|
struct tmio_mmc_data;
|
||||||
|
|
||||||
|
#define SH_MOBILE_SDHI_IRQ_CARD_DETECT "card_detect"
|
||||||
|
#define SH_MOBILE_SDHI_IRQ_SDCARD "sdcard"
|
||||||
|
#define SH_MOBILE_SDHI_IRQ_SDIO "sdio"
|
||||||
|
|
||||||
struct sh_mobile_sdhi_info {
|
struct sh_mobile_sdhi_info {
|
||||||
int dma_slave_tx;
|
int dma_slave_tx;
|
||||||
int dma_slave_rx;
|
int dma_slave_rx;
|
||||||
|
|
|
@ -1070,6 +1070,17 @@ config FAIL_IO_TIMEOUT
|
||||||
Only works with drivers that use the generic timeout handling,
|
Only works with drivers that use the generic timeout handling,
|
||||||
for others it wont do anything.
|
for others it wont do anything.
|
||||||
|
|
||||||
|
config FAIL_MMC_REQUEST
|
||||||
|
bool "Fault-injection capability for MMC IO"
|
||||||
|
select DEBUG_FS
|
||||||
|
depends on FAULT_INJECTION && MMC
|
||||||
|
help
|
||||||
|
Provide fault-injection capability for MMC IO.
|
||||||
|
This will make the mmc core return data errors. This is
|
||||||
|
useful to test the error handling in the mmc block device
|
||||||
|
and to test how the mmc host driver handles retries from
|
||||||
|
the block device.
|
||||||
|
|
||||||
config FAULT_INJECTION_DEBUG_FS
|
config FAULT_INJECTION_DEBUG_FS
|
||||||
bool "Debugfs entries for fault-injection capabilities"
|
bool "Debugfs entries for fault-injection capabilities"
|
||||||
depends on FAULT_INJECTION && SYSFS && DEBUG_FS
|
depends on FAULT_INJECTION && SYSFS && DEBUG_FS
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* setup_fault_attr() is a helper function for various __setup handlers, so it
|
* setup_fault_attr() is a helper function for various __setup handlers, so it
|
||||||
* returns 0 on error, because that is what __setup handlers do.
|
* returns 0 on error, because that is what __setup handlers do.
|
||||||
*/
|
*/
|
||||||
int __init setup_fault_attr(struct fault_attr *attr, char *str)
|
int setup_fault_attr(struct fault_attr *attr, char *str)
|
||||||
{
|
{
|
||||||
unsigned long probability;
|
unsigned long probability;
|
||||||
unsigned long interval;
|
unsigned long interval;
|
||||||
|
@ -36,6 +36,7 @@ int __init setup_fault_attr(struct fault_attr *attr, char *str)
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(setup_fault_attr);
|
||||||
|
|
||||||
static void fail_dump(struct fault_attr *attr)
|
static void fail_dump(struct fault_attr *attr)
|
||||||
{
|
{
|
||||||
|
@ -130,6 +131,7 @@ bool should_fail(struct fault_attr *attr, ssize_t size)
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(should_fail);
|
||||||
|
|
||||||
#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
|
#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
|
||||||
|
|
||||||
|
@ -243,5 +245,6 @@ fail:
|
||||||
|
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(fault_create_debugfs_attr);
|
||||||
|
|
||||||
#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
|
#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
|
||||||
|
|
Loading…
Reference in New Issue