MMC core:

- Forward compatibility for eMMC.
  - Fix some blacklisted cards with broken secure discard.
 
 MMC host:
  - mmci: Add support for Qualcomm variant.
  - mmci: Fix regression for arm_variant.
  - sdhci: Various fixes and cleanups.
  - sdhci: Improve external VDD regulator support.
  - sdhci: Support for DDR50 1.8V mode for BayTrail.
  - sdhci-st: Add driver for ST SDHCI controller.
  - sh-mmcif: DMA fixes.
  - omap_hsmmc: Add support for SDIO interrupts.
  - sdhci-pci: Add support for Intel Quark X1000.
  - dw_mmc: Update the reset sequence.
  - s3cmci: port DMA code to dmaengine API.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJT6K/QAAoJEP4mhCVzWIwpj5UP/3KWAsUCz5PlPaTxWgh/U+aN
 lwO8aryFGjxfengCtJaw4cVksg3Lc5EvaSweg7BUqTucHoq/epdAU+Doqf5OVZ9Y
 kicfx+WOAwIGTTuY9Wu7W884WgF+litZW4O4LtQnr6hHzx+xQhPabTc6zKJVS7WD
 /Fx1Qcc0WFdGA3D0VdpCqGa7Y8IYk8cEP16MOA4s0HxZxONkjFbPdpQRF/zywGOA
 tqJC7kAhlY+Rx5bsrr5Lvbhu2Ut+cfLBPdPYuJz50wnymQ15IrLj5n4rSd5GLY+f
 HjMeCJzDHi1gk26wn3kYyWwz3JkJGWhtf6fC1hDqvkWEmHd872v7EuU9V4D3ehV1
 xPVVVyUYqMgs0Vi+PXAlMhoajrwgnlM1Ox0sheOOefcaRoFVpV6PZOuAsAOfKRfC
 ySOuEhOKn5OOvjH2TB4dNH1a3hegR9B3fJqjFe4UmUQP4FBicOfUKi+n6HwC+Omb
 VDCuVvwmMCVPi/Lop6thpPepJBfR8Ncb3vanLpK64LcIjEfoPTwoAyNI32Qnry52
 XMO4Tppl+bPzglGpV427KXlFbBwccp+8sCMRc/4lRiIMH9QhbZfVA1FcxwpvZ419
 6SKXmqhA62nHLcWlQrRlvdnNyr+Ow2o5gJ0iVffByfmzcgPmja9YZYyDfx+DxWOo
 W1oItq+qMd25rnnOPTLB
 =kGSY
 -----END PGP SIGNATURE-----

Merge tag 'mmc-v3.17-1' of git://git.linaro.org/people/ulf.hansson/mmc

Pull MMC updates from Ulf Hansson:
 "Me and Chris Ball decided to try out using my MMC tree as the primary
  one, to simplify handling of patches.

  This pull does thus contains all the MMC patches for 3.17 rc1, no pull
  from Chris this time.

  Details:

  MMC core:
   - forward compatibility for eMMC
   - fix some blacklisted cards with broken secure discard

  MMC host:
   - mmci: Add support for Qualcomm variant
   - mmci: Fix regression for arm_variant
   - sdhci: Various fixes and cleanups
   - sdhci: Improve external VDD regulator support
   - sdhci: Support for DDR50 1.8V mode for BayTrail
   - sdhci-st: Add driver for ST SDHCI controller
   - sh-mmcif: DMA fixes
   - omap_hsmmc: Add support for SDIO interrupts
   - sdhci-pci: Add support for Intel Quark X1000
   - dw_mmc: Update the reset sequence
   - s3cmci: port DMA code to dmaengine API"

* tag 'mmc-v3.17-1' of git://git.linaro.org/people/ulf.hansson/mmc: (67 commits)
  mmc: dw_mmc: modify the dt-binding for removing slot-node and supports-highspeed
  mmc: dw_mmc: Slot quirk "disable-wp" is deprecated.
  mmc: mmci: Reverse IRQ handling for the arm_variant
  mmc: mmci: Move all CMD irq handling to mmci_cmd_irq()
  mmc: mmci: Remove redundant check of status for DATA irq
  mmc: dw_mmc: change to use recommended reset procedure
  mmc: sdhci-pxav3: Use devm_* managed helpers
  mmc: tmio: Configure DMA slave bus width
  mmc: sh_mmcif: Configure DMA slave bus width
  mmc: sh_mmcif: Fix DMA slave address configuration
  mmc: sh_mmcif: Document DT bindings
  mmc: sdhci-pci: remove PCI PM functions in suspend/resume callback
  mmc: Do not advertise secure discard if it is blacklisted
  mmc: sdhci-msm: Get COMPILE_TEST support
  mmc: sdhci-msm: Remove unnecessary header file inclusion
  mmc: sdhci-msm: Fix the binding example
  mmc: sdhci: add DDR50 1.8V mode support for BayTrail eMMC Controller
  mmc: sdhci: Preset value not supported in Baytrail eMMC
  mmc: MMC_USDHI6ROL0 should depend on HAS_DMA
  mmc: MMC_SH_MMCIF should depend on HAS_DMA
  ...
This commit is contained in:
Linus Torvalds 2014-08-14 09:15:53 -06:00
commit a8e4def604
40 changed files with 1050 additions and 471 deletions

View File

@ -46,13 +46,14 @@ Required Properties:
- if CIU clock divider value is 0 (that is divide by 1), both tx and rx
phase shift clocks should be 0.
Required properties for a slot:
Required properties for a slot (Deprecated - Recommend to use one slot per host):
* gpios: specifies a list of gpios used for command, clock and data bus. The
first gpio is the command line and the second gpio is the clock line. The
rest of the gpios (depending on the bus-width property) are the data lines in
no particular order. The format of the gpio specifier depends on the gpio
controller.
(Deprecated - Refer to Documentation/devicetree/binding/pinctrl/samsung-pinctrl.txt)
Example:
@ -69,21 +70,13 @@ Example:
dwmmc0@12200000 {
num-slots = <1>;
supports-highspeed;
cap-mmc-highspeed;
cap-sd-highspeed;
broken-cd;
fifo-depth = <0x80>;
card-detect-delay = <200>;
samsung,dw-mshc-ciu-div = <3>;
samsung,dw-mshc-sdr-timing = <2 3>;
samsung,dw-mshc-ddr-timing = <1 2>;
slot@0 {
reg = <0>;
bus-width = <8>;
gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>,
<&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>,
<&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>,
<&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>,
<&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>;
};
bus-width = <8>;
};

View File

@ -34,13 +34,11 @@ Example:
num-slots = <1>;
vmmc-supply = <&ldo12>;
fifo-depth = <0x100>;
supports-highspeed;
pinctrl-names = "default";
pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>;
slot@0 {
reg = <0>;
bus-width = <4>;
disable-wp;
cd-gpios = <&gpio10 3 0>;
};
bus-width = <4>;
disable-wp;
cd-gpios = <&gpio10 3 0>;
cap-mmc-highspeed;
cap-sd-highspeed;
};

View File

@ -34,8 +34,8 @@ Optional properties:
- cap-power-off-card: powering off the card is safe
- cap-sdio-irq: enable SDIO IRQ signalling on this interface
- full-pwr-cycle: full power cycle of the card is supported
- mmc-highspeed-ddr-1_8v: eMMC high-speed DDR mode(1.8V I/O) is supported
- mmc-highspeed-ddr-1_2v: eMMC high-speed DDR mode(1.2V I/O) is supported
- mmc-ddr-1_8v: eMMC high-speed DDR mode(1.8V I/O) is supported
- mmc-ddr-1_2v: eMMC high-speed DDR mode(1.2V I/O) is supported
- mmc-hs200-1_8v: eMMC HS200 mode(1.8V I/O) is supported
- mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported
- mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported

View File

@ -0,0 +1,32 @@
* Renesas Multi Media Card Interface (MMCIF) Controller
This file documents differences between the core properties in mmc.txt
and the properties used by the MMCIF device.
Required properties:
- compatible: must contain one of the following
- "renesas,mmcif-r8a7740" for the MMCIF found in r8a7740 SoCs
- "renesas,mmcif-r8a7790" for the MMCIF found in r8a7790 SoCs
- "renesas,mmcif-r8a7791" for the MMCIF found in r8a7791 SoCs
- "renesas,sh-mmcif" for the generic MMCIF
- clocks: reference to the functional clock
- dmas: reference to the DMA channels, one per channel name listed in the
dma-names property.
- dma-names: must contain "tx" for the transmit DMA channel and "rx" for the
receive DMA channel.
Example: R8A7790 (R-Car H2) MMCIF0
mmcif0: mmc@ee200000 {
compatible = "renesas,mmcif-r8a7790", "renesas,sh-mmcif";
reg = <0 0xee200000 0 0x80>;
interrupts = <0 169 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp3_clks R8A7790_CLK_MMCIF0>;
dmas = <&dmac0 0xd1>, <&dmac0 0xd2>;
dma-names = "tx", "rx";
};

View File

@ -27,8 +27,8 @@ Example:
bus-width = <8>;
non-removable;
vmmc = <&pm8941_l20>;
vqmmc = <&pm8941_s3>;
vmmc-supply = <&pm8941_l20>;
vqmmc-supply = <&pm8941_s3>;
pinctrl-names = "default";
pinctrl-0 = <&sdc1_clk &sdc1_cmd &sdc1_data>;
@ -44,8 +44,8 @@ Example:
bus-width = <4>;
cd-gpios = <&msmgpio 62 0x1>;
vmmc = <&pm8941_l21>;
vqmmc = <&pm8941_l13>;
vmmc-supply = <&pm8941_l21>;
vqmmc-supply = <&pm8941_l13>;
pinctrl-names = "default";
pinctrl-0 = <&sdc2_clk &sdc2_cmd &sdc2_data>;

View File

@ -0,0 +1,33 @@
* STMicroelectronics sdhci-st MMC/SD controller
This file documents the differences between the core properties in
Documentation/devicetree/bindings/mmc/mmc.txt and the properties
used by the sdhci-st driver.
Required properties:
- compatible : Must be "st,sdhci"
- clock-names : Should be "mmc"
See: Documentation/devicetree/bindings/resource-names.txt
- clocks : Phandle of the clock used by the sdhci controler
See: Documentation/devicetree/bindings/clock/clock-bindings.txt
Optional properties:
- non-removable: non-removable slot
See: Documentation/devicetree/bindings/mmc/mmc.txt
- bus-width: Number of data lines
See: Documentation/devicetree/bindings/mmc/mmc.txt
Example:
mmc0: sdhci@fe81e000 {
compatible = "st,sdhci";
status = "disabled";
reg = <0xfe81e000 0x1000>;
interrupts = <GIC_SPI 127 IRQ_TYPE_NONE>;
interrupt-names = "mmcirq";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_mmc0>;
clock-names = "mmc";
clocks = <&clk_s_a1_ls 1>;
bus-width = <8>
};

View File

@ -67,7 +67,8 @@ Optional properties:
* card-detect-delay: Delay in milli-seconds before detecting card after card
insert event. The default value is 0.
* supports-highspeed: Enables support for high speed cards (up to 50MHz)
* supports-highspeed (DEPRECATED): Enables support for high speed cards (up to 50MHz)
(use "cap-mmc-highspeed" or "cap-sd-highspeed" instead)
* broken-cd: as documented in mmc core bindings.
@ -98,14 +99,11 @@ board specific portions as listed below.
clock-frequency = <400000000>;
clock-freq-min-max = <400000 200000000>;
num-slots = <1>;
supports-highspeed;
broken-cd;
fifo-depth = <0x80>;
card-detect-delay = <200>;
vmmc-supply = <&buck8>;
slot@0 {
reg = <0>;
bus-width = <8>;
};
bus-width = <8>;
cap-mmc-highspeed;
cap-sd-highspeed;
};

View File

@ -12,6 +12,7 @@ Required properties:
Should be "ti,omap3-hsmmc", for OMAP3 controllers
Should be "ti,omap3-pre-es3-hsmmc" for OMAP3 controllers pre ES3.0
Should be "ti,omap4-hsmmc", for OMAP4 controllers
Should be "ti,am33xx-hsmmc", for AM335x controllers
- ti,hwmods: Must be "mmc<n>", n is controller instance starting 1
Optional properties:
@ -56,3 +57,56 @@ Examples:
&edma 25>;
dma-names = "tx", "rx";
};
[workaround for missing swakeup on am33xx]
This SOC is missing the swakeup line, it will not detect SDIO irq
while in suspend.
------
| PRCM |
------
^ |
swakeup | | fclk
| v
------ ------- -----
| card | -- CIRQ --> | hsmmc | -- IRQ --> | CPU |
------ ------- -----
In suspend the fclk is off and the module is disfunctional. Even register reads
will fail. A small logic in the host will request fclk restore, when an
external event is detected. Once the clock is restored, the host detects the
event normally. Since am33xx doesn't have this line it never wakes from
suspend.
The workaround is to reconfigure the dat1 line as a GPIO upon suspend. To make
this work, we need to set the named pinctrl states "default" and "idle".
Prepare idle to remux dat1 as a gpio, and default to remux it back as sdio
dat1. The MMC driver will then toggle between idle and default state during
runtime.
In summary:
1. select matching 'compatible' section, see example below.
2. specify pinctrl states "default" and "idle", "sleep" is optional.
3. specify the gpio irq used for detecting sdio irq in suspend
If configuration is incomplete, a warning message is emitted "falling back to
polling". Also check the "sdio irq mode" in /sys/kernel/debug/mmc0/regs. Mind
not every application needs SDIO irq, e.g. MMC cards.
mmc1: mmc@48060100 {
compatible = "ti,am33xx-hsmmc";
...
pinctrl-names = "default", "idle", "sleep"
pinctrl-0 = <&mmc1_pins>;
pinctrl-1 = <&mmc1_idle>;
pinctrl-2 = <&mmc1_sleep>;
...
interrupts-extended = <&intc 64 &gpio2 28 0>;
};
mmc1_idle : pinmux_cirq_pin {
pinctrl-single,pins = <
0x0f8 0x3f /* GPIO2_28 */
>;
};

View File

@ -18,6 +18,7 @@ Required properties:
"renesas,sdhi-r8a7778" - SDHI IP on R8A7778 SoC
"renesas,sdhi-r8a7779" - SDHI IP on R8A7779 SoC
"renesas,sdhi-r8a7790" - SDHI IP on R8A7790 SoC
"renesas,sdhi-r8a7791" - SDHI IP on R8A7791 SoC
Optional properties:
- toshiba,mmc-wrprotect-disable: write-protect detection is unavailable

View File

@ -2028,8 +2028,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
/* complete ongoing async transfer before issuing discard */
if (card->host->areq)
mmc_blk_issue_rw_rq(mq, NULL);
if (req->cmd_flags & REQ_SECURE &&
!(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN))
if (req->cmd_flags & REQ_SECURE)
ret = mmc_blk_issue_secdiscard_rq(mq, req);
else
ret = mmc_blk_issue_discard_rq(mq, req);
@ -2432,6 +2431,8 @@ static int mmc_blk_probe(struct mmc_card *card)
if (!(card->csd.cmdclass & CCC_BLOCK_READ))
return -ENODEV;
mmc_fixup_device(card, blk_fixups);
md = mmc_blk_alloc(card);
if (IS_ERR(md))
return PTR_ERR(md);
@ -2446,7 +2447,6 @@ static int mmc_blk_probe(struct mmc_card *card)
goto out;
mmc_set_drvdata(card, md);
mmc_fixup_device(card, blk_fixups);
if (mmc_add_disk(md))
goto out;

View File

@ -180,7 +180,6 @@ static int mmc_bus_resume(struct device *dev)
#endif
#ifdef CONFIG_PM_RUNTIME
static int mmc_runtime_suspend(struct device *dev)
{
struct mmc_card *card = mmc_dev_to_card(dev);
@ -196,17 +195,10 @@ static int mmc_runtime_resume(struct device *dev)
return host->bus_ops->runtime_resume(host);
}
static int mmc_runtime_idle(struct device *dev)
{
return 0;
}
#endif /* !CONFIG_PM_RUNTIME */
static const struct dev_pm_ops mmc_bus_pm_ops = {
SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume,
mmc_runtime_idle)
SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_suspend, mmc_bus_resume)
};

View File

@ -2102,7 +2102,8 @@ EXPORT_SYMBOL(mmc_can_sanitize);
int mmc_can_secure_erase_trim(struct mmc_card *card)
{
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN)
if ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN) &&
!(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN))
return 1;
return 0;
}

View File

@ -324,13 +324,12 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
}
}
/*
* The EXT_CSD format is meant to be forward compatible. As long
* as CSD_STRUCTURE does not change, all values for EXT_CSD_REV
* are authorized, see JEDEC JESD84-B50 section B.8.
*/
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
if (card->ext_csd.rev > 7) {
pr_err("%s: unrecognised EXT_CSD revision %d\n",
mmc_hostname(card->host), card->ext_csd.rev);
err = -EINVAL;
goto out;
}
card->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0];
card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1];

View File

@ -91,7 +91,7 @@ void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table)
(f->cis_device == card->cis.device ||
f->cis_device == (u16) SDIO_ANY_ID) &&
rev >= f->rev_start && rev <= f->rev_end) {
dev_dbg(&card->dev, "calling %pF\n", f->vendor_fixup);
dev_dbg(&card->dev, "calling %pf\n", f->vendor_fixup);
f->vendor_fixup(card, f->data);
}
}

View File

@ -184,6 +184,9 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
mmc_delay(10);
}
if (!i)
pr_err("%s: card never left busy state\n", mmc_hostname(host));
if (rocr && !mmc_host_is_spi(host))
*rocr = cmd.resp[0];

View File

@ -290,6 +290,18 @@ config MMC_MOXART
be found on some embedded hardware such as UC-7112-LX.
If you have a controller with this interface, say Y here.
config MMC_SDHCI_ST
tristate "SDHCI support on STMicroelectronics SoC"
depends on ARCH_STI
depends on MMC_SDHCI_PLTFM
select MMC_SDHCI_IO_ACCESSORS
help
This selects the Secure Digital Host Controller Interface in
STMicroelectronics SoCs.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_OMAP
tristate "TI OMAP Multimedia Card Interface support"
depends on ARCH_OMAP
@ -303,6 +315,7 @@ config MMC_OMAP
config MMC_OMAP_HS
tristate "TI OMAP High Speed Multimedia Card Interface support"
depends on HAS_DMA
depends on ARCH_OMAP2PLUS || COMPILE_TEST
help
This selects the TI OMAP High Speed Multimedia card Interface.
@ -343,7 +356,7 @@ config MMC_ATMELMCI
config MMC_SDHCI_MSM
tristate "Qualcomm SDHCI Controller Support"
depends on ARCH_QCOM
depends on ARCH_QCOM || (ARM && COMPILE_TEST)
depends on MMC_SDHCI_PLTFM
help
This selects the Secure Digital Host Controller Interface (SDHCI)
@ -440,6 +453,7 @@ config MMC_SPI
config MMC_S3C
tristate "Samsung S3C SD/MMC Card Interface support"
depends on ARCH_S3C24XX
depends on S3C24XX_DMAC
help
This selects a driver for the MCI interface found in
Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
@ -477,15 +491,6 @@ config MMC_S3C_DMA
working properly and needs to be debugged before this
option is useful.
config MMC_S3C_PIODMA
bool "Support for both PIO and DMA"
help
Compile both the PIO and DMA transfer routines into the
driver and let the platform select at run-time which one
is best.
See notes for the DMA option.
endchoice
config MMC_SDRICOH_CS
@ -623,7 +628,7 @@ config MMC_DW_PCI
config MMC_SH_MMCIF
tristate "SuperH Internal MMCIF support"
depends on MMC_BLOCK
depends on MMC_BLOCK && HAS_DMA
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
help
This selects the MMC Host Interface controller (MMCIF).
@ -697,6 +702,7 @@ config MMC_WMT
config MMC_USDHI6ROL0
tristate "Renesas USDHI6ROL0 SD/SDIO Host Controller support"
depends on HAS_DMA
help
This selects support for the Renesas USDHI6ROL0 SD/SDIO
Host Controller

View File

@ -68,6 +68,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
ifeq ($(CONFIG_CB710_DEBUG),y)
CFLAGS-cb710-mmc += -DDEBUG

View File

@ -111,8 +111,7 @@ static const u8 tuning_blk_pattern_8bit[] = {
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
};
static inline bool dw_mci_fifo_reset(struct dw_mci *host);
static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host);
static bool dw_mci_reset(struct dw_mci *host);
#if defined(CONFIG_DEBUG_FS)
static int dw_mci_req_show(struct seq_file *s, void *v)
@ -997,7 +996,8 @@ static int dw_mci_get_ro(struct mmc_host *mmc)
int gpio_ro = mmc_gpio_get_ro(mmc);
/* Use platform get_ro function, else try on board write protect */
if (slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT)
if ((slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT) ||
(slot->host->quirks & DW_MCI_QUIRK_NO_WRITE_PROTECT))
read_only = 0;
else if (!IS_ERR_VALUE(gpio_ro))
read_only = gpio_ro;
@ -1235,7 +1235,7 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)
* After an error, there may be data lingering
* in the FIFO
*/
dw_mci_fifo_reset(host);
dw_mci_reset(host);
} else {
data->bytes_xfered = data->blocks * data->blksz;
data->error = 0;
@ -1352,7 +1352,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
/* CMD error in data command */
if (mrq->cmd->error && mrq->data)
dw_mci_fifo_reset(host);
dw_mci_reset(host);
host->cmd = NULL;
host->data = NULL;
@ -1963,14 +1963,8 @@ static void dw_mci_work_routine_card(struct work_struct *work)
}
/* Power down slot */
if (present == 0) {
/* Clear down the FIFO */
dw_mci_fifo_reset(host);
#ifdef CONFIG_MMC_DW_IDMAC
dw_mci_idmac_reset(host);
#endif
}
if (present == 0)
dw_mci_reset(host);
spin_unlock_bh(&host->lock);
@ -2021,8 +2015,11 @@ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
/* get quirks */
for (idx = 0; idx < ARRAY_SIZE(of_slot_quirks); idx++)
if (of_get_property(np, of_slot_quirks[idx].quirk, NULL))
if (of_get_property(np, of_slot_quirks[idx].quirk, NULL)) {
dev_warn(dev, "Slot quirk %s is deprecated\n",
of_slot_quirks[idx].quirk);
quirks |= of_slot_quirks[idx].id;
}
return quirks;
}
@ -2208,8 +2205,11 @@ static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset)
return false;
}
static inline bool dw_mci_fifo_reset(struct dw_mci *host)
static bool dw_mci_reset(struct dw_mci *host)
{
u32 flags = SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET;
bool ret = false;
/*
* Reseting generates a block interrupt, hence setting
* the scatter-gather pointer to NULL.
@ -2219,15 +2219,60 @@ static inline bool dw_mci_fifo_reset(struct dw_mci *host)
host->sg = NULL;
}
return dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET);
}
if (host->use_dma)
flags |= SDMMC_CTRL_DMA_RESET;
static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host)
{
return dw_mci_ctrl_reset(host,
SDMMC_CTRL_FIFO_RESET |
SDMMC_CTRL_RESET |
SDMMC_CTRL_DMA_RESET);
if (dw_mci_ctrl_reset(host, flags)) {
/*
* In all cases we clear the RAWINTS register to clear any
* interrupts.
*/
mci_writel(host, RINTSTS, 0xFFFFFFFF);
/* if using dma we wait for dma_req to clear */
if (host->use_dma) {
unsigned long timeout = jiffies + msecs_to_jiffies(500);
u32 status;
do {
status = mci_readl(host, STATUS);
if (!(status & SDMMC_STATUS_DMA_REQ))
break;
cpu_relax();
} while (time_before(jiffies, timeout));
if (status & SDMMC_STATUS_DMA_REQ) {
dev_err(host->dev,
"%s: Timeout waiting for dma_req to "
"clear during reset\n", __func__);
goto ciu_out;
}
/* when using DMA next we reset the fifo again */
if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET))
goto ciu_out;
}
} else {
/* if the controller reset bit did clear, then set clock regs */
if (!(mci_readl(host, CTRL) & SDMMC_CTRL_RESET)) {
dev_err(host->dev, "%s: fifo/dma reset bits didn't "
"clear but ciu was reset, doing clock update\n",
__func__);
goto ciu_out;
}
}
#if IS_ENABLED(CONFIG_MMC_DW_IDMAC)
/* It is also recommended that we reset and reprogram idmac */
dw_mci_idmac_reset(host);
#endif
ret = true;
ciu_out:
/* After a CTRL reset we need to have CIU set clock registers */
mci_send_cmd(host->cur_slot, SDMMC_CMD_UPD_CLK, 0);
return ret;
}
#ifdef CONFIG_OF
@ -2238,6 +2283,9 @@ static struct dw_mci_of_quirks {
{
.quirk = "broken-cd",
.id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION,
}, {
.quirk = "disable-wp",
.id = DW_MCI_QUIRK_NO_WRITE_PROTECT,
},
};
@ -2425,7 +2473,7 @@ int dw_mci_probe(struct dw_mci *host)
}
/* Reset all blocks */
if (!dw_mci_ctrl_all_reset(host))
if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS))
return -ENODEV;
host->dma_ops = host->pdata->dma_ops;
@ -2612,7 +2660,7 @@ int dw_mci_resume(struct dw_mci *host)
}
}
if (!dw_mci_ctrl_all_reset(host)) {
if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS)) {
ret = -ENODEV;
return ret;
}

