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:
commit
a8e4def604
|
@ -46,13 +46,14 @@ Required Properties:
|
||||||
- if CIU clock divider value is 0 (that is divide by 1), both tx and rx
|
- if CIU clock divider value is 0 (that is divide by 1), both tx and rx
|
||||||
phase shift clocks should be 0.
|
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
|
* 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
|
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
|
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
|
no particular order. The format of the gpio specifier depends on the gpio
|
||||||
controller.
|
controller.
|
||||||
|
(Deprecated - Refer to Documentation/devicetree/binding/pinctrl/samsung-pinctrl.txt)
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -69,21 +70,13 @@ Example:
|
||||||
|
|
||||||
dwmmc0@12200000 {
|
dwmmc0@12200000 {
|
||||||
num-slots = <1>;
|
num-slots = <1>;
|
||||||
supports-highspeed;
|
cap-mmc-highspeed;
|
||||||
|
cap-sd-highspeed;
|
||||||
broken-cd;
|
broken-cd;
|
||||||
fifo-depth = <0x80>;
|
fifo-depth = <0x80>;
|
||||||
card-detect-delay = <200>;
|
card-detect-delay = <200>;
|
||||||
samsung,dw-mshc-ciu-div = <3>;
|
samsung,dw-mshc-ciu-div = <3>;
|
||||||
samsung,dw-mshc-sdr-timing = <2 3>;
|
samsung,dw-mshc-sdr-timing = <2 3>;
|
||||||
samsung,dw-mshc-ddr-timing = <1 2>;
|
samsung,dw-mshc-ddr-timing = <1 2>;
|
||||||
|
|
||||||
slot@0 {
|
|
||||||
reg = <0>;
|
|
||||||
bus-width = <8>;
|
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>;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,13 +34,11 @@ Example:
|
||||||
num-slots = <1>;
|
num-slots = <1>;
|
||||||
vmmc-supply = <&ldo12>;
|
vmmc-supply = <&ldo12>;
|
||||||
fifo-depth = <0x100>;
|
fifo-depth = <0x100>;
|
||||||
supports-highspeed;
|
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>;
|
pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>;
|
||||||
slot@0 {
|
|
||||||
reg = <0>;
|
|
||||||
bus-width = <4>;
|
bus-width = <4>;
|
||||||
disable-wp;
|
disable-wp;
|
||||||
cd-gpios = <&gpio10 3 0>;
|
cd-gpios = <&gpio10 3 0>;
|
||||||
};
|
cap-mmc-highspeed;
|
||||||
|
cap-sd-highspeed;
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,8 +34,8 @@ Optional properties:
|
||||||
- cap-power-off-card: powering off the card is safe
|
- cap-power-off-card: powering off the card is safe
|
||||||
- cap-sdio-irq: enable SDIO IRQ signalling on this interface
|
- cap-sdio-irq: enable SDIO IRQ signalling on this interface
|
||||||
- full-pwr-cycle: full power cycle of the card is supported
|
- 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-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_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_8v: eMMC HS200 mode(1.8V I/O) is supported
|
||||||
- mmc-hs200-1_2v: eMMC HS200 mode(1.2V 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
|
- mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported
|
||||||
|
|
32
Documentation/devicetree/bindings/mmc/renesas,mmcif.txt
Normal file
32
Documentation/devicetree/bindings/mmc/renesas,mmcif.txt
Normal 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";
|
||||||
|
};
|
|
@ -27,8 +27,8 @@ Example:
|
||||||
bus-width = <8>;
|
bus-width = <8>;
|
||||||
non-removable;
|
non-removable;
|
||||||
|
|
||||||
vmmc = <&pm8941_l20>;
|
vmmc-supply = <&pm8941_l20>;
|
||||||
vqmmc = <&pm8941_s3>;
|
vqmmc-supply = <&pm8941_s3>;
|
||||||
|
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
pinctrl-0 = <&sdc1_clk &sdc1_cmd &sdc1_data>;
|
pinctrl-0 = <&sdc1_clk &sdc1_cmd &sdc1_data>;
|
||||||
|
@ -44,8 +44,8 @@ Example:
|
||||||
bus-width = <4>;
|
bus-width = <4>;
|
||||||
cd-gpios = <&msmgpio 62 0x1>;
|
cd-gpios = <&msmgpio 62 0x1>;
|
||||||
|
|
||||||
vmmc = <&pm8941_l21>;
|
vmmc-supply = <&pm8941_l21>;
|
||||||
vqmmc = <&pm8941_l13>;
|
vqmmc-supply = <&pm8941_l13>;
|
||||||
|
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
pinctrl-0 = <&sdc2_clk &sdc2_cmd &sdc2_data>;
|
pinctrl-0 = <&sdc2_clk &sdc2_cmd &sdc2_data>;
|
||||||
|
|
33
Documentation/devicetree/bindings/mmc/sdhci-st.txt
Normal file
33
Documentation/devicetree/bindings/mmc/sdhci-st.txt
Normal 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>
|
||||||
|
};
|
|
@ -67,7 +67,8 @@ Optional properties:
|
||||||
* card-detect-delay: Delay in milli-seconds before detecting card after card
|
* card-detect-delay: Delay in milli-seconds before detecting card after card
|
||||||
insert event. The default value is 0.
|
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.
|
* broken-cd: as documented in mmc core bindings.
|
||||||
|
|
||||||
|
@ -98,14 +99,11 @@ board specific portions as listed below.
|
||||||
clock-frequency = <400000000>;
|
clock-frequency = <400000000>;
|
||||||
clock-freq-min-max = <400000 200000000>;
|
clock-freq-min-max = <400000 200000000>;
|
||||||
num-slots = <1>;
|
num-slots = <1>;
|
||||||
supports-highspeed;
|
|
||||||
broken-cd;
|
broken-cd;
|
||||||
fifo-depth = <0x80>;
|
fifo-depth = <0x80>;
|
||||||
card-detect-delay = <200>;
|
card-detect-delay = <200>;
|
||||||
vmmc-supply = <&buck8>;
|
vmmc-supply = <&buck8>;
|
||||||
|
|
||||||
slot@0 {
|
|
||||||
reg = <0>;
|
|
||||||
bus-width = <8>;
|
bus-width = <8>;
|
||||||
};
|
cap-mmc-highspeed;
|
||||||
|
cap-sd-highspeed;
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,6 +12,7 @@ Required properties:
|
||||||
Should be "ti,omap3-hsmmc", for OMAP3 controllers
|
Should be "ti,omap3-hsmmc", for OMAP3 controllers
|
||||||
Should be "ti,omap3-pre-es3-hsmmc" for OMAP3 controllers pre ES3.0
|
Should be "ti,omap3-pre-es3-hsmmc" for OMAP3 controllers pre ES3.0
|
||||||
Should be "ti,omap4-hsmmc", for OMAP4 controllers
|
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
|
- ti,hwmods: Must be "mmc<n>", n is controller instance starting 1
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
|
@ -56,3 +57,56 @@ Examples:
|
||||||
&edma 25>;
|
&edma 25>;
|
||||||
dma-names = "tx", "rx";
|
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 */
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
|
@ -18,6 +18,7 @@ Required properties:
|
||||||
"renesas,sdhi-r8a7778" - SDHI IP on R8A7778 SoC
|
"renesas,sdhi-r8a7778" - SDHI IP on R8A7778 SoC
|
||||||
"renesas,sdhi-r8a7779" - SDHI IP on R8A7779 SoC
|
"renesas,sdhi-r8a7779" - SDHI IP on R8A7779 SoC
|
||||||
"renesas,sdhi-r8a7790" - SDHI IP on R8A7790 SoC
|
"renesas,sdhi-r8a7790" - SDHI IP on R8A7790 SoC
|
||||||
|
"renesas,sdhi-r8a7791" - SDHI IP on R8A7791 SoC
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
- toshiba,mmc-wrprotect-disable: write-protect detection is unavailable
|
- toshiba,mmc-wrprotect-disable: write-protect detection is unavailable
|
||||||
|
|
|
@ -2028,8 +2028,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
||||||
/* complete ongoing async transfer before issuing discard */
|
/* complete ongoing async transfer before issuing discard */
|
||||||
if (card->host->areq)
|
if (card->host->areq)
|
||||||
mmc_blk_issue_rw_rq(mq, NULL);
|
mmc_blk_issue_rw_rq(mq, NULL);
|
||||||
if (req->cmd_flags & REQ_SECURE &&
|
if (req->cmd_flags & REQ_SECURE)
|
||||||
!(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN))
|
|
||||||
ret = mmc_blk_issue_secdiscard_rq(mq, req);
|
ret = mmc_blk_issue_secdiscard_rq(mq, req);
|
||||||
else
|
else
|
||||||
ret = mmc_blk_issue_discard_rq(mq, req);
|
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))
|
if (!(card->csd.cmdclass & CCC_BLOCK_READ))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
mmc_fixup_device(card, blk_fixups);
|
||||||
|
|
||||||
md = mmc_blk_alloc(card);
|
md = mmc_blk_alloc(card);
|
||||||
if (IS_ERR(md))
|
if (IS_ERR(md))
|
||||||
return PTR_ERR(md);
|
return PTR_ERR(md);
|
||||||
|
@ -2446,7 +2447,6 @@ static int mmc_blk_probe(struct mmc_card *card)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
mmc_set_drvdata(card, md);
|
mmc_set_drvdata(card, md);
|
||||||
mmc_fixup_device(card, blk_fixups);
|
|
||||||
|
|
||||||
if (mmc_add_disk(md))
|
if (mmc_add_disk(md))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -180,7 +180,6 @@ static int mmc_bus_resume(struct device *dev)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PM_RUNTIME
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
|
||||||
static int mmc_runtime_suspend(struct device *dev)
|
static int mmc_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mmc_card *card = mmc_dev_to_card(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);
|
return host->bus_ops->runtime_resume(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_runtime_idle(struct device *dev)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* !CONFIG_PM_RUNTIME */
|
#endif /* !CONFIG_PM_RUNTIME */
|
||||||
|
|
||||||
static const struct dev_pm_ops mmc_bus_pm_ops = {
|
static const struct dev_pm_ops mmc_bus_pm_ops = {
|
||||||
SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume,
|
SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume, NULL)
|
||||||
mmc_runtime_idle)
|
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_suspend, mmc_bus_resume)
|
SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_suspend, mmc_bus_resume)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2102,7 +2102,8 @@ EXPORT_SYMBOL(mmc_can_sanitize);
|
||||||
|
|
||||||
int mmc_can_secure_erase_trim(struct mmc_card *card)
|
int mmc_can_secure_erase_trim(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN)
|
if ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN) &&
|
||||||
|
!(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN))
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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];
|
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[0] = ext_csd[EXT_CSD_SEC_CNT + 0];
|
||||||
card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1];
|
card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1];
|
||||||
|
|
|
@ -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 == card->cis.device ||
|
||||||
f->cis_device == (u16) SDIO_ANY_ID) &&
|
f->cis_device == (u16) SDIO_ANY_ID) &&
|
||||||
rev >= f->rev_start && rev <= f->rev_end) {
|
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);
|
f->vendor_fixup(card, f->data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,6 +184,9 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||||
mmc_delay(10);
|
mmc_delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!i)
|
||||||
|
pr_err("%s: card never left busy state\n", mmc_hostname(host));
|
||||||
|
|
||||||
if (rocr && !mmc_host_is_spi(host))
|
if (rocr && !mmc_host_is_spi(host))
|
||||||
*rocr = cmd.resp[0];
|
*rocr = cmd.resp[0];
|
||||||
|
|
||||||
|
|
|
@ -290,6 +290,18 @@ config MMC_MOXART
|
||||||
be found on some embedded hardware such as UC-7112-LX.
|
be found on some embedded hardware such as UC-7112-LX.
|
||||||
If you have a controller with this interface, say Y here.
|
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
|
config MMC_OMAP
|
||||||
tristate "TI OMAP Multimedia Card Interface support"
|
tristate "TI OMAP Multimedia Card Interface support"
|
||||||
depends on ARCH_OMAP
|
depends on ARCH_OMAP
|
||||||
|
@ -303,6 +315,7 @@ config MMC_OMAP
|
||||||
|
|
||||||
config MMC_OMAP_HS
|
config MMC_OMAP_HS
|
||||||
tristate "TI OMAP High Speed Multimedia Card Interface support"
|
tristate "TI OMAP High Speed Multimedia Card Interface support"
|
||||||
|
depends on HAS_DMA
|
||||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This selects the TI OMAP High Speed Multimedia card Interface.
|
This selects the TI OMAP High Speed Multimedia card Interface.
|
||||||
|
@ -343,7 +356,7 @@ config MMC_ATMELMCI
|
||||||
|
|
||||||
config MMC_SDHCI_MSM
|
config MMC_SDHCI_MSM
|
||||||
tristate "Qualcomm SDHCI Controller Support"
|
tristate "Qualcomm SDHCI Controller Support"
|
||||||
depends on ARCH_QCOM
|
depends on ARCH_QCOM || (ARM && COMPILE_TEST)
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
help
|
help
|
||||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||||
|
@ -440,6 +453,7 @@ config MMC_SPI
|
||||||
config MMC_S3C
|
config MMC_S3C
|
||||||
tristate "Samsung S3C SD/MMC Card Interface support"
|
tristate "Samsung S3C SD/MMC Card Interface support"
|
||||||
depends on ARCH_S3C24XX
|
depends on ARCH_S3C24XX
|
||||||
|
depends on S3C24XX_DMAC
|
||||||
help
|
help
|
||||||
This selects a driver for the MCI interface found in
|
This selects a driver for the MCI interface found in
|
||||||
Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
|
Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
|
||||||
|
@ -477,15 +491,6 @@ config MMC_S3C_DMA
|
||||||
working properly and needs to be debugged before this
|
working properly and needs to be debugged before this
|
||||||
option is useful.
|
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
|
endchoice
|
||||||
|
|
||||||
config MMC_SDRICOH_CS
|
config MMC_SDRICOH_CS
|
||||||
|
@ -623,7 +628,7 @@ config MMC_DW_PCI
|
||||||
|
|
||||||
config MMC_SH_MMCIF
|
config MMC_SH_MMCIF
|
||||||
tristate "SuperH Internal MMCIF support"
|
tristate "SuperH Internal MMCIF support"
|
||||||
depends on MMC_BLOCK
|
depends on MMC_BLOCK && HAS_DMA
|
||||||
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This selects the MMC Host Interface controller (MMCIF).
|
This selects the MMC Host Interface controller (MMCIF).
|
||||||
|
@ -697,6 +702,7 @@ config MMC_WMT
|
||||||
|
|
||||||
config MMC_USDHI6ROL0
|
config MMC_USDHI6ROL0
|
||||||
tristate "Renesas USDHI6ROL0 SD/SDIO Host Controller support"
|
tristate "Renesas USDHI6ROL0 SD/SDIO Host Controller support"
|
||||||
|
depends on HAS_DMA
|
||||||
help
|
help
|
||||||
This selects support for the Renesas USDHI6ROL0 SD/SDIO
|
This selects support for the Renesas USDHI6ROL0 SD/SDIO
|
||||||
Host Controller
|
Host Controller
|
||||||
|
|
|
@ -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_BCM_KONA) += sdhci-bcm-kona.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
|
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
|
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
|
||||||
|
obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
|
||||||
|
|
||||||
ifeq ($(CONFIG_CB710_DEBUG),y)
|
ifeq ($(CONFIG_CB710_DEBUG),y)
|
||||||
CFLAGS-cb710-mmc += -DDEBUG
|
CFLAGS-cb710-mmc += -DDEBUG
|
||||||
|
|
|
@ -111,8 +111,7 @@ static const u8 tuning_blk_pattern_8bit[] = {
|
||||||
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
|
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline bool dw_mci_fifo_reset(struct dw_mci *host);
|
static bool dw_mci_reset(struct dw_mci *host);
|
||||||
static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host);
|
|
||||||
|
|
||||||
#if defined(CONFIG_DEBUG_FS)
|
#if defined(CONFIG_DEBUG_FS)
|
||||||
static int dw_mci_req_show(struct seq_file *s, void *v)
|
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);
|
int gpio_ro = mmc_gpio_get_ro(mmc);
|
||||||
|
|
||||||
/* Use platform get_ro function, else try on board write protect */
|
/* 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;
|
read_only = 0;
|
||||||
else if (!IS_ERR_VALUE(gpio_ro))
|
else if (!IS_ERR_VALUE(gpio_ro))
|
||||||
read_only = 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
|
* After an error, there may be data lingering
|
||||||
* in the FIFO
|
* in the FIFO
|
||||||
*/
|
*/
|
||||||
dw_mci_fifo_reset(host);
|
dw_mci_reset(host);
|
||||||
} else {
|
} else {
|
||||||
data->bytes_xfered = data->blocks * data->blksz;
|
data->bytes_xfered = data->blocks * data->blksz;
|
||||||
data->error = 0;
|
data->error = 0;
|
||||||
|
@ -1352,7 +1352,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
||||||
|
|
||||||
/* CMD error in data command */
|
/* CMD error in data command */
|
||||||
if (mrq->cmd->error && mrq->data)
|
if (mrq->cmd->error && mrq->data)
|
||||||
dw_mci_fifo_reset(host);
|
dw_mci_reset(host);
|
||||||
|
|
||||||
host->cmd = NULL;
|
host->cmd = NULL;
|
||||||
host->data = NULL;
|
host->data = NULL;
|
||||||
|
@ -1963,14 +1963,8 @@ static void dw_mci_work_routine_card(struct work_struct *work)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Power down slot */
|
/* Power down slot */
|
||||||
if (present == 0) {
|
if (present == 0)
|
||||||
/* Clear down the FIFO */
|
dw_mci_reset(host);
|
||||||
dw_mci_fifo_reset(host);
|
|
||||||
#ifdef CONFIG_MMC_DW_IDMAC
|
|
||||||
dw_mci_idmac_reset(host);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_bh(&host->lock);
|
spin_unlock_bh(&host->lock);
|
||||||
|
|
||||||
|
@ -2021,8 +2015,11 @@ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
|
||||||
|
|
||||||
/* get quirks */
|
/* get quirks */
|
||||||
for (idx = 0; idx < ARRAY_SIZE(of_slot_quirks); idx++)
|
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;
|
quirks |= of_slot_quirks[idx].id;
|
||||||
|
}
|
||||||
|
|
||||||
return quirks;
|
return quirks;
|
||||||
}
|
}
|
||||||
|
@ -2208,8 +2205,11 @@ static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset)
|
||||||
return false;
|
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
|
* Reseting generates a block interrupt, hence setting
|
||||||
* the scatter-gather pointer to NULL.
|
* the scatter-gather pointer to NULL.
|
||||||
|
@ -2219,15 +2219,60 @@ static inline bool dw_mci_fifo_reset(struct dw_mci *host)
|
||||||
host->sg = NULL;
|
host->sg = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET);
|
if (host->use_dma)
|
||||||
|
flags |= 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host)
|
/* when using DMA next we reset the fifo again */
|
||||||
{
|
if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET))
|
||||||
return dw_mci_ctrl_reset(host,
|
goto ciu_out;
|
||||||
SDMMC_CTRL_FIFO_RESET |
|
}
|
||||||
SDMMC_CTRL_RESET |
|
} else {
|
||||||
SDMMC_CTRL_DMA_RESET);
|
/* 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
|
#ifdef CONFIG_OF
|
||||||
|
@ -2238,6 +2283,9 @@ static struct dw_mci_of_quirks {
|
||||||
{
|
{
|
||||||
.quirk = "broken-cd",
|
.quirk = "broken-cd",
|
||||||
.id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION,
|
.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 */
|
/* Reset all blocks */
|
||||||
if (!dw_mci_ctrl_all_reset(host))
|
if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
host->dma_ops = host->pdata->dma_ops;
|
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;
|
ret = -ENODEV;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,7 @@
|
||||||
#define SDMMC_CMD_INDX(n) ((n) & 0x1F)
|
#define SDMMC_CMD_INDX(n) ((n) & 0x1F)
|
||||||
/* Status register defines */
|
/* Status register defines */
|
||||||
#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF)
|
#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF)
|
||||||
|
#define SDMMC_STATUS_DMA_REQ BIT(31)
|
||||||
/* FIFOTH register defines */
|
/* FIFOTH register defines */
|
||||||
#define SDMMC_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \
|
#define SDMMC_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \
|
||||||
((r) & 0xFFF) << 16 | \
|
((r) & 0xFFF) << 16 | \
|
||||||
|
@ -150,6 +151,10 @@
|
||||||
/* Card read threshold */
|
/* Card read threshold */
|
||||||
#define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x))
|
#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 */
|
/* Register access macros */
|
||||||
#define mci_readl(dev, reg) \
|
#define mci_readl(dev, reg) \
|
||||||
__raw_readl((dev)->regs + SDMMC_##reg)
|
__raw_readl((dev)->regs + SDMMC_##reg)
|
||||||
|
|
|
@ -52,34 +52,53 @@ static unsigned int fmax = 515633;
|
||||||
* struct variant_data - MMCI variant-specific quirks
|
* struct variant_data - MMCI variant-specific quirks
|
||||||
* @clkreg: default value for MCICLOCK register
|
* @clkreg: default value for MCICLOCK register
|
||||||
* @clkreg_enable: enable value for MMCICLOCK 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
|
* @datalength_bits: number of bits in the MMCIDATALENGTH register
|
||||||
* @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
|
* @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
|
||||||
* is asserted (likewise for RX)
|
* is asserted (likewise for RX)
|
||||||
* @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
|
* @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
|
||||||
* is asserted (likewise for RX)
|
* is asserted (likewise for RX)
|
||||||
|
* @data_cmd_enable: enable value for data commands.
|
||||||
* @sdio: variant supports SDIO
|
* @sdio: variant supports SDIO
|
||||||
* @st_clkdiv: true if using a ST-specific clock divider algorithm
|
* @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_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
|
* @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
|
* @signal_direction: input/out direction of bus signals can be indicated
|
||||||
* @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
|
* @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
|
||||||
* @busy_detect: true if busy detection on dat0 is supported
|
* @busy_detect: true if busy detection on dat0 is supported
|
||||||
* @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
|
* @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 {
|
struct variant_data {
|
||||||
unsigned int clkreg;
|
unsigned int clkreg;
|
||||||
unsigned int clkreg_enable;
|
unsigned int clkreg_enable;
|
||||||
|
unsigned int clkreg_8bit_bus_enable;
|
||||||
|
unsigned int clkreg_neg_edge_enable;
|
||||||
unsigned int datalength_bits;
|
unsigned int datalength_bits;
|
||||||
unsigned int fifosize;
|
unsigned int fifosize;
|
||||||
unsigned int fifohalfsize;
|
unsigned int fifohalfsize;
|
||||||
|
unsigned int data_cmd_enable;
|
||||||
|
unsigned int datactrl_mask_ddrmode;
|
||||||
bool sdio;
|
bool sdio;
|
||||||
bool st_clkdiv;
|
bool st_clkdiv;
|
||||||
bool blksz_datactrl16;
|
bool blksz_datactrl16;
|
||||||
|
bool blksz_datactrl4;
|
||||||
u32 pwrreg_powerup;
|
u32 pwrreg_powerup;
|
||||||
|
u32 f_max;
|
||||||
bool signal_direction;
|
bool signal_direction;
|
||||||
bool pwrreg_clkgate;
|
bool pwrreg_clkgate;
|
||||||
bool busy_detect;
|
bool busy_detect;
|
||||||
bool pwrreg_nopower;
|
bool pwrreg_nopower;
|
||||||
|
bool explicit_mclk_control;
|
||||||
|
bool qcom_fifo;
|
||||||
|
bool reversed_irq_handling;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct variant_data variant_arm = {
|
static struct variant_data variant_arm = {
|
||||||
|
@ -87,6 +106,8 @@ static struct variant_data variant_arm = {
|
||||||
.fifohalfsize = 8 * 4,
|
.fifohalfsize = 8 * 4,
|
||||||
.datalength_bits = 16,
|
.datalength_bits = 16,
|
||||||
.pwrreg_powerup = MCI_PWR_UP,
|
.pwrreg_powerup = MCI_PWR_UP,
|
||||||
|
.f_max = 100000000,
|
||||||
|
.reversed_irq_handling = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct variant_data variant_arm_extended_fifo = {
|
static struct variant_data variant_arm_extended_fifo = {
|
||||||
|
@ -94,6 +115,7 @@ static struct variant_data variant_arm_extended_fifo = {
|
||||||
.fifohalfsize = 64 * 4,
|
.fifohalfsize = 64 * 4,
|
||||||
.datalength_bits = 16,
|
.datalength_bits = 16,
|
||||||
.pwrreg_powerup = MCI_PWR_UP,
|
.pwrreg_powerup = MCI_PWR_UP,
|
||||||
|
.f_max = 100000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct variant_data variant_arm_extended_fifo_hwfc = {
|
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,
|
.clkreg_enable = MCI_ARM_HWFCEN,
|
||||||
.datalength_bits = 16,
|
.datalength_bits = 16,
|
||||||
.pwrreg_powerup = MCI_PWR_UP,
|
.pwrreg_powerup = MCI_PWR_UP,
|
||||||
|
.f_max = 100000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct variant_data variant_u300 = {
|
static struct variant_data variant_u300 = {
|
||||||
.fifosize = 16 * 4,
|
.fifosize = 16 * 4,
|
||||||
.fifohalfsize = 8 * 4,
|
.fifohalfsize = 8 * 4,
|
||||||
.clkreg_enable = MCI_ST_U300_HWFCEN,
|
.clkreg_enable = MCI_ST_U300_HWFCEN,
|
||||||
|
.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
|
||||||
.datalength_bits = 16,
|
.datalength_bits = 16,
|
||||||
.sdio = true,
|
.sdio = true,
|
||||||
.pwrreg_powerup = MCI_PWR_ON,
|
.pwrreg_powerup = MCI_PWR_ON,
|
||||||
|
.f_max = 100000000,
|
||||||
.signal_direction = true,
|
.signal_direction = true,
|
||||||
.pwrreg_clkgate = true,
|
.pwrreg_clkgate = true,
|
||||||
.pwrreg_nopower = true,
|
.pwrreg_nopower = true,
|
||||||
|
@ -124,6 +149,7 @@ static struct variant_data variant_nomadik = {
|
||||||
.sdio = true,
|
.sdio = true,
|
||||||
.st_clkdiv = true,
|
.st_clkdiv = true,
|
||||||
.pwrreg_powerup = MCI_PWR_ON,
|
.pwrreg_powerup = MCI_PWR_ON,
|
||||||
|
.f_max = 100000000,
|
||||||
.signal_direction = true,
|
.signal_direction = true,
|
||||||
.pwrreg_clkgate = true,
|
.pwrreg_clkgate = true,
|
||||||
.pwrreg_nopower = true,
|
.pwrreg_nopower = true,
|
||||||
|
@ -134,10 +160,13 @@ static struct variant_data variant_ux500 = {
|
||||||
.fifohalfsize = 8 * 4,
|
.fifohalfsize = 8 * 4,
|
||||||
.clkreg = MCI_CLK_ENABLE,
|
.clkreg = MCI_CLK_ENABLE,
|
||||||
.clkreg_enable = MCI_ST_UX500_HWFCEN,
|
.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,
|
.datalength_bits = 24,
|
||||||
.sdio = true,
|
.sdio = true,
|
||||||
.st_clkdiv = true,
|
.st_clkdiv = true,
|
||||||
.pwrreg_powerup = MCI_PWR_ON,
|
.pwrreg_powerup = MCI_PWR_ON,
|
||||||
|
.f_max = 100000000,
|
||||||
.signal_direction = true,
|
.signal_direction = true,
|
||||||
.pwrreg_clkgate = true,
|
.pwrreg_clkgate = true,
|
||||||
.busy_detect = true,
|
.busy_detect = true,
|
||||||
|
@ -149,17 +178,38 @@ static struct variant_data variant_ux500v2 = {
|
||||||
.fifohalfsize = 8 * 4,
|
.fifohalfsize = 8 * 4,
|
||||||
.clkreg = MCI_CLK_ENABLE,
|
.clkreg = MCI_CLK_ENABLE,
|
||||||
.clkreg_enable = MCI_ST_UX500_HWFCEN,
|
.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,
|
.datalength_bits = 24,
|
||||||
.sdio = true,
|
.sdio = true,
|
||||||
.st_clkdiv = true,
|
.st_clkdiv = true,
|
||||||
.blksz_datactrl16 = true,
|
.blksz_datactrl16 = true,
|
||||||
.pwrreg_powerup = MCI_PWR_ON,
|
.pwrreg_powerup = MCI_PWR_ON,
|
||||||
|
.f_max = 100000000,
|
||||||
.signal_direction = true,
|
.signal_direction = true,
|
||||||
.pwrreg_clkgate = true,
|
.pwrreg_clkgate = true,
|
||||||
.busy_detect = true,
|
.busy_detect = true,
|
||||||
.pwrreg_nopower = 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)
|
static int mmci_card_busy(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
struct mmci_host *host = mmc_priv(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;
|
host->cclk = 0;
|
||||||
|
|
||||||
if (desired) {
|
if (desired) {
|
||||||
if (desired >= host->mclk) {
|
if (variant->explicit_mclk_control) {
|
||||||
|
host->cclk = host->mclk;
|
||||||
|
} else if (desired >= host->mclk) {
|
||||||
clk = MCI_CLK_BYPASS;
|
clk = MCI_CLK_BYPASS;
|
||||||
if (variant->st_clkdiv)
|
if (variant->st_clkdiv)
|
||||||
clk |= MCI_ST_UX500_NEG_EDGE;
|
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)
|
if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4)
|
||||||
clk |= MCI_4BIT_BUS;
|
clk |= MCI_4BIT_BUS;
|
||||||
if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
|
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 ||
|
if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 ||
|
||||||
host->mmc->ios.timing == MMC_TIMING_MMC_DDR52)
|
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);
|
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;
|
data->bytes_xfered = 0;
|
||||||
|
|
||||||
clks = (unsigned long long)data->timeout_ns * host->cclk;
|
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;
|
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)
|
if (variant->blksz_datactrl16)
|
||||||
datactrl = MCI_DPSM_ENABLE | (data->blksz << 16);
|
datactrl = MCI_DPSM_ENABLE | (data->blksz << 16);
|
||||||
|
else if (variant->blksz_datactrl4)
|
||||||
|
datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);
|
||||||
else
|
else
|
||||||
datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;
|
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 ||
|
if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 ||
|
||||||
host->mmc->ios.timing == MMC_TIMING_MMC_DDR52)
|
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
|
* 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) {
|
if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
|
||||||
writel(0, base + MMCICOMMAND);
|
writel(0, base + MMCICOMMAND);
|
||||||
udelay(1);
|
mmci_reg_delay(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
c |= cmd->opcode | MCI_CPSM_ENABLE;
|
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)
|
if (/*interrupt*/0)
|
||||||
c |= MCI_CPSM_INTERRUPT;
|
c |= MCI_CPSM_INTERRUPT;
|
||||||
|
|
||||||
|
if (mmc_cmd_type(cmd) == MMC_CMD_ADTC)
|
||||||
|
c |= host->variant->data_cmd_enable;
|
||||||
|
|
||||||
host->cmd = cmd;
|
host->cmd = cmd;
|
||||||
|
|
||||||
writel(cmd->arg, base + MMCIARGUMENT);
|
writel(cmd->arg, base + MMCIARGUMENT);
|
||||||
|
@ -834,6 +891,10 @@ static void
|
||||||
mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
|
mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
|
||||||
unsigned int status)
|
unsigned int status)
|
||||||
{
|
{
|
||||||
|
/* Make sure we have data to handle */
|
||||||
|
if (!data)
|
||||||
|
return;
|
||||||
|
|
||||||
/* First check for errors */
|
/* First check for errors */
|
||||||
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR|
|
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR|
|
||||||
MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
|
MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
|
||||||
|
@ -902,9 +963,17 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
|
||||||
unsigned int status)
|
unsigned int status)
|
||||||
{
|
{
|
||||||
void __iomem *base = host->base;
|
void __iomem *base = host->base;
|
||||||
bool sbc = (cmd == host->mrq->sbc);
|
bool sbc, busy_resp;
|
||||||
bool busy_resp = host->variant->busy_detect &&
|
|
||||||
(cmd->flags & MMC_RSP_BUSY);
|
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. */
|
/* Check if we need to wait for busy completion. */
|
||||||
if (host->busy_status && (status & MCI_ST_CARDBUSY))
|
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)
|
static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain)
|
||||||
{
|
{
|
||||||
void __iomem *base = host->base;
|
void __iomem *base = host->base;
|
||||||
char *ptr = buffer;
|
char *ptr = buffer;
|
||||||
u32 status;
|
u32 status = readl(host->base + MMCISTATUS);
|
||||||
int host_remain = host->size;
|
int host_remain = host->size;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int count = host_remain - (readl(base + MMCIFIFOCNT) << 2);
|
int count = host->get_rx_fifocnt(host, status, host_remain);
|
||||||
|
|
||||||
if (count > remain)
|
if (count > remain)
|
||||||
count = remain;
|
count = remain;
|
||||||
|
@ -1132,9 +1220,6 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
|
||||||
spin_lock(&host->lock);
|
spin_lock(&host->lock);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
struct mmc_command *cmd;
|
|
||||||
struct mmc_data *data;
|
|
||||||
|
|
||||||
status = readl(host->base + MMCISTATUS);
|
status = readl(host->base + MMCISTATUS);
|
||||||
|
|
||||||
if (host->singleirq) {
|
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);
|
dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);
|
||||||
|
|
||||||
cmd = host->cmd;
|
if (host->variant->reversed_irq_handling) {
|
||||||
if ((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|
|
mmci_data_irq(host, host->data, status);
|
||||||
MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
|
mmci_cmd_irq(host, host->cmd, status);
|
||||||
mmci_cmd_irq(host, cmd, status);
|
} else {
|
||||||
|
mmci_cmd_irq(host, host->cmd, status);
|
||||||
data = host->data;
|
mmci_data_irq(host, host->data, status);
|
||||||
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR|
|
}
|
||||||
MCI_TXUNDERRUN|MCI_RXOVERRUN|MCI_DATAEND|
|
|
||||||
MCI_DATABLOCKEND) && data)
|
|
||||||
mmci_data_irq(host, data, status);
|
|
||||||
|
|
||||||
/* Don't poll for busy completion in irq context. */
|
/* Don't poll for busy completion in irq context. */
|
||||||
if (host->busy_status)
|
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)
|
if (!ios->clock && variant->pwrreg_clkgate)
|
||||||
pwr &= ~MCI_PWR_ON;
|
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);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
mmci_set_clkreg(host, ios->clock);
|
mmci_set_clkreg(host, ios->clock);
|
||||||
|
@ -1443,6 +1536,11 @@ static int mmci_probe(struct amba_device *dev,
|
||||||
if (ret)
|
if (ret)
|
||||||
goto host_free;
|
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->plat = plat;
|
||||||
host->variant = variant;
|
host->variant = variant;
|
||||||
host->mclk = clk_get_rate(host->clk);
|
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,
|
* so we try to adjust the clock down to this,
|
||||||
* (if possible).
|
* (if possible).
|
||||||
*/
|
*/
|
||||||
if (host->mclk > 100000000) {
|
if (host->mclk > variant->f_max) {
|
||||||
ret = clk_set_rate(host->clk, 100000000);
|
ret = clk_set_rate(host->clk, variant->f_max);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto clk_disable;
|
goto clk_disable;
|
||||||
host->mclk = clk_get_rate(host->clk);
|
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
|
* The ARM and ST versions of the block have slightly different
|
||||||
* clock divider equations which means that the minimum divider
|
* clock divider equations which means that the minimum divider
|
||||||
* differs too.
|
* differs too.
|
||||||
|
* on Qualcomm like controllers get the nearest minimum clock to 100Khz
|
||||||
*/
|
*/
|
||||||
if (variant->st_clkdiv)
|
if (variant->st_clkdiv)
|
||||||
mmc->f_min = DIV_ROUND_UP(host->mclk, 257);
|
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
|
else
|
||||||
mmc->f_min = DIV_ROUND_UP(host->mclk, 512);
|
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.
|
* the block, of course.
|
||||||
*/
|
*/
|
||||||
if (mmc->f_max)
|
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
|
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);
|
dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max);
|
||||||
|
|
||||||
/* Get regulators and the supported OCR mask */
|
/* Get regulators and the supported OCR mask */
|
||||||
|
@ -1752,6 +1858,12 @@ static struct amba_id mmci_ids[] = {
|
||||||
.mask = 0xf0ffffff,
|
.mask = 0xf0ffffff,
|
||||||
.data = &variant_ux500v2,
|
.data = &variant_ux500v2,
|
||||||
},
|
},
|
||||||
|
/* Qualcomm variants */
|
||||||
|
{
|
||||||
|
.id = 0x00051180,
|
||||||
|
.mask = 0x000fffff,
|
||||||
|
.data = &variant_qcom,
|
||||||
|
},
|
||||||
{ 0, 0 },
|
{ 0, 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,15 @@
|
||||||
/* Modified PL180 on Versatile Express platform */
|
/* Modified PL180 on Versatile Express platform */
|
||||||
#define MCI_ARM_HWFCEN (1 << 12)
|
#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 MMCIARGUMENT 0x008
|
||||||
#define MMCICOMMAND 0x00c
|
#define MMCICOMMAND 0x00c
|
||||||
#define MCI_CPSM_RESPONSE (1 << 6)
|
#define MCI_CPSM_RESPONSE (1 << 6)
|
||||||
|
@ -54,6 +63,14 @@
|
||||||
#define MCI_ST_NIEN (1 << 13)
|
#define MCI_ST_NIEN (1 << 13)
|
||||||
#define MCI_ST_CE_ATACMD (1 << 14)
|
#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 MMCIRESPCMD 0x010
|
||||||
#define MMCIRESPONSE0 0x014
|
#define MMCIRESPONSE0 0x014
|
||||||
#define MMCIRESPONSE1 0x018
|
#define MMCIRESPONSE1 0x018
|
||||||
|
@ -191,6 +208,8 @@ struct mmci_host {
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
|
||||||
unsigned int mclk;
|
unsigned int mclk;
|
||||||
|
/* cached value of requested clk in set_ios */
|
||||||
|
unsigned int clock_cache;
|
||||||
unsigned int cclk;
|
unsigned int cclk;
|
||||||
u32 pwr_reg;
|
u32 pwr_reg;
|
||||||
u32 pwr_reg_add;
|
u32 pwr_reg_add;
|
||||||
|
@ -210,6 +229,7 @@ struct mmci_host {
|
||||||
/* pio stuff */
|
/* pio stuff */
|
||||||
struct sg_mapping_iter sg_miter;
|
struct sg_mapping_iter sg_miter;
|
||||||
unsigned int size;
|
unsigned int size;
|
||||||
|
int (*get_rx_fifocnt)(struct mmci_host *h, u32 status, int remain);
|
||||||
|
|
||||||
#ifdef CONFIG_DMA_ENGINE
|
#ifdef CONFIG_DMA_ENGINE
|
||||||
/* DMA stuff */
|
/* DMA stuff */
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
* warranty of any kind, whether express or implied.
|
* warranty of any kind, whether express or implied.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/version.h>
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
|
@ -86,7 +86,8 @@ static int mxs_mmc_get_cd(struct mmc_host *mmc)
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
return ret;
|
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);
|
BM_SSP_STATUS_CARD_DETECT);
|
||||||
|
|
||||||
if (mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH)
|
if (mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH)
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/omap-dmaengine.h>
|
#include <linux/omap-dmaengine.h>
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
#include <linux/mmc/core.h>
|
#include <linux/mmc/core.h>
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/mmc/mmc.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
@ -54,6 +56,7 @@
|
||||||
#define OMAP_HSMMC_RSP54 0x0118
|
#define OMAP_HSMMC_RSP54 0x0118
|
||||||
#define OMAP_HSMMC_RSP76 0x011C
|
#define OMAP_HSMMC_RSP76 0x011C
|
||||||
#define OMAP_HSMMC_DATA 0x0120
|
#define OMAP_HSMMC_DATA 0x0120
|
||||||
|
#define OMAP_HSMMC_PSTATE 0x0124
|
||||||
#define OMAP_HSMMC_HCTL 0x0128
|
#define OMAP_HSMMC_HCTL 0x0128
|
||||||
#define OMAP_HSMMC_SYSCTL 0x012C
|
#define OMAP_HSMMC_SYSCTL 0x012C
|
||||||
#define OMAP_HSMMC_STAT 0x0130
|
#define OMAP_HSMMC_STAT 0x0130
|
||||||
|
@ -91,7 +94,10 @@
|
||||||
#define BCE (1 << 1)
|
#define BCE (1 << 1)
|
||||||
#define FOUR_BIT (1 << 1)
|
#define FOUR_BIT (1 << 1)
|
||||||
#define HSPE (1 << 2)
|
#define HSPE (1 << 2)
|
||||||
|
#define IWE (1 << 24)
|
||||||
#define DDR (1 << 19)
|
#define DDR (1 << 19)
|
||||||
|
#define CLKEXTFREE (1 << 16)
|
||||||
|
#define CTPL (1 << 11)
|
||||||
#define DW8 (1 << 5)
|
#define DW8 (1 << 5)
|
||||||
#define OD 0x1
|
#define OD 0x1
|
||||||
#define STAT_CLEAR 0xFFFFFFFF
|
#define STAT_CLEAR 0xFFFFFFFF
|
||||||
|
@ -101,11 +107,15 @@
|
||||||
#define SRD (1 << 26)
|
#define SRD (1 << 26)
|
||||||
#define SOFTRESET (1 << 1)
|
#define SOFTRESET (1 << 1)
|
||||||
|
|
||||||
|
/* PSTATE */
|
||||||
|
#define DLEV_DAT(x) (1 << (20 + (x)))
|
||||||
|
|
||||||
/* Interrupt masks for IE and ISE register */
|
/* Interrupt masks for IE and ISE register */
|
||||||
#define CC_EN (1 << 0)
|
#define CC_EN (1 << 0)
|
||||||
#define TC_EN (1 << 1)
|
#define TC_EN (1 << 1)
|
||||||
#define BWR_EN (1 << 4)
|
#define BWR_EN (1 << 4)
|
||||||
#define BRR_EN (1 << 5)
|
#define BRR_EN (1 << 5)
|
||||||
|
#define CIRQ_EN (1 << 8)
|
||||||
#define ERR_EN (1 << 15)
|
#define ERR_EN (1 << 15)
|
||||||
#define CTO_EN (1 << 16)
|
#define CTO_EN (1 << 16)
|
||||||
#define CCRC_EN (1 << 17)
|
#define CCRC_EN (1 << 17)
|
||||||
|
@ -140,7 +150,6 @@
|
||||||
#define VDD_3V0 3000000 /* 300000 uV */
|
#define VDD_3V0 3000000 /* 300000 uV */
|
||||||
#define VDD_165_195 (ffs(MMC_VDD_165_195) - 1)
|
#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
|
* 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
|
* omap.c controller driver. Luckily this is not currently done on any known
|
||||||
|
@ -194,6 +203,7 @@ struct omap_hsmmc_host {
|
||||||
u32 sysctl;
|
u32 sysctl;
|
||||||
u32 capa;
|
u32 capa;
|
||||||
int irq;
|
int irq;
|
||||||
|
int wake_irq;
|
||||||
int use_dma, dma_ch;
|
int use_dma, dma_ch;
|
||||||
struct dma_chan *tx_chan;
|
struct dma_chan *tx_chan;
|
||||||
struct dma_chan *rx_chan;
|
struct dma_chan *rx_chan;
|
||||||
|
@ -206,6 +216,9 @@ struct omap_hsmmc_host {
|
||||||
int req_in_progress;
|
int req_in_progress;
|
||||||
unsigned long clk_rate;
|
unsigned long clk_rate;
|
||||||
unsigned int flags;
|
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_hsmmc_next next_data;
|
||||||
struct omap_mmc_platform_data *pdata;
|
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,
|
static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
|
||||||
struct mmc_command *cmd)
|
struct mmc_command *cmd)
|
||||||
{
|
{
|
||||||
unsigned int irq_mask;
|
u32 irq_mask = INT_EN_MASK;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
if (host->use_dma)
|
if (host->use_dma)
|
||||||
irq_mask = INT_EN_MASK & ~(BRR_EN | BWR_EN);
|
irq_mask &= ~(BRR_EN | BWR_EN);
|
||||||
else
|
|
||||||
irq_mask = INT_EN_MASK;
|
|
||||||
|
|
||||||
/* Disable timeout for erases */
|
/* Disable timeout for erases */
|
||||||
if (cmd->opcode == MMC_ERASE)
|
if (cmd->opcode == MMC_ERASE)
|
||||||
irq_mask &= ~DTO_EN;
|
irq_mask &= ~DTO_EN;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&host->irq_lock, flags);
|
||||||
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
|
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
|
||||||
OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
|
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);
|
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)
|
static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
|
||||||
{
|
{
|
||||||
OMAP_HSMMC_WRITE(host->base, ISE, 0);
|
u32 irq_mask = 0;
|
||||||
OMAP_HSMMC_WRITE(host->base, IE, 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);
|
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
|
||||||
|
spin_unlock_irqrestore(&host->irq_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate divisor for the given clock frequency */
|
/* Calculate divisor for the given clock frequency */
|
||||||
|
@ -667,6 +693,9 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
|
||||||
capa = VS18;
|
capa = VS18;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (host->mmc->caps & MMC_CAP_SDIO_IRQ)
|
||||||
|
hctl |= IWE;
|
||||||
|
|
||||||
OMAP_HSMMC_WRITE(host->base, HCTL,
|
OMAP_HSMMC_WRITE(host->base, HCTL,
|
||||||
OMAP_HSMMC_READ(host->base, HCTL) | 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))
|
&& 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 */
|
/* Do not initialize card-specific things if the power is off */
|
||||||
if (host->power_mode == MMC_POWER_OFF)
|
if (host->power_mode == MMC_POWER_OFF)
|
||||||
|
@ -1118,9 +1149,13 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
status = OMAP_HSMMC_READ(host->base, STAT);
|
status = OMAP_HSMMC_READ(host->base, STAT);
|
||||||
while (status & INT_EN_MASK && host->req_in_progress) {
|
while (status & (INT_EN_MASK | CIRQ_EN)) {
|
||||||
|
if (host->req_in_progress)
|
||||||
omap_hsmmc_do_irq(host, status);
|
omap_hsmmc_do_irq(host, status);
|
||||||
|
|
||||||
|
if (status & CIRQ_EN)
|
||||||
|
mmc_signal_sdio_irq(host->mmc);
|
||||||
|
|
||||||
/* Flush posted write */
|
/* Flush posted write */
|
||||||
status = OMAP_HSMMC_READ(host->base, STAT);
|
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;
|
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)
|
static void set_sd_bus_power(struct omap_hsmmc_host *host)
|
||||||
{
|
{
|
||||||
unsigned long i;
|
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);
|
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)
|
static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
|
||||||
{
|
{
|
||||||
u32 hctl, capa, value;
|
u32 hctl, capa, value;
|
||||||
|
@ -1691,7 +1839,7 @@ static const struct mmc_host_ops omap_hsmmc_ops = {
|
||||||
.get_cd = omap_hsmmc_get_cd,
|
.get_cd = omap_hsmmc_get_cd,
|
||||||
.get_ro = omap_hsmmc_get_ro,
|
.get_ro = omap_hsmmc_get_ro,
|
||||||
.init_card = omap_hsmmc_init_card,
|
.init_card = omap_hsmmc_init_card,
|
||||||
/* NYET -- enable_sdio_irq */
|
.enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#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 mmc_host *mmc = s->private;
|
||||||
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n",
|
seq_printf(s, "mmc%d:\n", mmc->index);
|
||||||
mmc->index, host->context_loss);
|
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);
|
pm_runtime_get_sync(host->dev);
|
||||||
|
seq_puts(s, "\nregs:\n");
|
||||||
seq_printf(s, "CON:\t\t0x%08x\n",
|
seq_printf(s, "CON:\t\t0x%08x\n",
|
||||||
OMAP_HSMMC_READ(host->base, CON));
|
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",
|
seq_printf(s, "HCTL:\t\t0x%08x\n",
|
||||||
OMAP_HSMMC_READ(host->base, HCTL));
|
OMAP_HSMMC_READ(host->base, HCTL));
|
||||||
seq_printf(s, "SYSCTL:\t\t0x%08x\n",
|
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 = {
|
static const struct omap_mmc_of_data omap4_mmc_of_data = {
|
||||||
.reg_offset = 0x100,
|
.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[] = {
|
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",
|
.compatible = "ti,omap4-hsmmc",
|
||||||
.data = &omap4_mmc_of_data,
|
.data = &omap4_mmc_of_data,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "ti,am33xx-hsmmc",
|
||||||
|
.data = &am33xx_mmc_of_data,
|
||||||
|
},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
|
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;
|
const struct of_device_id *match;
|
||||||
dma_cap_mask_t mask;
|
dma_cap_mask_t mask;
|
||||||
unsigned tx_req, rx_req;
|
unsigned tx_req, rx_req;
|
||||||
struct pinctrl *pinctrl;
|
|
||||||
const struct omap_mmc_of_data *data;
|
const struct omap_mmc_of_data *data;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
|
||||||
|
@ -1913,6 +2078,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
platform_set_drvdata(pdev, host);
|
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->ops = &omap_hsmmc_ops;
|
||||||
|
|
||||||
mmc->f_min = OMAP_MMC_MIN_CLOCK;
|
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);
|
omap_hsmmc_disable_irq(host);
|
||||||
|
|
||||||
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
/*
|
||||||
if (IS_ERR(pinctrl))
|
* For now, only support SDIO interrupt if we have a separate
|
||||||
dev_warn(&pdev->dev,
|
* wake-up interrupt configured from device tree. This is because
|
||||||
"pins are not configured from the driver\n");
|
* 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);
|
omap_hsmmc_protect_card(host);
|
||||||
|
|
||||||
|
@ -2170,11 +2345,18 @@ static int omap_hsmmc_suspend(struct device *dev)
|
||||||
pm_runtime_get_sync(host->dev);
|
pm_runtime_get_sync(host->dev);
|
||||||
|
|
||||||
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
|
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_WRITE(host->base, HCTL,
|
||||||
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
|
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)
|
if (host->dbclk)
|
||||||
clk_disable_unprepare(host->dbclk);
|
clk_disable_unprepare(host->dbclk);
|
||||||
|
|
||||||
|
@ -2200,6 +2382,10 @@ static int omap_hsmmc_resume(struct device *dev)
|
||||||
|
|
||||||
omap_hsmmc_protect_card(host);
|
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_mark_last_busy(host->dev);
|
||||||
pm_runtime_put_autosuspend(host->dev);
|
pm_runtime_put_autosuspend(host->dev);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2215,22 +2401,77 @@ static int omap_hsmmc_resume(struct device *dev)
|
||||||
static int omap_hsmmc_runtime_suspend(struct device *dev)
|
static int omap_hsmmc_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host;
|
struct omap_hsmmc_host *host;
|
||||||
|
unsigned long flags;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
host = platform_get_drvdata(to_platform_device(dev));
|
host = platform_get_drvdata(to_platform_device(dev));
|
||||||
omap_hsmmc_context_save(host);
|
omap_hsmmc_context_save(host);
|
||||||
dev_dbg(dev, "disabled\n");
|
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)
|
static int omap_hsmmc_runtime_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host;
|
struct omap_hsmmc_host *host;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
host = platform_get_drvdata(to_platform_device(dev));
|
host = platform_get_drvdata(to_platform_device(dev));
|
||||||
omap_hsmmc_context_restore(host);
|
omap_hsmmc_context_restore(host);
|
||||||
dev_dbg(dev, "enabled\n");
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
#include <mach/dma.h>
|
#include <mach/dma.h>
|
||||||
#include <mach/gpio-samsung.h>
|
#include <mach/gpio-samsung.h>
|
||||||
|
|
||||||
|
#include <linux/platform_data/dma-s3c24xx.h>
|
||||||
#include <linux/platform_data/mmc-s3cmci.h>
|
#include <linux/platform_data/mmc-s3cmci.h>
|
||||||
|
|
||||||
#include "s3cmci.h"
|
#include "s3cmci.h"
|
||||||
|
@ -140,10 +142,6 @@ static const int dbgmap_debug = dbg_err | dbg_debug;
|
||||||
dev_dbg(&host->pdev->dev, args); \
|
dev_dbg(&host->pdev->dev, args); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
static struct s3c2410_dma_client s3cmci_dma_client = {
|
|
||||||
.name = "s3c-mci",
|
|
||||||
};
|
|
||||||
|
|
||||||
static void finalize_request(struct s3cmci_host *host);
|
static void finalize_request(struct s3cmci_host *host);
|
||||||
static void s3cmci_send_request(struct mmc_host *mmc);
|
static void s3cmci_send_request(struct mmc_host *mmc);
|
||||||
static void s3cmci_reset(struct s3cmci_host *host);
|
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
|
#ifdef CONFIG_MMC_S3C_PIO
|
||||||
return false;
|
return false;
|
||||||
#elif defined(CONFIG_MMC_S3C_DMA)
|
#else /* CONFIG_MMC_S3C_DMA */
|
||||||
return true;
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -841,60 +822,24 @@ static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch,
|
static void s3cmci_dma_done_callback(void *arg)
|
||||||
void *buf_id, int size,
|
|
||||||
enum s3c2410_dma_buffresult result)
|
|
||||||
{
|
{
|
||||||
struct s3cmci_host *host = buf_id;
|
struct s3cmci_host *host = arg;
|
||||||
unsigned long iflags;
|
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);
|
||||||
BUG_ON(!host->mrq->data);
|
BUG_ON(!host->mrq->data);
|
||||||
BUG_ON(!host->dmatogo);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&host->complete_lock, iflags);
|
spin_lock_irqsave(&host->complete_lock, iflags);
|
||||||
|
|
||||||
if (result != S3C2410_RES_OK) {
|
dbg(host, dbg_dma, "DMA FINISHED\n");
|
||||||
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);
|
|
||||||
|
|
||||||
host->dma_complete = 1;
|
host->dma_complete = 1;
|
||||||
host->complete_what = COMPLETION_FINALIZE;
|
host->complete_what = COMPLETION_FINALIZE;
|
||||||
|
|
||||||
out:
|
|
||||||
tasklet_schedule(&host->pio_tasklet);
|
tasklet_schedule(&host->pio_tasklet);
|
||||||
spin_unlock_irqrestore(&host->complete_lock, iflags);
|
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)
|
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. */
|
* DMA channel and the fifo to clear out any garbage. */
|
||||||
if (mrq->data->error != 0) {
|
if (mrq->data->error != 0) {
|
||||||
if (s3cmci_host_usedma(host))
|
if (s3cmci_host_usedma(host))
|
||||||
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
|
dmaengine_terminate_all(host->dma);
|
||||||
|
|
||||||
if (host->is2440) {
|
if (host->is2440) {
|
||||||
/* Clear failure register and reset fifo. */
|
/* Clear failure register and reset fifo. */
|
||||||
|
@ -992,29 +937,6 @@ request_done:
|
||||||
mmc_request_done(host->mmc, mrq);
|
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,
|
static void s3cmci_send_command(struct s3cmci_host *host,
|
||||||
struct mmc_command *cmd)
|
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)
|
static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
|
||||||
{
|
{
|
||||||
int dma_len, i;
|
|
||||||
int rw = data->flags & MMC_DATA_WRITE;
|
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);
|
BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
|
||||||
|
|
||||||
s3cmci_dma_setup(host, rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
/* Restore prescaler value */
|
||||||
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
|
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);
|
rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||||
|
|
||||||
if (dma_len == 0)
|
dmaengine_slave_config(host->dma, &conf);
|
||||||
return -ENOMEM;
|
desc = dmaengine_prep_slave_sg(host->dma, data->sg, data->sg_len,
|
||||||
|
conf.direction,
|
||||||
host->dma_complete = 0;
|
DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
|
||||||
host->dmatogo = dma_len;
|
if (!desc)
|
||||||
|
goto unmap_exit;
|
||||||
for (i = 0; i < dma_len; i++) {
|
desc->callback = s3cmci_dma_done_callback;
|
||||||
int res;
|
desc->callback_param = host;
|
||||||
|
dmaengine_submit(desc);
|
||||||
dbg(host, dbg_dma, "enqueue %i: %08x@%u\n", i,
|
dma_async_issue_pending(host->dma);
|
||||||
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);
|
|
||||||
|
|
||||||
return 0;
|
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)
|
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->complete_what = COMPLETION_NONE;
|
||||||
host->pio_active = XFER_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);
|
host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!host->mem) {
|
if (!host->mem) {
|
||||||
dev_err(&pdev->dev,
|
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. */
|
/* depending on the dma state, get a dma channel to use. */
|
||||||
|
|
||||||
if (s3cmci_host_usedma(host)) {
|
if (s3cmci_host_usedma(host)) {
|
||||||
host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client,
|
dma_cap_mask_t mask;
|
||||||
host);
|
|
||||||
if (host->dma < 0) {
|
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");
|
dev_err(&pdev->dev, "cannot get DMA channel.\n");
|
||||||
if (!s3cmci_host_canpio()) {
|
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
goto probe_free_gpio_wp;
|
goto probe_free_gpio_wp;
|
||||||
} else {
|
|
||||||
dev_warn(&pdev->dev, "falling back to PIO.\n");
|
|
||||||
host->dodma = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1787,7 +1707,7 @@ static int s3cmci_probe(struct platform_device *pdev)
|
||||||
goto probe_free_dma;
|
goto probe_free_dma;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = clk_enable(host->clk);
|
ret = clk_prepare_enable(host->clk);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "failed to enable clock source.\n");
|
dev_err(&pdev->dev, "failed to enable clock source.\n");
|
||||||
goto clk_free;
|
goto clk_free;
|
||||||
|
@ -1816,7 +1736,7 @@ static int s3cmci_probe(struct platform_device *pdev)
|
||||||
mmc->max_segs = 128;
|
mmc->max_segs = 128;
|
||||||
|
|
||||||
dbg(host, dbg_debug,
|
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->is2440?"2440":""),
|
||||||
host->base, host->irq, host->irq_cd, host->dma);
|
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);
|
s3cmci_cpufreq_deregister(host);
|
||||||
|
|
||||||
free_dmabuf:
|
free_dmabuf:
|
||||||
clk_disable(host->clk);
|
clk_disable_unprepare(host->clk);
|
||||||
|
|
||||||
clk_free:
|
clk_free:
|
||||||
clk_put(host->clk);
|
clk_put(host->clk);
|
||||||
|
|
||||||
probe_free_dma:
|
probe_free_dma:
|
||||||
if (s3cmci_host_usedma(host))
|
if (s3cmci_host_usedma(host))
|
||||||
s3c2410_dma_free(host->dma, &s3cmci_dma_client);
|
dma_release_channel(host->dma);
|
||||||
|
|
||||||
probe_free_gpio_wp:
|
probe_free_gpio_wp:
|
||||||
if (!host->pdata->no_wprotect)
|
if (!host->pdata->no_wprotect)
|
||||||
|
@ -1897,7 +1817,7 @@ static void s3cmci_shutdown(struct platform_device *pdev)
|
||||||
s3cmci_debugfs_remove(host);
|
s3cmci_debugfs_remove(host);
|
||||||
s3cmci_cpufreq_deregister(host);
|
s3cmci_cpufreq_deregister(host);
|
||||||
mmc_remove_host(mmc);
|
mmc_remove_host(mmc);
|
||||||
clk_disable(host->clk);
|
clk_disable_unprepare(host->clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s3cmci_remove(struct platform_device *pdev)
|
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);
|
tasklet_disable(&host->pio_tasklet);
|
||||||
|
|
||||||
if (s3cmci_host_usedma(host))
|
if (s3cmci_host_usedma(host))
|
||||||
s3c2410_dma_free(host->dma, &s3cmci_dma_client);
|
dma_release_channel(host->dma);
|
||||||
|
|
||||||
free_irq(host->irq, host);
|
free_irq(host->irq, host);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ struct s3cmci_host {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
int irq;
|
int irq;
|
||||||
int irq_cd;
|
int irq_cd;
|
||||||
int dma;
|
struct dma_chan *dma;
|
||||||
|
|
||||||
unsigned long clk_rate;
|
unsigned long clk_rate;
|
||||||
unsigned long clk_div;
|
unsigned long clk_div;
|
||||||
|
@ -36,8 +36,6 @@ struct s3cmci_host {
|
||||||
int is2440;
|
int is2440;
|
||||||
unsigned sdiimsk;
|
unsigned sdiimsk;
|
||||||
unsigned sdidata;
|
unsigned sdidata;
|
||||||
int dodma;
|
|
||||||
int dmatogo;
|
|
||||||
|
|
||||||
bool irq_disabled;
|
bool irq_disabled;
|
||||||
bool irq_enabled;
|
bool irq_enabled;
|
||||||
|
|
|
@ -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 = {
|
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
|
||||||
.chip = &sdhci_acpi_chip_int,
|
.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,
|
.caps2 = MMC_CAP2_HC_ERASE_SZ,
|
||||||
.flags = SDHCI_ACPI_RUNTIME_PM,
|
.flags = SDHCI_ACPI_RUNTIME_PM,
|
||||||
|
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
|
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/regulator/consumer.h>
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/mmc/mmc.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
|
@ -103,6 +103,10 @@ static const struct sdhci_pci_fixes sdhci_cafe = {
|
||||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
|
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)
|
static int mrst_hc_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
{
|
{
|
||||||
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA;
|
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)
|
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
{
|
{
|
||||||
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
|
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->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
|
||||||
slot->hw_reset = sdhci_pci_int_hw_reset;
|
slot->hw_reset = sdhci_pci_int_hw_reset;
|
||||||
return 0;
|
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 = {
|
static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
|
||||||
.allow_runtime_pm = true,
|
.allow_runtime_pm = true,
|
||||||
.probe_slot = byt_emmc_probe_slot,
|
.probe_slot = byt_emmc_probe_slot,
|
||||||
|
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
|
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,
|
.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,
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
.device = PCI_DEVICE_ID_INTEL_MRST_SD0,
|
.device = PCI_DEVICE_ID_INTEL_MRST_SD0,
|
||||||
|
@ -1130,18 +1143,13 @@ static int sdhci_pci_suspend(struct device *dev)
|
||||||
goto err_pci_suspend;
|
goto err_pci_suspend;
|
||||||
}
|
}
|
||||||
|
|
||||||
pci_save_state(pdev);
|
|
||||||
if (pm_flags & MMC_PM_KEEP_POWER) {
|
if (pm_flags & MMC_PM_KEEP_POWER) {
|
||||||
if (pm_flags & MMC_PM_WAKE_SDIO_IRQ) {
|
if (pm_flags & MMC_PM_WAKE_SDIO_IRQ)
|
||||||
pci_pme_active(pdev, true);
|
device_init_wakeup(dev, true);
|
||||||
pci_enable_wake(pdev, PCI_D3hot, 1);
|
else
|
||||||
}
|
device_init_wakeup(dev, false);
|
||||||
pci_set_power_state(pdev, PCI_D3hot);
|
} else
|
||||||
} else {
|
device_init_wakeup(dev, false);
|
||||||
pci_enable_wake(pdev, PCI_D3hot, 0);
|
|
||||||
pci_disable_device(pdev);
|
|
||||||
pci_set_power_state(pdev, PCI_D3hot);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -1162,12 +1170,6 @@ static int sdhci_pci_resume(struct device *dev)
|
||||||
if (!chip)
|
if (!chip)
|
||||||
return 0;
|
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) {
|
if (chip->fixes && chip->fixes->resume) {
|
||||||
ret = chip->fixes->resume(chip);
|
ret = chip->fixes->resume(chip);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb
|
#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb
|
||||||
#define PCI_DEVICE_ID_INTEL_CLV_EMMC0 0x08e5
|
#define PCI_DEVICE_ID_INTEL_CLV_EMMC0 0x08e5
|
||||||
#define PCI_DEVICE_ID_INTEL_CLV_EMMC1 0x08e6
|
#define PCI_DEVICE_ID_INTEL_CLV_EMMC1 0x08e6
|
||||||
|
#define PCI_DEVICE_ID_INTEL_QRK_SD 0x08A7
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PCI registers
|
* PCI registers
|
||||||
|
|
|
@ -288,15 +288,13 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||||
int ret;
|
int ret;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
|
||||||
pxa = kzalloc(sizeof(struct sdhci_pxa), GFP_KERNEL);
|
pxa = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_pxa), GFP_KERNEL);
|
||||||
if (!pxa)
|
if (!pxa)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata, 0);
|
host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata, 0);
|
||||||
if (IS_ERR(host)) {
|
if (IS_ERR(host))
|
||||||
kfree(pxa);
|
|
||||||
return PTR_ERR(host);
|
return PTR_ERR(host);
|
||||||
}
|
|
||||||
|
|
||||||
if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) {
|
if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) {
|
||||||
ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info());
|
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 = sdhci_priv(host);
|
||||||
pltfm_host->priv = pxa;
|
pltfm_host->priv = pxa;
|
||||||
|
|
||||||
clk = clk_get(dev, NULL);
|
clk = devm_clk_get(dev, NULL);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
dev_err(dev, "failed to get io clock\n");
|
dev_err(dev, "failed to get io clock\n");
|
||||||
ret = PTR_ERR(clk);
|
ret = PTR_ERR(clk);
|
||||||
|
@ -389,11 +387,9 @@ err_add_host:
|
||||||
pm_runtime_put_sync(&pdev->dev);
|
pm_runtime_put_sync(&pdev->dev);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
clk_disable_unprepare(clk);
|
clk_disable_unprepare(clk);
|
||||||
clk_put(clk);
|
|
||||||
err_clk_get:
|
err_clk_get:
|
||||||
err_mbus_win:
|
err_mbus_win:
|
||||||
sdhci_pltfm_free(pdev);
|
sdhci_pltfm_free(pdev);
|
||||||
kfree(pxa);
|
|
||||||
return ret;
|
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_host *host = platform_get_drvdata(pdev);
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
|
||||||
|
|
||||||
pm_runtime_get_sync(&pdev->dev);
|
pm_runtime_get_sync(&pdev->dev);
|
||||||
sdhci_remove_host(host, 1);
|
sdhci_remove_host(host, 1);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
clk_disable_unprepare(pltfm_host->clk);
|
clk_disable_unprepare(pltfm_host->clk);
|
||||||
clk_put(pltfm_host->clk);
|
|
||||||
|
|
||||||
sdhci_pltfm_free(pdev);
|
sdhci_pltfm_free(pdev);
|
||||||
kfree(pxa);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
176
drivers/mmc/host/sdhci-st.c
Normal file
176
drivers/mmc/host/sdhci-st.c
Normal 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");
|
|
@ -26,8 +26,6 @@
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/slot-gpio.h>
|
#include <linux/mmc/slot-gpio.h>
|
||||||
|
|
||||||
#include <asm/gpio.h>
|
|
||||||
|
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
/* Tegra SDHOST controller vendor register definitions */
|
/* Tegra SDHOST controller vendor register definitions */
|
||||||
|
|
|
@ -1223,8 +1223,16 @@ EXPORT_SYMBOL_GPL(sdhci_set_clock);
|
||||||
static void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
|
static void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
|
||||||
unsigned short vdd)
|
unsigned short vdd)
|
||||||
{
|
{
|
||||||
|
struct mmc_host *mmc = host->mmc;
|
||||||
u8 pwr = 0;
|
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) {
|
if (mode != MMC_POWER_OFF) {
|
||||||
switch (1 << vdd) {
|
switch (1 << vdd) {
|
||||||
case MMC_VDD_165_195:
|
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)
|
if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
|
||||||
mdelay(10);
|
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;
|
unsigned long flags;
|
||||||
u8 ctrl;
|
u8 ctrl;
|
||||||
|
struct mmc_host *mmc = host->mmc;
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
if (host->flags & SDHCI_DEVICE_DEAD) {
|
if (host->flags & SDHCI_DEVICE_DEAD) {
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
if (host->vmmc && ios->power_mode == MMC_POWER_OFF)
|
if (!IS_ERR(mmc->supply.vmmc) &&
|
||||||
mmc_regulator_set_ocr(host->mmc, host->vmmc, 0);
|
ios->power_mode == MMC_POWER_OFF)
|
||||||
|
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
|
||||||
return;
|
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);
|
host->ops->set_clock(host, host->clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Reset SD Clock Enable */
|
/* Reset SD Clock Enable */
|
||||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||||
clk &= ~SDHCI_CLOCK_CARD_EN;
|
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,
|
static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
|
||||||
struct mmc_ios *ios)
|
struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
|
struct mmc_host *mmc = host->mmc;
|
||||||
u16 ctrl;
|
u16 ctrl;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -1725,11 +1729,12 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
|
||||||
ctrl &= ~SDHCI_CTRL_VDD_180;
|
ctrl &= ~SDHCI_CTRL_VDD_180;
|
||||||
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
||||||
|
|
||||||
if (host->vqmmc) {
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||||
ret = regulator_set_voltage(host->vqmmc, 2700000, 3600000);
|
ret = regulator_set_voltage(mmc->supply.vqmmc, 2700000,
|
||||||
|
3600000);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_warning("%s: Switching to 3.3V signalling voltage "
|
pr_warning("%s: Switching to 3.3V signalling voltage "
|
||||||
" failed\n", mmc_hostname(host->mmc));
|
" failed\n", mmc_hostname(mmc));
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1742,16 +1747,16 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pr_warning("%s: 3.3V regulator output did not became stable\n",
|
pr_warning("%s: 3.3V regulator output did not became stable\n",
|
||||||
mmc_hostname(host->mmc));
|
mmc_hostname(mmc));
|
||||||
|
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
case MMC_SIGNAL_VOLTAGE_180:
|
case MMC_SIGNAL_VOLTAGE_180:
|
||||||
if (host->vqmmc) {
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||||
ret = regulator_set_voltage(host->vqmmc,
|
ret = regulator_set_voltage(mmc->supply.vqmmc,
|
||||||
1700000, 1950000);
|
1700000, 1950000);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_warning("%s: Switching to 1.8V signalling voltage "
|
pr_warning("%s: Switching to 1.8V signalling voltage "
|
||||||
" failed\n", mmc_hostname(host->mmc));
|
" failed\n", mmc_hostname(mmc));
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1763,24 +1768,22 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
|
||||||
ctrl |= SDHCI_CTRL_VDD_180;
|
ctrl |= SDHCI_CTRL_VDD_180;
|
||||||
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
||||||
|
|
||||||
/* Wait for 5ms */
|
|
||||||
usleep_range(5000, 5500);
|
|
||||||
|
|
||||||
/* 1.8V regulator output should be stable within 5 ms */
|
/* 1.8V regulator output should be stable within 5 ms */
|
||||||
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||||
if (ctrl & SDHCI_CTRL_VDD_180)
|
if (ctrl & SDHCI_CTRL_VDD_180)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pr_warning("%s: 1.8V regulator output did not became stable\n",
|
pr_warning("%s: 1.8V regulator output did not became stable\n",
|
||||||
mmc_hostname(host->mmc));
|
mmc_hostname(mmc));
|
||||||
|
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
case MMC_SIGNAL_VOLTAGE_120:
|
case MMC_SIGNAL_VOLTAGE_120:
|
||||||
if (host->vqmmc) {
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||||
ret = regulator_set_voltage(host->vqmmc, 1100000, 1300000);
|
ret = regulator_set_voltage(mmc->supply.vqmmc, 1100000,
|
||||||
|
1300000);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_warning("%s: Switching to 1.2V signalling voltage "
|
pr_warning("%s: Switching to 1.2V signalling voltage "
|
||||||
" failed\n", mmc_hostname(host->mmc));
|
" failed\n", mmc_hostname(mmc));
|
||||||
return -EIO;
|
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)
|
int sdhci_runtime_suspend_host(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
/* Disable tuning since we are suspending */
|
/* Disable tuning since we are suspending */
|
||||||
if (host->flags & SDHCI_USING_RETUNING_TIMER) {
|
if (host->flags & SDHCI_USING_RETUNING_TIMER) {
|
||||||
|
@ -2663,14 +2665,14 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host)
|
||||||
host->runtime_suspended = true;
|
host->runtime_suspended = true;
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
|
EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
|
||||||
|
|
||||||
int sdhci_runtime_resume_host(struct sdhci_host *host)
|
int sdhci_runtime_resume_host(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
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_flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
||||||
if (host->ops->enable_dma)
|
if (host->ops->enable_dma)
|
||||||
|
@ -2709,7 +2711,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
|
||||||
|
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host);
|
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
|
* (128) and potentially one alignment transfer for
|
||||||
* each of those entries.
|
* 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,
|
ADMA_SIZE, &host->adma_addr,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
host->align_buffer = kmalloc(128 * 4, GFP_KERNEL);
|
host->align_buffer = kmalloc(128 * 4, GFP_KERNEL);
|
||||||
if (!host->adma_desc || !host->align_buffer) {
|
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);
|
host->adma_desc, host->adma_addr);
|
||||||
kfree(host->align_buffer);
|
kfree(host->align_buffer);
|
||||||
pr_warning("%s: Unable to allocate ADMA "
|
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",
|
pr_warning("%s: unable to allocate aligned ADMA descriptor\n",
|
||||||
mmc_hostname(mmc));
|
mmc_hostname(mmc));
|
||||||
host->flags &= ~SDHCI_USE_ADMA;
|
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);
|
host->adma_desc, host->adma_addr);
|
||||||
kfree(host->align_buffer);
|
kfree(host->align_buffer);
|
||||||
host->adma_desc = NULL;
|
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))) {
|
if (!(host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA))) {
|
||||||
host->dma_mask = DMA_BIT_MASK(64);
|
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)
|
if (host->version >= SDHCI_SPEC_300)
|
||||||
|
@ -2959,20 +2961,17 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
|
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
|
||||||
|
|
||||||
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
|
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;
|
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 */
|
/* 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(mmc->supply.vqmmc)) {
|
||||||
if (IS_ERR_OR_NULL(host->vqmmc)) {
|
ret = regulator_enable(mmc->supply.vqmmc);
|
||||||
if (PTR_ERR(host->vqmmc) < 0) {
|
if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000,
|
||||||
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))
|
1950000))
|
||||||
caps[1] &= ~(SDHCI_SUPPORT_SDR104 |
|
caps[1] &= ~(SDHCI_SUPPORT_SDR104 |
|
||||||
SDHCI_SUPPORT_SDR50 |
|
SDHCI_SUPPORT_SDR50 |
|
||||||
|
@ -2980,7 +2979,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_warn("%s: Failed to enable vqmmc regulator: %d\n",
|
pr_warn("%s: Failed to enable vqmmc regulator: %d\n",
|
||||||
mmc_hostname(mmc), ret);
|
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;
|
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
|
* 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
|
* 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.
|
* value.
|
||||||
*/
|
*/
|
||||||
max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
|
max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
|
||||||
if (!max_current_caps && host->vmmc) {
|
if (!max_current_caps && !IS_ERR(mmc->supply.vmmc)) {
|
||||||
u32 curr = regulator_get_current_limit(host->vmmc);
|
u32 curr = regulator_get_current_limit(mmc->supply.vmmc);
|
||||||
if (curr > 0) {
|
if (curr > 0) {
|
||||||
|
|
||||||
/* convert to SDHCI_MAX_CURRENT format */
|
/* convert to SDHCI_MAX_CURRENT format */
|
||||||
|
@ -3118,8 +3089,12 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
SDHCI_MAX_CURRENT_MULTIPLIER;
|
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)
|
if (host->ocr_mask)
|
||||||
ocr_avail = host->ocr_mask;
|
ocr_avail &= host->ocr_mask;
|
||||||
|
|
||||||
mmc->ocr_avail = ocr_avail;
|
mmc->ocr_avail = ocr_avail;
|
||||||
mmc->ocr_avail_sdio = 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)
|
void sdhci_remove_host(struct sdhci_host *host, int dead)
|
||||||
{
|
{
|
||||||
|
struct mmc_host *mmc = host->mmc;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
if (dead) {
|
if (dead) {
|
||||||
|
@ -3282,7 +3258,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
|
||||||
|
|
||||||
if (host->mrq) {
|
if (host->mrq) {
|
||||||
pr_err("%s: Controller removed during "
|
pr_err("%s: Controller removed during "
|
||||||
" transfer!\n", mmc_hostname(host->mmc));
|
" transfer!\n", mmc_hostname(mmc));
|
||||||
|
|
||||||
host->mrq->cmd->error = -ENOMEDIUM;
|
host->mrq->cmd->error = -ENOMEDIUM;
|
||||||
tasklet_schedule(&host->finish_tasklet);
|
tasklet_schedule(&host->finish_tasklet);
|
||||||
|
@ -3293,7 +3269,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
|
||||||
|
|
||||||
sdhci_disable_card_detection(host);
|
sdhci_disable_card_detection(host);
|
||||||
|
|
||||||
mmc_remove_host(host->mmc);
|
mmc_remove_host(mmc);
|
||||||
|
|
||||||
#ifdef SDHCI_USE_LEDS_CLASS
|
#ifdef SDHCI_USE_LEDS_CLASS
|
||||||
led_classdev_unregister(&host->led);
|
led_classdev_unregister(&host->led);
|
||||||
|
@ -3310,18 +3286,14 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
|
||||||
|
|
||||||
tasklet_kill(&host->finish_tasklet);
|
tasklet_kill(&host->finish_tasklet);
|
||||||
|
|
||||||
if (host->vmmc) {
|
if (!IS_ERR(mmc->supply.vmmc))
|
||||||
regulator_disable(host->vmmc);
|
regulator_disable(mmc->supply.vmmc);
|
||||||
regulator_put(host->vmmc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (host->vqmmc) {
|
if (!IS_ERR(mmc->supply.vqmmc))
|
||||||
regulator_disable(host->vqmmc);
|
regulator_disable(mmc->supply.vqmmc);
|
||||||
regulator_put(host->vqmmc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (host->adma_desc)
|
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);
|
host->adma_desc, host->adma_addr);
|
||||||
kfree(host->align_buffer);
|
kfree(host->align_buffer);
|
||||||
|
|
||||||
|
|
|
@ -386,7 +386,7 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
|
||||||
struct sh_mmcif_plat_data *pdata,
|
struct sh_mmcif_plat_data *pdata,
|
||||||
enum dma_transfer_direction direction)
|
enum dma_transfer_direction direction)
|
||||||
{
|
{
|
||||||
struct dma_slave_config cfg;
|
struct dma_slave_config cfg = { 0, };
|
||||||
struct dma_chan *chan;
|
struct dma_chan *chan;
|
||||||
unsigned int slave_id;
|
unsigned int slave_id;
|
||||||
struct resource *res;
|
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 */
|
/* In the OF case the driver will get the slave ID from the DT */
|
||||||
cfg.slave_id = slave_id;
|
cfg.slave_id = slave_id;
|
||||||
cfg.direction = direction;
|
cfg.direction = direction;
|
||||||
|
|
||||||
|
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 = res->start + MMCIF_CE_DATA;
|
||||||
cfg.src_addr = 0;
|
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
ret = dmaengine_slave_config(chan, &cfg);
|
ret = dmaengine_slave_config(chan, &cfg);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dma_release_channel(chan);
|
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");
|
dev_err(&pdev->dev, "Get irq error\n");
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!res) {
|
reg = devm_ioremap_resource(&pdev->dev, res);
|
||||||
dev_err(&pdev->dev, "platform_get_resource error.\n");
|
if (IS_ERR(reg))
|
||||||
return -ENXIO;
|
return PTR_ERR(reg);
|
||||||
}
|
|
||||||
reg = ioremap(res->start, resource_size(res));
|
|
||||||
if (!reg) {
|
|
||||||
dev_err(&pdev->dev, "ioremap error.\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), &pdev->dev);
|
mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), &pdev->dev);
|
||||||
if (!mmc) {
|
if (!mmc)
|
||||||
ret = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto ealloch;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mmc_of_parse(mmc);
|
ret = mmc_of_parse(mmc);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto eofparse;
|
goto err_host;
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
host->mmc = mmc;
|
host->mmc = mmc;
|
||||||
|
@ -1427,19 +1427,19 @@ static int sh_mmcif_probe(struct platform_device *pdev)
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
host->power = false;
|
host->power = false;
|
||||||
|
|
||||||
host->hclk = clk_get(&pdev->dev, NULL);
|
host->hclk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(host->hclk)) {
|
if (IS_ERR(host->hclk)) {
|
||||||
ret = PTR_ERR(host->hclk);
|
ret = PTR_ERR(host->hclk);
|
||||||
dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
|
dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
|
||||||
goto eclkget;
|
goto err_pm;
|
||||||
}
|
}
|
||||||
ret = sh_mmcif_clk_update(host);
|
ret = sh_mmcif_clk_update(host);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto eclkupdate;
|
goto err_pm;
|
||||||
|
|
||||||
ret = pm_runtime_resume(&pdev->dev);
|
ret = pm_runtime_resume(&pdev->dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto eresume;
|
goto err_clk;
|
||||||
|
|
||||||
INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work);
|
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);
|
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
|
||||||
|
|
||||||
name = irq[1] < 0 ? dev_name(&pdev->dev) : "sh_mmc:error";
|
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) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "request_irq error (%s)\n", name);
|
dev_err(&pdev->dev, "request_irq error (%s)\n", name);
|
||||||
goto ereqirq0;
|
goto err_clk;
|
||||||
}
|
}
|
||||||
if (irq[1] >= 0) {
|
if (irq[1] >= 0) {
|
||||||
ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt,
|
ret = devm_request_threaded_irq(&pdev->dev, irq[1],
|
||||||
|
sh_mmcif_intr, sh_mmcif_irqt,
|
||||||
0, "sh_mmc:int", host);
|
0, "sh_mmc:int", host);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n");
|
dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n");
|
||||||
goto ereqirq1;
|
goto err_clk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pd && pd->use_cd_gpio) {
|
if (pd && pd->use_cd_gpio) {
|
||||||
ret = mmc_gpio_request_cd(mmc, pd->cd_gpio, 0);
|
ret = mmc_gpio_request_cd(mmc, pd->cd_gpio, 0);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto erqcd;
|
goto err_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_init(&host->thread_lock);
|
mutex_init(&host->thread_lock);
|
||||||
|
|
||||||
clk_disable_unprepare(host->hclk);
|
|
||||||
ret = mmc_add_host(mmc);
|
ret = mmc_add_host(mmc);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto emmcaddh;
|
goto err_clk;
|
||||||
|
|
||||||
dev_pm_qos_expose_latency_limit(&pdev->dev, 100);
|
dev_pm_qos_expose_latency_limit(&pdev->dev, 100);
|
||||||
|
|
||||||
dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION);
|
dev_info(&pdev->dev, "Chip version 0x%04x, clock rate %luMHz\n",
|
||||||
dev_dbg(&pdev->dev, "chip ver H'%04x\n",
|
sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0xffff,
|
||||||
sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff);
|
clk_get_rate(host->hclk) / 1000000UL);
|
||||||
|
|
||||||
|
clk_disable_unprepare(host->hclk);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
emmcaddh:
|
err_clk:
|
||||||
erqcd:
|
|
||||||
if (irq[1] >= 0)
|
|
||||||
free_irq(irq[1], host);
|
|
||||||
ereqirq1:
|
|
||||||
free_irq(irq[0], host);
|
|
||||||
ereqirq0:
|
|
||||||
pm_runtime_suspend(&pdev->dev);
|
|
||||||
eresume:
|
|
||||||
clk_disable_unprepare(host->hclk);
|
clk_disable_unprepare(host->hclk);
|
||||||
eclkupdate:
|
err_pm:
|
||||||
clk_put(host->hclk);
|
|
||||||
eclkget:
|
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
eofparse:
|
err_host:
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
ealloch:
|
|
||||||
iounmap(reg);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sh_mmcif_remove(struct platform_device *pdev)
|
static int sh_mmcif_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sh_mmcif_host *host = platform_get_drvdata(pdev);
|
struct sh_mmcif_host *host = platform_get_drvdata(pdev);
|
||||||
int irq[2];
|
|
||||||
|
|
||||||
host->dying = true;
|
host->dying = true;
|
||||||
clk_prepare_enable(host->hclk);
|
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);
|
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);
|
clk_disable_unprepare(host->hclk);
|
||||||
mmc_free_host(host->mmc);
|
mmc_free_host(host->mmc);
|
||||||
pm_runtime_put_sync(&pdev->dev);
|
pm_runtime_put_sync(&pdev->dev);
|
||||||
|
|
|
@ -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.slave_id = pdata->dma->slave_id_tx;
|
||||||
cfg.direction = DMA_MEM_TO_DEV;
|
cfg.direction = DMA_MEM_TO_DEV;
|
||||||
cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->pdata->bus_shift);
|
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;
|
cfg.src_addr = 0;
|
||||||
ret = dmaengine_slave_config(host->chan_tx, &cfg);
|
ret = dmaengine_slave_config(host->chan_tx, &cfg);
|
||||||
if (ret < 0)
|
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.slave_id = pdata->dma->slave_id_rx;
|
||||||
cfg.direction = DMA_DEV_TO_MEM;
|
cfg.direction = DMA_DEV_TO_MEM;
|
||||||
cfg.src_addr = cfg.dst_addr;
|
cfg.src_addr = cfg.dst_addr;
|
||||||
|
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||||
cfg.dst_addr = 0;
|
cfg.dst_addr = 0;
|
||||||
ret = dmaengine_slave_config(host->chan_rx, &cfg);
|
ret = dmaengine_slave_config(host->chan_rx, &cfg);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
|
|
@ -72,7 +72,6 @@
|
||||||
#define BM_SPI_CS 0x20
|
#define BM_SPI_CS 0x20
|
||||||
#define BM_SD_POWER 0x40
|
#define BM_SD_POWER 0x40
|
||||||
#define BM_SOFT_RESET 0x80
|
#define BM_SOFT_RESET 0x80
|
||||||
#define BM_ONEBIT_MASK 0xFD
|
|
||||||
|
|
||||||
/* SDMMC_BLKLEN bit fields */
|
/* SDMMC_BLKLEN bit fields */
|
||||||
#define BLKL_CRCERR_ABORT 0x0800
|
#define BLKL_CRCERR_ABORT 0x0800
|
||||||
|
@ -120,6 +119,8 @@
|
||||||
#define STS2_DATARSP_BUSY 0x20
|
#define STS2_DATARSP_BUSY 0x20
|
||||||
#define STS2_DIS_FORCECLK 0x80
|
#define STS2_DIS_FORCECLK 0x80
|
||||||
|
|
||||||
|
/* SDMMC_EXTCTRL bit fields */
|
||||||
|
#define EXT_EIGHTBIT 0x04
|
||||||
|
|
||||||
/* MMC/SD DMA Controller Registers */
|
/* MMC/SD DMA Controller Registers */
|
||||||
#define SDDMA_GCR 0x100
|
#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)
|
static void wmt_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
struct wmt_mci_priv *priv;
|
struct wmt_mci_priv *priv;
|
||||||
u32 reg_tmp;
|
u32 busmode, extctrl;
|
||||||
|
|
||||||
priv = mmc_priv(mmc);
|
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)
|
if (ios->clock != 0)
|
||||||
clk_set_rate(priv->clk_sdmmc, ios->clock);
|
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) {
|
switch (ios->bus_width) {
|
||||||
case MMC_BUS_WIDTH_8:
|
case MMC_BUS_WIDTH_8:
|
||||||
reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL);
|
busmode |= BM_EIGHTBIT_MODE;
|
||||||
writeb(reg_tmp | 0x04, priv->sdmmc_base + SDMMC_EXTCTRL);
|
extctrl |= EXT_EIGHTBIT;
|
||||||
break;
|
break;
|
||||||
case MMC_BUS_WIDTH_4:
|
case MMC_BUS_WIDTH_4:
|
||||||
reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
|
busmode |= BM_FOURBIT_MODE;
|
||||||
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);
|
|
||||||
break;
|
break;
|
||||||
case MMC_BUS_WIDTH_1:
|
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;
|
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)
|
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;
|
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) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Register DMA IRQ fail\n");
|
dev_err(&pdev->dev, "Register DMA IRQ fail\n");
|
||||||
goto fail4;
|
goto fail4;
|
||||||
|
|
|
@ -213,6 +213,8 @@ struct dw_mci_dma_ops {
|
||||||
#define DW_MCI_QUIRK_HIGHSPEED BIT(2)
|
#define DW_MCI_QUIRK_HIGHSPEED BIT(2)
|
||||||
/* Unreliable card detection */
|
/* Unreliable card detection */
|
||||||
#define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3)
|
#define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3)
|
||||||
|
/* No write protect */
|
||||||
|
#define DW_MCI_QUIRK_NO_WRITE_PROTECT BIT(4)
|
||||||
|
|
||||||
/* Slot level quirks */
|
/* Slot level quirks */
|
||||||
/* This slot has no write protect */
|
/* This slot has no write protect */
|
||||||
|
|
|
@ -104,9 +104,6 @@ struct sdhci_host {
|
||||||
|
|
||||||
const struct sdhci_ops *ops; /* Low level hw interface */
|
const struct sdhci_ops *ops; /* Low level hw interface */
|
||||||
|
|
||||||
struct regulator *vmmc; /* Power regulator (vmmc) */
|
|
||||||
struct regulator *vqmmc; /* Signaling regulator (vccq) */
|
|
||||||
|
|
||||||
/* Internal data */
|
/* Internal data */
|
||||||
struct mmc_host *mmc; /* MMC structure */
|
struct mmc_host *mmc; /* MMC structure */
|
||||||
u64 dma_mask; /* custom DMA mask */
|
u64 dma_mask; /* custom DMA mask */
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
*/
|
*/
|
||||||
#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0)
|
#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0)
|
||||||
#define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1)
|
#define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1)
|
||||||
|
#define OMAP_HSMMC_SWAKEUP_MISSING BIT(2)
|
||||||
|
|
||||||
struct mmc_card;
|
struct mmc_card;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue