forked from Minki/linux
MMC highlights for 3.10:
Core: - Introduce MMC_CAP2_NO_PRESCAN_POWERUP to allow skipping mmc_power_up() at boot/initialization time if it's already happened, for performance (faster boot time) reasons. - Fix a bit width test failure that resulted in old eMMC cards being put into 1-bit mode when 4-bit mode was available. - Expose fwrev/hwrev for MMCv4 parts. - Improve card removal logic in the case where the card's removed slowly; we were missing card removal events if the card retained contact with the slot pads for long enough to reply to a CMD13 while being removed. Drivers: - davinci_mmc: Support using PIO instead of DMA. - dw_mmc: Add support for Exynos4412. - mxcmmc: DT support, use slot-gpio API. - mxs-mmc: Add broken-cd/cd-inverted/non-removable DT property support. - sdhci-sirf: New sdhci-pltfm driver for CSR SiRF SoCs: SiRFprimaII: unicore ARM Cortex-A9 SiRFatlas6: unicore ARM Cortex-A9 SiRFmarco: dual core ARM Cortex-A9 SMP - sdhci-tegra: Add support for Tegra114 platforms, use mmc_of_parse(). -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.13 (GNU/Linux) iQIcBAABAgAGBQJRfr+qAAoJEHNBYZ7TNxYMG90QALW6ArdTxkfNKNybCx/s3ttm hyP2BB9eXF/FbZ9bGHGuv9NXV/4YZ4yr4c3yV4paOMW3s0z/+IhqajLfCgPFPCGK MOk5GAStN/+znBjLy+5V/17EJuVpE+WVwUqDg3RafB8NGQtaClymwPg6R+SMWwL9 uiRcCVgmgzlk9l+4Z1p9wHUeQlhZQ/Okzj4CjkOpWfgsUpo0PcWQRosd+CHHirWz iWnhFBM1cP4Nj59ashYSedu797s29pKGd31JcuWWQE228iykGfcCJWzMr9YMiU6x LwESeuTST8b9HblI7WYnFvpVvD9qTuBkkKMIKMNb4E34xBf8+H2zUh1hK7iEnDXt W0F1xy3WsxpJ1yF0BY9SqqnNeLpJBOenHlTZfGlf5r+kHWYUUQvvqw/ZdmWStRgv mSoqXSmz/oM3rn1KjMritJ9oyr0EH8kHDMAsoiCF0EuGipmenFTdJJiwbh0JZJxK bjqPUROtSK8TdPvTw1BPRX5YtJLzsG4HdsI3C8W05BTbCL9iIE1NaWP27nikPVfq glt9EilzgR252Wc649OrB7JAHBZjlVFIndNzjziGdxNzH6m1AK05qxTL5hBvba99 plccrsVMDGWLSmsGVwz7Eo+TLflRCdtrE+7O4NrlydZzp2t24HZrMc9rN8GDWQQE dP91YUdpDYrTsUBKiKGQ =Pr7P -----END PGP SIGNATURE----- Merge tag 'mmc-updates-for-3.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc Pull MMC update from Chris Ball: "MMC highlights for 3.10: Core: - Introduce MMC_CAP2_NO_PRESCAN_POWERUP to allow skipping mmc_power_up() at boot/initialization time if it's already happened, for performance (faster boot time) reasons. - Fix a bit width test failure that resulted in old eMMC cards being put into 1-bit mode when 4-bit mode was available. - Expose fwrev/hwrev for MMCv4 parts. - Improve card removal logic in the case where the card's removed slowly; we were missing card removal events if the card retained contact with the slot pads for long enough to reply to a CMD13 while being removed. Drivers: - davinci_mmc: Support using PIO instead of DMA. - dw_mmc: Add support for Exynos4412. - mxcmmc: DT support, use slot-gpio API. - mxs-mmc: Add broken-cd/cd-inverted/non-removable DT property support. - sdhci-sirf: New sdhci-pltfm driver for CSR SiRF SoCs: SiRFprimaII: unicore ARM Cortex-A9 SiRFatlas6: unicore ARM Cortex-A9 SiRFmarco: dual core ARM Cortex-A9 SMP - sdhci-tegra: Add support for Tegra114 platforms, use mmc_of_parse()" * tag 'mmc-updates-for-3.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (66 commits) mmc: sdhci-tegra: fix MODULE_DEVICE_TABLE mmc: core: fix init controller performance regression, updated patch mmc: mxcmmc: enable DMA support on mpc512x mmc: mxcmmc: constify mxcmci_devtype mmc: mxcmmc: use slot-gpio API for write-protect detection mmc: mxcmmc: add mpc512x SDHC support mmc: mxcmmc: fix race conditions for host->req and host->data access mmc: mxcmmc: DT support mmc: dw_mmc: let device core setup the default pin configuration mmc: mxs-mmc: add broken-cd property mmc: mxs-mmc: add non-removable property mmc: mxs-mmc: add cd-inverted property mmc: core: call pm_runtime_put_noidle in pm_runtime_get_sync failed case mmc: mxcmmc: Fix bug when card is present during boot mmc: core: fix performance regression initializing MMC host controllers Revert "mmc: core: wait while adding MMC host to ensure root mounts successfully" mmc: atmel-mci: pio hang on block errors mmc: core: Fix bit width test failing on old eMMC cards mmc: dw_mmc: Use pr_info instead of printk mmc: dw_mmc: Check return value of regulator_enable ...
This commit is contained in:
commit
173192958d
24
Documentation/devicetree/bindings/mmc/fsl-imx-mmc.txt
Normal file
24
Documentation/devicetree/bindings/mmc/fsl-imx-mmc.txt
Normal file
@ -0,0 +1,24 @@
|
||||
* Freescale Secure Digital Host Controller for i.MX2/3 series
|
||||
|
||||
This file documents differences to the properties defined in mmc.txt.
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "fsl,<chip>-mmc", chip can be imx21 or imx31
|
||||
|
||||
Optional properties:
|
||||
- dmas: One DMA phandle with arguments as defined by the devicetree bindings
|
||||
of the used DMA controller.
|
||||
- dma-names: Has to be "rx-tx".
|
||||
|
||||
Example:
|
||||
|
||||
sdhci1: sdhci@10014000 {
|
||||
compatible = "fsl,imx27-mmc", "fsl,imx21-mmc";
|
||||
reg = <0x10014000 0x1000>;
|
||||
interrupts = <11>;
|
||||
dmas = <&dma 7>;
|
||||
dma-names = "rx-tx";
|
||||
bus-width = <4>;
|
||||
cd-gpios = <&gpio3 29>;
|
||||
status = "okay";
|
||||
};
|
@ -5,13 +5,6 @@ MMC, SD and eMMC storage mediums. This file documents differences between the
|
||||
core mmc properties described by mmc.txt and the properties used by the
|
||||
Samsung implmentation of the SDHCI controller.
|
||||
|
||||
Note: The mmc core bindings documentation states that if none of the core
|
||||
card-detect bindings are used, then the standard sdhci card detect mechanism
|
||||
is used. The Samsung's SDHCI controller bindings extends this as listed below.
|
||||
|
||||
[A] The property "samsung,cd-pinmux-gpio" can be used as stated in the
|
||||
"Optional Board Specific Properties" section below.
|
||||
|
||||
Required SoC Specific Properties:
|
||||
- compatible: should be one of the following
|
||||
- "samsung,s3c6410-sdhci": For controllers compatible with s3c6410 sdhci
|
||||
@ -20,18 +13,8 @@ Required SoC Specific Properties:
|
||||
controller.
|
||||
|
||||
Required Board Specific Properties:
|
||||
- Samsung GPIO variant (will be completely replaced by pinctrl):
|
||||
- gpios: Should specify the gpios used for clock, command and data lines. The
|
||||
gpio specifier format depends on the gpio controller.
|
||||
- Pinctrl variant (preferred if available):
|
||||
- pinctrl-0: Should specify pin control groups used for this controller.
|
||||
- pinctrl-names: Should contain only one value - "default".
|
||||
|
||||
Optional Board Specific Properties:
|
||||
- samsung,cd-pinmux-gpio: Specifies the card detect line that is routed
|
||||
through a pinmux to the card-detect pin of the card slot. This property
|
||||
should be used only if none of the mmc core card-detect properties are
|
||||
used. Only for Samsung GPIO variant.
|
||||
- pinctrl-0: Should specify pin control groups used for this controller.
|
||||
- pinctrl-names: Should contain only one value - "default".
|
||||
|
||||
Example:
|
||||
sdhci@12530000 {
|
||||
@ -39,19 +22,9 @@ Example:
|
||||
reg = <0x12530000 0x100>;
|
||||
interrupts = <0 75 0>;
|
||||
bus-width = <4>;
|
||||
cd-gpios = <&gpk2 2 2 3 3>;
|
||||
|
||||
/* Samsung GPIO variant */
|
||||
gpios = <&gpk2 0 2 0 3>, /* clock line */
|
||||
<&gpk2 1 2 0 3>, /* command line */
|
||||
<&gpk2 3 2 3 3>, /* data line 0 */
|
||||
<&gpk2 4 2 3 3>, /* data line 1 */
|
||||
<&gpk2 5 2 3 3>, /* data line 2 */
|
||||
<&gpk2 6 2 3 3>; /* data line 3 */
|
||||
|
||||
/* Pinctrl variant */
|
||||
pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus4>;
|
||||
cd-gpios = <&gpk2 2 0>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus4>;
|
||||
};
|
||||
|
||||
Note: This example shows both SoC specific and board specific properties
|
||||
|
18
Documentation/devicetree/bindings/mmc/sdhci-sirf.txt
Normal file
18
Documentation/devicetree/bindings/mmc/sdhci-sirf.txt
Normal file
@ -0,0 +1,18 @@
|
||||
* SiRFprimII/marco/atlas6 SDHCI Controller
|
||||
|
||||
This file documents differences between the core properties in mmc.txt
|
||||
and the properties used by the sdhci-sirf driver.
|
||||
|
||||
Required properties:
|
||||
- compatible: sirf,prima2-sdhc
|
||||
|
||||
Optional properties:
|
||||
- cd-gpios: card detect gpio, with zero flags.
|
||||
|
||||
Example:
|
||||
|
||||
sd0: sdhci@56000000 {
|
||||
compatible = "sirf,prima2-sdhc";
|
||||
reg = <0xcd000000 0x100000>;
|
||||
cd-gpios = <&gpio 6 0>;
|
||||
};
|
@ -22,6 +22,7 @@ All attributes are read-only.
|
||||
manfid Manufacturer ID (from CID Register)
|
||||
name Product Name (from CID Register)
|
||||
oemid OEM/Application ID (from CID Register)
|
||||
prv Product Revision (from CID Register) (SD and MMCv4 only)
|
||||
serial Product Serial Number (from CID Register)
|
||||
erase_size Erase group size
|
||||
preferred_erase_size Preferred erase size
|
||||
|
@ -801,6 +801,7 @@ S: Maintained
|
||||
F: arch/arm/mach-prima2/
|
||||
F: drivers/dma/sirf-dma.c
|
||||
F: drivers/i2c/busses/i2c-sirf.c
|
||||
F: drivers/mmc/host/sdhci-sirf.c
|
||||
F: drivers/pinctrl/pinctrl-sirf.c
|
||||
F: drivers/spi/spi-sirf.c
|
||||
|
||||
|
@ -173,7 +173,6 @@ CONFIG_MMC=y
|
||||
# CONFIG_MMC_BLOCK_BOUNCE is not set
|
||||
CONFIG_SDIO_UART=m
|
||||
CONFIG_MMC_ATMELMCI=y
|
||||
CONFIG_MMC_ATMELMCI_DMA=y
|
||||
CONFIG_LEDS_ATMEL_PWM=y
|
||||
CONFIG_LEDS_GPIO=y
|
||||
CONFIG_LEDS_TRIGGER_TIMER=y
|
||||
|
@ -122,7 +122,6 @@ CONFIG_USB_G_SERIAL=m
|
||||
CONFIG_USB_CDC_COMPOSITE=m
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_ATMELMCI=y
|
||||
CONFIG_MMC_ATMELMCI_DMA=y
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
CONFIG_LEDS_ATMEL_PWM=m
|
||||
|
@ -102,7 +102,6 @@ CONFIG_FRAMEBUFFER_CONSOLE=y
|
||||
CONFIG_LOGO=y
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_ATMELMCI=y
|
||||
CONFIG_MMC_ATMELMCI_DMA=y
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
CONFIG_LEDS_ATMEL_PWM=y
|
||||
|
@ -152,6 +152,8 @@
|
||||
compatible = "fsl,mpc5121-sdhc";
|
||||
reg = <0x1500 0x100>;
|
||||
interrupts = <8 0x8>;
|
||||
dmas = <&dma0 30>;
|
||||
dma-names = "rx-tx";
|
||||
};
|
||||
|
||||
i2c@1700 {
|
||||
|
@ -1932,8 +1932,14 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
||||
}
|
||||
|
||||
out:
|
||||
if (!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST))
|
||||
/* release host only when there are no more requests */
|
||||
if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) ||
|
||||
(req && (req->cmd_flags & MMC_REQ_SPECIAL_MASK)))
|
||||
/*
|
||||
* Release host when there are no more requests
|
||||
* and after special request(discard, flush) is done.
|
||||
* In case sepecial request, there is no reentry to
|
||||
* the 'mmc_blk_issue_rq' with 'mqrq_prev->req'.
|
||||
*/
|
||||
mmc_release_host(card->host);
|
||||
return ret;
|
||||
}
|
||||
|
@ -22,9 +22,6 @@
|
||||
|
||||
#define MMC_QUEUE_BOUNCESZ 65536
|
||||
|
||||
|
||||
#define MMC_REQ_SPECIAL_MASK (REQ_DISCARD | REQ_FLUSH)
|
||||
|
||||
/*
|
||||
* Prepare a MMC request. This just filters out odd stuff.
|
||||
*/
|
||||
|
@ -1,6 +1,8 @@
|
||||
#ifndef MMC_QUEUE_H
|
||||
#define MMC_QUEUE_H
|
||||
|
||||
#define MMC_REQ_SPECIAL_MASK (REQ_DISCARD | REQ_FLUSH)
|
||||
|
||||
struct request;
|
||||
struct task_struct;
|
||||
|
||||
|
@ -2289,6 +2289,19 @@ int _mmc_detect_card_removed(struct mmc_host *host)
|
||||
return 1;
|
||||
|
||||
ret = host->bus_ops->alive(host);
|
||||
|
||||
/*
|
||||
* Card detect status and alive check may be out of sync if card is
|
||||
* removed slowly, when card detect switch changes while card/slot
|
||||
* pads are still contacted in hardware (refer to "SD Card Mechanical
|
||||
* Addendum, Appendix C: Card Detection Switch"). So reschedule a
|
||||
* detect work 200ms later for this case.
|
||||
*/
|
||||
if (!ret && host->ops->get_cd && !host->ops->get_cd(host)) {
|
||||
mmc_detect_change(host, msecs_to_jiffies(200));
|
||||
pr_debug("%s: card removed too slowly\n", mmc_hostname(host));
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
mmc_card_set_removed(host->card);
|
||||
pr_debug("%s: card remove detected\n", mmc_hostname(host));
|
||||
@ -2403,7 +2416,10 @@ void mmc_start_host(struct mmc_host *host)
|
||||
{
|
||||
host->f_init = max(freqs[0], host->f_min);
|
||||
host->rescan_disable = 0;
|
||||
mmc_power_up(host);
|
||||
if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
|
||||
mmc_power_off(host);
|
||||
else
|
||||
mmc_power_up(host);
|
||||
mmc_detect_change(host, 0);
|
||||
}
|
||||
|
||||
|
@ -96,6 +96,7 @@ static int mmc_decode_cid(struct mmc_card *card)
|
||||
card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
|
||||
card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
|
||||
card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8);
|
||||
card->cid.prv = UNSTUFF_BITS(resp, 48, 8);
|
||||
card->cid.serial = UNSTUFF_BITS(resp, 16, 32);
|
||||
card->cid.month = UNSTUFF_BITS(resp, 12, 4);
|
||||
card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997;
|
||||
@ -368,13 +369,13 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT];
|
||||
card->ext_csd.raw_trim_mult =
|
||||
ext_csd[EXT_CSD_TRIM_MULT];
|
||||
card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT];
|
||||
if (card->ext_csd.rev >= 4) {
|
||||
/*
|
||||
* Enhanced area feature support -- check whether the eMMC
|
||||
* card has the Enhanced area enabled. If so, export enhanced
|
||||
* 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) &&
|
||||
(ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) {
|
||||
hc_erase_grp_sz =
|
||||
@ -627,6 +628,7 @@ MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
|
||||
MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
|
||||
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
|
||||
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
|
||||
MMC_DEV_ATTR(prv, "0x%x\n", card->cid.prv);
|
||||
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
|
||||
MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
|
||||
card->ext_csd.enhanced_area_offset);
|
||||
@ -645,6 +647,7 @@ static struct attribute *mmc_std_attrs[] = {
|
||||
&dev_attr_manfid.attr,
|
||||
&dev_attr_name.attr,
|
||||
&dev_attr_oemid.attr,
|
||||
&dev_attr_prv.attr,
|
||||
&dev_attr_serial.attr,
|
||||
&dev_attr_enhanced_area_offset.attr,
|
||||
&dev_attr_enhanced_area_size.attr,
|
||||
|
@ -861,8 +861,10 @@ static void mmc_sdio_detect(struct mmc_host *host)
|
||||
/* Make sure card is powered before detecting it */
|
||||
if (host->caps & MMC_CAP_POWER_OFF_CARD) {
|
||||
err = pm_runtime_get_sync(&host->card->dev);
|
||||
if (err < 0)
|
||||
if (err < 0) {
|
||||
pm_runtime_put_noidle(&host->card->dev);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/host.h>
|
||||
@ -137,7 +138,7 @@ static int sdio_bus_probe(struct device *dev)
|
||||
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
goto disable_runtimepm;
|
||||
}
|
||||
|
||||
/* Set the default block size so the driver is sure it's something
|
||||
@ -157,7 +158,6 @@ static int sdio_bus_probe(struct device *dev)
|
||||
disable_runtimepm:
|
||||
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
|
||||
pm_runtime_put_noidle(dev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -299,6 +299,19 @@ struct sdio_func *sdio_alloc_func(struct mmc_card *card)
|
||||
return func;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static void sdio_acpi_set_handle(struct sdio_func *func)
|
||||
{
|
||||
struct mmc_host *host = func->card->host;
|
||||
u64 addr = (host->slotno << 16) | func->num;
|
||||
|
||||
ACPI_HANDLE_SET(&func->dev,
|
||||
acpi_get_child(ACPI_HANDLE(host->parent), addr));
|
||||
}
|
||||
#else
|
||||
static inline void sdio_acpi_set_handle(struct sdio_func *func) {}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Register a new SDIO function with the driver model.
|
||||
*/
|
||||
@ -308,9 +321,12 @@ int sdio_add_func(struct sdio_func *func)
|
||||
|
||||
dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num);
|
||||
|
||||
sdio_acpi_set_handle(func);
|
||||
ret = device_add(&func->dev);
|
||||
if (ret == 0)
|
||||
if (ret == 0) {
|
||||
sdio_func_set_present(func);
|
||||
acpi_dev_pm_attach(&func->dev, false);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -326,6 +342,7 @@ void sdio_remove_func(struct sdio_func *func)
|
||||
if (!sdio_func_present(func))
|
||||
return;
|
||||
|
||||
acpi_dev_pm_detach(&func->dev, false);
|
||||
device_del(&func->dev);
|
||||
put_device(&func->dev);
|
||||
}
|
||||
|
@ -190,6 +190,17 @@ config MMC_SDHCI_S3C
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_SIRF
|
||||
tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs"
|
||||
depends on ARCH_SIRF
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
help
|
||||
This selects the SDHCI support for SiRF System-on-Chip devices.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_PXAV3
|
||||
tristate "Marvell MMP2 SD Host Controller support (PXAV3)"
|
||||
depends on CLKDEV_LOOKUP
|
||||
@ -300,16 +311,6 @@ config MMC_ATMELMCI
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_ATMELMCI_DMA
|
||||
bool "Atmel MCI DMA support"
|
||||
depends on MMC_ATMELMCI && (AVR32 || ARCH_AT91SAM9G45) && DMA_ENGINE
|
||||
help
|
||||
Say Y here to have the Atmel MCI driver use a DMA engine to
|
||||
do data transfers and thus increase the throughput and
|
||||
reduce the CPU utilization.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_MSM
|
||||
tristate "Qualcomm SDCC Controller Support"
|
||||
depends on MMC && ARCH_MSM
|
||||
@ -319,12 +320,12 @@ config MMC_MSM
|
||||
support for SDIO devices.
|
||||
|
||||
config MMC_MXC
|
||||
tristate "Freescale i.MX21/27/31 Multimedia Card Interface support"
|
||||
depends on ARCH_MXC
|
||||
tristate "Freescale i.MX21/27/31 or MPC512x Multimedia Card support"
|
||||
depends on ARCH_MXC || PPC_MPC512x
|
||||
help
|
||||
This selects the Freescale i.MX21, i.MX27 and i.MX31 Multimedia card
|
||||
Interface. If you have a i.MX platform with a Multimedia Card slot,
|
||||
say Y or M here.
|
||||
This selects the Freescale i.MX21, i.MX27, i.MX31 or MPC512x
|
||||
Multimedia Card Interface. If you have an i.MX or MPC512x platform
|
||||
with a Multimedia Card slot, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
|
@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
|
||||
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
|
||||
obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o
|
||||
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
|
||||
obj-$(CONFIG_MMC_WBSD) += wbsd.o
|
||||
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
|
||||
|
@ -476,7 +476,7 @@ static int goldfish_mmc_probe(struct platform_device *pdev)
|
||||
host->mmc = mmc;
|
||||
|
||||
pr_err("mmc: Mapping %lX to %lX\n", (long)res->start, (long)res->end);
|
||||
host->reg_base = ioremap(res->start, res->end - res->start + 1);
|
||||
host->reg_base = ioremap(res->start, resource_size(res));
|
||||
if (host->reg_base == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto ioremap_failed;
|
||||
|
@ -178,6 +178,7 @@ struct atmel_mci {
|
||||
void __iomem *regs;
|
||||
|
||||
struct scatterlist *sg;
|
||||
unsigned int sg_len;
|
||||
unsigned int pio_offset;
|
||||
unsigned int *buffer;
|
||||
unsigned int buf_size;
|
||||
@ -892,6 +893,7 @@ static u32 atmci_prepare_data(struct atmel_mci *host, struct mmc_data *data)
|
||||
data->error = -EINPROGRESS;
|
||||
|
||||
host->sg = data->sg;
|
||||
host->sg_len = data->sg_len;
|
||||
host->data = data;
|
||||
host->data_chan = NULL;
|
||||
|
||||
@ -1826,7 +1828,8 @@ static void atmci_read_data_pio(struct atmel_mci *host)
|
||||
if (offset == sg->length) {
|
||||
flush_dcache_page(sg_page(sg));
|
||||
host->sg = sg = sg_next(sg);
|
||||
if (!sg)
|
||||
host->sg_len--;
|
||||
if (!sg || !host->sg_len)
|
||||
goto done;
|
||||
|
||||
offset = 0;
|
||||
@ -1839,7 +1842,8 @@ static void atmci_read_data_pio(struct atmel_mci *host)
|
||||
|
||||
flush_dcache_page(sg_page(sg));
|
||||
host->sg = sg = sg_next(sg);
|
||||
if (!sg)
|
||||
host->sg_len--;
|
||||
if (!sg || !host->sg_len)
|
||||
goto done;
|
||||
|
||||
offset = 4 - remaining;
|
||||
@ -1890,7 +1894,8 @@ static void atmci_write_data_pio(struct atmel_mci *host)
|
||||
nbytes += 4;
|
||||
if (offset == sg->length) {
|
||||
host->sg = sg = sg_next(sg);
|
||||
if (!sg)
|
||||
host->sg_len--;
|
||||
if (!sg || !host->sg_len)
|
||||
goto done;
|
||||
|
||||
offset = 0;
|
||||
@ -1904,7 +1909,8 @@ static void atmci_write_data_pio(struct atmel_mci *host)
|
||||
nbytes += remaining;
|
||||
|
||||
host->sg = sg = sg_next(sg);
|
||||
if (!sg) {
|
||||
host->sg_len--;
|
||||
if (!sg || !host->sg_len) {
|
||||
atmci_writel(host, ATMCI_TDR, value);
|
||||
goto done;
|
||||
}
|
||||
@ -2487,10 +2493,8 @@ static int __exit atmci_remove(struct platform_device *pdev)
|
||||
atmci_readl(host, ATMCI_SR);
|
||||
clk_disable(host->mck);
|
||||
|
||||
#ifdef CONFIG_MMC_ATMELMCI_DMA
|
||||
if (host->dma.chan)
|
||||
dma_release_channel(host->dma.chan);
|
||||
#endif
|
||||
|
||||
free_irq(platform_get_irq(pdev, 0), host);
|
||||
iounmap(host->regs);
|
||||
|
@ -1264,13 +1264,15 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!r)
|
||||
goto out;
|
||||
host->rxdma = r->start;
|
||||
dev_warn(&pdev->dev, "RX DMA resource not specified\n");
|
||||
else
|
||||
host->rxdma = r->start;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!r)
|
||||
goto out;
|
||||
host->txdma = r->start;
|
||||
dev_warn(&pdev->dev, "TX DMA resource not specified\n");
|
||||
else
|
||||
host->txdma = r->start;
|
||||
|
||||
host->mem_res = mem;
|
||||
host->base = ioremap(mem->start, mem_size);
|
||||
@ -1488,18 +1490,7 @@ static struct platform_driver davinci_mmcsd_driver = {
|
||||
.id_table = davinci_mmc_devtype,
|
||||
};
|
||||
|
||||
static int __init davinci_mmcsd_init(void)
|
||||
{
|
||||
return platform_driver_probe(&davinci_mmcsd_driver,
|
||||
davinci_mmcsd_probe);
|
||||
}
|
||||
module_init(davinci_mmcsd_init);
|
||||
|
||||
static void __exit davinci_mmcsd_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&davinci_mmcsd_driver);
|
||||
}
|
||||
module_exit(davinci_mmcsd_exit);
|
||||
module_platform_driver_probe(davinci_mmcsd_driver, davinci_mmcsd_probe);
|
||||
|
||||
MODULE_AUTHOR("Texas Instruments India");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -152,45 +152,8 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_exynos_setup_bus(struct dw_mci *host,
|
||||
struct device_node *slot_np, u8 bus_width)
|
||||
{
|
||||
int idx, gpio, ret;
|
||||
|
||||
if (!slot_np)
|
||||
return -EINVAL;
|
||||
|
||||
/* cmd + clock + bus-width pins */
|
||||
for (idx = 0; idx < NUM_PINS(bus_width); idx++) {
|
||||
gpio = of_get_gpio(slot_np, idx);
|
||||
if (!gpio_is_valid(gpio)) {
|
||||
dev_err(host->dev, "invalid gpio: %d\n", gpio);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_gpio_request(host->dev, gpio, "dw-mci-bus");
|
||||
if (ret) {
|
||||
dev_err(host->dev, "gpio [%d] request failed\n", gpio);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
if (host->pdata->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
|
||||
return 0;
|
||||
|
||||
gpio = of_get_named_gpio(slot_np, "samsung,cd-pinmux-gpio", 0);
|
||||
if (gpio_is_valid(gpio)) {
|
||||
if (devm_gpio_request(host->dev, gpio, "dw-mci-cd"))
|
||||
dev_err(host->dev, "gpio [%d] request failed\n", gpio);
|
||||
} else {
|
||||
dev_info(host->dev, "cd gpio not available");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Exynos5250 controller specific capabilities */
|
||||
static unsigned long exynos5250_dwmmc_caps[4] = {
|
||||
/* Common capabilities of Exynos4/Exynos5 SoC */
|
||||
static unsigned long exynos_dwmmc_caps[4] = {
|
||||
MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
|
||||
MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
|
||||
MMC_CAP_CMD23,
|
||||
@ -198,24 +161,25 @@ static unsigned long exynos5250_dwmmc_caps[4] = {
|
||||
MMC_CAP_CMD23,
|
||||
};
|
||||
|
||||
static const struct dw_mci_drv_data exynos5250_drv_data = {
|
||||
.caps = exynos5250_dwmmc_caps,
|
||||
static const struct dw_mci_drv_data exynos_drv_data = {
|
||||
.caps = exynos_dwmmc_caps,
|
||||
.init = dw_mci_exynos_priv_init,
|
||||
.setup_clock = dw_mci_exynos_setup_clock,
|
||||
.prepare_command = dw_mci_exynos_prepare_command,
|
||||
.set_ios = dw_mci_exynos_set_ios,
|
||||
.parse_dt = dw_mci_exynos_parse_dt,
|
||||
.setup_bus = dw_mci_exynos_setup_bus,
|
||||
};
|
||||
|
||||
static const struct of_device_id dw_mci_exynos_match[] = {
|
||||
{ .compatible = "samsung,exynos4412-dw-mshc",
|
||||
.data = &exynos_drv_data, },
|
||||
{ .compatible = "samsung,exynos5250-dw-mshc",
|
||||
.data = &exynos5250_drv_data, },
|
||||
.data = &exynos_drv_data, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
|
||||
|
||||
int dw_mci_exynos_probe(struct platform_device *pdev)
|
||||
static int dw_mci_exynos_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct dw_mci_drv_data *drv_data;
|
||||
const struct of_device_id *match;
|
||||
@ -230,7 +194,7 @@ static struct platform_driver dw_mci_exynos_pltfm_driver = {
|
||||
.remove = __exit_p(dw_mci_pltfm_remove),
|
||||
.driver = {
|
||||
.name = "dwmmc_exynos",
|
||||
.of_match_table = of_match_ptr(dw_mci_exynos_match),
|
||||
.of_match_table = dw_mci_exynos_match,
|
||||
.pm = &dw_mci_pltfm_pmops,
|
||||
},
|
||||
};
|
||||
|
@ -795,9 +795,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
|
||||
/* DDR mode set */
|
||||
if (ios->timing == MMC_TIMING_UHS_DDR50)
|
||||
regs |= (0x1 << slot->id) << 16;
|
||||
regs |= ((0x1 << slot->id) << 16);
|
||||
else
|
||||
regs &= ~(0x1 << slot->id) << 16;
|
||||
regs &= ~((0x1 << slot->id) << 16);
|
||||
|
||||
mci_writel(slot->host, UHS_REG, regs);
|
||||
|
||||
@ -818,6 +818,20 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
switch (ios->power_mode) {
|
||||
case MMC_POWER_UP:
|
||||
set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
|
||||
/* Power up slot */
|
||||
if (slot->host->pdata->setpower)
|
||||
slot->host->pdata->setpower(slot->id, mmc->ocr_avail);
|
||||
regs = mci_readl(slot->host, PWREN);
|
||||
regs |= (1 << slot->id);
|
||||
mci_writel(slot->host, PWREN, regs);
|
||||
break;
|
||||
case MMC_POWER_OFF:
|
||||
/* Power down slot */
|
||||
if (slot->host->pdata->setpower)
|
||||
slot->host->pdata->setpower(slot->id, 0);
|
||||
regs = mci_readl(slot->host, PWREN);
|
||||
regs &= ~(1 << slot->id);
|
||||
mci_writel(slot->host, PWREN, regs);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -1191,12 +1205,15 @@ static void dw_mci_pull_final_bytes(struct dw_mci *host, void *buf, int cnt)
|
||||
|
||||
static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
|
||||
{
|
||||
struct mmc_data *data = host->data;
|
||||
int init_cnt = cnt;
|
||||
|
||||
/* try and push anything in the part_buf */
|
||||
if (unlikely(host->part_buf_count)) {
|
||||
int len = dw_mci_push_part_bytes(host, buf, cnt);
|
||||
buf += len;
|
||||
cnt -= len;
|
||||
if (!sg_next(host->sg) || host->part_buf_count == 2) {
|
||||
if (host->part_buf_count == 2) {
|
||||
mci_writew(host, DATA(host->data_offset),
|
||||
host->part_buf16);
|
||||
host->part_buf_count = 0;
|
||||
@ -1229,9 +1246,11 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
|
||||
/* put anything remaining in the part_buf */
|
||||
if (cnt) {
|
||||
dw_mci_set_part_bytes(host, buf, cnt);
|
||||
if (!sg_next(host->sg))
|
||||
/* Push data if we have reached the expected data length */
|
||||
if ((data->bytes_xfered + init_cnt) ==
|
||||
(data->blksz * data->blocks))
|
||||
mci_writew(host, DATA(host->data_offset),
|
||||
host->part_buf16);
|
||||
host->part_buf16);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1269,12 +1288,15 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
|
||||
|
||||
static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
|
||||
{
|
||||
struct mmc_data *data = host->data;
|
||||
int init_cnt = cnt;
|
||||
|
||||
/* try and push anything in the part_buf */
|
||||
if (unlikely(host->part_buf_count)) {
|
||||
int len = dw_mci_push_part_bytes(host, buf, cnt);
|
||||
buf += len;
|
||||
cnt -= len;
|
||||
if (!sg_next(host->sg) || host->part_buf_count == 4) {
|
||||
if (host->part_buf_count == 4) {
|
||||
mci_writel(host, DATA(host->data_offset),
|
||||
host->part_buf32);
|
||||
host->part_buf_count = 0;
|
||||
@ -1307,9 +1329,11 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
|
||||
/* put anything remaining in the part_buf */
|
||||
if (cnt) {
|
||||
dw_mci_set_part_bytes(host, buf, cnt);
|
||||
if (!sg_next(host->sg))
|
||||
/* Push data if we have reached the expected data length */
|
||||
if ((data->bytes_xfered + init_cnt) ==
|
||||
(data->blksz * data->blocks))
|
||||
mci_writel(host, DATA(host->data_offset),
|
||||
host->part_buf32);
|
||||
host->part_buf32);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1347,13 +1371,17 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
|
||||
|
||||
static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
|
||||
{
|
||||
struct mmc_data *data = host->data;
|
||||
int init_cnt = cnt;
|
||||
|
||||
/* try and push anything in the part_buf */
|
||||
if (unlikely(host->part_buf_count)) {
|
||||
int len = dw_mci_push_part_bytes(host, buf, cnt);
|
||||
buf += len;
|
||||
cnt -= len;
|
||||
if (!sg_next(host->sg) || host->part_buf_count == 8) {
|
||||
mci_writew(host, DATA(host->data_offset),
|
||||
|
||||
if (host->part_buf_count == 8) {
|
||||
mci_writeq(host, DATA(host->data_offset),
|
||||
host->part_buf);
|
||||
host->part_buf_count = 0;
|
||||
}
|
||||
@ -1385,9 +1413,11 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
|
||||
/* put anything remaining in the part_buf */
|
||||
if (cnt) {
|
||||
dw_mci_set_part_bytes(host, buf, cnt);
|
||||
if (!sg_next(host->sg))
|
||||
/* Push data if we have reached the expected data length */
|
||||
if ((data->bytes_xfered + init_cnt) ==
|
||||
(data->blksz * data->blocks))
|
||||
mci_writeq(host, DATA(host->data_offset),
|
||||
host->part_buf);
|
||||
host->part_buf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1438,7 +1468,7 @@ static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt)
|
||||
host->pull_data(host, buf, cnt);
|
||||
}
|
||||
|
||||
static void dw_mci_read_data_pio(struct dw_mci *host)
|
||||
static void dw_mci_read_data_pio(struct dw_mci *host, bool dto)
|
||||
{
|
||||
struct sg_mapping_iter *sg_miter = &host->sg_miter;
|
||||
void *buf;
|
||||
@ -1446,7 +1476,7 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
|
||||
struct mmc_data *data = host->data;
|
||||
int shift = host->data_shift;
|
||||
u32 status;
|
||||
unsigned int nbytes = 0, len;
|
||||
unsigned int len;
|
||||
unsigned int remain, fcnt;
|
||||
|
||||
do {
|
||||
@ -1465,16 +1495,17 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
|
||||
if (!len)
|
||||
break;
|
||||
dw_mci_pull_data(host, (void *)(buf + offset), len);
|
||||
data->bytes_xfered += len;
|
||||
offset += len;
|
||||
nbytes += len;
|
||||
remain -= len;
|
||||
} while (remain);
|
||||
|
||||
sg_miter->consumed = offset;
|
||||
status = mci_readl(host, MINTSTS);
|
||||
mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
|
||||
} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/
|
||||
data->bytes_xfered += nbytes;
|
||||
/* if the RXDR is ready read again */
|
||||
} while ((status & SDMMC_INT_RXDR) ||
|
||||
(dto && SDMMC_GET_FCNT(mci_readl(host, STATUS))));
|
||||
|
||||
if (!remain) {
|
||||
if (!sg_miter_next(sg_miter))
|
||||
@ -1485,7 +1516,6 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
|
||||
return;
|
||||
|
||||
done:
|
||||
data->bytes_xfered += nbytes;
|
||||
sg_miter_stop(sg_miter);
|
||||
host->sg = NULL;
|
||||
smp_wmb();
|
||||
@ -1500,7 +1530,7 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
|
||||
struct mmc_data *data = host->data;
|
||||
int shift = host->data_shift;
|
||||
u32 status;
|
||||
unsigned int nbytes = 0, len;
|
||||
unsigned int len;
|
||||
unsigned int fifo_depth = host->fifo_depth;
|
||||
unsigned int remain, fcnt;
|
||||
|
||||
@ -1521,8 +1551,8 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
|
||||
if (!len)
|
||||
break;
|
||||
host->push_data(host, (void *)(buf + offset), len);
|
||||
data->bytes_xfered += len;
|
||||
offset += len;
|
||||
nbytes += len;
|
||||
remain -= len;
|
||||
} while (remain);
|
||||
|
||||
@ -1530,7 +1560,6 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
|
||||
status = mci_readl(host, MINTSTS);
|
||||
mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
|
||||
} while (status & SDMMC_INT_TXDR); /* if TXDR write again */
|
||||
data->bytes_xfered += nbytes;
|
||||
|
||||
if (!remain) {
|
||||
if (!sg_miter_next(sg_miter))
|
||||
@ -1541,7 +1570,6 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
|
||||
return;
|
||||
|
||||
done:
|
||||
data->bytes_xfered += nbytes;
|
||||
sg_miter_stop(sg_miter);
|
||||
host->sg = NULL;
|
||||
smp_wmb();
|
||||
@ -1563,11 +1591,11 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct dw_mci *host = dev_id;
|
||||
u32 pending;
|
||||
unsigned int pass_count = 0;
|
||||
int i;
|
||||
|
||||
do {
|
||||
pending = mci_readl(host, MINTSTS); /* read-only mask reg */
|
||||
pending = mci_readl(host, MINTSTS); /* read-only mask reg */
|
||||
|
||||
if (pending) {
|
||||
|
||||
/*
|
||||
* DTO fix - version 2.10a and below, and only if internal DMA
|
||||
@ -1579,9 +1607,6 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
||||
pending |= SDMMC_INT_DATA_OVER;
|
||||
}
|
||||
|
||||
if (!pending)
|
||||
break;
|
||||
|
||||
if (pending & DW_MCI_CMD_ERROR_FLAGS) {
|
||||
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
|
||||
host->cmd_status = pending;
|
||||
@ -1605,7 +1630,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
||||
smp_wmb();
|
||||
if (host->dir_status == DW_MCI_RECV_STATUS) {
|
||||
if (host->sg != NULL)
|
||||
dw_mci_read_data_pio(host);
|
||||
dw_mci_read_data_pio(host, true);
|
||||
}
|
||||
set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
@ -1614,7 +1639,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
||||
if (pending & SDMMC_INT_RXDR) {
|
||||
mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
|
||||
if (host->dir_status == DW_MCI_RECV_STATUS && host->sg)
|
||||
dw_mci_read_data_pio(host);
|
||||
dw_mci_read_data_pio(host, false);
|
||||
}
|
||||
|
||||
if (pending & SDMMC_INT_TXDR) {
|
||||
@ -1642,7 +1667,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
||||
}
|
||||
}
|
||||
|
||||
} while (pass_count++ < 5);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MMC_DW_IDMAC
|
||||
/* Handle DMA interrupts */
|
||||
@ -1674,10 +1699,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)
|
||||
dev_dbg(&slot->mmc->class_dev, "card %s\n",
|
||||
present ? "inserted" : "removed");
|
||||
|
||||
/* Power up slot (before spin_lock, may sleep) */
|
||||
if (present != 0 && host->pdata->setpower)
|
||||
host->pdata->setpower(slot->id, mmc->ocr_avail);
|
||||
|
||||
spin_lock_bh(&host->lock);
|
||||
|
||||
/* Card change detected */
|
||||
@ -1760,10 +1781,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)
|
||||
|
||||
spin_unlock_bh(&host->lock);
|
||||
|
||||
/* Power down slot (after spin_unlock, may sleep) */
|
||||
if (present == 0 && host->pdata->setpower)
|
||||
host->pdata->setpower(slot->id, 0);
|
||||
|
||||
present = dw_mci_get_cd(mmc);
|
||||
}
|
||||
|
||||
@ -1935,14 +1952,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
||||
else
|
||||
bus_width = 1;
|
||||
|
||||
if (drv_data && drv_data->setup_bus) {
|
||||
struct device_node *slot_np;
|
||||
slot_np = dw_mci_of_find_slot_node(host->dev, slot->id);
|
||||
ret = drv_data->setup_bus(host, slot_np, bus_width);
|
||||
if (ret)
|
||||
goto err_setup_bus;
|
||||
}
|
||||
|
||||
switch (bus_width) {
|
||||
case 8:
|
||||
mmc->caps |= MMC_CAP_8_BIT_DATA;
|
||||
@ -1980,8 +1989,14 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
||||
if (IS_ERR(host->vmmc)) {
|
||||
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
|
||||
host->vmmc = NULL;
|
||||
} else
|
||||
regulator_enable(host->vmmc);
|
||||
} else {
|
||||
ret = regulator_enable(host->vmmc);
|
||||
if (ret) {
|
||||
dev_err(host->dev,
|
||||
"failed to enable regulator: %d\n", ret);
|
||||
goto err_setup_bus;
|
||||
}
|
||||
}
|
||||
|
||||
if (dw_mci_get_cd(mmc))
|
||||
set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
||||
@ -1990,7 +2005,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
||||
|
||||
slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id);
|
||||
|
||||
mmc_add_host(mmc);
|
||||
ret = mmc_add_host(mmc);
|
||||
if (ret)
|
||||
goto err_setup_bus;
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
dw_mci_init_debugfs(slot);
|
||||
@ -2289,6 +2306,18 @@ int dw_mci_probe(struct dw_mci *host)
|
||||
mci_writel(host, CLKENA, 0);
|
||||
mci_writel(host, CLKSRC, 0);
|
||||
|
||||
/*
|
||||
* 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(host->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;
|
||||
|
||||
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
|
||||
host->card_workqueue = alloc_workqueue("dw-mci-card",
|
||||
WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1);
|
||||
@ -2337,18 +2366,6 @@ int dw_mci_probe(struct dw_mci *host)
|
||||
goto err_workqueue;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(host->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;
|
||||
|
||||
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
|
||||
dev_info(host->dev, "Internal DMAC interrupt fix enabled.\n");
|
||||
|
||||
@ -2445,8 +2462,14 @@ int dw_mci_resume(struct dw_mci *host)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
if (host->vmmc)
|
||||
regulator_enable(host->vmmc);
|
||||
if (host->vmmc) {
|
||||
ret = regulator_enable(host->vmmc);
|
||||
if (ret) {
|
||||
dev_err(host->dev,
|
||||
"failed to enable regulator: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mci_wait_reset(host->dev, host)) {
|
||||
ret = -ENODEV;
|
||||
@ -2485,7 +2508,7 @@ EXPORT_SYMBOL(dw_mci_resume);
|
||||
|
||||
static int __init dw_mci_init(void)
|
||||
{
|
||||
printk(KERN_INFO "Synopsys Designware Multimedia Card Interface Driver");
|
||||
pr_info("Synopsys Designware Multimedia Card Interface Driver\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,6 @@ extern int dw_mci_resume(struct dw_mci *host);
|
||||
* @prepare_command: handle CMD register extensions.
|
||||
* @set_ios: handle bus specific extensions.
|
||||
* @parse_dt: parse implementation specific device tree properties.
|
||||
* @setup_bus: initialize io-interface
|
||||
*
|
||||
* Provide controller implementation specific extensions. The usage of this
|
||||
* data structure is fully optional and usage of each member in this structure
|
||||
@ -203,7 +202,5 @@ struct dw_mci_drv_data {
|
||||
void (*prepare_command)(struct dw_mci *host, u32 *cmdr);
|
||||
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
|
||||
int (*parse_dt)(struct dw_mci *host);
|
||||
int (*setup_bus)(struct dw_mci *host,
|
||||
struct device_node *slot_np, u8 bus_width);
|
||||
};
|
||||
#endif /* _DW_MMC_H_ */
|
||||
|
@ -119,10 +119,8 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
|
||||
host->pio_size = data->blocks * data->blksz;
|
||||
host->pio_ptr = sg_virt(data->sg);
|
||||
if (!nodma)
|
||||
pr_debug("%s: fallback to PIO for data "
|
||||
"at 0x%p size %d\n",
|
||||
mmc_hostname(host->mmc),
|
||||
host->pio_ptr, host->pio_size);
|
||||
dev_dbg(host->dev, "fallback to PIO for data at 0x%p size %d\n",
|
||||
host->pio_ptr, host->pio_size);
|
||||
return 1;
|
||||
} else {
|
||||
dma_addr_t phys_addr;
|
||||
@ -473,8 +471,8 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
|
||||
if (mrq->data)
|
||||
err_status = mvsd_finish_data(host, mrq->data, err_status);
|
||||
if (err_status) {
|
||||
pr_err("%s: unhandled error status %#04x\n",
|
||||
mmc_hostname(host->mmc), err_status);
|
||||
dev_err(host->dev, "unhandled error status %#04x\n",
|
||||
err_status);
|
||||
cmd->error = -ENOMSG;
|
||||
}
|
||||
|
||||
@ -491,9 +489,8 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
|
||||
if (irq_handled)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
pr_err("%s: unhandled interrupt status=0x%04x en=0x%04x "
|
||||
"pio=%d\n", mmc_hostname(host->mmc), intr_status,
|
||||
host->intr_en, host->pio_size);
|
||||
dev_err(host->dev, "unhandled interrupt status=0x%04x en=0x%04x pio=%d\n",
|
||||
intr_status, host->intr_en, host->pio_size);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
@ -507,13 +504,11 @@ static void mvsd_timeout_timer(unsigned long data)
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
mrq = host->mrq;
|
||||
if (mrq) {
|
||||
pr_err("%s: Timeout waiting for hardware interrupt.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
pr_err("%s: hw_state=0x%04x, intr_status=0x%04x "
|
||||
"intr_en=0x%04x\n", mmc_hostname(host->mmc),
|
||||
mvsd_read(MVSD_HW_STATE),
|
||||
mvsd_read(MVSD_NOR_INTR_STATUS),
|
||||
mvsd_read(MVSD_NOR_INTR_EN));
|
||||
dev_err(host->dev, "Timeout waiting for hardware interrupt.\n");
|
||||
dev_err(host->dev, "hw_state=0x%04x, intr_status=0x%04x intr_en=0x%04x\n",
|
||||
mvsd_read(MVSD_HW_STATE),
|
||||
mvsd_read(MVSD_NOR_INTR_STATUS),
|
||||
mvsd_read(MVSD_NOR_INTR_EN));
|
||||
|
||||
host->mrq = NULL;
|
||||
|
||||
@ -741,8 +736,8 @@ static int __init mvsd_probe(struct platform_device *pdev)
|
||||
goto out;
|
||||
}
|
||||
host->base_clock = mvsd_data->clock / 2;
|
||||
gpio_card_detect = mvsd_data->gpio_card_detect;
|
||||
gpio_write_protect = mvsd_data->gpio_write_protect;
|
||||
gpio_card_detect = mvsd_data->gpio_card_detect ? : -EINVAL;
|
||||
gpio_write_protect = mvsd_data->gpio_write_protect ? : -EINVAL;
|
||||
}
|
||||
|
||||
mmc->ops = &mvsd_ops;
|
||||
@ -778,7 +773,7 @@ static int __init mvsd_probe(struct platform_device *pdev)
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, mvsd_irq, 0, DRIVER_NAME, host);
|
||||
if (ret) {
|
||||
pr_err("%s: cannot assign irq %d\n", DRIVER_NAME, irq);
|
||||
dev_err(&pdev->dev, "cannot assign irq %d\n", irq);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -797,13 +792,11 @@ static int __init mvsd_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
pr_notice("%s: %s driver initialized, ",
|
||||
mmc_hostname(mmc), DRIVER_NAME);
|
||||
if (!(mmc->caps & MMC_CAP_NEEDS_POLL))
|
||||
printk("using GPIO %d for card detection\n",
|
||||
gpio_card_detect);
|
||||
dev_notice(&pdev->dev, "using GPIO %d for card detection\n",
|
||||
gpio_card_detect);
|
||||
else
|
||||
printk("lacking card detect (fall back to polling)\n");
|
||||
dev_notice(&pdev->dev, "lacking card detect (fall back to polling)\n");
|
||||
return 0;
|
||||
|
||||
out:
|
||||
@ -881,18 +874,7 @@ static struct platform_driver mvsd_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static int __init mvsd_init(void)
|
||||
{
|
||||
return platform_driver_probe(&mvsd_driver, mvsd_probe);
|
||||
}
|
||||
|
||||
static void __exit mvsd_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mvsd_driver);
|
||||
}
|
||||
|
||||
module_init(mvsd_init);
|
||||
module_exit(mvsd_exit);
|
||||
module_platform_driver_probe(mvsd_driver, mvsd_probe);
|
||||
|
||||
/* maximum card clock frequency (default 50MHz) */
|
||||
module_param(maxfreq, int, 0);
|
||||
|
@ -34,10 +34,14 @@
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/sizes.h>
|
||||
#include <linux/platform_data/mmc-mxcmmc.h>
|
||||
|
||||
#include <linux/platform_data/dma-imx.h>
|
||||
@ -115,6 +119,7 @@
|
||||
enum mxcmci_type {
|
||||
IMX21_MMC,
|
||||
IMX31_MMC,
|
||||
MPC512X_MMC,
|
||||
};
|
||||
|
||||
struct mxcmci_host {
|
||||
@ -160,24 +165,80 @@ struct mxcmci_host {
|
||||
enum mxcmci_type devtype;
|
||||
};
|
||||
|
||||
static struct platform_device_id mxcmci_devtype[] = {
|
||||
static const struct platform_device_id mxcmci_devtype[] = {
|
||||
{
|
||||
.name = "imx21-mmc",
|
||||
.driver_data = IMX21_MMC,
|
||||
}, {
|
||||
.name = "imx31-mmc",
|
||||
.driver_data = IMX31_MMC,
|
||||
}, {
|
||||
.name = "mpc512x-sdhc",
|
||||
.driver_data = MPC512X_MMC,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, mxcmci_devtype);
|
||||
|
||||
static const struct of_device_id mxcmci_of_match[] = {
|
||||
{
|
||||
.compatible = "fsl,imx21-mmc",
|
||||
.data = &mxcmci_devtype[IMX21_MMC],
|
||||
}, {
|
||||
.compatible = "fsl,imx31-mmc",
|
||||
.data = &mxcmci_devtype[IMX31_MMC],
|
||||
}, {
|
||||
.compatible = "fsl,mpc5121-sdhc",
|
||||
.data = &mxcmci_devtype[MPC512X_MMC],
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mxcmci_of_match);
|
||||
|
||||
static inline int is_imx31_mmc(struct mxcmci_host *host)
|
||||
{
|
||||
return host->devtype == IMX31_MMC;
|
||||
}
|
||||
|
||||
static inline int is_mpc512x_mmc(struct mxcmci_host *host)
|
||||
{
|
||||
return host->devtype == MPC512X_MMC;
|
||||
}
|
||||
|
||||
static inline u32 mxcmci_readl(struct mxcmci_host *host, int reg)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_PPC_MPC512x))
|
||||
return ioread32be(host->base + reg);
|
||||
else
|
||||
return readl(host->base + reg);
|
||||
}
|
||||
|
||||
static inline void mxcmci_writel(struct mxcmci_host *host, u32 val, int reg)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_PPC_MPC512x))
|
||||
iowrite32be(val, host->base + reg);
|
||||
else
|
||||
writel(val, host->base + reg);
|
||||
}
|
||||
|
||||
static inline u16 mxcmci_readw(struct mxcmci_host *host, int reg)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_PPC_MPC512x))
|
||||
return ioread32be(host->base + reg);
|
||||
else
|
||||
return readw(host->base + reg);
|
||||
}
|
||||
|
||||
static inline void mxcmci_writew(struct mxcmci_host *host, u16 val, int reg)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_PPC_MPC512x))
|
||||
iowrite32be(val, host->base + reg);
|
||||
else
|
||||
writew(val, host->base + reg);
|
||||
}
|
||||
|
||||
static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
|
||||
|
||||
static inline void mxcmci_init_ocr(struct mxcmci_host *host)
|
||||
@ -229,17 +290,40 @@ static void mxcmci_softreset(struct mxcmci_host *host)
|
||||
dev_dbg(mmc_dev(host->mmc), "mxcmci_softreset\n");
|
||||
|
||||
/* reset sequence */
|
||||
writew(STR_STP_CLK_RESET, host->base + MMC_REG_STR_STP_CLK);
|
||||
writew(STR_STP_CLK_RESET | STR_STP_CLK_START_CLK,
|
||||
host->base + MMC_REG_STR_STP_CLK);
|
||||
mxcmci_writew(host, STR_STP_CLK_RESET, MMC_REG_STR_STP_CLK);
|
||||
mxcmci_writew(host, STR_STP_CLK_RESET | STR_STP_CLK_START_CLK,
|
||||
MMC_REG_STR_STP_CLK);
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
|
||||
mxcmci_writew(host, STR_STP_CLK_START_CLK, MMC_REG_STR_STP_CLK);
|
||||
|
||||
writew(0xff, host->base + MMC_REG_RES_TO);
|
||||
mxcmci_writew(host, 0xff, MMC_REG_RES_TO);
|
||||
}
|
||||
static int mxcmci_setup_dma(struct mmc_host *mmc);
|
||||
|
||||
#if IS_ENABLED(CONFIG_PPC_MPC512x)
|
||||
static inline void buffer_swap32(u32 *buf, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ((len + 3) / 4); i++) {
|
||||
st_le32(buf, *buf);
|
||||
buf++;
|
||||
}
|
||||
}
|
||||
|
||||
static void mxcmci_swap_buffers(struct mmc_data *data)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
for_each_sg(data->sg, sg, data->sg_len, i)
|
||||
buffer_swap32(sg_virt(sg), sg->length);
|
||||
}
|
||||
#else
|
||||
static inline void mxcmci_swap_buffers(struct mmc_data *data) {}
|
||||
#endif
|
||||
|
||||
static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
||||
{
|
||||
unsigned int nob = data->blocks;
|
||||
@ -255,8 +339,8 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
||||
host->data = data;
|
||||
data->bytes_xfered = 0;
|
||||
|
||||
writew(nob, host->base + MMC_REG_NOB);
|
||||
writew(blksz, host->base + MMC_REG_BLK_LEN);
|
||||
mxcmci_writew(host, nob, MMC_REG_NOB);
|
||||
mxcmci_writew(host, blksz, MMC_REG_BLK_LEN);
|
||||
host->datasize = datasize;
|
||||
|
||||
if (!mxcmci_use_dma(host))
|
||||
@ -275,6 +359,8 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
||||
} else {
|
||||
host->dma_dir = DMA_TO_DEVICE;
|
||||
slave_dirn = DMA_MEM_TO_DEV;
|
||||
|
||||
mxcmci_swap_buffers(data);
|
||||
}
|
||||
|
||||
nents = dma_map_sg(host->dma->device->dev, data->sg,
|
||||
@ -312,13 +398,13 @@ static void mxcmci_dma_callback(void *data)
|
||||
|
||||
del_timer(&host->watchdog);
|
||||
|
||||
stat = readl(host->base + MMC_REG_STATUS);
|
||||
writel(stat & ~STATUS_DATA_TRANS_DONE, host->base + MMC_REG_STATUS);
|
||||
stat = mxcmci_readl(host, MMC_REG_STATUS);
|
||||
mxcmci_writel(host, stat & ~STATUS_DATA_TRANS_DONE, MMC_REG_STATUS);
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
|
||||
|
||||
if (stat & STATUS_READ_OP_DONE)
|
||||
writel(STATUS_READ_OP_DONE, host->base + MMC_REG_STATUS);
|
||||
mxcmci_writel(host, STATUS_READ_OP_DONE, MMC_REG_STATUS);
|
||||
|
||||
mxcmci_data_done(host, stat);
|
||||
}
|
||||
@ -366,12 +452,12 @@ static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
if (host->use_sdio)
|
||||
int_cntr |= INT_SDIO_IRQ_EN;
|
||||
writel(int_cntr, host->base + MMC_REG_INT_CNTR);
|
||||
mxcmci_writel(host, int_cntr, MMC_REG_INT_CNTR);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
writew(cmd->opcode, host->base + MMC_REG_CMD);
|
||||
writel(cmd->arg, host->base + MMC_REG_ARG);
|
||||
writew(cmdat, host->base + MMC_REG_CMD_DAT_CONT);
|
||||
mxcmci_writew(host, cmd->opcode, MMC_REG_CMD);
|
||||
mxcmci_writel(host, cmd->arg, MMC_REG_ARG);
|
||||
mxcmci_writew(host, cmdat, MMC_REG_CMD_DAT_CONT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -385,7 +471,7 @@ static void mxcmci_finish_request(struct mxcmci_host *host,
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
if (host->use_sdio)
|
||||
int_cntr |= INT_SDIO_IRQ_EN;
|
||||
writel(int_cntr, host->base + MMC_REG_INT_CNTR);
|
||||
mxcmci_writel(host, int_cntr, MMC_REG_INT_CNTR);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
host->req = NULL;
|
||||
@ -400,9 +486,11 @@ static int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat)
|
||||
struct mmc_data *data = host->data;
|
||||
int data_error;
|
||||
|
||||
if (mxcmci_use_dma(host))
|
||||
if (mxcmci_use_dma(host)) {
|
||||
dma_unmap_sg(host->dma->device->dev, data->sg, data->sg_len,
|
||||
host->dma_dir);
|
||||
mxcmci_swap_buffers(data);
|
||||
}
|
||||
|
||||
if (stat & STATUS_ERR_MASK) {
|
||||
dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",
|
||||
@ -460,14 +548,14 @@ static void mxcmci_read_response(struct mxcmci_host *host, unsigned int stat)
|
||||
if (cmd->flags & MMC_RSP_PRESENT) {
|
||||
if (cmd->flags & MMC_RSP_136) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
a = readw(host->base + MMC_REG_RES_FIFO);
|
||||
b = readw(host->base + MMC_REG_RES_FIFO);
|
||||
a = mxcmci_readw(host, MMC_REG_RES_FIFO);
|
||||
b = mxcmci_readw(host, MMC_REG_RES_FIFO);
|
||||
cmd->resp[i] = a << 16 | b;
|
||||
}
|
||||
} else {
|
||||
a = readw(host->base + MMC_REG_RES_FIFO);
|
||||
b = readw(host->base + MMC_REG_RES_FIFO);
|
||||
c = readw(host->base + MMC_REG_RES_FIFO);
|
||||
a = mxcmci_readw(host, MMC_REG_RES_FIFO);
|
||||
b = mxcmci_readw(host, MMC_REG_RES_FIFO);
|
||||
c = mxcmci_readw(host, MMC_REG_RES_FIFO);
|
||||
cmd->resp[0] = a << 24 | b << 8 | c >> 8;
|
||||
}
|
||||
}
|
||||
@ -479,7 +567,7 @@ static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask)
|
||||
unsigned long timeout = jiffies + HZ;
|
||||
|
||||
do {
|
||||
stat = readl(host->base + MMC_REG_STATUS);
|
||||
stat = mxcmci_readl(host, MMC_REG_STATUS);
|
||||
if (stat & STATUS_ERR_MASK)
|
||||
return stat;
|
||||
if (time_after(jiffies, timeout)) {
|
||||
@ -503,7 +591,7 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
|
||||
STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
|
||||
if (stat)
|
||||
return stat;
|
||||
*buf++ = readl(host->base + MMC_REG_BUFFER_ACCESS);
|
||||
*buf++ = cpu_to_le32(mxcmci_readl(host, MMC_REG_BUFFER_ACCESS));
|
||||
bytes -= 4;
|
||||
}
|
||||
|
||||
@ -515,7 +603,7 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
|
||||
STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
|
||||
if (stat)
|
||||
return stat;
|
||||
tmp = readl(host->base + MMC_REG_BUFFER_ACCESS);
|
||||
tmp = cpu_to_le32(mxcmci_readl(host, MMC_REG_BUFFER_ACCESS));
|
||||
memcpy(b, &tmp, bytes);
|
||||
}
|
||||
|
||||
@ -531,7 +619,7 @@ static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
|
||||
stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
|
||||
if (stat)
|
||||
return stat;
|
||||
writel(*buf++, host->base + MMC_REG_BUFFER_ACCESS);
|
||||
mxcmci_writel(host, cpu_to_le32(*buf++), MMC_REG_BUFFER_ACCESS);
|
||||
bytes -= 4;
|
||||
}
|
||||
|
||||
@ -544,7 +632,7 @@ static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
|
||||
return stat;
|
||||
|
||||
memcpy(&tmp, b, bytes);
|
||||
writel(tmp, host->base + MMC_REG_BUFFER_ACCESS);
|
||||
mxcmci_writel(host, cpu_to_le32(tmp), MMC_REG_BUFFER_ACCESS);
|
||||
}
|
||||
|
||||
stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
|
||||
@ -590,8 +678,8 @@ static void mxcmci_datawork(struct work_struct *work)
|
||||
datawork);
|
||||
int datastat = mxcmci_transfer_data(host);
|
||||
|
||||
writel(STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE,
|
||||
host->base + MMC_REG_STATUS);
|
||||
mxcmci_writel(host, STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE,
|
||||
MMC_REG_STATUS);
|
||||
mxcmci_finish_data(host, datastat);
|
||||
|
||||
if (host->req->stop) {
|
||||
@ -606,24 +694,40 @@ static void mxcmci_datawork(struct work_struct *work)
|
||||
|
||||
static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat)
|
||||
{
|
||||
struct mmc_data *data = host->data;
|
||||
struct mmc_request *req;
|
||||
int data_error;
|
||||
unsigned long flags;
|
||||
|
||||
if (!data)
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
if (!host->data) {
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!host->req) {
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
req = host->req;
|
||||
if (!req->stop)
|
||||
host->req = NULL; /* we will handle finish req below */
|
||||
|
||||
data_error = mxcmci_finish_data(host, stat);
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
mxcmci_read_response(host, stat);
|
||||
host->cmd = NULL;
|
||||
|
||||
if (host->req->stop) {
|
||||
if (mxcmci_start_cmd(host, host->req->stop, 0)) {
|
||||
mxcmci_finish_request(host, host->req);
|
||||
if (req->stop) {
|
||||
if (mxcmci_start_cmd(host, req->stop, 0)) {
|
||||
mxcmci_finish_request(host, req);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
mxcmci_finish_request(host, host->req);
|
||||
mxcmci_finish_request(host, req);
|
||||
}
|
||||
}
|
||||
|
||||
@ -653,9 +757,11 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
|
||||
bool sdio_irq;
|
||||
u32 stat;
|
||||
|
||||
stat = readl(host->base + MMC_REG_STATUS);
|
||||
writel(stat & ~(STATUS_SDIO_INT_ACTIVE | STATUS_DATA_TRANS_DONE |
|
||||
STATUS_WRITE_OP_DONE), host->base + MMC_REG_STATUS);
|
||||
stat = mxcmci_readl(host, MMC_REG_STATUS);
|
||||
mxcmci_writel(host,
|
||||
stat & ~(STATUS_SDIO_INT_ACTIVE | STATUS_DATA_TRANS_DONE |
|
||||
STATUS_WRITE_OP_DONE),
|
||||
MMC_REG_STATUS);
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
|
||||
|
||||
@ -665,11 +771,11 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
|
||||
|
||||
if (mxcmci_use_dma(host) &&
|
||||
(stat & (STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE)))
|
||||
writel(STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE,
|
||||
host->base + MMC_REG_STATUS);
|
||||
mxcmci_writel(host, STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE,
|
||||
MMC_REG_STATUS);
|
||||
|
||||
if (sdio_irq) {
|
||||
writel(STATUS_SDIO_INT_ACTIVE, host->base + MMC_REG_STATUS);
|
||||
mxcmci_writel(host, STATUS_SDIO_INT_ACTIVE, MMC_REG_STATUS);
|
||||
mmc_signal_sdio_irq(host->mmc);
|
||||
}
|
||||
|
||||
@ -751,7 +857,7 @@ static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios)
|
||||
prescaler <<= 1;
|
||||
}
|
||||
|
||||
writew((prescaler << 4) | divider, host->base + MMC_REG_CLK_RATE);
|
||||
mxcmci_writew(host, (prescaler << 4) | divider, MMC_REG_CLK_RATE);
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "scaler: %d divider: %d in: %d out: %d\n",
|
||||
prescaler, divider, clk_in, clk_ios);
|
||||
@ -814,9 +920,9 @@ static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
|
||||
if (ios->clock) {
|
||||
mxcmci_set_clk_rate(host, ios->clock);
|
||||
writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
|
||||
mxcmci_writew(host, STR_STP_CLK_START_CLK, MMC_REG_STR_STP_CLK);
|
||||
} else {
|
||||
writew(STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
|
||||
mxcmci_writew(host, STR_STP_CLK_STOP_CLK, MMC_REG_STR_STP_CLK);
|
||||
}
|
||||
|
||||
host->clock = ios->clock;
|
||||
@ -839,10 +945,11 @@ static int mxcmci_get_ro(struct mmc_host *mmc)
|
||||
if (host->pdata && host->pdata->get_ro)
|
||||
return !!host->pdata->get_ro(mmc_dev(mmc));
|
||||
/*
|
||||
* Board doesn't support read only detection; let the mmc core
|
||||
* decide what to do.
|
||||
* If board doesn't support read only detection (no mmc_gpio
|
||||
* context or gpio is invalid), then let the mmc core decide
|
||||
* what to do.
|
||||
*/
|
||||
return -ENOSYS;
|
||||
return mmc_gpio_get_ro(mmc);
|
||||
}
|
||||
|
||||
static void mxcmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||
@ -853,14 +960,14 @@ static void mxcmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->use_sdio = enable;
|
||||
int_cntr = readl(host->base + MMC_REG_INT_CNTR);
|
||||
int_cntr = mxcmci_readl(host, MMC_REG_INT_CNTR);
|
||||
|
||||
if (enable)
|
||||
int_cntr |= INT_SDIO_IRQ_EN;
|
||||
else
|
||||
int_cntr &= ~INT_SDIO_IRQ_EN;
|
||||
|
||||
writel(int_cntr, host->base + MMC_REG_INT_CNTR);
|
||||
mxcmci_writel(host, int_cntr, MMC_REG_INT_CNTR);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
@ -898,7 +1005,7 @@ static void mxcmci_watchdog(unsigned long data)
|
||||
struct mmc_host *mmc = (struct mmc_host *)data;
|
||||
struct mxcmci_host *host = mmc_priv(mmc);
|
||||
struct mmc_request *req = host->req;
|
||||
unsigned int stat = readl(host->base + MMC_REG_STATUS);
|
||||
unsigned int stat = mxcmci_readl(host, MMC_REG_STATUS);
|
||||
|
||||
if (host->dma_dir == DMA_FROM_DEVICE) {
|
||||
dmaengine_terminate_all(host->dma);
|
||||
@ -914,7 +1021,8 @@ static void mxcmci_watchdog(unsigned long data)
|
||||
|
||||
/* Mark transfer as erroneus and inform the upper layers */
|
||||
|
||||
host->data->error = -ETIMEDOUT;
|
||||
if (host->data)
|
||||
host->data->error = -ETIMEDOUT;
|
||||
host->req = NULL;
|
||||
host->cmd = NULL;
|
||||
host->data = NULL;
|
||||
@ -935,9 +1043,14 @@ static int mxcmci_probe(struct platform_device *pdev)
|
||||
struct mxcmci_host *host = NULL;
|
||||
struct resource *iores, *r;
|
||||
int ret = 0, irq;
|
||||
bool dat3_card_detect = false;
|
||||
dma_cap_mask_t mask;
|
||||
const struct of_device_id *of_id;
|
||||
struct imxmmc_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
pr_info("i.MX SDHC driver\n");
|
||||
pr_info("i.MX/MPC512x SDHC driver\n");
|
||||
|
||||
of_id = of_match_device(mxcmci_of_match, &pdev->dev);
|
||||
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
@ -954,11 +1067,16 @@ static int mxcmci_probe(struct platform_device *pdev)
|
||||
goto out_release_mem;
|
||||
}
|
||||
|
||||
mmc_of_parse(mmc);
|
||||
mmc->ops = &mxcmci_ops;
|
||||
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
|
||||
|
||||
/* For devicetree parsing, the bus width is read from devicetree */
|
||||
if (pdata)
|
||||
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
|
||||
else
|
||||
mmc->caps |= MMC_CAP_SDIO_IRQ;
|
||||
|
||||
/* MMC core transfer sizes tunable parameters */
|
||||
mmc->max_segs = 64;
|
||||
mmc->max_blk_size = 2048;
|
||||
mmc->max_blk_count = 65535;
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
@ -971,14 +1089,30 @@ static int mxcmci_probe(struct platform_device *pdev)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (of_id) {
|
||||
const struct platform_device_id *id_entry = of_id->data;
|
||||
host->devtype = id_entry->driver_data;
|
||||
} else {
|
||||
host->devtype = pdev->id_entry->driver_data;
|
||||
}
|
||||
|
||||
/* adjust max_segs after devtype detection */
|
||||
if (!is_mpc512x_mmc(host))
|
||||
mmc->max_segs = 64;
|
||||
|
||||
host->mmc = mmc;
|
||||
host->pdata = pdev->dev.platform_data;
|
||||
host->devtype = pdev->id_entry->driver_data;
|
||||
host->pdata = pdata;
|
||||
spin_lock_init(&host->lock);
|
||||
|
||||
if (pdata)
|
||||
dat3_card_detect = pdata->dat3_card_detect;
|
||||
else if (!(mmc->caps & MMC_CAP_NONREMOVABLE)
|
||||
&& !of_property_read_bool(pdev->dev.of_node, "cd-gpios"))
|
||||
dat3_card_detect = true;
|
||||
|
||||
mxcmci_init_ocr(host);
|
||||
|
||||
if (host->pdata && host->pdata->dat3_card_detect)
|
||||
if (dat3_card_detect)
|
||||
host->default_irq_mask =
|
||||
INT_CARD_INSERTION_EN | INT_CARD_REMOVAL_EN;
|
||||
else
|
||||
@ -1004,7 +1138,7 @@ static int mxcmci_probe(struct platform_device *pdev)
|
||||
|
||||
mxcmci_softreset(host);
|
||||
|
||||
host->rev_no = readw(host->base + MMC_REG_REV_NO);
|
||||
host->rev_no = mxcmci_readw(host, MMC_REG_REV_NO);
|
||||
if (host->rev_no != 0x400) {
|
||||
ret = -ENODEV;
|
||||
dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n",
|
||||
@ -1016,25 +1150,28 @@ static int mxcmci_probe(struct platform_device *pdev)
|
||||
mmc->f_max = clk_get_rate(host->clk_per) >> 1;
|
||||
|
||||
/* recommended in data sheet */
|
||||
writew(0x2db4, host->base + MMC_REG_READ_TO);
|
||||
mxcmci_writew(host, 0x2db4, MMC_REG_READ_TO);
|
||||
|
||||
writel(host->default_irq_mask, host->base + MMC_REG_INT_CNTR);
|
||||
mxcmci_writel(host, host->default_irq_mask, MMC_REG_INT_CNTR);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (r) {
|
||||
host->dmareq = r->start;
|
||||
host->dma_data.peripheral_type = IMX_DMATYPE_SDHC;
|
||||
host->dma_data.priority = DMA_PRIO_LOW;
|
||||
host->dma_data.dma_request = host->dmareq;
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
host->dma = dma_request_channel(mask, filter, host);
|
||||
if (host->dma)
|
||||
mmc->max_seg_size = dma_get_max_seg_size(
|
||||
host->dma->device->dev);
|
||||
if (!host->pdata) {
|
||||
host->dma = dma_request_slave_channel(&pdev->dev, "rx-tx");
|
||||
} else {
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (r) {
|
||||
host->dmareq = r->start;
|
||||
host->dma_data.peripheral_type = IMX_DMATYPE_SDHC;
|
||||
host->dma_data.priority = DMA_PRIO_LOW;
|
||||
host->dma_data.dma_request = host->dmareq;
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
host->dma = dma_request_channel(mask, filter, host);
|
||||
}
|
||||
}
|
||||
|
||||
if (!host->dma)
|
||||
if (host->dma)
|
||||
mmc->max_seg_size = dma_get_max_seg_size(
|
||||
host->dma->device->dev);
|
||||
else
|
||||
dev_info(mmc_dev(host->mmc), "dma not available. Using PIO\n");
|
||||
|
||||
INIT_WORK(&host->datawork, mxcmci_datawork);
|
||||
@ -1052,12 +1189,12 @@ static int mxcmci_probe(struct platform_device *pdev)
|
||||
goto out_free_irq;
|
||||
}
|
||||
|
||||
mmc_add_host(mmc);
|
||||
|
||||
init_timer(&host->watchdog);
|
||||
host->watchdog.function = &mxcmci_watchdog;
|
||||
host->watchdog.data = (unsigned long)mmc;
|
||||
|
||||
mmc_add_host(mmc);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_irq:
|
||||
@ -1153,6 +1290,7 @@ static struct platform_driver mxcmci_driver = {
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &mxcmci_pm_ops,
|
||||
#endif
|
||||
.of_match_table = mxcmci_of_match,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -72,6 +72,9 @@ struct mxs_mmc_host {
|
||||
int sdio_irq_en;
|
||||
int wp_gpio;
|
||||
bool wp_inverted;
|
||||
bool cd_inverted;
|
||||
bool broken_cd;
|
||||
bool non_removable;
|
||||
};
|
||||
|
||||
static int mxs_mmc_get_ro(struct mmc_host *mmc)
|
||||
@ -95,8 +98,9 @@ static int mxs_mmc_get_cd(struct mmc_host *mmc)
|
||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
|
||||
return !(readl(ssp->base + HW_SSP_STATUS(ssp)) &
|
||||
BM_SSP_STATUS_CARD_DETECT);
|
||||
return host->non_removable || host->broken_cd ||
|
||||
!(readl(ssp->base + HW_SSP_STATUS(ssp)) &
|
||||
BM_SSP_STATUS_CARD_DETECT) ^ host->cd_inverted;
|
||||
}
|
||||
|
||||
static void mxs_mmc_reset(struct mxs_mmc_host *host)
|
||||
@ -686,11 +690,16 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
else if (bus_width == 8)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
|
||||
host->broken_cd = of_property_read_bool(np, "broken-cd");
|
||||
host->non_removable = of_property_read_bool(np, "non-removable");
|
||||
if (host->non_removable)
|
||||
mmc->caps |= MMC_CAP_NONREMOVABLE;
|
||||
host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags);
|
||||
|
||||
if (flags & OF_GPIO_ACTIVE_LOW)
|
||||
host->wp_inverted = 1;
|
||||
|
||||
host->cd_inverted = of_property_read_bool(np, "cd-inverted");
|
||||
|
||||
mmc->f_min = 400000;
|
||||
mmc->f_max = 288000000;
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
|
@ -1717,6 +1717,12 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
|
||||
struct omap_mmc_platform_data *pdata;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 bus_width, max_freq;
|
||||
int cd_gpio, wp_gpio;
|
||||
|
||||
cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
|
||||
wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
|
||||
if (cd_gpio == -EPROBE_DEFER || wp_gpio == -EPROBE_DEFER)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
@ -1727,8 +1733,8 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
|
||||
|
||||
/* This driver only supports 1 slot */
|
||||
pdata->nr_slots = 1;
|
||||
pdata->slots[0].switch_pin = of_get_named_gpio(np, "cd-gpios", 0);
|
||||
pdata->slots[0].gpio_wp = of_get_named_gpio(np, "wp-gpios", 0);
|
||||
pdata->slots[0].switch_pin = cd_gpio;
|
||||
pdata->slots[0].gpio_wp = wp_gpio;
|
||||
|
||||
if (of_find_property(np, "ti,non-removable", NULL)) {
|
||||
pdata->slots[0].nonremovable = true;
|
||||
@ -1774,6 +1780,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||
match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
|
||||
if (match) {
|
||||
pdata = of_get_hsmmc_pdata(&pdev->dev);
|
||||
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
|
||||
if (match->data) {
|
||||
const u16 *offsetp = match->data;
|
||||
pdata->reg_offset = *offsetp;
|
||||
|
@ -57,6 +57,9 @@ struct realtek_pci_sdmmc {
|
||||
bool eject;
|
||||
bool initial_mode;
|
||||
bool ddr_mode;
|
||||
int power_state;
|
||||
#define SDMMC_POWER_ON 1
|
||||
#define SDMMC_POWER_OFF 0
|
||||
};
|
||||
|
||||
static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host)
|
||||
@ -765,6 +768,9 @@ static int sd_power_on(struct realtek_pci_sdmmc *host)
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
int err;
|
||||
|
||||
if (host->power_state == SDMMC_POWER_ON)
|
||||
return 0;
|
||||
|
||||
rtsx_pci_init_cmd(pcr);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE,
|
||||
@ -787,6 +793,7 @@ static int sd_power_on(struct realtek_pci_sdmmc *host)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
host->power_state = SDMMC_POWER_ON;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -795,6 +802,8 @@ static int sd_power_off(struct realtek_pci_sdmmc *host)
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
int err;
|
||||
|
||||
host->power_state = SDMMC_POWER_OFF;
|
||||
|
||||
rtsx_pci_init_cmd(pcr);
|
||||
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, SD_CLK_EN, 0);
|
||||
@ -1260,6 +1269,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
|
||||
host->pcr = pcr;
|
||||
host->mmc = mmc;
|
||||
host->pdev = pdev;
|
||||
host->power_state = SDMMC_POWER_OFF;
|
||||
platform_set_drvdata(pdev, host);
|
||||
pcr->slots[RTSX_SD_CARD].p_dev = pdev;
|
||||
pcr->slots[RTSX_SD_CARD].card_event = rtsx_pci_sdmmc_card_event;
|
||||
|
@ -195,6 +195,8 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
|
||||
host->mmc->pm_caps |= c->slot->pm_caps;
|
||||
}
|
||||
|
||||
host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
|
||||
|
||||
err = sdhci_add_host(host);
|
||||
if (err)
|
||||
goto err_free;
|
||||
|
@ -124,7 +124,7 @@ unsigned int bcm2835_sdhci_get_min_clock(struct sdhci_host *host)
|
||||
return MIN_FREQ;
|
||||
}
|
||||
|
||||
static struct sdhci_ops bcm2835_sdhci_ops = {
|
||||
static const struct sdhci_ops bcm2835_sdhci_ops = {
|
||||
.write_l = bcm2835_sdhci_writel,
|
||||
.write_w = bcm2835_sdhci_writew,
|
||||
.write_b = bcm2835_sdhci_writeb,
|
||||
@ -135,7 +135,7 @@ static struct sdhci_ops bcm2835_sdhci_ops = {
|
||||
.get_min_clock = bcm2835_sdhci_get_min_clock,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data bcm2835_sdhci_pdata = {
|
||||
static const struct sdhci_pltfm_data bcm2835_sdhci_pdata = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
|
||||
.ops = &bcm2835_sdhci_ops,
|
||||
|
@ -79,12 +79,12 @@ out:
|
||||
host->clock = clock;
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_cns3xxx_ops = {
|
||||
static const struct sdhci_ops sdhci_cns3xxx_ops = {
|
||||
.get_max_clock = sdhci_cns3xxx_get_max_clk,
|
||||
.set_clock = sdhci_cns3xxx_set_clock,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
|
||||
static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
|
||||
.ops = &sdhci_cns3xxx_ops,
|
||||
.quirks = SDHCI_QUIRK_BROKEN_DMA |
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
|
@ -83,12 +83,12 @@ static u32 sdhci_dove_readl(struct sdhci_host *host, int reg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_dove_ops = {
|
||||
static const struct sdhci_ops sdhci_dove_ops = {
|
||||
.read_w = sdhci_dove_readw,
|
||||
.read_l = sdhci_dove_readl,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_dove_pdata = {
|
||||
static const struct sdhci_pltfm_data sdhci_dove_pdata = {
|
||||
.ops = &sdhci_dove_ops,
|
||||
.quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
|
||||
SDHCI_QUIRK_NO_BUSY_IRQ |
|
||||
|
@ -399,7 +399,7 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_esdhc_ops = {
|
||||
static const struct sdhci_ops sdhci_esdhc_ops = {
|
||||
.read_l = esdhc_readl_le,
|
||||
.read_w = esdhc_readw_le,
|
||||
.write_l = esdhc_writel_le,
|
||||
@ -412,7 +412,7 @@ static struct sdhci_ops sdhci_esdhc_ops = {
|
||||
.platform_bus_width = esdhc_pltfm_bus_width,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
|
||||
static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
|
||||
.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_HISPD_BIT
|
||||
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
|
||||
| SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC
|
||||
|
@ -230,7 +230,7 @@ static void esdhc_of_platform_init(struct sdhci_host *host)
|
||||
host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_esdhc_ops = {
|
||||
static const struct sdhci_ops sdhci_esdhc_ops = {
|
||||
.read_l = esdhc_readl,
|
||||
.read_w = esdhc_readw,
|
||||
.read_b = esdhc_readb,
|
||||
@ -249,7 +249,7 @@ static struct sdhci_ops sdhci_esdhc_ops = {
|
||||
.adma_workaround = esdhci_of_adma_workaround,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_esdhc_pdata = {
|
||||
static const struct sdhci_pltfm_data sdhci_esdhc_pdata = {
|
||||
/*
|
||||
* card detection could be handled via GPIO
|
||||
* eSDHC cannot support End Attribute in NOP ADMA descriptor
|
||||
|
@ -51,7 +51,7 @@ static void sdhci_hlwd_writeb(struct sdhci_host *host, u8 val, int reg)
|
||||
udelay(SDHCI_HLWD_WRITE_DELAY);
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_hlwd_ops = {
|
||||
static const struct sdhci_ops sdhci_hlwd_ops = {
|
||||
.read_l = sdhci_be32bs_readl,
|
||||
.read_w = sdhci_be32bs_readw,
|
||||
.read_b = sdhci_be32bs_readb,
|
||||
@ -60,7 +60,7 @@ static struct sdhci_ops sdhci_hlwd_ops = {
|
||||
.write_b = sdhci_hlwd_writeb,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_hlwd_pdata = {
|
||||
static const struct sdhci_pltfm_data sdhci_hlwd_pdata = {
|
||||
.quirks = SDHCI_QUIRK_32BIT_DMA_ADDR |
|
||||
SDHCI_QUIRK_32BIT_DMA_SIZE,
|
||||
.ops = &sdhci_hlwd_ops,
|
||||
|
@ -975,7 +975,7 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host)
|
||||
usleep_range(300, 1000);
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_pci_ops = {
|
||||
static const struct sdhci_ops sdhci_pci_ops = {
|
||||
.enable_dma = sdhci_pci_enable_dma,
|
||||
.platform_bus_width = sdhci_pci_bus_width,
|
||||
.hw_reset = sdhci_pci_hw_reset,
|
||||
@ -1279,6 +1279,8 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
|
||||
}
|
||||
|
||||
host->mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ;
|
||||
host->mmc->slotno = slotno;
|
||||
host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
|
@ -44,7 +44,7 @@ unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock);
|
||||
|
||||
static struct sdhci_ops sdhci_pltfm_ops = {
|
||||
static const struct sdhci_ops sdhci_pltfm_ops = {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
@ -94,6 +94,7 @@ void sdhci_get_of_property(struct platform_device *pdev)
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,p2020-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,p1010-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,t4240-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,mpc8536-esdhc"))
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||
|
||||
@ -114,7 +115,7 @@ void sdhci_get_of_property(struct platform_device *pdev) {}
|
||||
EXPORT_SYMBOL_GPL(sdhci_get_of_property);
|
||||
|
||||
struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
|
||||
struct sdhci_pltfm_data *pdata)
|
||||
const struct sdhci_pltfm_data *pdata)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
@ -201,7 +202,7 @@ void sdhci_pltfm_free(struct platform_device *pdev)
|
||||
EXPORT_SYMBOL_GPL(sdhci_pltfm_free);
|
||||
|
||||
int sdhci_pltfm_register(struct platform_device *pdev,
|
||||
struct sdhci_pltfm_data *pdata)
|
||||
const struct sdhci_pltfm_data *pdata)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
int ret = 0;
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "sdhci.h"
|
||||
|
||||
struct sdhci_pltfm_data {
|
||||
struct sdhci_ops *ops;
|
||||
const struct sdhci_ops *ops;
|
||||
unsigned int quirks;
|
||||
};
|
||||
|
||||
@ -91,11 +91,11 @@ static inline void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg)
|
||||
extern void sdhci_get_of_property(struct platform_device *pdev);
|
||||
|
||||
extern struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
|
||||
struct sdhci_pltfm_data *pdata);
|
||||
const struct sdhci_pltfm_data *pdata);
|
||||
extern void sdhci_pltfm_free(struct platform_device *pdev);
|
||||
|
||||
extern int sdhci_pltfm_register(struct platform_device *pdev,
|
||||
struct sdhci_pltfm_data *pdata);
|
||||
const struct sdhci_pltfm_data *pdata);
|
||||
extern int sdhci_pltfm_unregister(struct platform_device *pdev);
|
||||
|
||||
extern unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host);
|
||||
|
@ -111,7 +111,7 @@ static int pxav2_mmc_set_width(struct sdhci_host *host, int width)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sdhci_ops pxav2_sdhci_ops = {
|
||||
static const struct sdhci_ops pxav2_sdhci_ops = {
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.platform_reset_exit = pxav2_set_private_registers,
|
||||
.platform_bus_width = pxav2_mmc_set_width,
|
||||
|
@ -167,13 +167,21 @@ static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sdhci_ops pxav3_sdhci_ops = {
|
||||
static const struct sdhci_ops pxav3_sdhci_ops = {
|
||||
.platform_reset_exit = pxav3_set_private_registers,
|
||||
.set_uhs_signaling = pxav3_set_uhs_signaling,
|
||||
.platform_send_init_74_clocks = pxav3_gen_init_74_clocks,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_pxav3_pdata = {
|
||||
.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK
|
||||
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
|
||||
| SDHCI_QUIRK_32BIT_ADMA_SIZE
|
||||
| SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||
.ops = &pxav3_sdhci_ops,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id sdhci_pxav3_of_match[] = {
|
||||
{
|
||||
@ -187,29 +195,16 @@ static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev)
|
||||
{
|
||||
struct sdhci_pxa_platdata *pdata;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 bus_width;
|
||||
u32 clk_delay_cycles;
|
||||
enum of_gpio_flags gpio_flags;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return NULL;
|
||||
|
||||
if (of_find_property(np, "non-removable", NULL))
|
||||
pdata->flags |= PXA_FLAG_CARD_PERMANENT;
|
||||
|
||||
of_property_read_u32(np, "bus-width", &bus_width);
|
||||
if (bus_width == 8)
|
||||
pdata->flags |= PXA_FLAG_SD_8_BIT_CAPABLE_SLOT;
|
||||
|
||||
of_property_read_u32(np, "mrvl,clk-delay-cycles", &clk_delay_cycles);
|
||||
if (clk_delay_cycles > 0)
|
||||
pdata->clk_delay_cycles = clk_delay_cycles;
|
||||
|
||||
pdata->ext_cd_gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &gpio_flags);
|
||||
if (gpio_flags != OF_GPIO_ACTIVE_LOW)
|
||||
pdata->host_caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
@ -235,7 +230,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||
if (!pxa)
|
||||
return -ENOMEM;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, NULL);
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata);
|
||||
if (IS_ERR(host)) {
|
||||
kfree(pxa);
|
||||
return PTR_ERR(host);
|
||||
@ -252,24 +247,18 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||
pltfm_host->clk = clk;
|
||||
clk_prepare_enable(clk);
|
||||
|
||||
host->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
|
||||
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
|
||||
| SDHCI_QUIRK_32BIT_ADMA_SIZE
|
||||
| SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
|
||||
|
||||
/* enable 1/8V DDR capable */
|
||||
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||
|
||||
match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev);
|
||||
if (match)
|
||||
if (match) {
|
||||
mmc_of_parse(host->mmc);
|
||||
sdhci_get_of_property(pdev);
|
||||
pdata = pxav3_get_mmc_pdata(dev);
|
||||
|
||||
if (pdata) {
|
||||
if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
|
||||
/* on-chip device */
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||
} else if (pdata) {
|
||||
/* on-chip device */
|
||||
if (pdata->flags & PXA_FLAG_CARD_PERMANENT)
|
||||
host->mmc->caps |= MMC_CAP_NONREMOVABLE;
|
||||
}
|
||||
|
||||
/* If slot design supports 8 bit data, indicate this to MMC. */
|
||||
if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
|
||||
@ -296,10 +285,6 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
host->ops = &pxav3_sdhci_ops;
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS);
|
||||
@ -317,7 +302,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
if (pdata->pm_caps & MMC_PM_KEEP_POWER) {
|
||||
if (host->mmc->pm_caps & MMC_PM_KEEP_POWER) {
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
host->mmc->pm_flags |= MMC_PM_WAKE_SDIO_IRQ;
|
||||
} else {
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
@ -44,7 +43,6 @@
|
||||
* @ioarea: The resource created when we claimed the IO area.
|
||||
* @pdata: The platform data for this controller.
|
||||
* @cur_clk: The index of the current bus clock.
|
||||
* @gpios: List of gpio numbers parsed from device tree.
|
||||
* @clk_io: The clock for the internal bus interface.
|
||||
* @clk_bus: The clocks that are available for the SD/MMC bus clock.
|
||||
*/
|
||||
@ -56,8 +54,6 @@ struct sdhci_s3c {
|
||||
unsigned int cur_clk;
|
||||
int ext_cd_irq;
|
||||
int ext_cd_gpio;
|
||||
int *gpios;
|
||||
struct pinctrl *pctrl;
|
||||
|
||||
struct clk *clk_io;
|
||||
struct clk *clk_bus[MAX_BUS_CLK];
|
||||
@ -446,42 +442,32 @@ static int sdhci_s3c_parse_dt(struct device *dev,
|
||||
struct device_node *node = dev->of_node;
|
||||
struct sdhci_s3c *ourhost = to_s3c(host);
|
||||
u32 max_width;
|
||||
int gpio, cnt, ret;
|
||||
int gpio;
|
||||
|
||||
/* if the bus-width property is not specified, assume width as 1 */
|
||||
if (of_property_read_u32(node, "bus-width", &max_width))
|
||||
max_width = 1;
|
||||
pdata->max_width = max_width;
|
||||
|
||||
ourhost->gpios = devm_kzalloc(dev, NUM_GPIOS(pdata->max_width) *
|
||||
sizeof(int), GFP_KERNEL);
|
||||
if (!ourhost->gpios)
|
||||
return -ENOMEM;
|
||||
|
||||
/* get the card detection method */
|
||||
if (of_get_property(node, "broken-cd", NULL)) {
|
||||
pdata->cd_type = S3C_SDHCI_CD_NONE;
|
||||
goto setup_bus;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (of_get_property(node, "non-removable", NULL)) {
|
||||
pdata->cd_type = S3C_SDHCI_CD_PERMANENT;
|
||||
goto setup_bus;
|
||||
return 0;
|
||||
}
|
||||
|
||||
gpio = of_get_named_gpio(node, "cd-gpios", 0);
|
||||
if (gpio_is_valid(gpio)) {
|
||||
pdata->cd_type = S3C_SDHCI_CD_GPIO;
|
||||
goto found_cd;
|
||||
} else if (gpio != -ENOENT) {
|
||||
dev_err(dev, "invalid card detect gpio specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpio = of_get_named_gpio(node, "samsung,cd-pinmux-gpio", 0);
|
||||
if (gpio_is_valid(gpio)) {
|
||||
pdata->cd_type = S3C_SDHCI_CD_INTERNAL;
|
||||
goto found_cd;
|
||||
pdata->ext_cd_gpio = gpio;
|
||||
ourhost->ext_cd_gpio = -1;
|
||||
if (of_get_property(node, "cd-inverted", NULL))
|
||||
pdata->ext_cd_gpio_invert = 1;
|
||||
return 0;
|
||||
} else if (gpio != -ENOENT) {
|
||||
dev_err(dev, "invalid card detect gpio specified\n");
|
||||
return -EINVAL;
|
||||
@ -489,45 +475,6 @@ static int sdhci_s3c_parse_dt(struct device *dev,
|
||||
|
||||
/* assuming internal card detect that will be configured by pinctrl */
|
||||
pdata->cd_type = S3C_SDHCI_CD_INTERNAL;
|
||||
goto setup_bus;
|
||||
|
||||
found_cd:
|
||||
if (pdata->cd_type == S3C_SDHCI_CD_GPIO) {
|
||||
pdata->ext_cd_gpio = gpio;
|
||||
ourhost->ext_cd_gpio = -1;
|
||||
if (of_get_property(node, "cd-inverted", NULL))
|
||||
pdata->ext_cd_gpio_invert = 1;
|
||||
} else if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) {
|
||||
ret = devm_gpio_request(dev, gpio, "sdhci-cd");
|
||||
if (ret) {
|
||||
dev_err(dev, "card detect gpio request failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ourhost->ext_cd_gpio = gpio;
|
||||
}
|
||||
|
||||
setup_bus:
|
||||
if (!IS_ERR(ourhost->pctrl))
|
||||
return 0;
|
||||
|
||||
/* get the gpios for command, clock and data lines */
|
||||
for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) {
|
||||
gpio = of_get_gpio(node, cnt);
|
||||
if (!gpio_is_valid(gpio)) {
|
||||
dev_err(dev, "invalid gpio[%d]\n", cnt);
|
||||
return -EINVAL;
|
||||
}
|
||||
ourhost->gpios[cnt] = gpio;
|
||||
}
|
||||
|
||||
for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) {
|
||||
ret = devm_gpio_request(dev, ourhost->gpios[cnt], "sdhci-gpio");
|
||||
if (ret) {
|
||||
dev_err(dev, "gpio[%d] request failed\n", cnt);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
@ -588,8 +535,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
||||
goto err_pdata_io_clk;
|
||||
}
|
||||
|
||||
sc->pctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata);
|
||||
if (ret)
|
||||
@ -607,7 +552,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
sc->clk_io = clk_get(dev, "hsmmc");
|
||||
sc->clk_io = devm_clk_get(dev, "hsmmc");
|
||||
if (IS_ERR(sc->clk_io)) {
|
||||
dev_err(dev, "failed to get io clock\n");
|
||||
ret = PTR_ERR(sc->clk_io);
|
||||
@ -622,7 +567,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
||||
char name[14];
|
||||
|
||||
snprintf(name, 14, "mmc_busclk.%d", ptr);
|
||||
clk = clk_get(dev, name);
|
||||
clk = devm_clk_get(dev, name);
|
||||
if (IS_ERR(clk))
|
||||
continue;
|
||||
|
||||
@ -763,15 +708,9 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
||||
#ifndef CONFIG_PM_RUNTIME
|
||||
clk_disable_unprepare(sc->clk_bus[sc->cur_clk]);
|
||||
#endif
|
||||
for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
|
||||
if (sc->clk_bus[ptr]) {
|
||||
clk_put(sc->clk_bus[ptr]);
|
||||
}
|
||||
}
|
||||
|
||||
err_no_busclks:
|
||||
clk_disable_unprepare(sc->clk_io);
|
||||
clk_put(sc->clk_io);
|
||||
|
||||
err_pdata_io_clk:
|
||||
sdhci_free_host(host);
|
||||
@ -784,7 +723,6 @@ static int sdhci_s3c_remove(struct platform_device *pdev)
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_s3c *sc = sdhci_priv(host);
|
||||
struct s3c_sdhci_platdata *pdata = sc->pdata;
|
||||
int ptr;
|
||||
|
||||
if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup)
|
||||
pdata->ext_cd_cleanup(&sdhci_s3c_notify_change);
|
||||
@ -804,13 +742,7 @@ static int sdhci_s3c_remove(struct platform_device *pdev)
|
||||
#ifndef CONFIG_PM_RUNTIME
|
||||
clk_disable_unprepare(sc->clk_bus[sc->cur_clk]);
|
||||
#endif
|
||||
for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
|
||||
if (sc->clk_bus[ptr]) {
|
||||
clk_put(sc->clk_bus[ptr]);
|
||||
}
|
||||
}
|
||||
clk_disable_unprepare(sc->clk_io);
|
||||
clk_put(sc->clk_io);
|
||||
|
||||
sdhci_free_host(host);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
193
drivers/mmc/host/sdhci-sirf.c
Normal file
193
drivers/mmc/host/sdhci-sirf.c
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* SDHCI support for SiRF primaII and marco SoCs
|
||||
*
|
||||
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
|
||||
*
|
||||
* Licensed under GPLv2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
struct sdhci_sirf_priv {
|
||||
struct clk *clk;
|
||||
int gpio_cd;
|
||||
};
|
||||
|
||||
static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_sirf_priv *priv = pltfm_host->priv;
|
||||
return clk_get_rate(priv->clk);
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_sirf_ops = {
|
||||
.get_max_clock = sdhci_sirf_get_max_clk,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_sirf_pdata = {
|
||||
.ops = &sdhci_sirf_ops,
|
||||
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
|
||||
SDHCI_QUIRK_DELAY_AFTER_POWER,
|
||||
};
|
||||
|
||||
static int sdhci_sirf_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_sirf_priv *priv;
|
||||
struct pinctrl *pinctrl;
|
||||
int ret;
|
||||
|
||||
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
||||
if (IS_ERR(pinctrl)) {
|
||||
dev_err(&pdev->dev, "unable to get pinmux");
|
||||
return PTR_ERR(pinctrl);
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_sirf_priv),
|
||||
GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev, "unable to allocate private data");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->clk)) {
|
||||
dev_err(&pdev->dev, "unable to get clock");
|
||||
return PTR_ERR(priv->clk);
|
||||
}
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node,
|
||||
"cd-gpios", 0);
|
||||
} else {
|
||||
priv->gpio_cd = -EINVAL;
|
||||
}
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata);
|
||||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
goto err_sdhci_pltfm_init;
|
||||
}
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = priv;
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
clk_prepare_enable(priv->clk);
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto err_sdhci_add;
|
||||
|
||||
/*
|
||||
* We must request the IRQ after sdhci_add_host(), as the tasklet only
|
||||
* gets setup in sdhci_add_host() and we oops.
|
||||
*/
|
||||
if (gpio_is_valid(priv->gpio_cd)) {
|
||||
ret = mmc_gpio_request_cd(host->mmc, priv->gpio_cd);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "card detect irq request failed: %d\n",
|
||||
ret);
|
||||
goto err_request_cd;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_request_cd:
|
||||
sdhci_remove_host(host, 0);
|
||||
err_sdhci_add:
|
||||
clk_disable_unprepare(priv->clk);
|
||||
sdhci_pltfm_free(pdev);
|
||||
err_sdhci_pltfm_init:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_sirf_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_sirf_priv *priv = pltfm_host->priv;
|
||||
|
||||
sdhci_pltfm_unregister(pdev);
|
||||
|
||||
if (gpio_is_valid(priv->gpio_cd))
|
||||
mmc_gpio_free_cd(host->mmc);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int sdhci_sirf_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_sirf_priv *priv = pltfm_host->priv;
|
||||
int ret;
|
||||
|
||||
ret = sdhci_suspend_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_sirf_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_sirf_priv *priv = pltfm_host->priv;
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(priv->clk);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "Resume: Error enabling clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sdhci_resume_host(host);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(sdhci_sirf_pm_ops, sdhci_sirf_suspend, sdhci_sirf_resume);
|
||||
#endif
|
||||
|
||||
static const struct of_device_id sdhci_sirf_of_match[] = {
|
||||
{ .compatible = "sirf,prima2-sdhc" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_sirf_of_match);
|
||||
|
||||
static struct platform_driver sdhci_sirf_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-sirf",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = sdhci_sirf_of_match,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.pm = &sdhci_sirf_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.probe = sdhci_sirf_probe,
|
||||
.remove = sdhci_sirf_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_sirf_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SDHCI driver for SiRFprimaII/SiRFmarco");
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -36,7 +36,7 @@ struct spear_sdhci {
|
||||
};
|
||||
|
||||
/* sdhci ops */
|
||||
static struct sdhci_ops sdhci_pltfm_ops = {
|
||||
static const struct sdhci_ops sdhci_pltfm_ops = {
|
||||
/* Nothing to do for now. */
|
||||
};
|
||||
|
||||
@ -291,7 +291,7 @@ static int sdhci_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int sdhci_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
|
||||
#include <asm/gpio.h>
|
||||
|
||||
@ -38,16 +39,13 @@
|
||||
#define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2)
|
||||
|
||||
struct sdhci_tegra_soc_data {
|
||||
struct sdhci_pltfm_data *pdata;
|
||||
const struct sdhci_pltfm_data *pdata;
|
||||
u32 nvquirks;
|
||||
};
|
||||
|
||||
struct sdhci_tegra {
|
||||
const struct sdhci_tegra_soc_data *soc_data;
|
||||
int cd_gpio;
|
||||
int wp_gpio;
|
||||
int power_gpio;
|
||||
int is_8bit;
|
||||
};
|
||||
|
||||
static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
|
||||
@ -107,23 +105,9 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
|
||||
|
||||
static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = pltfm_host->priv;
|
||||
|
||||
if (!gpio_is_valid(tegra_host->wp_gpio))
|
||||
return -1;
|
||||
|
||||
return gpio_get_value(tegra_host->wp_gpio);
|
||||
return mmc_gpio_get_ro(host->mmc);
|
||||
}
|
||||
|
||||
static irqreturn_t carddetect_irq(int irq, void *data)
|
||||
{
|
||||
struct sdhci_host *sdhost = (struct sdhci_host *)data;
|
||||
|
||||
tasklet_schedule(&sdhost->card_tasklet);
|
||||
return IRQ_HANDLED;
|
||||
};
|
||||
|
||||
static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
@ -145,12 +129,11 @@ static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask)
|
||||
|
||||
static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = pltfm_host->priv;
|
||||
u32 ctrl;
|
||||
|
||||
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
||||
if (tegra_host->is_8bit && bus_width == MMC_BUS_WIDTH_8) {
|
||||
if ((host->mmc->caps & MMC_CAP_8_BIT_DATA) &&
|
||||
(bus_width == MMC_BUS_WIDTH_8)) {
|
||||
ctrl &= ~SDHCI_CTRL_4BITBUS;
|
||||
ctrl |= SDHCI_CTRL_8BITBUS;
|
||||
} else {
|
||||
@ -164,7 +147,7 @@ static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sdhci_ops tegra_sdhci_ops = {
|
||||
static const struct sdhci_ops tegra_sdhci_ops = {
|
||||
.get_ro = tegra_sdhci_get_ro,
|
||||
.read_l = tegra_sdhci_readl,
|
||||
.read_w = tegra_sdhci_readw,
|
||||
@ -173,8 +156,7 @@ static struct sdhci_ops tegra_sdhci_ops = {
|
||||
.platform_reset_exit = tegra_sdhci_reset_exit,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
||||
static struct sdhci_pltfm_data sdhci_tegra20_pdata = {
|
||||
static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
|
||||
SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
||||
SDHCI_QUIRK_NO_HISPD_BIT |
|
||||
@ -187,10 +169,8 @@ static struct sdhci_tegra_soc_data soc_data_tegra20 = {
|
||||
.nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
|
||||
NVQUIRK_ENABLE_BLOCK_GAP_DET,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
|
||||
static struct sdhci_pltfm_data sdhci_tegra30_pdata = {
|
||||
static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
||||
@ -203,32 +183,37 @@ static struct sdhci_tegra_soc_data soc_data_tegra30 = {
|
||||
.pdata = &sdhci_tegra30_pdata,
|
||||
.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
||||
SDHCI_QUIRK_NO_HISPD_BIT |
|
||||
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
|
||||
.ops = &tegra_sdhci_ops,
|
||||
};
|
||||
|
||||
static struct sdhci_tegra_soc_data soc_data_tegra114 = {
|
||||
.pdata = &sdhci_tegra114_pdata,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_tegra_dt_match[] = {
|
||||
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
|
||||
{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
|
||||
{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
||||
{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
|
||||
#endif
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_dt_ids);
|
||||
MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
|
||||
|
||||
static void sdhci_tegra_parse_dt(struct device *dev,
|
||||
struct sdhci_tegra *tegra_host)
|
||||
static void sdhci_tegra_parse_dt(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 bus_width;
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = pltfm_host->priv;
|
||||
|
||||
tegra_host->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
|
||||
tegra_host->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
|
||||
tegra_host->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
|
||||
|
||||
if (of_property_read_u32(np, "bus-width", &bus_width) == 0 &&
|
||||
bus_width == 8)
|
||||
tegra_host->is_8bit = 1;
|
||||
mmc_of_parse(host->mmc);
|
||||
}
|
||||
|
||||
static int sdhci_tegra_probe(struct platform_device *pdev)
|
||||
@ -260,7 +245,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
|
||||
tegra_host->soc_data = soc_data;
|
||||
pltfm_host->priv = tegra_host;
|
||||
|
||||
sdhci_tegra_parse_dt(&pdev->dev, tegra_host);
|
||||
sdhci_tegra_parse_dt(&pdev->dev);
|
||||
|
||||
if (gpio_is_valid(tegra_host->power_gpio)) {
|
||||
rc = gpio_request(tegra_host->power_gpio, "sdhci_power");
|
||||
@ -272,37 +257,6 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
|
||||
gpio_direction_output(tegra_host->power_gpio, 1);
|
||||
}
|
||||
|
||||
if (gpio_is_valid(tegra_host->cd_gpio)) {
|
||||
rc = gpio_request(tegra_host->cd_gpio, "sdhci_cd");
|
||||
if (rc) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"failed to allocate cd gpio\n");
|
||||
goto err_cd_req;
|
||||
}
|
||||
gpio_direction_input(tegra_host->cd_gpio);
|
||||
|
||||
rc = request_irq(gpio_to_irq(tegra_host->cd_gpio),
|
||||
carddetect_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
||||
mmc_hostname(host->mmc), host);
|
||||
|
||||
if (rc) {
|
||||
dev_err(mmc_dev(host->mmc), "request irq error\n");
|
||||
goto err_cd_irq_req;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (gpio_is_valid(tegra_host->wp_gpio)) {
|
||||
rc = gpio_request(tegra_host->wp_gpio, "sdhci_wp");
|
||||
if (rc) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"failed to allocate wp gpio\n");
|
||||
goto err_wp_req;
|
||||
}
|
||||
gpio_direction_input(tegra_host->wp_gpio);
|
||||
}
|
||||
|
||||
clk = clk_get(mmc_dev(host->mmc), NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(mmc_dev(host->mmc), "clk err\n");
|
||||
@ -312,9 +266,6 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
|
||||
clk_prepare_enable(clk);
|
||||
pltfm_host->clk = clk;
|
||||
|
||||
if (tegra_host->is_8bit)
|
||||
host->mmc->caps |= MMC_CAP_8_BIT_DATA;
|
||||
|
||||
rc = sdhci_add_host(host);
|
||||
if (rc)
|
||||
goto err_add_host;
|
||||
@ -325,15 +276,6 @@ err_add_host:
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
clk_put(pltfm_host->clk);
|
||||
err_clk_get:
|
||||
if (gpio_is_valid(tegra_host->wp_gpio))
|
||||
gpio_free(tegra_host->wp_gpio);
|
||||
err_wp_req:
|
||||
if (gpio_is_valid(tegra_host->cd_gpio))
|
||||
free_irq(gpio_to_irq(tegra_host->cd_gpio), host);
|
||||
err_cd_irq_req:
|
||||
if (gpio_is_valid(tegra_host->cd_gpio))
|
||||
gpio_free(tegra_host->cd_gpio);
|
||||
err_cd_req:
|
||||
if (gpio_is_valid(tegra_host->power_gpio))
|
||||
gpio_free(tegra_host->power_gpio);
|
||||
err_power_req:
|
||||
@ -351,14 +293,6 @@ static int sdhci_tegra_remove(struct platform_device *pdev)
|
||||
|
||||
sdhci_remove_host(host, dead);
|
||||
|
||||
if (gpio_is_valid(tegra_host->wp_gpio))
|
||||
gpio_free(tegra_host->wp_gpio);
|
||||
|
||||
if (gpio_is_valid(tegra_host->cd_gpio)) {
|
||||
free_irq(gpio_to_irq(tegra_host->cd_gpio), host);
|
||||
gpio_free(tegra_host->cd_gpio);
|
||||
}
|
||||
|
||||
if (gpio_is_valid(tegra_host->power_gpio))
|
||||
gpio_free(tegra_host->power_gpio);
|
||||
|
||||
|
@ -1581,6 +1581,37 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
sdhci_runtime_pm_put(host);
|
||||
}
|
||||
|
||||
static int sdhci_do_get_cd(struct sdhci_host *host)
|
||||
{
|
||||
int gpio_cd = mmc_gpio_get_cd(host->mmc);
|
||||
|
||||
if (host->flags & SDHCI_DEVICE_DEAD)
|
||||
return 0;
|
||||
|
||||
/* If polling/nonremovable, assume that the card is always present. */
|
||||
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||
|
||||
(host->mmc->caps & MMC_CAP_NONREMOVABLE))
|
||||
return 1;
|
||||
|
||||
/* Try slot gpio detect */
|
||||
if (!IS_ERR_VALUE(gpio_cd))
|
||||
return !!gpio_cd;
|
||||
|
||||
/* Host native card detect */
|
||||
return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
|
||||
}
|
||||
|
||||
static int sdhci_get_cd(struct mmc_host *mmc)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
int ret;
|
||||
|
||||
sdhci_runtime_pm_get(host);
|
||||
ret = sdhci_do_get_cd(host);
|
||||
sdhci_runtime_pm_put(host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_check_ro(struct sdhci_host *host)
|
||||
{
|
||||
unsigned long flags;
|
||||
@ -2038,6 +2069,7 @@ static void sdhci_card_event(struct mmc_host *mmc)
|
||||
static const struct mmc_host_ops sdhci_ops = {
|
||||
.request = sdhci_request,
|
||||
.set_ios = sdhci_set_ios,
|
||||
.get_cd = sdhci_get_cd,
|
||||
.get_ro = sdhci_get_ro,
|
||||
.hw_reset = sdhci_hw_reset,
|
||||
.enable_sdio_irq = sdhci_enable_sdio_irq,
|
||||
@ -2907,12 +2939,17 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||
host->vqmmc = NULL;
|
||||
}
|
||||
} else {
|
||||
regulator_enable(host->vqmmc);
|
||||
ret = regulator_enable(host->vqmmc);
|
||||
if (!regulator_is_supported_voltage(host->vqmmc, 1700000,
|
||||
1950000))
|
||||
caps[1] &= ~(SDHCI_SUPPORT_SDR104 |
|
||||
SDHCI_SUPPORT_SDR50 |
|
||||
SDHCI_SUPPORT_DDR50);
|
||||
if (ret) {
|
||||
pr_warn("%s: Failed to enable vqmmc regulator: %d\n",
|
||||
mmc_hostname(mmc), ret);
|
||||
host->vqmmc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V)
|
||||
|
@ -348,13 +348,11 @@ static void wmt_complete_data_request(struct wmt_mci_priv *priv)
|
||||
|
||||
static irqreturn_t wmt_mci_dma_isr(int irq_num, void *data)
|
||||
{
|
||||
struct mmc_host *mmc;
|
||||
struct wmt_mci_priv *priv;
|
||||
|
||||
int status;
|
||||
|
||||
priv = (struct wmt_mci_priv *)data;
|
||||
mmc = priv->mmc;
|
||||
|
||||
status = readl(priv->sdmmc_base + SDDMA_CCR) & 0x0F;
|
||||
|
||||
@ -925,7 +923,7 @@ static int wmt_mci_remove(struct platform_device *pdev)
|
||||
clk_put(priv->clk_sdmmc);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(res->start, res->end - res->start + 1);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
mmc_free_host(mmc);
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
struct mmc_cid {
|
||||
unsigned int manfid;
|
||||
char prod_name[8];
|
||||
unsigned char prv;
|
||||
unsigned int serial;
|
||||
unsigned short oemid;
|
||||
unsigned short year;
|
||||
|
@ -280,6 +280,7 @@ struct mmc_host {
|
||||
#define MMC_CAP2_PACKED_WR (1 << 13) /* Allow packed write */
|
||||
#define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \
|
||||
MMC_CAP2_PACKED_WR)
|
||||
#define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */
|
||||
|
||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||
|
||||
@ -361,6 +362,8 @@ struct mmc_host {
|
||||
|
||||
unsigned int actual_clock; /* Actual HC clock rate */
|
||||
|
||||
unsigned int slotno; /* used for sdio acpi binding */
|
||||
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user