View File

@ -129,6 +129,7 @@
#define SDMMC_CMD_INDX(n) ((n) & 0x1F)
/* Status register defines */
#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF)
#define SDMMC_STATUS_DMA_REQ BIT(31)
/* FIFOTH register defines */
#define SDMMC_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \
((r) & 0xFFF) << 16 | \
@ -150,6 +151,10 @@
/* Card read threshold */
#define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x))
/* All ctrl reset bits */
#define SDMMC_CTRL_ALL_RESET_FLAGS \
(SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)
/* Register access macros */
#define mci_readl(dev, reg) \
__raw_readl((dev)->regs + SDMMC_##reg)

View File

@ -52,34 +52,53 @@ static unsigned int fmax = 515633;
* struct variant_data - MMCI variant-specific quirks
* @clkreg: default value for MCICLOCK register
* @clkreg_enable: enable value for MMCICLOCK register
* @clkreg_8bit_bus_enable: enable value for 8 bit bus
* @clkreg_neg_edge_enable: enable value for inverted data/cmd output
* @datalength_bits: number of bits in the MMCIDATALENGTH register
* @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
* is asserted (likewise for RX)
* @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
* is asserted (likewise for RX)
* @data_cmd_enable: enable value for data commands.
* @sdio: variant supports SDIO
* @st_clkdiv: true if using a ST-specific clock divider algorithm
* @datactrl_mask_ddrmode: ddr mode mask in datactrl register.
* @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
* @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl
* register
* @pwrreg_powerup: power up value for MMCIPOWER register
* @f_max: maximum clk frequency supported by the controller.
* @signal_direction: input/out direction of bus signals can be indicated
* @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
* @busy_detect: true if busy detection on dat0 is supported
* @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
* @explicit_mclk_control: enable explicit mclk control in driver.
* @qcom_fifo: enables qcom specific fifo pio read logic.
* @reversed_irq_handling: handle data irq before cmd irq.
*/
struct variant_data {
unsigned int clkreg;
unsigned int clkreg_enable;
unsigned int clkreg_8bit_bus_enable;
unsigned int clkreg_neg_edge_enable;
unsigned int datalength_bits;
unsigned int fifosize;
unsigned int fifohalfsize;
unsigned int data_cmd_enable;
unsigned int datactrl_mask_ddrmode;
bool sdio;
bool st_clkdiv;
bool blksz_datactrl16;
bool blksz_datactrl4;
u32 pwrreg_powerup;
u32 f_max;
bool signal_direction;
bool pwrreg_clkgate;
bool busy_detect;
bool pwrreg_nopower;
bool explicit_mclk_control;
bool qcom_fifo;
bool reversed_irq_handling;
};
static struct variant_data variant_arm = {
@ -87,6 +106,8 @@ static struct variant_data variant_arm = {
.fifohalfsize = 8 * 4,
.datalength_bits = 16,
.pwrreg_powerup = MCI_PWR_UP,
.f_max = 100000000,
.reversed_irq_handling = true,
};
static struct variant_data variant_arm_extended_fifo = {
@ -94,6 +115,7 @@ static struct variant_data variant_arm_extended_fifo = {
.fifohalfsize = 64 * 4,
.datalength_bits = 16,
.pwrreg_powerup = MCI_PWR_UP,
.f_max = 100000000,
};
static struct variant_data variant_arm_extended_fifo_hwfc = {
@ -102,15 +124,18 @@ static struct variant_data variant_arm_extended_fifo_hwfc = {
.clkreg_enable = MCI_ARM_HWFCEN,
.datalength_bits = 16,
.pwrreg_powerup = MCI_PWR_UP,
.f_max = 100000000,
};
static struct variant_data variant_u300 = {
.fifosize = 16 * 4,
.fifohalfsize = 8 * 4,
.clkreg_enable = MCI_ST_U300_HWFCEN,
.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
.datalength_bits = 16,
.sdio = true,
.pwrreg_powerup = MCI_PWR_ON,
.f_max = 100000000,
.signal_direction = true,
.pwrreg_clkgate = true,
.pwrreg_nopower = true,
@ -124,6 +149,7 @@ static struct variant_data variant_nomadik = {
.sdio = true,
.st_clkdiv = true,
.pwrreg_powerup = MCI_PWR_ON,
.f_max = 100000000,
.signal_direction = true,
.pwrreg_clkgate = true,
.pwrreg_nopower = true,
@ -134,10 +160,13 @@ static struct variant_data variant_ux500 = {
.fifohalfsize = 8 * 4,
.clkreg = MCI_CLK_ENABLE,
.clkreg_enable = MCI_ST_UX500_HWFCEN,
.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
.clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE,
.datalength_bits = 24,
.sdio = true,
.st_clkdiv = true,
.pwrreg_powerup = MCI_PWR_ON,
.f_max = 100000000,
.signal_direction = true,
.pwrreg_clkgate = true,
.busy_detect = true,
@ -149,17 +178,38 @@ static struct variant_data variant_ux500v2 = {
.fifohalfsize = 8 * 4,
.clkreg = MCI_CLK_ENABLE,
.clkreg_enable = MCI_ST_UX500_HWFCEN,
.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
.clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE,
.datactrl_mask_ddrmode = MCI_ST_DPSM_DDRMODE,
.datalength_bits = 24,
.sdio = true,
.st_clkdiv = true,
.blksz_datactrl16 = true,
.pwrreg_powerup = MCI_PWR_ON,
.f_max = 100000000,
.signal_direction = true,
.pwrreg_clkgate = true,
.busy_detect = true,
.pwrreg_nopower = true,
};
static struct variant_data variant_qcom = {
.fifosize = 16 * 4,
.fifohalfsize = 8 * 4,
.clkreg = MCI_CLK_ENABLE,
.clkreg_enable = MCI_QCOM_CLK_FLOWENA |
MCI_QCOM_CLK_SELECT_IN_FBCLK,
.clkreg_8bit_bus_enable = MCI_QCOM_CLK_WIDEBUS_8,
.datactrl_mask_ddrmode = MCI_QCOM_CLK_SELECT_IN_DDR_MODE,
.data_cmd_enable = MCI_QCOM_CSPM_DATCMD,
.blksz_datactrl4 = true,
.datalength_bits = 24,
.pwrreg_powerup = MCI_PWR_UP,
.f_max = 208000000,
.explicit_mclk_control = true,
.qcom_fifo = true,
};
static int mmci_card_busy(struct mmc_host *mmc)
{
struct mmci_host *host = mmc_priv(mmc);
@ -260,7 +310,9 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
host->cclk = 0;
if (desired) {
if (desired >= host->mclk) {
if (variant->explicit_mclk_control) {
host->cclk = host->mclk;
} else if (desired >= host->mclk) {
clk = MCI_CLK_BYPASS;
if (variant->st_clkdiv)
clk |= MCI_ST_UX500_NEG_EDGE;
@ -299,11 +351,11 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4)
clk |= MCI_4BIT_BUS;
if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
clk |= MCI_ST_8BIT_BUS;
clk |= variant->clkreg_8bit_bus_enable;
if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 ||
host->mmc->ios.timing == MMC_TIMING_MMC_DDR52)
clk |= MCI_ST_UX500_NEG_EDGE;
clk |= variant->clkreg_neg_edge_enable;
mmci_write_clkreg(host, clk);
}
@ -719,7 +771,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
data->bytes_xfered = 0;
clks = (unsigned long long)data->timeout_ns * host->cclk;
do_div(clks, 1000000000UL);
do_div(clks, NSEC_PER_SEC);
timeout = data->timeout_clks + (unsigned int)clks;
@ -732,6 +784,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
if (variant->blksz_datactrl16)
datactrl = MCI_DPSM_ENABLE | (data->blksz << 16);
else if (variant->blksz_datactrl4)
datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);
else
datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;
@ -767,7 +821,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 ||
host->mmc->ios.timing == MMC_TIMING_MMC_DDR52)
datactrl |= MCI_ST_DPSM_DDRMODE;
datactrl |= variant->datactrl_mask_ddrmode;
/*
* Attempt to use DMA operation mode, if this
@ -812,7 +866,7 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
writel(0, base + MMCICOMMAND);
udelay(1);
mmci_reg_delay(host);
}
c |= cmd->opcode | MCI_CPSM_ENABLE;
@ -824,6 +878,9 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
if (/*interrupt*/0)
c |= MCI_CPSM_INTERRUPT;
if (mmc_cmd_type(cmd) == MMC_CMD_ADTC)
c |= host->variant->data_cmd_enable;
host->cmd = cmd;
writel(cmd->arg, base + MMCIARGUMENT);
@ -834,6 +891,10 @@ static void
mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
unsigned int status)
{
/* Make sure we have data to handle */
if (!data)
return;
/* First check for errors */
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR|
MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
@ -902,9 +963,17 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
unsigned int status)
{
void __iomem *base = host->base;
bool sbc = (cmd == host->mrq->sbc);
bool busy_resp = host->variant->busy_detect &&
(cmd->flags & MMC_RSP_BUSY);
bool sbc, busy_resp;
if (!cmd)
return;
sbc = (cmd == host->mrq->sbc);
busy_resp = host->variant->busy_detect && (cmd->flags & MMC_RSP_BUSY);
if (!((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|
MCI_CMDSENT|MCI_CMDRESPEND)))
return;
/* Check if we need to wait for busy completion. */
if (host->busy_status && (status & MCI_ST_CARDBUSY))
@ -957,15 +1026,34 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
}
}
static int mmci_get_rx_fifocnt(struct mmci_host *host, u32 status, int remain)
{
return remain - (readl(host->base + MMCIFIFOCNT) << 2);
}
static int mmci_qcom_get_rx_fifocnt(struct mmci_host *host, u32 status, int r)
{
/*
* on qcom SDCC4 only 8 words are used in each burst so only 8 addresses
* from the fifo range should be used
*/
if (status & MCI_RXFIFOHALFFULL)
return host->variant->fifohalfsize;
else if (status & MCI_RXDATAAVLBL)
return 4;
return 0;
}
static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain)
{
void __iomem *base = host->base;
char *ptr = buffer;
u32 status;
u32 status = readl(host->base + MMCISTATUS);
int host_remain = host->size;
do {
int count = host_remain - (readl(base + MMCIFIFOCNT) << 2);
int count = host->get_rx_fifocnt(host, status, host_remain);
if (count > remain)
count = remain;
@ -1132,9 +1220,6 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
spin_lock(&host->lock);
do {
struct mmc_command *cmd;
struct mmc_data *data;
status = readl(host->base + MMCISTATUS);
if (host->singleirq) {
@ -1154,16 +1239,13 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);
cmd = host->cmd;
if ((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|
MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
mmci_cmd_irq(host, cmd, status);
data = host->data;
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR|
MCI_TXUNDERRUN|MCI_RXOVERRUN|MCI_DATAEND|
MCI_DATABLOCKEND) && data)
mmci_data_irq(host, data, status);
if (host->variant->reversed_irq_handling) {
mmci_data_irq(host, host->data, status);
mmci_cmd_irq(host, host->cmd, status);
} else {
mmci_cmd_irq(host, host->cmd, status);
mmci_data_irq(host, host->data, status);
}
/* Don't poll for busy completion in irq context. */
if (host->busy_status)
@ -1296,6 +1378,17 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (!ios->clock && variant->pwrreg_clkgate)
pwr &= ~MCI_PWR_ON;
if (host->variant->explicit_mclk_control &&
ios->clock != host->clock_cache) {
ret = clk_set_rate(host->clk, ios->clock);
if (ret < 0)
dev_err(mmc_dev(host->mmc),
"Error setting clock rate (%d)\n", ret);
else
host->mclk = clk_get_rate(host->clk);
}
host->clock_cache = ios->clock;
spin_lock_irqsave(&host->lock, flags);
mmci_set_clkreg(host, ios->clock);
@ -1443,6 +1536,11 @@ static int mmci_probe(struct amba_device *dev,
if (ret)
goto host_free;
if (variant->qcom_fifo)
host->get_rx_fifocnt = mmci_qcom_get_rx_fifocnt;
else
host->get_rx_fifocnt = mmci_get_rx_fifocnt;
host->plat = plat;
host->variant = variant;
host->mclk = clk_get_rate(host->clk);
@ -1451,8 +1549,8 @@ static int mmci_probe(struct amba_device *dev,
* so we try to adjust the clock down to this,
* (if possible).
*/
if (host->mclk > 100000000) {
ret = clk_set_rate(host->clk, 100000000);
if (host->mclk > variant->f_max) {
ret = clk_set_rate(host->clk, variant->f_max);
if (ret < 0)
goto clk_disable;
host->mclk = clk_get_rate(host->clk);
@ -1471,9 +1569,12 @@ static int mmci_probe(struct amba_device *dev,
* The ARM and ST versions of the block have slightly different
* clock divider equations which means that the minimum divider
* differs too.
* on Qualcomm like controllers get the nearest minimum clock to 100Khz
*/
if (variant->st_clkdiv)
mmc->f_min = DIV_ROUND_UP(host->mclk, 257);
else if (variant->explicit_mclk_control)
mmc->f_min = clk_round_rate(host->clk, 100000);
else
mmc->f_min = DIV_ROUND_UP(host->mclk, 512);
/*
@ -1483,9 +1584,14 @@ static int mmci_probe(struct amba_device *dev,
* the block, of course.
*/
if (mmc->f_max)
mmc->f_max = min(host->mclk, mmc->f_max);
mmc->f_max = variant->explicit_mclk_control ?
min(variant->f_max, mmc->f_max) :
min(host->mclk, mmc->f_max);
else
mmc->f_max = min(host->mclk, fmax);
mmc->f_max = variant->explicit_mclk_control ?
fmax : min(host->mclk, fmax);
dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max);
/* Get regulators and the supported OCR mask */
@ -1752,6 +1858,12 @@ static struct amba_id mmci_ids[] = {
.mask = 0xf0ffffff,
.data = &variant_ux500v2,
},
/* Qualcomm variants */
{
.id = 0x00051180,
.mask = 0x000fffff,
.data = &variant_qcom,
},
{ 0, 0 },
};

View File

@ -41,6 +41,15 @@
/* Modified PL180 on Versatile Express platform */
#define MCI_ARM_HWFCEN (1 << 12)
/* Modified on Qualcomm Integrations */
#define MCI_QCOM_CLK_WIDEBUS_8 (BIT(10) | BIT(11))
#define MCI_QCOM_CLK_FLOWENA BIT(12)
#define MCI_QCOM_CLK_INVERTOUT BIT(13)
/* select in latch data and command in */
#define MCI_QCOM_CLK_SELECT_IN_FBCLK BIT(15)
#define MCI_QCOM_CLK_SELECT_IN_DDR_MODE (BIT(14) | BIT(15))
#define MMCIARGUMENT 0x008
#define MMCICOMMAND 0x00c
#define MCI_CPSM_RESPONSE (1 << 6)
@ -54,6 +63,14 @@
#define MCI_ST_NIEN (1 << 13)
#define MCI_ST_CE_ATACMD (1 << 14)
/* Modified on Qualcomm Integrations */
#define MCI_QCOM_CSPM_DATCMD BIT(12)
#define MCI_QCOM_CSPM_MCIABORT BIT(13)
#define MCI_QCOM_CSPM_CCSENABLE BIT(14)
#define MCI_QCOM_CSPM_CCSDISABLE BIT(15)
#define MCI_QCOM_CSPM_AUTO_CMD19 BIT(16)
#define MCI_QCOM_CSPM_AUTO_CMD21 BIT(21)
#define MMCIRESPCMD 0x010
#define MMCIRESPONSE0 0x014
#define MMCIRESPONSE1 0x018
@ -191,6 +208,8 @@ struct mmci_host {
spinlock_t lock;
unsigned int mclk;
/* cached value of requested clk in set_ios */
unsigned int clock_cache;
unsigned int cclk;
u32 pwr_reg;
u32 pwr_reg_add;
@ -210,6 +229,7 @@ struct mmci_host {
/* pio stuff */
struct sg_mapping_iter sg_miter;
unsigned int size;
int (*get_rx_fifocnt)(struct mmci_host *h, u32 status, int remain);
#ifdef CONFIG_DMA_ENGINE
/* DMA stuff */

View File

@ -13,7 +13,6 @@
* warranty of any kind, whether express or implied.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>

View File

@ -86,7 +86,8 @@ static int mxs_mmc_get_cd(struct mmc_host *mmc)
if (ret >= 0)
return ret;
present = !(readl(ssp->base + HW_SSP_STATUS(ssp)) &
present = mmc->caps & MMC_CAP_NEEDS_POLL ||
!(readl(ssp->base + HW_SSP_STATUS(ssp)) &
BM_SSP_STATUS_CARD_DETECT);
if (mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH)

View File

@ -29,6 +29,7 @@
#include <linux/timer.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
#include <linux/omap-dmaengine.h>
@ -36,6 +37,7 @@
#include <linux/mmc/core.h>
#include <linux/mmc/mmc.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/pinctrl/consumer.h>
@ -54,6 +56,7 @@
#define OMAP_HSMMC_RSP54 0x0118
#define OMAP_HSMMC_RSP76 0x011C
#define OMAP_HSMMC_DATA 0x0120
#define OMAP_HSMMC_PSTATE 0x0124
#define OMAP_HSMMC_HCTL 0x0128
#define OMAP_HSMMC_SYSCTL 0x012C
#define OMAP_HSMMC_STAT 0x0130
@ -91,7 +94,10 @@
#define BCE (1 << 1)
#define FOUR_BIT (1 << 1)
#define HSPE (1 << 2)
#define IWE (1 << 24)
#define DDR (1 << 19)
#define CLKEXTFREE (1 << 16)
#define CTPL (1 << 11)
#define DW8 (1 << 5)
#define OD 0x1
#define STAT_CLEAR 0xFFFFFFFF
@ -101,11 +107,15 @@
#define SRD (1 << 26)
#define SOFTRESET (1 << 1)
/* PSTATE */
#define DLEV_DAT(x) (1 << (20 + (x)))
/* Interrupt masks for IE and ISE register */
#define CC_EN (1 << 0)
#define TC_EN (1 << 1)
#define BWR_EN (1 << 4)
#define BRR_EN (1 << 5)
#define CIRQ_EN (1 << 8)
#define ERR_EN (1 << 15)
#define CTO_EN (1 << 16)
#define CCRC_EN (1 << 17)
@ -140,7 +150,6 @@
#define VDD_3V0 3000000 /* 300000 uV */
#define VDD_165_195 (ffs(MMC_VDD_165_195) - 1)
#define AUTO_CMD23 (1 << 1) /* Auto CMD23 support */
/*
* One controller can have multiple slots, like on some omap boards using
* omap.c controller driver. Luckily this is not currently done on any known
@ -194,6 +203,7 @@ struct omap_hsmmc_host {
u32 sysctl;
u32 capa;
int irq;
int wake_irq;
int use_dma, dma_ch;
struct dma_chan *tx_chan;
struct dma_chan *rx_chan;
@ -206,6 +216,9 @@ struct omap_hsmmc_host {
int req_in_progress;
unsigned long clk_rate;
unsigned int flags;
#define AUTO_CMD23 (1 << 0) /* Auto CMD23 support */
#define HSMMC_SDIO_IRQ_ENABLED (1 << 1) /* SDIO irq enabled */
#define HSMMC_WAKE_IRQ_ENABLED (1 << 2)
struct omap_hsmmc_next next_data;
struct omap_mmc_platform_data *pdata;
};
@ -510,27 +523,40 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
struct mmc_command *cmd)
{
unsigned int irq_mask;
u32 irq_mask = INT_EN_MASK;
unsigned long flags;
if (host->use_dma)
irq_mask = INT_EN_MASK & ~(BRR_EN | BWR_EN);
else
irq_mask = INT_EN_MASK;
irq_mask &= ~(BRR_EN | BWR_EN);
/* Disable timeout for erases */
if (cmd->opcode == MMC_ERASE)
irq_mask &= ~DTO_EN;
spin_lock_irqsave(&host->irq_lock, flags);
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
/* latch pending CIRQ, but don't signal MMC core */
if (host->flags & HSMMC_SDIO_IRQ_ENABLED)
irq_mask |= CIRQ_EN;
OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
spin_unlock_irqrestore(&host->irq_lock, flags);
}
static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
{
OMAP_HSMMC_WRITE(host->base, ISE, 0);
OMAP_HSMMC_WRITE(host->base, IE, 0);
u32 irq_mask = 0;
unsigned long flags;
spin_lock_irqsave(&host->irq_lock, flags);
/* no transfer running but need to keep cirq if enabled */
if (host->flags & HSMMC_SDIO_IRQ_ENABLED)
irq_mask |= CIRQ_EN;
OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
spin_unlock_irqrestore(&host->irq_lock, flags);
}
/* Calculate divisor for the given clock frequency */
@ -667,6 +693,9 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
capa = VS18;
}
if (host->mmc->caps & MMC_CAP_SDIO_IRQ)
hctl |= IWE;
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) | hctl);
@ -681,7 +710,9 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
&& time_before(jiffies, timeout))
;
omap_hsmmc_disable_irq(host);
OMAP_HSMMC_WRITE(host->base, ISE, 0);
OMAP_HSMMC_WRITE(host->base, IE, 0);
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
/* Do not initialize card-specific things if the power is off */
if (host->power_mode == MMC_POWER_OFF)
@ -1118,8 +1149,12 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
int status;
status = OMAP_HSMMC_READ(host->base, STAT);
while (status & INT_EN_MASK && host->req_in_progress) {
omap_hsmmc_do_irq(host, status);
while (status & (INT_EN_MASK | CIRQ_EN)) {
if (host->req_in_progress)
omap_hsmmc_do_irq(host, status);
if (status & CIRQ_EN)
mmc_signal_sdio_irq(host->mmc);
/* Flush posted write */
status = OMAP_HSMMC_READ(host->base, STAT);
@ -1128,6 +1163,22 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id)
{
struct omap_hsmmc_host *host = dev_id;
/* cirq is level triggered, disable to avoid infinite loop */
spin_lock(&host->irq_lock);
if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
disable_irq_nosync(host->wake_irq);
host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
}
spin_unlock(&host->irq_lock);
pm_request_resume(host->dev); /* no use counter */
return IRQ_HANDLED;
}
static void set_sd_bus_power(struct omap_hsmmc_host *host)
{
unsigned long i;
@ -1639,6 +1690,103 @@ static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
mmc_slot(host).init_card(card);
}
static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
struct omap_hsmmc_host *host = mmc_priv(mmc);
u32 irq_mask, con;
unsigned long flags;
spin_lock_irqsave(&host->irq_lock, flags);
con = OMAP_HSMMC_READ(host->base, CON);
irq_mask = OMAP_HSMMC_READ(host->base, ISE);
if (enable) {
host->flags |= HSMMC_SDIO_IRQ_ENABLED;
irq_mask |= CIRQ_EN;
con |= CTPL | CLKEXTFREE;
} else {
host->flags &= ~HSMMC_SDIO_IRQ_ENABLED;
irq_mask &= ~CIRQ_EN;
con &= ~(CTPL | CLKEXTFREE);
}
OMAP_HSMMC_WRITE(host->base, CON, con);
OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
/*
* if enable, piggy back detection on current request
* but always disable immediately
*/
if (!host->req_in_progress || !enable)
OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
/* flush posted write */
OMAP_HSMMC_READ(host->base, IE);
spin_unlock_irqrestore(&host->irq_lock, flags);
}
static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
{
struct mmc_host *mmc = host->mmc;
int ret;
/*
* For omaps with wake-up path, wakeirq will be irq from pinctrl and
* for other omaps, wakeirq will be from GPIO (dat line remuxed to
* gpio). wakeirq is needed to detect sdio irq in runtime suspend state
* with functional clock disabled.
*/
if (!host->dev->of_node || !host->wake_irq)
return -ENODEV;
/* Prevent auto-enabling of IRQ */
irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
ret = devm_request_irq(host->dev, host->wake_irq, omap_hsmmc_wake_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
mmc_hostname(mmc), host);
if (ret) {
dev_err(mmc_dev(host->mmc), "Unable to request wake IRQ\n");
goto err;
}
/*
* Some omaps don't have wake-up path from deeper idle states
* and need to remux SDIO DAT1 to GPIO for wake-up from idle.
*/
if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) {
struct pinctrl *p = devm_pinctrl_get(host->dev);
if (!p) {
ret = -ENODEV;
goto err_free_irq;
}
if (IS_ERR(pinctrl_lookup_state(p, PINCTRL_STATE_DEFAULT))) {
dev_info(host->dev, "missing default pinctrl state\n");
devm_pinctrl_put(p);
ret = -EINVAL;
goto err_free_irq;
}
if (IS_ERR(pinctrl_lookup_state(p, PINCTRL_STATE_IDLE))) {
dev_info(host->dev, "missing idle pinctrl state\n");
devm_pinctrl_put(p);
ret = -EINVAL;
goto err_free_irq;
}
devm_pinctrl_put(p);
}
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) | IWE);
return 0;
err_free_irq:
devm_free_irq(host->dev, host->wake_irq, host);
err:
dev_warn(host->dev, "no SDIO IRQ support, falling back to polling\n");
host->wake_irq = 0;
return ret;
}
static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
{
u32 hctl, capa, value;
@ -1691,7 +1839,7 @@ static const struct mmc_host_ops omap_hsmmc_ops = {
.get_cd = omap_hsmmc_get_cd,
.get_ro = omap_hsmmc_get_ro,
.init_card = omap_hsmmc_init_card,
/* NYET -- enable_sdio_irq */
.enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
};
#ifdef CONFIG_DEBUG_FS
@ -1701,13 +1849,23 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
struct mmc_host *mmc = s->private;
struct omap_hsmmc_host *host = mmc_priv(mmc);
seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n",
mmc->index, host->context_loss);
seq_printf(s, "mmc%d:\n", mmc->index);
seq_printf(s, "sdio irq mode\t%s\n",
(mmc->caps & MMC_CAP_SDIO_IRQ) ? "interrupt" : "polling");
if (mmc->caps & MMC_CAP_SDIO_IRQ) {
seq_printf(s, "sdio irq \t%s\n",
(host->flags & HSMMC_SDIO_IRQ_ENABLED) ? "enabled"
: "disabled");
}
seq_printf(s, "ctx_loss:\t%d\n", host->context_loss);
pm_runtime_get_sync(host->dev);
seq_puts(s, "\nregs:\n");
seq_printf(s, "CON:\t\t0x%08x\n",
OMAP_HSMMC_READ(host->base, CON));
seq_printf(s, "PSTATE:\t\t0x%08x\n",
OMAP_HSMMC_READ(host->base, PSTATE));
seq_printf(s, "HCTL:\t\t0x%08x\n",
OMAP_HSMMC_READ(host->base, HCTL));
seq_printf(s, "SYSCTL:\t\t0x%08x\n",
@ -1761,6 +1919,10 @@ static const struct omap_mmc_of_data omap3_pre_es3_mmc_of_data = {
static const struct omap_mmc_of_data omap4_mmc_of_data = {
.reg_offset = 0x100,
};
static const struct omap_mmc_of_data am33xx_mmc_of_data = {
.reg_offset = 0x100,
.controller_flags = OMAP_HSMMC_SWAKEUP_MISSING,
};
static const struct of_device_id omap_mmc_of_match[] = {
{
@ -1777,6 +1939,10 @@ static const struct of_device_id omap_mmc_of_match[] = {
.compatible = "ti,omap4-hsmmc",
.data = &omap4_mmc_of_data,
},
{
.compatible = "ti,am33xx-hsmmc",
.data = &am33xx_mmc_of_data,
},
{},
};
MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
@ -1850,7 +2016,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
const struct of_device_id *match;
dma_cap_mask_t mask;
unsigned tx_req, rx_req;
struct pinctrl *pinctrl;
const struct omap_mmc_of_data *data;
void __iomem *base;
@ -1913,6 +2078,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
if (pdev->dev.of_node)
host->wake_irq = irq_of_parse_and_map(pdev->dev.of_node, 1);
mmc->ops = &omap_hsmmc_ops;
mmc->f_min = OMAP_MMC_MIN_CLOCK;
@ -2061,10 +2229,17 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_disable_irq(host);
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl))
dev_warn(&pdev->dev,
"pins are not configured from the driver\n");
/*
* For now, only support SDIO interrupt if we have a separate
* wake-up interrupt configured from device tree. This is because
* the wake-up interrupt is needed for idle state and some
* platforms need special quirks. And we don't want to add new
* legacy mux platform init code callbacks any longer as we
* are moving to DT based booting anyways.
*/
ret = omap_hsmmc_configure_wake_irq(host);
if (!ret)
mmc->caps |= MMC_CAP_SDIO_IRQ;
omap_hsmmc_protect_card(host);
@ -2170,11 +2345,18 @@ static int omap_hsmmc_suspend(struct device *dev)
pm_runtime_get_sync(host->dev);
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
omap_hsmmc_disable_irq(host);
OMAP_HSMMC_WRITE(host->base, ISE, 0);
OMAP_HSMMC_WRITE(host->base, IE, 0);
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
}
/* do not wake up due to sdio irq */
if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
!(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
disable_irq(host->wake_irq);
if (host->dbclk)
clk_disable_unprepare(host->dbclk);
@ -2200,6 +2382,10 @@ static int omap_hsmmc_resume(struct device *dev)
omap_hsmmc_protect_card(host);
if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
!(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
enable_irq(host->wake_irq);
pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
return 0;
@ -2215,22 +2401,77 @@ static int omap_hsmmc_resume(struct device *dev)
static int omap_hsmmc_runtime_suspend(struct device *dev)
{
struct omap_hsmmc_host *host;
unsigned long flags;
int ret = 0;
host = platform_get_drvdata(to_platform_device(dev));
omap_hsmmc_context_save(host);
dev_dbg(dev, "disabled\n");
return 0;
spin_lock_irqsave(&host->irq_lock, flags);
if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
(host->flags & HSMMC_SDIO_IRQ_ENABLED)) {
/* disable sdio irq handling to prevent race */
OMAP_HSMMC_WRITE(host->base, ISE, 0);
OMAP_HSMMC_WRITE(host->base, IE, 0);
if (!(OMAP_HSMMC_READ(host->base, PSTATE) & DLEV_DAT(1))) {
/*
* dat1 line low, pending sdio irq
* race condition: possible irq handler running on
* multi-core, abort
*/
dev_dbg(dev, "pending sdio irq, abort suspend\n");
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
pm_runtime_mark_last_busy(dev);
ret = -EBUSY;
goto abort;
}
pinctrl_pm_select_idle_state(dev);
WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED);
enable_irq(host->wake_irq);
host->flags |= HSMMC_WAKE_IRQ_ENABLED;
} else {
pinctrl_pm_select_idle_state(dev);
}
abort:
spin_unlock_irqrestore(&host->irq_lock, flags);
return ret;
}
static int omap_hsmmc_runtime_resume(struct device *dev)
{
struct omap_hsmmc_host *host;
unsigned long flags;
host = platform_get_drvdata(to_platform_device(dev));
omap_hsmmc_context_restore(host);
dev_dbg(dev, "enabled\n");
spin_lock_irqsave(&host->irq_lock, flags);
if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
(host->flags & HSMMC_SDIO_IRQ_ENABLED)) {
/* sdio irq flag can't change while in runtime suspend */
if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
disable_irq_nosync(host->wake_irq);
host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
}
pinctrl_pm_select_default_state(host->dev);
/* irq lost, if pinmux incorrect */
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
} else {
pinctrl_pm_select_default_state(host->dev);
}
spin_unlock_irqrestore(&host->irq_lock, flags);
return 0;
}

View File

@ -12,6 +12,7 @@
*/
#include <linux/module.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/mmc/host.h>
@ -27,6 +28,7 @@
#include <mach/dma.h>
#include <mach/gpio-samsung.h>
#include <linux/platform_data/dma-s3c24xx.h>
#include <linux/platform_data/mmc-s3cmci.h>
#include "s3cmci.h"
@ -140,10 +142,6 @@ static const int dbgmap_debug = dbg_err | dbg_debug;
dev_dbg(&host->pdev->dev, args); \
} while (0)
static struct s3c2410_dma_client s3cmci_dma_client = {
.name = "s3c-mci",
};
static void finalize_request(struct s3cmci_host *host);
static void s3cmci_send_request(struct mmc_host *mmc);
static void s3cmci_reset(struct s3cmci_host *host);
@ -256,25 +254,8 @@ static inline bool s3cmci_host_usedma(struct s3cmci_host *host)
{
#ifdef CONFIG_MMC_S3C_PIO
return false;
#elif defined(CONFIG_MMC_S3C_DMA)
#else /* CONFIG_MMC_S3C_DMA */
return true;
#else
return host->dodma;
#endif
}
/**
* s3cmci_host_canpio - return true if host has pio code available
*
* Return true if the driver has been compiled with the PIO support code
* available.
*/
static inline bool s3cmci_host_canpio(void)
{
#ifdef CONFIG_MMC_S3C_PIO
return true;
#else
return false;
#endif
}
@ -841,60 +822,24 @@ static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)
return IRQ_HANDLED;
}
static void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch,
void *buf_id, int size,
enum s3c2410_dma_buffresult result)
static void s3cmci_dma_done_callback(void *arg)
{
struct s3cmci_host *host = buf_id;
struct s3cmci_host *host = arg;
unsigned long iflags;
u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt;
mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
mci_dsta = readl(host->base + S3C2410_SDIDSTA);
mci_fsta = readl(host->base + S3C2410_SDIFSTA);
mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
BUG_ON(!host->mrq);
BUG_ON(!host->mrq->data);
BUG_ON(!host->dmatogo);
spin_lock_irqsave(&host->complete_lock, iflags);
if (result != S3C2410_RES_OK) {
dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x "
"fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n",
mci_csta, mci_dsta, mci_fsta,
mci_dcnt, result, host->dmatogo);
goto fail_request;
}
host->dmatogo--;
if (host->dmatogo) {
dbg(host, dbg_dma, "DMA DONE Size:%i DSTA:[%08x] "
"DCNT:[%08x] toGo:%u\n",
size, mci_dsta, mci_dcnt, host->dmatogo);
goto out;
}
dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n",
size, mci_dsta, mci_dcnt);
dbg(host, dbg_dma, "DMA FINISHED\n");
host->dma_complete = 1;
host->complete_what = COMPLETION_FINALIZE;
out:
tasklet_schedule(&host->pio_tasklet);
spin_unlock_irqrestore(&host->complete_lock, iflags);
return;
fail_request:
host->mrq->data->error = -EINVAL;
host->complete_what = COMPLETION_FINALIZE;
clear_imask(host);
goto out;
}
static void finalize_request(struct s3cmci_host *host)
@ -966,7 +911,7 @@ static void finalize_request(struct s3cmci_host *host)
* DMA channel and the fifo to clear out any garbage. */
if (mrq->data->error != 0) {
if (s3cmci_host_usedma(host))
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
dmaengine_terminate_all(host->dma);
if (host->is2440) {
/* Clear failure register and reset fifo. */
@ -992,29 +937,6 @@ request_done:
mmc_request_done(host->mmc, mrq);
}
static void s3cmci_dma_setup(struct s3cmci_host *host,
enum dma_data_direction source)
{
static enum dma_data_direction last_source = -1;
static int setup_ok;
if (last_source == source)
return;
last_source = source;
s3c2410_dma_devconfig(host->dma, source,
host->mem->start + host->sdidata);
if (!setup_ok) {
s3c2410_dma_config(host->dma, 4);
s3c2410_dma_set_buffdone_fn(host->dma,
s3cmci_dma_done_callback);
s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);
setup_ok = 1;
}
}
static void s3cmci_send_command(struct s3cmci_host *host,
struct mmc_command *cmd)
{
@ -1162,43 +1084,45 @@ static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data)
static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
{
int dma_len, i;
int rw = data->flags & MMC_DATA_WRITE;
struct dma_async_tx_descriptor *desc;
struct dma_slave_config conf = {
.src_addr = host->mem->start + host->sdidata,
.dst_addr = host->mem->start + host->sdidata,
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
};
BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
s3cmci_dma_setup(host, rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
/* Restore prescaler value */
writel(host->prescaler, host->base + S3C2410_SDIPRE);
dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
if (!rw)
conf.direction = DMA_DEV_TO_MEM;
else
conf.direction = DMA_MEM_TO_DEV;
dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
if (dma_len == 0)
return -ENOMEM;
host->dma_complete = 0;
host->dmatogo = dma_len;
for (i = 0; i < dma_len; i++) {
int res;
dbg(host, dbg_dma, "enqueue %i: %08x@%u\n", i,
sg_dma_address(&data->sg[i]),
sg_dma_len(&data->sg[i]));
res = s3c2410_dma_enqueue(host->dma, host,
sg_dma_address(&data->sg[i]),
sg_dma_len(&data->sg[i]));
if (res) {
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
return -EBUSY;
}
}
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);
dmaengine_slave_config(host->dma, &conf);
desc = dmaengine_prep_slave_sg(host->dma, data->sg, data->sg_len,
conf.direction,
DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
if (!desc)
goto unmap_exit;
desc->callback = s3cmci_dma_done_callback;
desc->callback_param = host;
dmaengine_submit(desc);
dma_async_issue_pending(host->dma);
return 0;
unmap_exit:
dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
return -ENOMEM;
}
static void s3cmci_send_request(struct mmc_host *mmc)
@ -1676,10 +1600,6 @@ static int s3cmci_probe(struct platform_device *pdev)
host->complete_what = COMPLETION_NONE;
host->pio_active = XFER_NONE;
#ifdef CONFIG_MMC_S3C_PIODMA
host->dodma = host->pdata->use_dma;
#endif
host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!host->mem) {
dev_err(&pdev->dev,
@ -1765,17 +1685,17 @@ static int s3cmci_probe(struct platform_device *pdev)
/* depending on the dma state, get a dma channel to use. */
if (s3cmci_host_usedma(host)) {
host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client,
host);
if (host->dma < 0) {
dma_cap_mask_t mask;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
host->dma = dma_request_slave_channel_compat(mask,
s3c24xx_dma_filter, (void *)DMACH_SDI, &pdev->dev, "rx-tx");
if (!host->dma) {
dev_err(&pdev->dev, "cannot get DMA channel.\n");
if (!s3cmci_host_canpio()) {
ret = -EBUSY;
goto probe_free_gpio_wp;
} else {
dev_warn(&pdev->dev, "falling back to PIO.\n");
host->dodma = 0;
}
ret = -EBUSY;
goto probe_free_gpio_wp;
}
}
@ -1787,7 +1707,7 @@ static int s3cmci_probe(struct platform_device *pdev)
goto probe_free_dma;
}
ret = clk_enable(host->clk);
ret = clk_prepare_enable(host->clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock source.\n");
goto clk_free;
@ -1816,7 +1736,7 @@ static int s3cmci_probe(struct platform_device *pdev)
mmc->max_segs = 128;
dbg(host, dbg_debug,
"probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
"probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%p.\n",
(host->is2440?"2440":""),
host->base, host->irq, host->irq_cd, host->dma);
@ -1845,14 +1765,14 @@ static int s3cmci_probe(struct platform_device *pdev)
s3cmci_cpufreq_deregister(host);
free_dmabuf:
clk_disable(host->clk);
clk_disable_unprepare(host->clk);
clk_free:
clk_put(host->clk);
probe_free_dma:
if (s3cmci_host_usedma(host))
s3c2410_dma_free(host->dma, &s3cmci_dma_client);
dma_release_channel(host->dma);
probe_free_gpio_wp:
if (!host->pdata->no_wprotect)
@ -1897,7 +1817,7 @@ static void s3cmci_shutdown(struct platform_device *pdev)
s3cmci_debugfs_remove(host);
s3cmci_cpufreq_deregister(host);
mmc_remove_host(mmc);
clk_disable(host->clk);
clk_disable_unprepare(host->clk);
}
static int s3cmci_remove(struct platform_device *pdev)
@ -1914,7 +1834,7 @@ static int s3cmci_remove(struct platform_device *pdev)
tasklet_disable(&host->pio_tasklet);
if (s3cmci_host_usedma(host))
s3c2410_dma_free(host->dma, &s3cmci_dma_client);
dma_release_channel(host->dma);
free_irq(host->irq, host);

View File

@ -26,7 +26,7 @@ struct s3cmci_host {
void __iomem *base;
int irq;
int irq_cd;
int dma;
struct dma_chan *dma;
unsigned long clk_rate;
unsigned long clk_div;
@ -36,8 +36,6 @@ struct s3cmci_host {
int is2440;
unsigned sdiimsk;
unsigned sdidata;
int dodma;
int dmatogo;
bool irq_disabled;
bool irq_enabled;

View File

@ -124,9 +124,11 @@ static const struct sdhci_acpi_chip sdhci_acpi_chip_int = {
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
.chip = &sdhci_acpi_chip_int,
.caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | MMC_CAP_HW_RESET,
.caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR,
.caps2 = MMC_CAP2_HC_ERASE_SZ,
.flags = SDHCI_ACPI_RUNTIME_PM,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
};
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {

View File

@ -16,7 +16,6 @@
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
#include <linux/mmc/mmc.h>
#include <linux/slab.h>

View File

@ -103,6 +103,10 @@ static const struct sdhci_pci_fixes sdhci_cafe = {
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
};
static const struct sdhci_pci_fixes sdhci_intel_qrk = {
.quirks = SDHCI_QUIRK_NO_HISPD_BIT,
};
static int mrst_hc_probe_slot(struct sdhci_pci_slot *slot)
{
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA;
@ -264,7 +268,7 @@ static void sdhci_pci_int_hw_reset(struct sdhci_host *host)
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
{
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
MMC_CAP_HW_RESET;
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR;
slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
slot->hw_reset = sdhci_pci_int_hw_reset;
return 0;
@ -279,6 +283,7 @@ static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
.allow_runtime_pm = true,
.probe_slot = byt_emmc_probe_slot,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
};
static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
@ -751,6 +756,14 @@ static const struct pci_device_id pci_ids[] = {
.driver_data = (kernel_ulong_t)&sdhci_rtsx,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_QRK_SD,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_intel_qrk,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_MRST_SD0,
@ -1130,18 +1143,13 @@ static int sdhci_pci_suspend(struct device *dev)
goto err_pci_suspend;
}
pci_save_state(pdev);
if (pm_flags & MMC_PM_KEEP_POWER) {
if (pm_flags & MMC_PM_WAKE_SDIO_IRQ) {
pci_pme_active(pdev, true);
pci_enable_wake(pdev, PCI_D3hot, 1);
}
pci_set_power_state(pdev, PCI_D3hot);
} else {
pci_enable_wake(pdev, PCI_D3hot, 0);
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
}
if (pm_flags & MMC_PM_WAKE_SDIO_IRQ)
device_init_wakeup(dev, true);
else
device_init_wakeup(dev, false);
} else
device_init_wakeup(dev, false);
return 0;
@ -1162,12 +1170,6 @@ static int sdhci_pci_resume(struct device *dev)
if (!chip)
return 0;
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
ret = pci_enable_device(pdev);
if (ret)
return ret;
if (chip->fixes && chip->fixes->resume) {
ret = chip->fixes->resume(chip);
if (ret)

View File

@ -17,6 +17,7 @@
#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb
#define PCI_DEVICE_ID_INTEL_CLV_EMMC0 0x08e5
#define PCI_DEVICE_ID_INTEL_CLV_EMMC1 0x08e6
#define PCI_DEVICE_ID_INTEL_QRK_SD 0x08A7
/*
* PCI registers

View File

@ -288,15 +288,13 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
int ret;
struct clk *clk;
pxa = kzalloc(sizeof(struct sdhci_pxa), GFP_KERNEL);
pxa = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_pxa), GFP_KERNEL);
if (!pxa)
return -ENOMEM;
host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata, 0);
if (IS_ERR(host)) {
kfree(pxa);
if (IS_ERR(host))
return PTR_ERR(host);
}
if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) {
ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info());
@ -308,7 +306,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
pltfm_host = sdhci_priv(host);
pltfm_host->priv = pxa;
clk = clk_get(dev, NULL);
clk = devm_clk_get(dev, NULL);
if (IS_ERR(clk)) {
dev_err(dev, "failed to get io clock\n");
ret = PTR_ERR(clk);
@ -389,11 +387,9 @@ err_add_host:
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(clk);
clk_put(clk);
err_clk_get:
err_mbus_win:
sdhci_pltfm_free(pdev);
kfree(pxa);
return ret;
}
@ -401,17 +397,14 @@ static int sdhci_pxav3_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_pxa *pxa = pltfm_host->priv;
pm_runtime_get_sync(&pdev->dev);
sdhci_remove_host(host, 1);
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(pltfm_host->clk);
clk_put(pltfm_host->clk);
sdhci_pltfm_free(pdev);
kfree(pxa);
return 0;
}

176
drivers/mmc/host/sdhci-st.c Normal file
View File

@ -0,0 +1,176 @@
/*
* Support for SDHCI on STMicroelectronics SoCs
*
* Copyright (C) 2014 STMicroelectronics Ltd
* Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
* Contributors: Peter Griffin <peter.griffin@linaro.org>
*
* Based on sdhci-cns3xxx.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/io.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/mmc/host.h>
#include "sdhci-pltfm.h"
static u32 sdhci_st_readl(struct sdhci_host *host, int reg)
{
u32 ret;
switch (reg) {
case SDHCI_CAPABILITIES:
ret = readl_relaxed(host->ioaddr + reg);
/* Support 3.3V and 1.8V */
ret &= ~SDHCI_CAN_VDD_300;
break;
default:
ret = readl_relaxed(host->ioaddr + reg);
}
return ret;
}
static const struct sdhci_ops sdhci_st_ops = {
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
.read_l = sdhci_st_readl,
.reset = sdhci_reset,
};
static const struct sdhci_pltfm_data sdhci_st_pdata = {
.ops = &sdhci_st_ops,
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
};
static int sdhci_st_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct clk *clk;
int ret = 0;
u16 host_version;
clk = devm_clk_get(&pdev->dev, "mmc");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "Peripheral clk not found\n");
return PTR_ERR(clk);
}
host = sdhci_pltfm_init(pdev, &sdhci_st_pdata, 0);
if (IS_ERR(host)) {
dev_err(&pdev->dev, "Failed sdhci_pltfm_init\n");
return PTR_ERR(host);
}
ret = mmc_of_parse(host->mmc);
if (ret) {
dev_err(&pdev->dev, "Failed mmc_of_parse\n");
return ret;
}
clk_prepare_enable(clk);
pltfm_host = sdhci_priv(host);
pltfm_host->clk = clk;
ret = sdhci_add_host(host);
if (ret) {
dev_err(&pdev->dev, "Failed sdhci_add_host\n");
goto err_out;
}
platform_set_drvdata(pdev, host);
host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
dev_info(&pdev->dev, "SDHCI ST Initialised: Host Version: 0x%x Vendor Version 0x%x\n",
((host_version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT),
((host_version & SDHCI_VENDOR_VER_MASK) >>
SDHCI_VENDOR_VER_SHIFT));
return 0;
err_out:
clk_disable_unprepare(clk);
sdhci_pltfm_free(pdev);
return ret;
}
static int sdhci_st_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
clk_disable_unprepare(pltfm_host->clk);
return sdhci_pltfm_unregister(pdev);
}
#ifdef CONFIG_PM_SLEEP
static int sdhci_st_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
int ret = sdhci_suspend_host(host);
if (ret)
goto out;
clk_disable_unprepare(pltfm_host->clk);
out:
return ret;
}
static int sdhci_st_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
clk_prepare_enable(pltfm_host->clk);
return sdhci_resume_host(host);
}
#endif
static SIMPLE_DEV_PM_OPS(sdhci_st_pmops, sdhci_st_suspend, sdhci_st_resume);
static const struct of_device_id st_sdhci_match[] = {
{ .compatible = "st,sdhci" },
{},
};
MODULE_DEVICE_TABLE(of, st_sdhci_match);
static struct platform_driver sdhci_st_driver = {
.probe = sdhci_st_probe,
.remove = sdhci_st_remove,
.driver = {
.name = "sdhci-st",
.pm = &sdhci_st_pmops,
.of_match_table = of_match_ptr(st_sdhci_match),
},
};
module_platform_driver(sdhci_st_driver);
MODULE_DESCRIPTION("SDHCI driver for STMicroelectronics SoCs");
MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:st-sdhci");

View File

@ -26,8 +26,6 @@
#include <linux/mmc/host.h>
#include <linux/mmc/slot-gpio.h>
#include <asm/gpio.h>
#include "sdhci-pltfm.h"
/* Tegra SDHOST controller vendor register definitions */

View File

@ -1223,8 +1223,16 @@ EXPORT_SYMBOL_GPL(sdhci_set_clock);
static void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
unsigned short vdd)
{
struct mmc_host *mmc = host->mmc;
u8 pwr = 0;
if (!IS_ERR(mmc->supply.vmmc)) {
spin_unlock_irq(&host->lock);
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
spin_lock_irq(&host->lock);
return;
}
if (mode != MMC_POWER_OFF) {
switch (1 << vdd) {
case MMC_VDD_165_195:
@ -1283,12 +1291,6 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
mdelay(10);
}
if (host->vmmc) {
spin_unlock_irq(&host->lock);
mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd);
spin_lock_irq(&host->lock);
}
}
/*****************************************************************************\
@ -1440,13 +1442,15 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
{
unsigned long flags;
u8 ctrl;
struct mmc_host *mmc = host->mmc;
spin_lock_irqsave(&host->lock, flags);
if (host->flags & SDHCI_DEVICE_DEAD) {
spin_unlock_irqrestore(&host->lock, flags);
if (host->vmmc && ios->power_mode == MMC_POWER_OFF)
mmc_regulator_set_ocr(host->mmc, host->vmmc, 0);
if (!IS_ERR(mmc->supply.vmmc) &&
ios->power_mode == MMC_POWER_OFF)
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
return;
}
@ -1530,7 +1534,6 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
host->ops->set_clock(host, host->clock);
}
/* Reset SD Clock Enable */
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
clk &= ~SDHCI_CLOCK_CARD_EN;
@ -1707,6 +1710,7 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
struct mmc_ios *ios)
{
struct mmc_host *mmc = host->mmc;
u16 ctrl;
int ret;
@ -1725,11 +1729,12 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
ctrl &= ~SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
if (host->vqmmc) {
ret = regulator_set_voltage(host->vqmmc, 2700000, 3600000);
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = regulator_set_voltage(mmc->supply.vqmmc, 2700000,
3600000);
if (ret) {
pr_warning("%s: Switching to 3.3V signalling voltage "
" failed\n", mmc_hostname(host->mmc));
" failed\n", mmc_hostname(mmc));
return -EIO;
}
}
@ -1742,16 +1747,16 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
return 0;
pr_warning("%s: 3.3V regulator output did not became stable\n",
mmc_hostname(host->mmc));
mmc_hostname(mmc));
return -EAGAIN;
case MMC_SIGNAL_VOLTAGE_180:
if (host->vqmmc) {
ret = regulator_set_voltage(host->vqmmc,
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = regulator_set_voltage(mmc->supply.vqmmc,
1700000, 1950000);
if (ret) {
pr_warning("%s: Switching to 1.8V signalling voltage "
" failed\n", mmc_hostname(host->mmc));
" failed\n", mmc_hostname(mmc));
return -EIO;
}
}
@ -1763,24 +1768,22 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
ctrl |= SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
/* Wait for 5ms */
usleep_range(5000, 5500);
/* 1.8V regulator output should be stable within 5 ms */
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (ctrl & SDHCI_CTRL_VDD_180)
return 0;
pr_warning("%s: 1.8V regulator output did not became stable\n",
mmc_hostname(host->mmc));
mmc_hostname(mmc));
return -EAGAIN;
case MMC_SIGNAL_VOLTAGE_120:
if (host->vqmmc) {
ret = regulator_set_voltage(host->vqmmc, 1100000, 1300000);
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = regulator_set_voltage(mmc->supply.vqmmc, 1100000,
1300000);
if (ret) {
pr_warning("%s: Switching to 1.2V signalling voltage "
" failed\n", mmc_hostname(host->mmc));
" failed\n", mmc_hostname(mmc));
return -EIO;
}
}
@ -2643,7 +2646,6 @@ static void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
int sdhci_runtime_suspend_host(struct sdhci_host *host)
{
unsigned long flags;
int ret = 0;
/* Disable tuning since we are suspending */
if (host->flags & SDHCI_USING_RETUNING_TIMER) {
@ -2663,14 +2665,14 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host)
host->runtime_suspended = true;
spin_unlock_irqrestore(&host->lock, flags);
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
int sdhci_runtime_resume_host(struct sdhci_host *host)
{
unsigned long flags;
int ret = 0, host_flags = host->flags;
int host_flags = host->flags;
if (host_flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma)
@ -2709,7 +2711,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
spin_unlock_irqrestore(&host->lock, flags);
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host);
@ -2820,12 +2822,12 @@ int sdhci_add_host(struct sdhci_host *host)
* (128) and potentially one alignment transfer for
* each of those entries.
*/
host->adma_desc = dma_alloc_coherent(mmc_dev(host->mmc),
host->adma_desc = dma_alloc_coherent(mmc_dev(mmc),
ADMA_SIZE, &host->adma_addr,
GFP_KERNEL);
host->align_buffer = kmalloc(128 * 4, GFP_KERNEL);
if (!host->adma_desc || !host->align_buffer) {
dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE,
dma_free_coherent(mmc_dev(mmc), ADMA_SIZE,
host->adma_desc, host->adma_addr);
kfree(host->align_buffer);
pr_warning("%s: Unable to allocate ADMA "
@ -2838,7 +2840,7 @@ int sdhci_add_host(struct sdhci_host *host)
pr_warning("%s: unable to allocate aligned ADMA descriptor\n",
mmc_hostname(mmc));
host->flags &= ~SDHCI_USE_ADMA;
dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE,
dma_free_coherent(mmc_dev(mmc), ADMA_SIZE,
host->adma_desc, host->adma_addr);
kfree(host->align_buffer);
host->adma_desc = NULL;
@ -2853,7 +2855,7 @@ int sdhci_add_host(struct sdhci_host *host)
*/
if (!(host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA))) {
host->dma_mask = DMA_BIT_MASK(64);
mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
mmc_dev(mmc)->dma_mask = &host->dma_mask;
}
if (host->version >= SDHCI_SPEC_300)
@ -2959,28 +2961,25 @@ int sdhci_add_host(struct sdhci_host *host)
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
!(host->mmc->caps & MMC_CAP_NONREMOVABLE))
!(mmc->caps & MMC_CAP_NONREMOVABLE))
mmc->caps |= MMC_CAP_NEEDS_POLL;
/* If there are external regulators, get them */
if (mmc_regulator_get_supply(mmc) == -EPROBE_DEFER)
return -EPROBE_DEFER;
/* If vqmmc regulator and no 1.8V signalling, then there's no UHS */
host->vqmmc = regulator_get_optional(mmc_dev(mmc), "vqmmc");
if (IS_ERR_OR_NULL(host->vqmmc)) {
if (PTR_ERR(host->vqmmc) < 0) {
pr_info("%s: no vqmmc regulator found\n",
mmc_hostname(mmc));
host->vqmmc = NULL;
}
} else {
ret = regulator_enable(host->vqmmc);
if (!regulator_is_supported_voltage(host->vqmmc, 1700000,
1950000))
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = regulator_enable(mmc->supply.vqmmc);
if (!regulator_is_supported_voltage(mmc->supply.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;
mmc->supply.vqmmc = NULL;
}
}
@ -3041,34 +3040,6 @@ int sdhci_add_host(struct sdhci_host *host)
ocr_avail = 0;
host->vmmc = regulator_get_optional(mmc_dev(mmc), "vmmc");
if (IS_ERR_OR_NULL(host->vmmc)) {
if (PTR_ERR(host->vmmc) < 0) {
pr_info("%s: no vmmc regulator found\n",
mmc_hostname(mmc));
host->vmmc = NULL;
}
}
#ifdef CONFIG_REGULATOR
/*
* Voltage range check makes sense only if regulator reports
* any voltage value.
*/
if (host->vmmc && regulator_get_voltage(host->vmmc) > 0) {
ret = regulator_is_supported_voltage(host->vmmc, 2700000,
3600000);
if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_330)))
caps[0] &= ~SDHCI_CAN_VDD_330;
if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_300)))
caps[0] &= ~SDHCI_CAN_VDD_300;
ret = regulator_is_supported_voltage(host->vmmc, 1700000,
1950000);
if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_180)))
caps[0] &= ~SDHCI_CAN_VDD_180;
}
#endif /* CONFIG_REGULATOR */
/*
* According to SD Host Controller spec v3.00, if the Host System
* can afford more than 150mA, Host Driver should set XPC to 1. Also
@ -3077,8 +3048,8 @@ int sdhci_add_host(struct sdhci_host *host)
* value.
*/
max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
if (!max_current_caps && host->vmmc) {
u32 curr = regulator_get_current_limit(host->vmmc);
if (!max_current_caps && !IS_ERR(mmc->supply.vmmc)) {
u32 curr = regulator_get_current_limit(mmc->supply.vmmc);
if (curr > 0) {
/* convert to SDHCI_MAX_CURRENT format */
@ -3118,8 +3089,12 @@ int sdhci_add_host(struct sdhci_host *host)
SDHCI_MAX_CURRENT_MULTIPLIER;
}
/* If OCR set by external regulators, use it instead */
if (mmc->ocr_avail)
ocr_avail = mmc->ocr_avail;
if (host->ocr_mask)
ocr_avail = host->ocr_mask;
ocr_avail &= host->ocr_mask;
mmc->ocr_avail = ocr_avail;
mmc->ocr_avail_sdio = ocr_avail;
@ -3273,6 +3248,7 @@ EXPORT_SYMBOL_GPL(sdhci_add_host);
void sdhci_remove_host(struct sdhci_host *host, int dead)
{
struct mmc_host *mmc = host->mmc;
unsigned long flags;
if (dead) {
@ -3282,7 +3258,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
if (host->mrq) {
pr_err("%s: Controller removed during "
" transfer!\n", mmc_hostname(host->mmc));
" transfer!\n", mmc_hostname(mmc));
host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
@ -3293,7 +3269,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
sdhci_disable_card_detection(host);
mmc_remove_host(host->mmc);
mmc_remove_host(mmc);
#ifdef SDHCI_USE_LEDS_CLASS
led_classdev_unregister(&host->led);
@ -3310,18 +3286,14 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
tasklet_kill(&host->finish_tasklet);
if (host->vmmc) {
regulator_disable(host->vmmc);
regulator_put(host->vmmc);
}
if (!IS_ERR(mmc->supply.vmmc))
regulator_disable(mmc->supply.vmmc);
if (host->vqmmc) {
regulator_disable(host->vqmmc);
regulator_put(host->vqmmc);
}
if (!IS_ERR(mmc->supply.vqmmc))
regulator_disable(mmc->supply.vqmmc);
if (host->adma_desc)
dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE,
dma_free_coherent(mmc_dev(mmc), ADMA_SIZE,
host->adma_desc, host->adma_addr);
kfree(host->align_buffer);

View File

@ -386,7 +386,7 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
struct sh_mmcif_plat_data *pdata,
enum dma_transfer_direction direction)
{
struct dma_slave_config cfg;
struct dma_slave_config cfg = { 0, };
struct dma_chan *chan;
unsigned int slave_id;
struct resource *res;
@ -417,8 +417,15 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
/* In the OF case the driver will get the slave ID from the DT */
cfg.slave_id = slave_id;
cfg.direction = direction;
cfg.dst_addr = res->start + MMCIF_CE_DATA;
cfg.src_addr = 0;
if (direction == DMA_DEV_TO_MEM) {
cfg.src_addr = res->start + MMCIF_CE_DATA;
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
} else {
cfg.dst_addr = res->start + MMCIF_CE_DATA;
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
}
ret = dmaengine_slave_config(chan, &cfg);
if (ret < 0) {
dma_release_channel(chan);
@ -1378,26 +1385,19 @@ static int sh_mmcif_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Get irq error\n");
return -ENXIO;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "platform_get_resource error.\n");
return -ENXIO;
}
reg = ioremap(res->start, resource_size(res));
if (!reg) {
dev_err(&pdev->dev, "ioremap error.\n");
return -ENOMEM;
}
reg = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(reg))
return PTR_ERR(reg);
mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), &pdev->dev);
if (!mmc) {
ret = -ENOMEM;
goto ealloch;
}
if (!mmc)
return -ENOMEM;
ret = mmc_of_parse(mmc);
if (ret < 0)
goto eofparse;
goto err_host;
host = mmc_priv(mmc);
host->mmc = mmc;
@ -1427,19 +1427,19 @@ static int sh_mmcif_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
host->power = false;
host->hclk = clk_get(&pdev->dev, NULL);
host->hclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(host->hclk)) {
ret = PTR_ERR(host->hclk);
dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
goto eclkget;
goto err_pm;
}
ret = sh_mmcif_clk_update(host);
if (ret < 0)
goto eclkupdate;
goto err_pm;
ret = pm_runtime_resume(&pdev->dev);
if (ret < 0)
goto eresume;
goto err_clk;
INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work);
@ -1447,65 +1447,55 @@ static int sh_mmcif_probe(struct platform_device *pdev)
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
name = irq[1] < 0 ? dev_name(&pdev->dev) : "sh_mmc:error";
ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, name, host);
ret = devm_request_threaded_irq(&pdev->dev, irq[0], sh_mmcif_intr,
sh_mmcif_irqt, 0, name, host);
if (ret) {
dev_err(&pdev->dev, "request_irq error (%s)\n", name);
goto ereqirq0;
goto err_clk;
}
if (irq[1] >= 0) {
ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt,
0, "sh_mmc:int", host);
ret = devm_request_threaded_irq(&pdev->dev, irq[1],
sh_mmcif_intr, sh_mmcif_irqt,
0, "sh_mmc:int", host);
if (ret) {
dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n");
goto ereqirq1;
goto err_clk;
}
}
if (pd && pd->use_cd_gpio) {
ret = mmc_gpio_request_cd(mmc, pd->cd_gpio, 0);
if (ret < 0)
goto erqcd;
goto err_clk;
}
mutex_init(&host->thread_lock);
clk_disable_unprepare(host->hclk);
ret = mmc_add_host(mmc);
if (ret < 0)
goto emmcaddh;
goto err_clk;
dev_pm_qos_expose_latency_limit(&pdev->dev, 100);
dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION);
dev_dbg(&pdev->dev, "chip ver H'%04x\n",
sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff);
dev_info(&pdev->dev, "Chip version 0x%04x, clock rate %luMHz\n",
sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0xffff,
clk_get_rate(host->hclk) / 1000000UL);
clk_disable_unprepare(host->hclk);
return ret;
emmcaddh:
erqcd:
if (irq[1] >= 0)
free_irq(irq[1], host);
ereqirq1:
free_irq(irq[0], host);
ereqirq0:
pm_runtime_suspend(&pdev->dev);
eresume:
err_clk:
clk_disable_unprepare(host->hclk);
eclkupdate:
clk_put(host->hclk);
eclkget:
err_pm:
pm_runtime_disable(&pdev->dev);
eofparse:
err_host:
mmc_free_host(mmc);
ealloch:
iounmap(reg);
return ret;
}
static int sh_mmcif_remove(struct platform_device *pdev)
{
struct sh_mmcif_host *host = platform_get_drvdata(pdev);
int irq[2];
host->dying = true;
clk_prepare_enable(host->hclk);
@ -1523,16 +1513,6 @@ static int sh_mmcif_remove(struct platform_device *pdev)
*/
cancel_delayed_work_sync(&host->timeout_work);
if (host->addr)
iounmap(host->addr);
irq[0] = platform_get_irq(pdev, 0);
irq[1] = platform_get_irq(pdev, 1);
free_irq(irq[0], host);
if (irq[1] >= 0)
free_irq(irq[1], host);
clk_disable_unprepare(host->hclk);
mmc_free_host(host->mmc);
pm_runtime_put_sync(&pdev->dev);

View File

@ -294,6 +294,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
cfg.slave_id = pdata->dma->slave_id_tx;
cfg.direction = DMA_MEM_TO_DEV;
cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->pdata->bus_shift);
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
cfg.src_addr = 0;
ret = dmaengine_slave_config(host->chan_tx, &cfg);
if (ret < 0)
@ -312,6 +313,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
cfg.slave_id = pdata->dma->slave_id_rx;
cfg.direction = DMA_DEV_TO_MEM;
cfg.src_addr = cfg.dst_addr;
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
cfg.dst_addr = 0;
ret = dmaengine_slave_config(host->chan_rx, &cfg);
if (ret < 0)

View File

@ -72,7 +72,6 @@
#define BM_SPI_CS 0x20
#define BM_SD_POWER 0x40
#define BM_SOFT_RESET 0x80
#define BM_ONEBIT_MASK 0xFD
/* SDMMC_BLKLEN bit fields */
#define BLKL_CRCERR_ABORT 0x0800
@ -120,6 +119,8 @@
#define STS2_DATARSP_BUSY 0x20
#define STS2_DIS_FORCECLK 0x80
/* SDMMC_EXTCTRL bit fields */
#define EXT_EIGHTBIT 0x04
/* MMC/SD DMA Controller Registers */
#define SDDMA_GCR 0x100
@ -672,7 +673,7 @@ static void wmt_mci_request(struct mmc_host *mmc, struct mmc_request *req)
static void wmt_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct wmt_mci_priv *priv;
u32 reg_tmp;
u32 busmode, extctrl;
priv = mmc_priv(mmc);
@ -687,28 +688,26 @@ static void wmt_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->clock != 0)
clk_set_rate(priv->clk_sdmmc, ios->clock);
busmode = readb(priv->sdmmc_base + SDMMC_BUSMODE);
extctrl = readb(priv->sdmmc_base + SDMMC_EXTCTRL);
busmode &= ~(BM_EIGHTBIT_MODE | BM_FOURBIT_MODE);
extctrl &= ~EXT_EIGHTBIT;
switch (ios->bus_width) {
case MMC_BUS_WIDTH_8:
reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL);
writeb(reg_tmp | 0x04, priv->sdmmc_base + SDMMC_EXTCTRL);
busmode |= BM_EIGHTBIT_MODE;
extctrl |= EXT_EIGHTBIT;
break;
case MMC_BUS_WIDTH_4:
reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
writeb(reg_tmp | BM_FOURBIT_MODE, priv->sdmmc_base +
SDMMC_BUSMODE);
reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL);
writeb(reg_tmp & 0xFB, priv->sdmmc_base + SDMMC_EXTCTRL);
busmode |= BM_FOURBIT_MODE;
break;
case MMC_BUS_WIDTH_1:
reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
writeb(reg_tmp & BM_ONEBIT_MASK, priv->sdmmc_base +
SDMMC_BUSMODE);
reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL);
writeb(reg_tmp & 0xFB, priv->sdmmc_base + SDMMC_EXTCTRL);
break;
}
writeb(busmode, priv->sdmmc_base + SDMMC_BUSMODE);
writeb(extctrl, priv->sdmmc_base + SDMMC_EXTCTRL);
}
static int wmt_mci_get_ro(struct mmc_host *mmc)
@ -830,7 +829,7 @@ static int wmt_mci_probe(struct platform_device *pdev)
goto fail3;
}
ret = request_irq(dma_irq, wmt_mci_dma_isr, 32, "sdmmc", priv);
ret = request_irq(dma_irq, wmt_mci_dma_isr, 0, "sdmmc", priv);
if (ret) {
dev_err(&pdev->dev, "Register DMA IRQ fail\n");
goto fail4;

View File

@ -213,6 +213,8 @@ struct dw_mci_dma_ops {
#define DW_MCI_QUIRK_HIGHSPEED BIT(2)
/* Unreliable card detection */
#define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3)
/* No write protect */
#define DW_MCI_QUIRK_NO_WRITE_PROTECT BIT(4)
/* Slot level quirks */
/* This slot has no write protect */

View File

@ -104,9 +104,6 @@ struct sdhci_host {
const struct sdhci_ops *ops; /* Low level hw interface */
struct regulator *vmmc; /* Power regulator (vmmc) */
struct regulator *vqmmc; /* Signaling regulator (vccq) */
/* Internal data */
struct mmc_host *mmc; /* MMC structure */
u64 dma_mask; /* custom DMA mask */

View File

@ -28,6 +28,7 @@
*/
#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0)
#define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1)
#define OMAP_HSMMC_SWAKEUP_MISSING BIT(2)
struct mmc_card;