mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-05 18:39:59 +00:00
MMC core:
- Fix an error path in the mmc block layer - Fix PM domain attachment for the SDIO bus - Add support for driver strength selection - Increase a delay to let voltage stabilize - Add support for disabling write-protect detection - Add facility to support re-tuning - Re-tune and retry in the recovery path - Add reset option for SDIO - Consolidations and clean-ups MMC host: - Add Mediatek MMC driver - Constify platform_device_id for a couple of hosts - Fix modalias to make module auto-loading work for a couple of hosts - sdhci: Add support for sdhci-arasan4.9a - sdhci: Fix low memory corruption - sdhci: Restore behavior while creating OCR mask - sdhci: Add a callback to select drive strength - sdhci: Fix driver type B and D handling - sdhci: Add support for drive strength selection for SPT - sdhci: Enable HS400 for some Intel host controllers - sdhci: Convert to use the new re-tuning facility - sdhci: Various minor fixes and clean-ups - dw_mmc: Add support for hi6220 - dw_mmc: Use core to handle absent write protect line - dw_mmc: Add support to switch voltage - tmio: Some fixes and modernizations - sh_mmcif: Improve clock rate calculation -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVh+y8AAoJEP4mhCVzWIwp+fYQAIlZVzdLVCAgHHMKHlXeJ/Ib 5Ltf7Vi6kO9WJUQc+/X0kicidNjeOiw8qvItYeZpVvZCuzMnBXLHGt99G0ACvagw hS/4GsI+keNbBLTlSriZVxMdjEbB4RIkC5TWsHuhcP16L6UmgDVil3Hqf6qasGU2 cAr9lSUpe3uYlgyn7W5voolVWU2PNJq7Cy5E3y1052VbUsnhJZRvpsoCne++nG2O rJH9BQauVQ08VX3S6PcXYYDk2xe2rk7ExTiNw+tuMhjZkcd3dlKBRajoALpBqYFS nY3nSfMIVm1PmzpklrgF+7gUTdlihmi0ao1DsrFBHRISZ/ljb7yQ5Vma8Pg36NVK tdrZSBiOWhIXTgp+AIUSjuyv8S2wwsFozsmkVUNyA6XmfVmq2+lqz6rA7w+OIm7D iS9SBsAz1Iv6gpt388/Bb/yxULvYOVKECmORydu21yXxHT7eWTVfvQYHC9h40qhw oL86omRVFbeuouM1T867aFWKzaeA7hLXKbC4sWxdypyE2bPLraiC5izjlh4p3RzH UkQ4UUsym7b0UlL4lxKTUgRD8FuGK0ML+zeRsA1VgJmcCdzRaa2NFQfrX5aMVSgc iJ2zkHdxLCxkKTfcNn3k+B0PH3tMMn2cPIE7AhoVaIl79ACg9UB+/wG8f5pIifh5 pPCWXFcaJ7gJkYpFDTnJ =ofUZ -----END PGP SIGNATURE----- Merge tag 'mmc-v4.2' of git://git.linaro.org/people/ulf.hansson/mmc Pull MMC updates from Ulf Hansson: "Here are the changes for MMC for v4.2. MMC core: - Fix an error path in the mmc block layer - Fix PM domain attachment for the SDIO bus - Add support for driver strength selection - Increase a delay to let voltage stabilize - Add support for disabling write-protect detection - Add facility to support re-tuning - Re-tune and retry in the recovery path - Add reset option for SDIO - Consolidations and clean-ups MMC host: - Add Mediatek MMC driver - Constify platform_device_id for a couple of hosts - Fix modalias to make module auto-loading work for a couple of hosts - sdhci: Add support for sdhci-arasan4.9a - sdhci: Fix low memory corruption - sdhci: Restore behavior while creating OCR mask - sdhci: Add a callback to select drive strength - sdhci: Fix driver type B and D handling - sdhci: Add support for drive strength selection for SPT - sdhci: Enable HS400 for some Intel host controllers - sdhci: Convert to use the new re-tuning facility - sdhci: Various minor fixes and clean-ups - dw_mmc: Add support for hi6220 - dw_mmc: Use core to handle absent write protect line - dw_mmc: Add support to switch voltage - tmio: Some fixes and modernizations - sh_mmcif: Improve clock rate calculation" * tag 'mmc-v4.2' of git://git.linaro.org/people/ulf.hansson/mmc: (98 commits) mmc: queue: prevent soft lockups on PREEMPT=n mmc: mediatek: Add PM support for MMC driver mmc: mediatek: Add Mediatek MMC driver mmc: dt-bindings: add Mediatek MMC bindings mmc: card: Fixup request missing in mmc_blk_issue_rw_rq mmc: sdhci: fix low memory corruption mmc: sdhci-pci: Change AMD SDHCI quirk application scope i2c-piix4: Use Macro for AMD CZ SMBus device ID pci_ids: Add AMD KERNCZ device ID support mmc: queue: use swap() in mmc_queue_thread() mmc: dw_mmc: insmod followed by rmmod will hung for eMMC mmc: sdhci: Restore behavior while creating OCR mask mmc: sdhci-pxav3: fix device wakeup initialization mmc: core: Attach PM domain prior probing of SDIO func driver mmc: core: Remove redundant ->power_restore() callback for SD mmc: core: Remove redundant ->power_restore() callback for MMC mmc: sdhci-bcm2835: Actually enable the clock mmc: sdhci-bcm2835: Clean up platform allocations if sdhci init fails. mmc: sdhci-of-esdhc: enable interrupt mode to detect card mmc: sdhci-esdhc-imx: add quirk SDHCI_QUIRK2_BROKEN_HS200 for imx6qdl ...
This commit is contained in:
commit
a57f14bac0
67 changed files with 2727 additions and 607 deletions
|
@ -8,7 +8,8 @@ Device Tree Bindings for the Arasan SDHCI Controller
|
||||||
[3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
[3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||||
|
|
||||||
Required Properties:
|
Required Properties:
|
||||||
- compatible: Compatibility string. Must be 'arasan,sdhci-8.9a'
|
- compatible: Compatibility string. Must be 'arasan,sdhci-8.9a' or
|
||||||
|
'arasan,sdhci-4.9a'
|
||||||
- reg: From mmc bindings: Register location and length.
|
- reg: From mmc bindings: Register location and length.
|
||||||
- clocks: From clock bindings: Handles to clock inputs.
|
- clocks: From clock bindings: Handles to clock inputs.
|
||||||
- clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb"
|
- clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb"
|
||||||
|
|
|
@ -7,7 +7,14 @@ This file documents differences between the core properties described
|
||||||
by mmc.txt and the properties used by the sdhci-esdhc-imx driver.
|
by mmc.txt and the properties used by the sdhci-esdhc-imx driver.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : Should be "fsl,<chip>-esdhc"
|
- compatible : Should be "fsl,<chip>-esdhc", the supported chips include
|
||||||
|
"fsl,imx25-esdhc"
|
||||||
|
"fsl,imx35-esdhc"
|
||||||
|
"fsl,imx51-esdhc"
|
||||||
|
"fsl,imx53-esdhc"
|
||||||
|
"fsl,imx6q-usdhc"
|
||||||
|
"fsl,imx6sl-usdhc"
|
||||||
|
"fsl,imx6sx-usdhc"
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
- fsl,cd-controller : Indicate to use controller internal card detection
|
- fsl,cd-controller : Indicate to use controller internal card detection
|
||||||
|
|
|
@ -13,6 +13,10 @@ Required Properties:
|
||||||
|
|
||||||
* compatible: should be one of the following.
|
* compatible: should be one of the following.
|
||||||
- "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extensions.
|
- "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extensions.
|
||||||
|
- "hisilicon,hi6220-dw-mshc": for controllers with hi6220 specific extensions.
|
||||||
|
|
||||||
|
Optional Properties:
|
||||||
|
- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -42,3 +46,27 @@ Example:
|
||||||
cap-mmc-highspeed;
|
cap-mmc-highspeed;
|
||||||
cap-sd-highspeed;
|
cap-sd-highspeed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* for Hi6220 */
|
||||||
|
|
||||||
|
dwmmc_1: dwmmc1@f723e000 {
|
||||||
|
compatible = "hisilicon,hi6220-dw-mshc";
|
||||||
|
num-slots = <0x1>;
|
||||||
|
bus-width = <0x4>;
|
||||||
|
disable-wp;
|
||||||
|
cap-sd-highspeed;
|
||||||
|
sd-uhs-sdr12;
|
||||||
|
sd-uhs-sdr25;
|
||||||
|
card-detect-delay = <200>;
|
||||||
|
hisilicon,peripheral-syscon = <&ao_ctrl>;
|
||||||
|
reg = <0x0 0xf723e000 0x0 0x1000>;
|
||||||
|
interrupts = <0x0 0x49 0x4>;
|
||||||
|
clocks = <&clock_sys HI6220_MMC1_CIUCLK>, <&clock_sys HI6220_MMC1_CLK>;
|
||||||
|
clock-names = "ciu", "biu";
|
||||||
|
cd-gpios = <&gpio1 0 1>;
|
||||||
|
pinctrl-names = "default", "idle";
|
||||||
|
pinctrl-0 = <&sd_pmx_func &sd_clk_cfg_func &sd_cfg_func>;
|
||||||
|
pinctrl-1 = <&sd_pmx_idle &sd_clk_cfg_idle &sd_cfg_idle>;
|
||||||
|
vqmmc-supply = <&ldo7>;
|
||||||
|
vmmc-supply = <&ldo10>;
|
||||||
|
};
|
||||||
|
|
|
@ -21,5 +21,7 @@ Example:
|
||||||
|
|
||||||
sdhci0_pwrseq {
|
sdhci0_pwrseq {
|
||||||
compatible = "mmc-pwrseq-simple";
|
compatible = "mmc-pwrseq-simple";
|
||||||
reset-gpios = <&gpio1 12 0>;
|
reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
|
||||||
|
clocks = <&clk_32768_ck>;
|
||||||
|
clock-names = "ext_clock";
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,11 @@ Optional properties:
|
||||||
below for the case, when a GPIO is used for the CD line
|
below for the case, when a GPIO is used for the CD line
|
||||||
- wp-inverted: when present, polarity on the WP line is inverted. See the note
|
- wp-inverted: when present, polarity on the WP line is inverted. See the note
|
||||||
below for the case, when a GPIO is used for the WP line
|
below for the case, when a GPIO is used for the WP line
|
||||||
|
- disable-wp: When set no physical WP line is present. This property should
|
||||||
|
only be specified when the controller has a dedicated write-protect
|
||||||
|
detection logic. If a GPIO is always used for the write-protect detection
|
||||||
|
logic it is sufficient to not specify wp-gpios property in the absence of a WP
|
||||||
|
line.
|
||||||
- max-frequency: maximum operating clock frequency
|
- max-frequency: maximum operating clock frequency
|
||||||
- no-1-8-v: when present, denotes that 1.8v card voltage is not supported on
|
- no-1-8-v: when present, denotes that 1.8v card voltage is not supported on
|
||||||
this system, even if the controller claims it is.
|
this system, even if the controller claims it is.
|
||||||
|
|
32
Documentation/devicetree/bindings/mmc/mtk-sd.txt
Normal file
32
Documentation/devicetree/bindings/mmc/mtk-sd.txt
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
* MTK MMC controller
|
||||||
|
|
||||||
|
The MTK MSDC can act as a MMC controller
|
||||||
|
to support MMC, SD, and SDIO types of memory cards.
|
||||||
|
|
||||||
|
This file documents differences between the core properties in mmc.txt
|
||||||
|
and the properties used by the msdc driver.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: Should be "mediatek,mt8173-mmc","mediatek,mt8135-mmc"
|
||||||
|
- interrupts: Should contain MSDC interrupt number
|
||||||
|
- clocks: MSDC source clock, HCLK
|
||||||
|
- clock-names: "source", "hclk"
|
||||||
|
- pinctrl-names: should be "default", "state_uhs"
|
||||||
|
- pinctrl-0: should contain default/high speed pin ctrl
|
||||||
|
- pinctrl-1: should contain uhs mode pin ctrl
|
||||||
|
- vmmc-supply: power to the Core
|
||||||
|
- vqmmc-supply: power to the IO
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
mmc0: mmc@11230000 {
|
||||||
|
compatible = "mediatek,mt8173-mmc", "mediatek,mt8135-mmc";
|
||||||
|
reg = <0 0x11230000 0 0x108>;
|
||||||
|
interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_LOW>;
|
||||||
|
vmmc-supply = <&mt6397_vemc_3v3_reg>;
|
||||||
|
vqmmc-supply = <&mt6397_vio18_reg>;
|
||||||
|
clocks = <&pericfg CLK_PERI_MSDC30_0>, <&topckgen CLK_TOP_MSDC50_0_H_SEL>;
|
||||||
|
clock-names = "source", "hclk";
|
||||||
|
pinctrl-names = "default", "state_uhs";
|
||||||
|
pinctrl-0 = <&mmc0_pins_default>;
|
||||||
|
pinctrl-1 = <&mmc0_pins_uhs>;
|
||||||
|
};
|
|
@ -18,6 +18,8 @@ Required properties:
|
||||||
dma-names property.
|
dma-names property.
|
||||||
- dma-names: must contain "tx" for the transmit DMA channel and "rx" for the
|
- dma-names: must contain "tx" for the transmit DMA channel and "rx" for the
|
||||||
receive DMA channel.
|
receive DMA channel.
|
||||||
|
- max-frequency: Maximum operating clock frequency, driver uses default clock
|
||||||
|
frequency if it is not set.
|
||||||
|
|
||||||
|
|
||||||
Example: R8A7790 (R-Car H2) MMCIF0
|
Example: R8A7790 (R-Car H2) MMCIF0
|
||||||
|
@ -29,4 +31,5 @@ Example: R8A7790 (R-Car H2) MMCIF0
|
||||||
clocks = <&mstp3_clks R8A7790_CLK_MMCIF0>;
|
clocks = <&mstp3_clks R8A7790_CLK_MMCIF0>;
|
||||||
dmas = <&dmac0 0xd1>, <&dmac0 0xd2>;
|
dmas = <&dmac0 0xd1>, <&dmac0 0xd2>;
|
||||||
dma-names = "tx", "rx";
|
dma-names = "tx", "rx";
|
||||||
|
max-frequency = <97500000>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -10033,7 +10033,7 @@ F: include/linux/toshiba.h
|
||||||
F: include/uapi/linux/toshiba.h
|
F: include/uapi/linux/toshiba.h
|
||||||
|
|
||||||
TMIO MMC DRIVER
|
TMIO MMC DRIVER
|
||||||
M: Ian Molton <ian.molton@codethink.co.uk>
|
M: Ian Molton <ian@mnementh.co.uk>
|
||||||
L: linux-mmc@vger.kernel.org
|
L: linux-mmc@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/mmc/host/tmio_mmc*
|
F: drivers/mmc/host/tmio_mmc*
|
||||||
|
|
|
@ -370,6 +370,7 @@ spi@110000 {
|
||||||
|
|
||||||
/include/ "qoriq-esdhc-0.dtsi"
|
/include/ "qoriq-esdhc-0.dtsi"
|
||||||
sdhc@114000 {
|
sdhc@114000 {
|
||||||
|
compatible = "fsl,p2041-esdhc", "fsl,esdhc";
|
||||||
fsl,iommu-parent = <&pamu1>;
|
fsl,iommu-parent = <&pamu1>;
|
||||||
fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
|
fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
|
||||||
sdhci,auto-cmd12;
|
sdhci,auto-cmd12;
|
||||||
|
|
|
@ -397,6 +397,7 @@ spi@110000 {
|
||||||
|
|
||||||
/include/ "qoriq-esdhc-0.dtsi"
|
/include/ "qoriq-esdhc-0.dtsi"
|
||||||
sdhc@114000 {
|
sdhc@114000 {
|
||||||
|
compatible = "fsl,p3041-esdhc", "fsl,esdhc";
|
||||||
fsl,iommu-parent = <&pamu1>;
|
fsl,iommu-parent = <&pamu1>;
|
||||||
fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
|
fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
|
||||||
sdhci,auto-cmd12;
|
sdhci,auto-cmd12;
|
||||||
|
|
|
@ -469,6 +469,7 @@ spi@110000 {
|
||||||
|
|
||||||
/include/ "qoriq-esdhc-0.dtsi"
|
/include/ "qoriq-esdhc-0.dtsi"
|
||||||
sdhc@114000 {
|
sdhc@114000 {
|
||||||
|
compatible = "fsl,p4080-esdhc", "fsl,esdhc";
|
||||||
fsl,iommu-parent = <&pamu1>;
|
fsl,iommu-parent = <&pamu1>;
|
||||||
fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
|
fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
|
||||||
voltage-ranges = <3300 3300>;
|
voltage-ranges = <3300 3300>;
|
||||||
|
|
|
@ -384,6 +384,7 @@ spi@110000 {
|
||||||
|
|
||||||
/include/ "qoriq-esdhc-0.dtsi"
|
/include/ "qoriq-esdhc-0.dtsi"
|
||||||
sdhc@114000 {
|
sdhc@114000 {
|
||||||
|
compatible = "fsl,p5020-esdhc", "fsl,esdhc";
|
||||||
fsl,iommu-parent = <&pamu1>;
|
fsl,iommu-parent = <&pamu1>;
|
||||||
fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
|
fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
|
||||||
sdhci,auto-cmd12;
|
sdhci,auto-cmd12;
|
||||||
|
|
|
@ -362,6 +362,7 @@ spi@110000 {
|
||||||
|
|
||||||
/include/ "qoriq-esdhc-0.dtsi"
|
/include/ "qoriq-esdhc-0.dtsi"
|
||||||
sdhc@114000 {
|
sdhc@114000 {
|
||||||
|
compatible = "fsl,p5040-esdhc", "fsl,esdhc";
|
||||||
fsl,iommu-parent = <&pamu2>;
|
fsl,iommu-parent = <&pamu2>;
|
||||||
fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
|
fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
|
||||||
sdhci,auto-cmd12;
|
sdhci,auto-cmd12;
|
||||||
|
|
|
@ -245,7 +245,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
|
||||||
PIIX4_dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS &&
|
PIIX4_dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS &&
|
||||||
PIIX4_dev->revision >= 0x41) ||
|
PIIX4_dev->revision >= 0x41) ||
|
||||||
(PIIX4_dev->vendor == PCI_VENDOR_ID_AMD &&
|
(PIIX4_dev->vendor == PCI_VENDOR_ID_AMD &&
|
||||||
PIIX4_dev->device == 0x790b &&
|
PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
|
||||||
PIIX4_dev->revision >= 0x49))
|
PIIX4_dev->revision >= 0x49))
|
||||||
smb_en = 0x00;
|
smb_en = 0x00;
|
||||||
else
|
else
|
||||||
|
@ -545,7 +545,7 @@ static const struct pci_device_id piix4_ids[] = {
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x790b) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
|
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
|
||||||
PCI_DEVICE_ID_SERVERWORKS_OSB4) },
|
PCI_DEVICE_ID_SERVERWORKS_OSB4) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
|
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
|
||||||
|
|
|
@ -913,6 +913,9 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
|
||||||
if (!err)
|
if (!err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* Re-tune if needed */
|
||||||
|
mmc_retune_recheck(card->host);
|
||||||
|
|
||||||
prev_cmd_status_valid = false;
|
prev_cmd_status_valid = false;
|
||||||
pr_err("%s: error %d sending status command, %sing\n",
|
pr_err("%s: error %d sending status command, %sing\n",
|
||||||
req->rq_disk->disk_name, err, retry ? "retry" : "abort");
|
req->rq_disk->disk_name, err, retry ? "retry" : "abort");
|
||||||
|
@ -1204,6 +1207,7 @@ static int mmc_blk_err_check(struct mmc_card *card,
|
||||||
mmc_active);
|
mmc_active);
|
||||||
struct mmc_blk_request *brq = &mq_mrq->brq;
|
struct mmc_blk_request *brq = &mq_mrq->brq;
|
||||||
struct request *req = mq_mrq->req;
|
struct request *req = mq_mrq->req;
|
||||||
|
int need_retune = card->host->need_retune;
|
||||||
int ecc_err = 0, gen_err = 0;
|
int ecc_err = 0, gen_err = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1271,6 +1275,12 @@ static int mmc_blk_err_check(struct mmc_card *card,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (brq->data.error) {
|
if (brq->data.error) {
|
||||||
|
if (need_retune && !brq->retune_retry_done) {
|
||||||
|
pr_info("%s: retrying because a re-tune was needed\n",
|
||||||
|
req->rq_disk->disk_name);
|
||||||
|
brq->retune_retry_done = 1;
|
||||||
|
return MMC_BLK_RETRY;
|
||||||
|
}
|
||||||
pr_err("%s: error %d transferring data, sector %u, nr %u, cmd response %#x, card status %#x\n",
|
pr_err("%s: error %d transferring data, sector %u, nr %u, cmd response %#x, card status %#x\n",
|
||||||
req->rq_disk->disk_name, brq->data.error,
|
req->rq_disk->disk_name, brq->data.error,
|
||||||
(unsigned)blk_rq_pos(req),
|
(unsigned)blk_rq_pos(req),
|
||||||
|
@ -1830,7 +1840,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
||||||
struct mmc_blk_data *md = mq->data;
|
struct mmc_blk_data *md = mq->data;
|
||||||
struct mmc_card *card = md->queue.card;
|
struct mmc_card *card = md->queue.card;
|
||||||
struct mmc_blk_request *brq = &mq->mqrq_cur->brq;
|
struct mmc_blk_request *brq = &mq->mqrq_cur->brq;
|
||||||
int ret = 1, disable_multi = 0, retry = 0, type;
|
int ret = 1, disable_multi = 0, retry = 0, type, retune_retry_done = 0;
|
||||||
enum mmc_blk_status status;
|
enum mmc_blk_status status;
|
||||||
struct mmc_queue_req *mq_rq;
|
struct mmc_queue_req *mq_rq;
|
||||||
struct request *req = rqc;
|
struct request *req = rqc;
|
||||||
|
@ -1910,10 +1920,13 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
||||||
break;
|
break;
|
||||||
case MMC_BLK_CMD_ERR:
|
case MMC_BLK_CMD_ERR:
|
||||||
ret = mmc_blk_cmd_err(md, card, brq, req, ret);
|
ret = mmc_blk_cmd_err(md, card, brq, req, ret);
|
||||||
if (!mmc_blk_reset(md, card->host, type))
|
if (mmc_blk_reset(md, card->host, type))
|
||||||
break;
|
|
||||||
goto cmd_abort;
|
goto cmd_abort;
|
||||||
|
if (!ret)
|
||||||
|
goto start_new_req;
|
||||||
|
break;
|
||||||
case MMC_BLK_RETRY:
|
case MMC_BLK_RETRY:
|
||||||
|
retune_retry_done = brq->retune_retry_done;
|
||||||
if (retry++ < 5)
|
if (retry++ < 5)
|
||||||
break;
|
break;
|
||||||
/* Fall through */
|
/* Fall through */
|
||||||
|
@ -1976,6 +1989,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
||||||
mmc_start_req(card->host,
|
mmc_start_req(card->host,
|
||||||
&mq_rq->mmc_active, NULL);
|
&mq_rq->mmc_active, NULL);
|
||||||
}
|
}
|
||||||
|
mq_rq->brq.retune_retry_done = retune_retry_done;
|
||||||
}
|
}
|
||||||
} while (ret);
|
} while (ret);
|
||||||
|
|
||||||
|
@ -2217,7 +2231,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
||||||
* The CSD capacity field is in units of read_blkbits.
|
* The CSD capacity field is in units of read_blkbits.
|
||||||
* set_capacity takes units of 512 bytes.
|
* set_capacity takes units of 512 bytes.
|
||||||
*/
|
*/
|
||||||
size = card->csd.capacity << (card->csd.read_blkbits - 9);
|
size = (typeof(sector_t))card->csd.capacity
|
||||||
|
<< (card->csd.read_blkbits - 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
|
return mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
|
||||||
|
|
|
@ -268,8 +268,6 @@ static int mmc_test_wait_busy(struct mmc_test_card *test)
|
||||||
static int mmc_test_buffer_transfer(struct mmc_test_card *test,
|
static int mmc_test_buffer_transfer(struct mmc_test_card *test,
|
||||||
u8 *buffer, unsigned addr, unsigned blksz, int write)
|
u8 *buffer, unsigned addr, unsigned blksz, int write)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
|
|
||||||
struct mmc_request mrq = {0};
|
struct mmc_request mrq = {0};
|
||||||
struct mmc_command cmd = {0};
|
struct mmc_command cmd = {0};
|
||||||
struct mmc_command stop = {0};
|
struct mmc_command stop = {0};
|
||||||
|
@ -292,11 +290,7 @@ static int mmc_test_buffer_transfer(struct mmc_test_card *test,
|
||||||
if (data.error)
|
if (data.error)
|
||||||
return data.error;
|
return data.error;
|
||||||
|
|
||||||
ret = mmc_test_wait_busy(test);
|
return mmc_test_wait_busy(test);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mmc_test_free_mem(struct mmc_test_mem *mem)
|
static void mmc_test_free_mem(struct mmc_test_mem *mem)
|
||||||
|
@ -826,9 +820,7 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
|
||||||
mmc_test_nonblock_reset(&mrq1, &cmd1,
|
mmc_test_nonblock_reset(&mrq1, &cmd1,
|
||||||
&stop1, &data1);
|
&stop1, &data1);
|
||||||
}
|
}
|
||||||
done_areq = cur_areq;
|
swap(cur_areq, other_areq);
|
||||||
cur_areq = other_areq;
|
|
||||||
other_areq = done_areq;
|
|
||||||
dev_addr += blocks;
|
dev_addr += blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -994,11 +986,7 @@ static int mmc_test_basic_write(struct mmc_test_card *test)
|
||||||
|
|
||||||
sg_init_one(&sg, test->buffer, 512);
|
sg_init_one(&sg, test->buffer, 512);
|
||||||
|
|
||||||
ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);
|
return mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_test_basic_read(struct mmc_test_card *test)
|
static int mmc_test_basic_read(struct mmc_test_card *test)
|
||||||
|
@ -1012,44 +1000,29 @@ static int mmc_test_basic_read(struct mmc_test_card *test)
|
||||||
|
|
||||||
sg_init_one(&sg, test->buffer, 512);
|
sg_init_one(&sg, test->buffer, 512);
|
||||||
|
|
||||||
ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 0);
|
return mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 0);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_test_verify_write(struct mmc_test_card *test)
|
static int mmc_test_verify_write(struct mmc_test_card *test)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
|
||||||
sg_init_one(&sg, test->buffer, 512);
|
sg_init_one(&sg, test->buffer, 512);
|
||||||
|
|
||||||
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
|
return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_test_verify_read(struct mmc_test_card *test)
|
static int mmc_test_verify_read(struct mmc_test_card *test)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
|
||||||
sg_init_one(&sg, test->buffer, 512);
|
sg_init_one(&sg, test->buffer, 512);
|
||||||
|
|
||||||
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
|
return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_test_multi_write(struct mmc_test_card *test)
|
static int mmc_test_multi_write(struct mmc_test_card *test)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
unsigned int size;
|
unsigned int size;
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
|
||||||
|
@ -1066,16 +1039,11 @@ static int mmc_test_multi_write(struct mmc_test_card *test)
|
||||||
|
|
||||||
sg_init_one(&sg, test->buffer, size);
|
sg_init_one(&sg, test->buffer, size);
|
||||||
|
|
||||||
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
|
return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_test_multi_read(struct mmc_test_card *test)
|
static int mmc_test_multi_read(struct mmc_test_card *test)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
unsigned int size;
|
unsigned int size;
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
|
||||||
|
@ -1092,11 +1060,7 @@ static int mmc_test_multi_read(struct mmc_test_card *test)
|
||||||
|
|
||||||
sg_init_one(&sg, test->buffer, size);
|
sg_init_one(&sg, test->buffer, size);
|
||||||
|
|
||||||
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
|
return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_test_pow2_write(struct mmc_test_card *test)
|
static int mmc_test_pow2_write(struct mmc_test_card *test)
|
||||||
|
@ -1263,11 +1227,7 @@ static int mmc_test_xfersize_write(struct mmc_test_card *test)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = mmc_test_broken_transfer(test, 1, 512, 1);
|
return mmc_test_broken_transfer(test, 1, 512, 1);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_test_xfersize_read(struct mmc_test_card *test)
|
static int mmc_test_xfersize_read(struct mmc_test_card *test)
|
||||||
|
@ -1278,11 +1238,7 @@ static int mmc_test_xfersize_read(struct mmc_test_card *test)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = mmc_test_broken_transfer(test, 1, 512, 0);
|
return mmc_test_broken_transfer(test, 1, 512, 0);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_test_multi_xfersize_write(struct mmc_test_card *test)
|
static int mmc_test_multi_xfersize_write(struct mmc_test_card *test)
|
||||||
|
@ -1296,11 +1252,7 @@ static int mmc_test_multi_xfersize_write(struct mmc_test_card *test)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = mmc_test_broken_transfer(test, 2, 512, 1);
|
return mmc_test_broken_transfer(test, 2, 512, 1);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
|
static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
|
||||||
|
@ -1314,48 +1266,33 @@ static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = mmc_test_broken_transfer(test, 2, 512, 0);
|
return mmc_test_broken_transfer(test, 2, 512, 0);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_HIGHMEM
|
#ifdef CONFIG_HIGHMEM
|
||||||
|
|
||||||
static int mmc_test_write_high(struct mmc_test_card *test)
|
static int mmc_test_write_high(struct mmc_test_card *test)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
|
||||||
sg_init_table(&sg, 1);
|
sg_init_table(&sg, 1);
|
||||||
sg_set_page(&sg, test->highmem, 512, 0);
|
sg_set_page(&sg, test->highmem, 512, 0);
|
||||||
|
|
||||||
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
|
return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_test_read_high(struct mmc_test_card *test)
|
static int mmc_test_read_high(struct mmc_test_card *test)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
|
||||||
sg_init_table(&sg, 1);
|
sg_init_table(&sg, 1);
|
||||||
sg_set_page(&sg, test->highmem, 512, 0);
|
sg_set_page(&sg, test->highmem, 512, 0);
|
||||||
|
|
||||||
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
|
return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_test_multi_write_high(struct mmc_test_card *test)
|
static int mmc_test_multi_write_high(struct mmc_test_card *test)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
unsigned int size;
|
unsigned int size;
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
|
||||||
|
@ -1373,16 +1310,11 @@ static int mmc_test_multi_write_high(struct mmc_test_card *test)
|
||||||
sg_init_table(&sg, 1);
|
sg_init_table(&sg, 1);
|
||||||
sg_set_page(&sg, test->highmem, size, 0);
|
sg_set_page(&sg, test->highmem, size, 0);
|
||||||
|
|
||||||
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
|
return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_test_multi_read_high(struct mmc_test_card *test)
|
static int mmc_test_multi_read_high(struct mmc_test_card *test)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
unsigned int size;
|
unsigned int size;
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
|
||||||
|
@ -1400,11 +1332,7 @@ static int mmc_test_multi_read_high(struct mmc_test_card *test)
|
||||||
sg_init_table(&sg, 1);
|
sg_init_table(&sg, 1);
|
||||||
sg_set_page(&sg, test->highmem, size, 0);
|
sg_set_page(&sg, test->highmem, size, 0);
|
||||||
|
|
||||||
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
|
return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -56,7 +56,6 @@ static int mmc_queue_thread(void *d)
|
||||||
down(&mq->thread_sem);
|
down(&mq->thread_sem);
|
||||||
do {
|
do {
|
||||||
struct request *req = NULL;
|
struct request *req = NULL;
|
||||||
struct mmc_queue_req *tmp;
|
|
||||||
unsigned int cmd_flags = 0;
|
unsigned int cmd_flags = 0;
|
||||||
|
|
||||||
spin_lock_irq(q->queue_lock);
|
spin_lock_irq(q->queue_lock);
|
||||||
|
@ -69,6 +68,7 @@ static int mmc_queue_thread(void *d)
|
||||||
set_current_state(TASK_RUNNING);
|
set_current_state(TASK_RUNNING);
|
||||||
cmd_flags = req ? req->cmd_flags : 0;
|
cmd_flags = req ? req->cmd_flags : 0;
|
||||||
mq->issue_fn(mq, req);
|
mq->issue_fn(mq, req);
|
||||||
|
cond_resched();
|
||||||
if (mq->flags & MMC_QUEUE_NEW_REQUEST) {
|
if (mq->flags & MMC_QUEUE_NEW_REQUEST) {
|
||||||
mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
|
mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
|
||||||
continue; /* fetch again */
|
continue; /* fetch again */
|
||||||
|
@ -86,9 +86,7 @@ static int mmc_queue_thread(void *d)
|
||||||
|
|
||||||
mq->mqrq_prev->brq.mrq.data = NULL;
|
mq->mqrq_prev->brq.mrq.data = NULL;
|
||||||
mq->mqrq_prev->req = NULL;
|
mq->mqrq_prev->req = NULL;
|
||||||
tmp = mq->mqrq_prev;
|
swap(mq->mqrq_prev, mq->mqrq_cur);
|
||||||
mq->mqrq_prev = mq->mqrq_cur;
|
|
||||||
mq->mqrq_cur = tmp;
|
|
||||||
} else {
|
} else {
|
||||||
if (kthread_should_stop()) {
|
if (kthread_should_stop()) {
|
||||||
set_current_state(TASK_RUNNING);
|
set_current_state(TASK_RUNNING);
|
||||||
|
|
|
@ -12,6 +12,7 @@ struct mmc_blk_request {
|
||||||
struct mmc_command cmd;
|
struct mmc_command cmd;
|
||||||
struct mmc_command stop;
|
struct mmc_command stop;
|
||||||
struct mmc_data data;
|
struct mmc_data data;
|
||||||
|
int retune_retry_done;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum mmc_packed_type {
|
enum mmc_packed_type {
|
||||||
|
|
|
@ -133,6 +133,12 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
|
||||||
struct mmc_command *cmd = mrq->cmd;
|
struct mmc_command *cmd = mrq->cmd;
|
||||||
int err = cmd->error;
|
int err = cmd->error;
|
||||||
|
|
||||||
|
/* Flag re-tuning needed on CRC errors */
|
||||||
|
if (err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) ||
|
||||||
|
(mrq->data && mrq->data->error == -EILSEQ) ||
|
||||||
|
(mrq->stop && mrq->stop->error == -EILSEQ))
|
||||||
|
mmc_retune_needed(host);
|
||||||
|
|
||||||
if (err && cmd->retries && mmc_host_is_spi(host)) {
|
if (err && cmd->retries && mmc_host_is_spi(host)) {
|
||||||
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
|
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
|
||||||
cmd->retries = 0;
|
cmd->retries = 0;
|
||||||
|
@ -186,12 +192,29 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
|
||||||
|
|
||||||
EXPORT_SYMBOL(mmc_request_done);
|
EXPORT_SYMBOL(mmc_request_done);
|
||||||
|
|
||||||
|
static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Assumes host controller has been runtime resumed by mmc_claim_host */
|
||||||
|
err = mmc_retune(host);
|
||||||
|
if (err) {
|
||||||
|
mrq->cmd->error = err;
|
||||||
|
mmc_request_done(host, mrq);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
host->ops->request(host, mrq);
|
||||||
|
}
|
||||||
|
|
||||||
static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_MMC_DEBUG
|
#ifdef CONFIG_MMC_DEBUG
|
||||||
unsigned int i, sz;
|
unsigned int i, sz;
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
#endif
|
#endif
|
||||||
|
mmc_retune_hold(host);
|
||||||
|
|
||||||
if (mmc_card_removed(host->card))
|
if (mmc_card_removed(host->card))
|
||||||
return -ENOMEDIUM;
|
return -ENOMEDIUM;
|
||||||
|
|
||||||
|
@ -252,7 +275,7 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||||
}
|
}
|
||||||
mmc_host_clk_hold(host);
|
mmc_host_clk_hold(host);
|
||||||
led_trigger_event(host->led, LED_FULL);
|
led_trigger_event(host->led, LED_FULL);
|
||||||
host->ops->request(host, mrq);
|
__mmc_start_request(host, mrq);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -301,12 +324,15 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
|
||||||
use_busy_signal = false;
|
use_busy_signal = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mmc_retune_hold(card->host);
|
||||||
|
|
||||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
EXT_CSD_BKOPS_START, 1, timeout,
|
EXT_CSD_BKOPS_START, 1, timeout,
|
||||||
use_busy_signal, true, false);
|
use_busy_signal, true, false);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_warn("%s: Error %d starting bkops\n",
|
pr_warn("%s: Error %d starting bkops\n",
|
||||||
mmc_hostname(card->host), err);
|
mmc_hostname(card->host), err);
|
||||||
|
mmc_retune_release(card->host);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,6 +343,8 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
|
||||||
*/
|
*/
|
||||||
if (!use_busy_signal)
|
if (!use_busy_signal)
|
||||||
mmc_card_set_doing_bkops(card);
|
mmc_card_set_doing_bkops(card);
|
||||||
|
else
|
||||||
|
mmc_retune_release(card->host);
|
||||||
out:
|
out:
|
||||||
mmc_release_host(card->host);
|
mmc_release_host(card->host);
|
||||||
}
|
}
|
||||||
|
@ -417,22 +445,22 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
|
||||||
host->areq);
|
host->areq);
|
||||||
break; /* return err */
|
break; /* return err */
|
||||||
} else {
|
} else {
|
||||||
|
mmc_retune_recheck(host);
|
||||||
pr_info("%s: req failed (CMD%u): %d, retrying...\n",
|
pr_info("%s: req failed (CMD%u): %d, retrying...\n",
|
||||||
mmc_hostname(host),
|
mmc_hostname(host),
|
||||||
cmd->opcode, cmd->error);
|
cmd->opcode, cmd->error);
|
||||||
cmd->retries--;
|
cmd->retries--;
|
||||||
cmd->error = 0;
|
cmd->error = 0;
|
||||||
host->ops->request(host, mrq);
|
__mmc_start_request(host, mrq);
|
||||||
continue; /* wait for done/new event again */
|
continue; /* wait for done/new event again */
|
||||||
}
|
}
|
||||||
} else if (context_info->is_new_req) {
|
} else if (context_info->is_new_req) {
|
||||||
context_info->is_new_req = false;
|
context_info->is_new_req = false;
|
||||||
if (!next_req) {
|
if (!next_req)
|
||||||
err = MMC_BLK_NEW_REQUEST;
|
return MMC_BLK_NEW_REQUEST;
|
||||||
break; /* return err */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mmc_retune_release(host);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,12 +495,16 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
|
||||||
mmc_card_removed(host->card))
|
mmc_card_removed(host->card))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
mmc_retune_recheck(host);
|
||||||
|
|
||||||
pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
|
pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
|
||||||
mmc_hostname(host), cmd->opcode, cmd->error);
|
mmc_hostname(host), cmd->opcode, cmd->error);
|
||||||
cmd->retries--;
|
cmd->retries--;
|
||||||
cmd->error = 0;
|
cmd->error = 0;
|
||||||
host->ops->request(host, mrq);
|
__mmc_start_request(host, mrq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mmc_retune_release(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -728,6 +760,7 @@ int mmc_stop_bkops(struct mmc_card *card)
|
||||||
*/
|
*/
|
||||||
if (!err || (err == -EINVAL)) {
|
if (!err || (err == -EINVAL)) {
|
||||||
mmc_card_clr_doing_bkops(card);
|
mmc_card_clr_doing_bkops(card);
|
||||||
|
mmc_retune_release(card->host);
|
||||||
err = 0;
|
err = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1109,6 +1142,8 @@ int mmc_execute_tuning(struct mmc_card *card)
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
pr_err("%s: tuning execution failed\n", mmc_hostname(host));
|
pr_err("%s: tuning execution failed\n", mmc_hostname(host));
|
||||||
|
else
|
||||||
|
mmc_retune_enable(host);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -1140,6 +1175,8 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
|
||||||
*/
|
*/
|
||||||
void mmc_set_initial_state(struct mmc_host *host)
|
void mmc_set_initial_state(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
|
mmc_retune_disable(host);
|
||||||
|
|
||||||
if (mmc_host_is_spi(host))
|
if (mmc_host_is_spi(host))
|
||||||
host->ios.chip_select = MMC_CS_HIGH;
|
host->ios.chip_select = MMC_CS_HIGH;
|
||||||
else
|
else
|
||||||
|
@ -1147,6 +1184,7 @@ void mmc_set_initial_state(struct mmc_host *host)
|
||||||
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
|
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
|
||||||
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
||||||
host->ios.timing = MMC_TIMING_LEGACY;
|
host->ios.timing = MMC_TIMING_LEGACY;
|
||||||
|
host->ios.drv_type = 0;
|
||||||
|
|
||||||
mmc_set_ios(host);
|
mmc_set_ios(host);
|
||||||
}
|
}
|
||||||
|
@ -1551,8 +1589,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
|
||||||
goto power_cycle;
|
goto power_cycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Keep clock gated for at least 5 ms */
|
/* Keep clock gated for at least 10 ms, though spec only says 5 ms */
|
||||||
mmc_delay(5);
|
mmc_delay(10);
|
||||||
host->ios.clock = clock;
|
host->ios.clock = clock;
|
||||||
mmc_set_ios(host);
|
mmc_set_ios(host);
|
||||||
|
|
||||||
|
@ -1601,6 +1639,44 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
|
||||||
mmc_host_clk_release(host);
|
mmc_host_clk_release(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
|
||||||
|
int card_drv_type, int *drv_type)
|
||||||
|
{
|
||||||
|
struct mmc_host *host = card->host;
|
||||||
|
int host_drv_type = SD_DRIVER_TYPE_B;
|
||||||
|
int drive_strength;
|
||||||
|
|
||||||
|
*drv_type = 0;
|
||||||
|
|
||||||
|
if (!host->ops->select_drive_strength)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Use SD definition of driver strength for hosts */
|
||||||
|
if (host->caps & MMC_CAP_DRIVER_TYPE_A)
|
||||||
|
host_drv_type |= SD_DRIVER_TYPE_A;
|
||||||
|
|
||||||
|
if (host->caps & MMC_CAP_DRIVER_TYPE_C)
|
||||||
|
host_drv_type |= SD_DRIVER_TYPE_C;
|
||||||
|
|
||||||
|
if (host->caps & MMC_CAP_DRIVER_TYPE_D)
|
||||||
|
host_drv_type |= SD_DRIVER_TYPE_D;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The drive strength that the hardware can support
|
||||||
|
* depends on the board design. Pass the appropriate
|
||||||
|
* information and let the hardware specific code
|
||||||
|
* return what is possible given the options
|
||||||
|
*/
|
||||||
|
mmc_host_clk_hold(host);
|
||||||
|
drive_strength = host->ops->select_drive_strength(card, max_dtr,
|
||||||
|
host_drv_type,
|
||||||
|
card_drv_type,
|
||||||
|
drv_type);
|
||||||
|
mmc_host_clk_release(host);
|
||||||
|
|
||||||
|
return drive_strength;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Apply power to the MMC stack. This is a two-stage process.
|
* Apply power to the MMC stack. This is a two-stage process.
|
||||||
* First, we enable power to the card without the clock running.
|
* First, we enable power to the card without the clock running.
|
||||||
|
@ -1970,6 +2046,8 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
mmc_retune_hold(card->host);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* qty is used to calculate the erase timeout which depends on how many
|
* qty is used to calculate the erase timeout which depends on how many
|
||||||
* erase groups (or allocation units in SD terminology) are affected.
|
* erase groups (or allocation units in SD terminology) are affected.
|
||||||
|
@ -2073,6 +2151,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
||||||
} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
|
} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
|
||||||
(R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG));
|
(R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG));
|
||||||
out:
|
out:
|
||||||
|
mmc_retune_release(card->host);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2331,6 +2410,7 @@ int mmc_hw_reset(struct mmc_host *host)
|
||||||
ret = host->bus_ops->reset(host);
|
ret = host->bus_ops->reset(host);
|
||||||
mmc_bus_put(host);
|
mmc_bus_put(host);
|
||||||
|
|
||||||
|
if (ret != -EOPNOTSUPP)
|
||||||
pr_warn("%s: tried to reset card\n", mmc_hostname(host));
|
pr_warn("%s: tried to reset card\n", mmc_hostname(host));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -50,6 +50,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr);
|
||||||
int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
|
int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
|
||||||
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
|
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
|
||||||
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
|
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
|
||||||
|
int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
|
||||||
|
int card_drv_type, int *drv_type);
|
||||||
void mmc_power_up(struct mmc_host *host, u32 ocr);
|
void mmc_power_up(struct mmc_host *host, u32 ocr);
|
||||||
void mmc_power_off(struct mmc_host *host);
|
void mmc_power_off(struct mmc_host *host);
|
||||||
void mmc_power_cycle(struct mmc_host *host, u32 ocr);
|
void mmc_power_cycle(struct mmc_host *host, u32 ocr);
|
||||||
|
@ -88,6 +90,8 @@ void mmc_remove_card_debugfs(struct mmc_card *card);
|
||||||
void mmc_init_context_info(struct mmc_host *host);
|
void mmc_init_context_info(struct mmc_host *host);
|
||||||
|
|
||||||
int mmc_execute_tuning(struct mmc_card *card);
|
int mmc_execute_tuning(struct mmc_card *card);
|
||||||
|
int mmc_hs200_to_hs400(struct mmc_card *card);
|
||||||
|
int mmc_hs400_to_hs200(struct mmc_card *card);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -301,6 +301,90 @@ static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void mmc_retune_enable(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
host->can_retune = 1;
|
||||||
|
if (host->retune_period)
|
||||||
|
mod_timer(&host->retune_timer,
|
||||||
|
jiffies + host->retune_period * HZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mmc_retune_disable(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
host->can_retune = 0;
|
||||||
|
del_timer_sync(&host->retune_timer);
|
||||||
|
host->retune_now = 0;
|
||||||
|
host->need_retune = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mmc_retune_timer_stop(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
del_timer_sync(&host->retune_timer);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(mmc_retune_timer_stop);
|
||||||
|
|
||||||
|
void mmc_retune_hold(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
if (!host->hold_retune)
|
||||||
|
host->retune_now = 1;
|
||||||
|
host->hold_retune += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mmc_retune_release(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
if (host->hold_retune)
|
||||||
|
host->hold_retune -= 1;
|
||||||
|
else
|
||||||
|
WARN_ON(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mmc_retune(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
bool return_to_hs400 = false;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (host->retune_now)
|
||||||
|
host->retune_now = 0;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!host->need_retune || host->doing_retune || !host->card)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
host->need_retune = 0;
|
||||||
|
|
||||||
|
host->doing_retune = 1;
|
||||||
|
|
||||||
|
if (host->ios.timing == MMC_TIMING_MMC_HS400) {
|
||||||
|
err = mmc_hs400_to_hs200(host->card);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
return_to_hs400 = true;
|
||||||
|
|
||||||
|
if (host->ops->prepare_hs400_tuning)
|
||||||
|
host->ops->prepare_hs400_tuning(host, &host->ios);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mmc_execute_tuning(host->card);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (return_to_hs400)
|
||||||
|
err = mmc_hs200_to_hs400(host->card);
|
||||||
|
out:
|
||||||
|
host->doing_retune = 0;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mmc_retune_timer(unsigned long data)
|
||||||
|
{
|
||||||
|
struct mmc_host *host = (struct mmc_host *)data;
|
||||||
|
|
||||||
|
mmc_retune_needed(host);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mmc_of_parse() - parse host's device-tree node
|
* mmc_of_parse() - parse host's device-tree node
|
||||||
* @host: host whose node should be parsed.
|
* @host: host whose node should be parsed.
|
||||||
|
@ -400,6 +484,9 @@ int mmc_of_parse(struct mmc_host *host)
|
||||||
else if (ret != -ENOENT)
|
else if (ret != -ENOENT)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
if (of_property_read_bool(np, "disable-wp"))
|
||||||
|
host->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
|
||||||
|
|
||||||
/* See the comment on CD inversion above */
|
/* See the comment on CD inversion above */
|
||||||
if (ro_cap_invert ^ ro_gpio_invert)
|
if (ro_cap_invert ^ ro_gpio_invert)
|
||||||
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||||
|
@ -504,6 +591,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
host->pm_notify.notifier_call = mmc_pm_notify;
|
host->pm_notify.notifier_call = mmc_pm_notify;
|
||||||
#endif
|
#endif
|
||||||
|
setup_timer(&host->retune_timer, mmc_retune_timer, (unsigned long)host);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* By default, hosts do not support SGIO or large requests.
|
* By default, hosts do not support SGIO or large requests.
|
||||||
|
|
|
@ -15,5 +15,11 @@
|
||||||
int mmc_register_host_class(void);
|
int mmc_register_host_class(void);
|
||||||
void mmc_unregister_host_class(void);
|
void mmc_unregister_host_class(void);
|
||||||
|
|
||||||
|
void mmc_retune_enable(struct mmc_host *host);
|
||||||
|
void mmc_retune_disable(struct mmc_host *host);
|
||||||
|
void mmc_retune_hold(struct mmc_host *host);
|
||||||
|
void mmc_retune_release(struct mmc_host *host);
|
||||||
|
int mmc_retune(struct mmc_host *host);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/mmc/mmc.h>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
#include "host.h"
|
||||||
#include "bus.h"
|
#include "bus.h"
|
||||||
#include "mmc_ops.h"
|
#include "mmc_ops.h"
|
||||||
#include "sd_ops.h"
|
#include "sd_ops.h"
|
||||||
|
@ -266,8 +267,10 @@ static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd)
|
||||||
* calculate the enhanced data area offset, in bytes
|
* calculate the enhanced data area offset, in bytes
|
||||||
*/
|
*/
|
||||||
card->ext_csd.enhanced_area_offset =
|
card->ext_csd.enhanced_area_offset =
|
||||||
(ext_csd[139] << 24) + (ext_csd[138] << 16) +
|
(((unsigned long long)ext_csd[139]) << 24) +
|
||||||
(ext_csd[137] << 8) + ext_csd[136];
|
(((unsigned long long)ext_csd[138]) << 16) +
|
||||||
|
(((unsigned long long)ext_csd[137]) << 8) +
|
||||||
|
(((unsigned long long)ext_csd[136]));
|
||||||
if (mmc_card_blockaddr(card))
|
if (mmc_card_blockaddr(card))
|
||||||
card->ext_csd.enhanced_area_offset <<= 9;
|
card->ext_csd.enhanced_area_offset <<= 9;
|
||||||
/*
|
/*
|
||||||
|
@ -434,6 +437,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||||
card->ext_csd.raw_trim_mult =
|
card->ext_csd.raw_trim_mult =
|
||||||
ext_csd[EXT_CSD_TRIM_MULT];
|
ext_csd[EXT_CSD_TRIM_MULT];
|
||||||
card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT];
|
card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT];
|
||||||
|
card->ext_csd.raw_driver_strength = ext_csd[EXT_CSD_DRIVER_STRENGTH];
|
||||||
if (card->ext_csd.rev >= 4) {
|
if (card->ext_csd.rev >= 4) {
|
||||||
if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED] &
|
if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED] &
|
||||||
EXT_CSD_PART_SETTING_COMPLETED)
|
EXT_CSD_PART_SETTING_COMPLETED)
|
||||||
|
@ -1040,6 +1044,7 @@ static int mmc_select_hs400(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
struct mmc_host *host = card->host;
|
struct mmc_host *host = card->host;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
u8 val;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* HS400 mode requires 8-bit bus width
|
* HS400 mode requires 8-bit bus width
|
||||||
|
@ -1055,8 +1060,10 @@ static int mmc_select_hs400(struct mmc_card *card)
|
||||||
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
||||||
mmc_set_bus_speed(card);
|
mmc_set_bus_speed(card);
|
||||||
|
|
||||||
|
val = EXT_CSD_TIMING_HS |
|
||||||
|
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
|
||||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
|
EXT_CSD_HS_TIMING, val,
|
||||||
card->ext_csd.generic_cmd6_time,
|
card->ext_csd.generic_cmd6_time,
|
||||||
true, true, true);
|
true, true, true);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -1075,8 +1082,10 @@ static int mmc_select_hs400(struct mmc_card *card)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val = EXT_CSD_TIMING_HS400 |
|
||||||
|
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
|
||||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400,
|
EXT_CSD_HS_TIMING, val,
|
||||||
card->ext_csd.generic_cmd6_time,
|
card->ext_csd.generic_cmd6_time,
|
||||||
true, true, true);
|
true, true, true);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -1091,6 +1100,115 @@ static int mmc_select_hs400(struct mmc_card *card)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mmc_hs200_to_hs400(struct mmc_card *card)
|
||||||
|
{
|
||||||
|
return mmc_select_hs400(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Caller must hold re-tuning */
|
||||||
|
static int mmc_switch_status(struct mmc_card *card)
|
||||||
|
{
|
||||||
|
u32 status;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = mmc_send_status(card, &status);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return mmc_switch_status_error(card->host, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mmc_hs400_to_hs200(struct mmc_card *card)
|
||||||
|
{
|
||||||
|
struct mmc_host *host = card->host;
|
||||||
|
bool send_status = true;
|
||||||
|
unsigned int max_dtr;
|
||||||
|
int err;
|
||||||
|
u8 val;
|
||||||
|
|
||||||
|
if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
|
||||||
|
send_status = false;
|
||||||
|
|
||||||
|
/* Reduce frequency to HS */
|
||||||
|
max_dtr = card->ext_csd.hs_max_dtr;
|
||||||
|
mmc_set_clock(host, max_dtr);
|
||||||
|
|
||||||
|
/* Switch HS400 to HS DDR */
|
||||||
|
val = EXT_CSD_TIMING_HS |
|
||||||
|
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
|
||||||
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
|
||||||
|
val, card->ext_csd.generic_cmd6_time,
|
||||||
|
true, send_status, true);
|
||||||
|
if (err)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
|
||||||
|
|
||||||
|
if (!send_status) {
|
||||||
|
err = mmc_switch_status(card);
|
||||||
|
if (err)
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Switch HS DDR to HS */
|
||||||
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
|
||||||
|
EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time,
|
||||||
|
true, send_status, true);
|
||||||
|
if (err)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
mmc_set_timing(host, MMC_TIMING_MMC_HS);
|
||||||
|
|
||||||
|
if (!send_status) {
|
||||||
|
err = mmc_switch_status(card);
|
||||||
|
if (err)
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Switch HS to HS200 */
|
||||||
|
val = EXT_CSD_TIMING_HS200 |
|
||||||
|
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
|
||||||
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
|
||||||
|
val, card->ext_csd.generic_cmd6_time, true,
|
||||||
|
send_status, true);
|
||||||
|
if (err)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
|
||||||
|
|
||||||
|
if (!send_status) {
|
||||||
|
err = mmc_switch_status(card);
|
||||||
|
if (err)
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
mmc_set_bus_speed(card);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_err:
|
||||||
|
pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
|
||||||
|
__func__, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mmc_select_driver_type(struct mmc_card *card)
|
||||||
|
{
|
||||||
|
int card_drv_type, drive_strength, drv_type;
|
||||||
|
|
||||||
|
card_drv_type = card->ext_csd.raw_driver_strength |
|
||||||
|
mmc_driver_type_mask(0);
|
||||||
|
|
||||||
|
drive_strength = mmc_select_drive_strength(card,
|
||||||
|
card->ext_csd.hs200_max_dtr,
|
||||||
|
card_drv_type, &drv_type);
|
||||||
|
|
||||||
|
card->drive_strength = drive_strength;
|
||||||
|
|
||||||
|
if (drv_type)
|
||||||
|
mmc_set_driver_type(card->host, drv_type);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For device supporting HS200 mode, the following sequence
|
* For device supporting HS200 mode, the following sequence
|
||||||
* should be done before executing the tuning process.
|
* should be done before executing the tuning process.
|
||||||
|
@ -1102,6 +1220,7 @@ static int mmc_select_hs200(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
struct mmc_host *host = card->host;
|
struct mmc_host *host = card->host;
|
||||||
int err = -EINVAL;
|
int err = -EINVAL;
|
||||||
|
u8 val;
|
||||||
|
|
||||||
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V)
|
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V)
|
||||||
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
|
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
|
||||||
|
@ -1113,14 +1232,18 @@ static int mmc_select_hs200(struct mmc_card *card)
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
mmc_select_driver_type(card);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the bus width(4 or 8) with host's support and
|
* Set the bus width(4 or 8) with host's support and
|
||||||
* switch to HS200 mode if bus width is set successfully.
|
* switch to HS200 mode if bus width is set successfully.
|
||||||
*/
|
*/
|
||||||
err = mmc_select_bus_width(card);
|
err = mmc_select_bus_width(card);
|
||||||
if (!IS_ERR_VALUE(err)) {
|
if (!IS_ERR_VALUE(err)) {
|
||||||
|
val = EXT_CSD_TIMING_HS200 |
|
||||||
|
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
|
||||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200,
|
EXT_CSD_HS_TIMING, val,
|
||||||
card->ext_csd.generic_cmd6_time,
|
card->ext_csd.generic_cmd6_time,
|
||||||
true, true, true);
|
true, true, true);
|
||||||
if (!err)
|
if (!err)
|
||||||
|
@ -1511,9 +1634,12 @@ static int mmc_sleep(struct mmc_host *host)
|
||||||
unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
|
unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
/* Re-tuning can't be done once the card is deselected */
|
||||||
|
mmc_retune_hold(host);
|
||||||
|
|
||||||
err = mmc_deselect_cards(host);
|
err = mmc_deselect_cards(host);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
goto out_release;
|
||||||
|
|
||||||
cmd.opcode = MMC_SLEEP_AWAKE;
|
cmd.opcode = MMC_SLEEP_AWAKE;
|
||||||
cmd.arg = card->rca << 16;
|
cmd.arg = card->rca << 16;
|
||||||
|
@ -1534,7 +1660,7 @@ static int mmc_sleep(struct mmc_host *host)
|
||||||
|
|
||||||
err = mmc_wait_for_cmd(host, &cmd, 0);
|
err = mmc_wait_for_cmd(host, &cmd, 0);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
goto out_release;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the host does not wait while the card signals busy, then we will
|
* If the host does not wait while the card signals busy, then we will
|
||||||
|
@ -1545,6 +1671,8 @@ static int mmc_sleep(struct mmc_host *host)
|
||||||
if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
|
if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
|
||||||
mmc_delay(timeout_ms);
|
mmc_delay(timeout_ms);
|
||||||
|
|
||||||
|
out_release:
|
||||||
|
mmc_retune_release(host);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1782,17 +1910,6 @@ static int mmc_runtime_resume(struct mmc_host *host)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_power_restore(struct mmc_host *host)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
mmc_claim_host(host);
|
|
||||||
ret = mmc_init_card(host, host->card->ocr, host->card);
|
|
||||||
mmc_release_host(host);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mmc_can_reset(struct mmc_card *card)
|
int mmc_can_reset(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
u8 rst_n_function;
|
u8 rst_n_function;
|
||||||
|
@ -1830,7 +1947,7 @@ static int mmc_reset(struct mmc_host *host)
|
||||||
mmc_set_initial_state(host);
|
mmc_set_initial_state(host);
|
||||||
mmc_host_clk_release(host);
|
mmc_host_clk_release(host);
|
||||||
|
|
||||||
return mmc_power_restore(host);
|
return mmc_init_card(host, card->ocr, card);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct mmc_bus_ops mmc_ops = {
|
static const struct mmc_bus_ops mmc_ops = {
|
||||||
|
@ -1840,7 +1957,6 @@ static const struct mmc_bus_ops mmc_ops = {
|
||||||
.resume = mmc_resume,
|
.resume = mmc_resume,
|
||||||
.runtime_suspend = mmc_runtime_suspend,
|
.runtime_suspend = mmc_runtime_suspend,
|
||||||
.runtime_resume = mmc_runtime_resume,
|
.runtime_resume = mmc_runtime_resume,
|
||||||
.power_restore = mmc_power_restore,
|
|
||||||
.alive = mmc_alive,
|
.alive = mmc_alive,
|
||||||
.shutdown = mmc_shutdown,
|
.shutdown = mmc_shutdown,
|
||||||
.reset = mmc_reset,
|
.reset = mmc_reset,
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/mmc/mmc.h>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
#include "host.h"
|
||||||
#include "mmc_ops.h"
|
#include "mmc_ops.h"
|
||||||
|
|
||||||
#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
|
#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
|
||||||
|
@ -449,6 +450,21 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mmc_switch_status_error(struct mmc_host *host, u32 status)
|
||||||
|
{
|
||||||
|
if (mmc_host_is_spi(host)) {
|
||||||
|
if (status & R1_SPI_ILLEGAL_COMMAND)
|
||||||
|
return -EBADMSG;
|
||||||
|
} else {
|
||||||
|
if (status & 0xFDFFA000)
|
||||||
|
pr_warn("%s: unexpected status %#x after switch\n",
|
||||||
|
mmc_hostname(host), status);
|
||||||
|
if (status & R1_SWITCH_ERROR)
|
||||||
|
return -EBADMSG;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __mmc_switch - modify EXT_CSD register
|
* __mmc_switch - modify EXT_CSD register
|
||||||
* @card: the MMC card associated with the data transfer
|
* @card: the MMC card associated with the data transfer
|
||||||
|
@ -474,6 +490,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||||
u32 status = 0;
|
u32 status = 0;
|
||||||
bool use_r1b_resp = use_busy_signal;
|
bool use_r1b_resp = use_busy_signal;
|
||||||
|
|
||||||
|
mmc_retune_hold(host);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the cmd timeout and the max_busy_timeout of the host are both
|
* If the cmd timeout and the max_busy_timeout of the host are both
|
||||||
* specified, let's validate them. A failure means we need to prevent
|
* specified, let's validate them. A failure means we need to prevent
|
||||||
|
@ -506,11 +524,11 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||||
|
|
||||||
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
|
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
goto out;
|
||||||
|
|
||||||
/* No need to check card status in case of unblocking command */
|
/* No need to check card status in case of unblocking command */
|
||||||
if (!use_busy_signal)
|
if (!use_busy_signal)
|
||||||
return 0;
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CRC errors shall only be ignored in cases were CMD13 is used to poll
|
* CRC errors shall only be ignored in cases were CMD13 is used to poll
|
||||||
|
@ -529,7 +547,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||||
if (send_status) {
|
if (send_status) {
|
||||||
err = __mmc_send_status(card, &status, ignore_crc);
|
err = __mmc_send_status(card, &status, ignore_crc);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
goto out;
|
||||||
}
|
}
|
||||||
if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
|
if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
|
||||||
break;
|
break;
|
||||||
|
@ -543,29 +561,23 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||||
*/
|
*/
|
||||||
if (!send_status) {
|
if (!send_status) {
|
||||||
mmc_delay(timeout_ms);
|
mmc_delay(timeout_ms);
|
||||||
return 0;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Timeout if the device never leaves the program state. */
|
/* Timeout if the device never leaves the program state. */
|
||||||
if (time_after(jiffies, timeout)) {
|
if (time_after(jiffies, timeout)) {
|
||||||
pr_err("%s: Card stuck in programming state! %s\n",
|
pr_err("%s: Card stuck in programming state! %s\n",
|
||||||
mmc_hostname(host), __func__);
|
mmc_hostname(host), __func__);
|
||||||
return -ETIMEDOUT;
|
err = -ETIMEDOUT;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
|
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
|
||||||
|
|
||||||
if (mmc_host_is_spi(host)) {
|
err = mmc_switch_status_error(host, status);
|
||||||
if (status & R1_SPI_ILLEGAL_COMMAND)
|
out:
|
||||||
return -EBADMSG;
|
mmc_retune_release(host);
|
||||||
} else {
|
|
||||||
if (status & 0xFDFFA000)
|
|
||||||
pr_warn("%s: unexpected status %#x after switch\n",
|
|
||||||
mmc_hostname(host), status);
|
|
||||||
if (status & R1_SWITCH_ERROR)
|
|
||||||
return -EBADMSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return err;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(__mmc_switch);
|
EXPORT_SYMBOL_GPL(__mmc_switch);
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
|
||||||
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
|
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
|
||||||
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
|
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
|
||||||
int mmc_can_ext_csd(struct mmc_card *card);
|
int mmc_can_ext_csd(struct mmc_card *card);
|
||||||
|
int mmc_switch_status_error(struct mmc_host *host, u32 status);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -386,64 +386,31 @@ int mmc_sd_switch_hs(struct mmc_card *card)
|
||||||
|
|
||||||
static int sd_select_driver_type(struct mmc_card *card, u8 *status)
|
static int sd_select_driver_type(struct mmc_card *card, u8 *status)
|
||||||
{
|
{
|
||||||
int host_drv_type = SD_DRIVER_TYPE_B;
|
int card_drv_type, drive_strength, drv_type;
|
||||||
int card_drv_type = SD_DRIVER_TYPE_B;
|
|
||||||
int drive_strength;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/*
|
card->drive_strength = 0;
|
||||||
* If the host doesn't support any of the Driver Types A,C or D,
|
|
||||||
* or there is no board specific handler then default Driver
|
|
||||||
* Type B is used.
|
|
||||||
*/
|
|
||||||
if (!(card->host->caps & (MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C
|
|
||||||
| MMC_CAP_DRIVER_TYPE_D)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!card->host->ops->select_drive_strength)
|
card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B;
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
|
drive_strength = mmc_select_drive_strength(card,
|
||||||
host_drv_type |= SD_DRIVER_TYPE_A;
|
|
||||||
|
|
||||||
if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
|
|
||||||
host_drv_type |= SD_DRIVER_TYPE_C;
|
|
||||||
|
|
||||||
if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
|
|
||||||
host_drv_type |= SD_DRIVER_TYPE_D;
|
|
||||||
|
|
||||||
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
|
|
||||||
card_drv_type |= SD_DRIVER_TYPE_A;
|
|
||||||
|
|
||||||
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
|
|
||||||
card_drv_type |= SD_DRIVER_TYPE_C;
|
|
||||||
|
|
||||||
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
|
|
||||||
card_drv_type |= SD_DRIVER_TYPE_D;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The drive strength that the hardware can support
|
|
||||||
* depends on the board design. Pass the appropriate
|
|
||||||
* information and let the hardware specific code
|
|
||||||
* return what is possible given the options
|
|
||||||
*/
|
|
||||||
mmc_host_clk_hold(card->host);
|
|
||||||
drive_strength = card->host->ops->select_drive_strength(
|
|
||||||
card->sw_caps.uhs_max_dtr,
|
card->sw_caps.uhs_max_dtr,
|
||||||
host_drv_type, card_drv_type);
|
card_drv_type, &drv_type);
|
||||||
mmc_host_clk_release(card->host);
|
|
||||||
|
|
||||||
|
if (drive_strength) {
|
||||||
err = mmc_sd_switch(card, 1, 2, drive_strength, status);
|
err = mmc_sd_switch(card, 1, 2, drive_strength, status);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if ((status[15] & 0xF) != drive_strength) {
|
if ((status[15] & 0xF) != drive_strength) {
|
||||||
pr_warn("%s: Problem setting drive strength!\n",
|
pr_warn("%s: Problem setting drive strength!\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
card->drive_strength = drive_strength;
|
||||||
|
}
|
||||||
|
|
||||||
mmc_set_driver_type(card->host, drive_strength);
|
if (drv_type)
|
||||||
|
mmc_set_driver_type(card->host, drv_type);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -804,6 +771,28 @@ int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mmc_sd_get_ro(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
int ro;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some systems don't feature a write-protect pin and don't need one.
|
||||||
|
* E.g. because they only have micro-SD card slot. For those systems
|
||||||
|
* assume that the SD card is always read-write.
|
||||||
|
*/
|
||||||
|
if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!host->ops->get_ro)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
mmc_host_clk_hold(host);
|
||||||
|
ro = host->ops->get_ro(host);
|
||||||
|
mmc_host_clk_release(host);
|
||||||
|
|
||||||
|
return ro;
|
||||||
|
}
|
||||||
|
|
||||||
int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
|
int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
|
||||||
bool reinit)
|
bool reinit)
|
||||||
{
|
{
|
||||||
|
@ -855,13 +844,7 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
|
||||||
* Check if read-only switch is active.
|
* Check if read-only switch is active.
|
||||||
*/
|
*/
|
||||||
if (!reinit) {
|
if (!reinit) {
|
||||||
int ro = -1;
|
int ro = mmc_sd_get_ro(host);
|
||||||
|
|
||||||
if (host->ops->get_ro) {
|
|
||||||
mmc_host_clk_hold(card->host);
|
|
||||||
ro = host->ops->get_ro(host);
|
|
||||||
mmc_host_clk_release(card->host);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ro < 0) {
|
if (ro < 0) {
|
||||||
pr_warn("%s: host does not support reading read-only switch, assuming write-enable\n",
|
pr_warn("%s: host does not support reading read-only switch, assuming write-enable\n",
|
||||||
|
@ -1181,21 +1164,10 @@ static int mmc_sd_runtime_resume(struct mmc_host *host)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_sd_power_restore(struct mmc_host *host)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
mmc_claim_host(host);
|
|
||||||
ret = mmc_sd_init_card(host, host->card->ocr, host->card);
|
|
||||||
mmc_release_host(host);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mmc_sd_reset(struct mmc_host *host)
|
static int mmc_sd_reset(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
mmc_power_cycle(host, host->card->ocr);
|
mmc_power_cycle(host, host->card->ocr);
|
||||||
return mmc_sd_power_restore(host);
|
return mmc_sd_init_card(host, host->card->ocr, host->card);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct mmc_bus_ops mmc_sd_ops = {
|
static const struct mmc_bus_ops mmc_sd_ops = {
|
||||||
|
@ -1205,7 +1177,6 @@ static const struct mmc_bus_ops mmc_sd_ops = {
|
||||||
.runtime_resume = mmc_sd_runtime_resume,
|
.runtime_resume = mmc_sd_runtime_resume,
|
||||||
.suspend = mmc_sd_suspend,
|
.suspend = mmc_sd_suspend,
|
||||||
.resume = mmc_sd_resume,
|
.resume = mmc_sd_resume,
|
||||||
.power_restore = mmc_sd_power_restore,
|
|
||||||
.alive = mmc_sd_alive,
|
.alive = mmc_sd_alive,
|
||||||
.shutdown = mmc_sd_suspend,
|
.shutdown = mmc_sd_suspend,
|
||||||
.reset = mmc_sd_reset,
|
.reset = mmc_sd_reset,
|
||||||
|
|
|
@ -402,54 +402,19 @@ static unsigned char host_drive_to_sdio_drive(int host_strength)
|
||||||
|
|
||||||
static void sdio_select_driver_type(struct mmc_card *card)
|
static void sdio_select_driver_type(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
int host_drv_type = SD_DRIVER_TYPE_B;
|
int card_drv_type, drive_strength, drv_type;
|
||||||
int card_drv_type = SD_DRIVER_TYPE_B;
|
|
||||||
int drive_strength;
|
|
||||||
unsigned char card_strength;
|
unsigned char card_strength;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/*
|
card->drive_strength = 0;
|
||||||
* If the host doesn't support any of the Driver Types A,C or D,
|
|
||||||
* or there is no board specific handler then default Driver
|
|
||||||
* Type B is used.
|
|
||||||
*/
|
|
||||||
if (!(card->host->caps &
|
|
||||||
(MMC_CAP_DRIVER_TYPE_A |
|
|
||||||
MMC_CAP_DRIVER_TYPE_C |
|
|
||||||
MMC_CAP_DRIVER_TYPE_D)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!card->host->ops->select_drive_strength)
|
card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B;
|
||||||
return;
|
|
||||||
|
|
||||||
if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
|
drive_strength = mmc_select_drive_strength(card,
|
||||||
host_drv_type |= SD_DRIVER_TYPE_A;
|
|
||||||
|
|
||||||
if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
|
|
||||||
host_drv_type |= SD_DRIVER_TYPE_C;
|
|
||||||
|
|
||||||
if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
|
|
||||||
host_drv_type |= SD_DRIVER_TYPE_D;
|
|
||||||
|
|
||||||
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
|
|
||||||
card_drv_type |= SD_DRIVER_TYPE_A;
|
|
||||||
|
|
||||||
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
|
|
||||||
card_drv_type |= SD_DRIVER_TYPE_C;
|
|
||||||
|
|
||||||
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
|
|
||||||
card_drv_type |= SD_DRIVER_TYPE_D;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The drive strength that the hardware can support
|
|
||||||
* depends on the board design. Pass the appropriate
|
|
||||||
* information and let the hardware specific code
|
|
||||||
* return what is possible given the options
|
|
||||||
*/
|
|
||||||
drive_strength = card->host->ops->select_drive_strength(
|
|
||||||
card->sw_caps.uhs_max_dtr,
|
card->sw_caps.uhs_max_dtr,
|
||||||
host_drv_type, card_drv_type);
|
card_drv_type, &drv_type);
|
||||||
|
|
||||||
|
if (drive_strength) {
|
||||||
/* if error just use default for drive strength B */
|
/* if error just use default for drive strength B */
|
||||||
err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0,
|
err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0,
|
||||||
&card_strength);
|
&card_strength);
|
||||||
|
@ -459,12 +424,16 @@ static void sdio_select_driver_type(struct mmc_card *card)
|
||||||
card_strength &= ~(SDIO_DRIVE_DTSx_MASK<<SDIO_DRIVE_DTSx_SHIFT);
|
card_strength &= ~(SDIO_DRIVE_DTSx_MASK<<SDIO_DRIVE_DTSx_SHIFT);
|
||||||
card_strength |= host_drive_to_sdio_drive(drive_strength);
|
card_strength |= host_drive_to_sdio_drive(drive_strength);
|
||||||
|
|
||||||
|
/* if error default to drive strength B */
|
||||||
err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_DRIVE_STRENGTH,
|
err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_DRIVE_STRENGTH,
|
||||||
card_strength, NULL);
|
card_strength, NULL);
|
||||||
|
if (err)
|
||||||
|
return;
|
||||||
|
card->drive_strength = drive_strength;
|
||||||
|
}
|
||||||
|
|
||||||
/* if error default to drive strength B */
|
if (drv_type)
|
||||||
if (!err)
|
mmc_set_driver_type(card->host, drv_type);
|
||||||
mmc_set_driver_type(card->host, drive_strength);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -934,8 +903,12 @@ static int mmc_sdio_suspend(struct mmc_host *host)
|
||||||
mmc_release_host(host);
|
mmc_release_host(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mmc_card_keep_power(host))
|
if (!mmc_card_keep_power(host)) {
|
||||||
mmc_power_off(host);
|
mmc_power_off(host);
|
||||||
|
} else if (host->retune_period) {
|
||||||
|
mmc_retune_timer_stop(host);
|
||||||
|
mmc_retune_needed(host);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1056,6 +1029,12 @@ static int mmc_sdio_runtime_resume(struct mmc_host *host)
|
||||||
return mmc_sdio_power_restore(host);
|
return mmc_sdio_power_restore(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mmc_sdio_reset(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
mmc_power_cycle(host, host->card->ocr);
|
||||||
|
return mmc_sdio_power_restore(host);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct mmc_bus_ops mmc_sdio_ops = {
|
static const struct mmc_bus_ops mmc_sdio_ops = {
|
||||||
.remove = mmc_sdio_remove,
|
.remove = mmc_sdio_remove,
|
||||||
.detect = mmc_sdio_detect,
|
.detect = mmc_sdio_detect,
|
||||||
|
@ -1066,6 +1045,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
|
||||||
.runtime_resume = mmc_sdio_runtime_resume,
|
.runtime_resume = mmc_sdio_runtime_resume,
|
||||||
.power_restore = mmc_sdio_power_restore,
|
.power_restore = mmc_sdio_power_restore,
|
||||||
.alive = mmc_sdio_alive,
|
.alive = mmc_sdio_alive,
|
||||||
|
.reset = mmc_sdio_reset,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,10 @@ static int sdio_bus_probe(struct device *dev)
|
||||||
if (!id)
|
if (!id)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
ret = dev_pm_domain_attach(dev, false);
|
||||||
|
if (ret == -EPROBE_DEFER)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/* Unbound SDIO functions are always suspended.
|
/* Unbound SDIO functions are always suspended.
|
||||||
* During probe, the function is set active and the usage count
|
* During probe, the function is set active and the usage count
|
||||||
* is incremented. If the driver supports runtime PM,
|
* is incremented. If the driver supports runtime PM,
|
||||||
|
@ -166,6 +170,7 @@ static int sdio_bus_probe(struct device *dev)
|
||||||
disable_runtimepm:
|
disable_runtimepm:
|
||||||
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
|
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
|
||||||
pm_runtime_put_noidle(dev);
|
pm_runtime_put_noidle(dev);
|
||||||
|
dev_pm_domain_detach(dev, false);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,6 +202,8 @@ static int sdio_bus_remove(struct device *dev)
|
||||||
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
|
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
|
||||||
pm_runtime_put_sync(dev);
|
pm_runtime_put_sync(dev);
|
||||||
|
|
||||||
|
dev_pm_domain_detach(dev, false);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,10 +323,8 @@ int sdio_add_func(struct sdio_func *func)
|
||||||
sdio_set_of_node(func);
|
sdio_set_of_node(func);
|
||||||
sdio_acpi_set_handle(func);
|
sdio_acpi_set_handle(func);
|
||||||
ret = device_add(&func->dev);
|
ret = device_add(&func->dev);
|
||||||
if (ret == 0) {
|
if (ret == 0)
|
||||||
sdio_func_set_present(func);
|
sdio_func_set_present(func);
|
||||||
dev_pm_domain_attach(&func->dev, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -335,7 +340,6 @@ void sdio_remove_func(struct sdio_func *func)
|
||||||
if (!sdio_func_present(func))
|
if (!sdio_func_present(func))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dev_pm_domain_detach(&func->dev, false);
|
|
||||||
device_del(&func->dev);
|
device_del(&func->dev);
|
||||||
of_node_put(func->dev.of_node);
|
of_node_put(func->dev.of_node);
|
||||||
put_device(&func->dev);
|
put_device(&func->dev);
|
||||||
|
|
|
@ -219,6 +219,7 @@ config MMC_SDHCI_SIRF
|
||||||
tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs"
|
tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs"
|
||||||
depends on ARCH_SIRF
|
depends on ARCH_SIRF
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
|
select MMC_SDHCI_IO_ACCESSORS
|
||||||
help
|
help
|
||||||
This selects the SDHCI support for SiRF System-on-Chip devices.
|
This selects the SDHCI support for SiRF System-on-Chip devices.
|
||||||
|
|
||||||
|
@ -775,3 +776,11 @@ config MMC_TOSHIBA_PCI
|
||||||
tristate "Toshiba Type A SD/MMC Card Interface Driver"
|
tristate "Toshiba Type A SD/MMC Card Interface Driver"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
help
|
help
|
||||||
|
|
||||||
|
config MMC_MTK
|
||||||
|
tristate "MediaTek SD/MMC Card Interface support"
|
||||||
|
help
|
||||||
|
This selects the MediaTek(R) Secure digital and Multimedia card Interface.
|
||||||
|
If you have a machine with a integrated SD/MMC card reader, say Y or M here.
|
||||||
|
This is needed if support for any SD/SDIO/MMC devices is required.
|
||||||
|
If unsure, say N.
|
||||||
|
|
|
@ -20,6 +20,7 @@ obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
|
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
|
||||||
obj-$(CONFIG_MMC_WBSD) += wbsd.o
|
obj-$(CONFIG_MMC_WBSD) += wbsd.o
|
||||||
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
|
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
|
||||||
|
obj-$(CONFIG_MMC_MTK) += mtk-sd.o
|
||||||
obj-$(CONFIG_MMC_OMAP) += omap.o
|
obj-$(CONFIG_MMC_OMAP) += omap.o
|
||||||
obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o
|
obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o
|
||||||
obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o
|
obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o
|
||||||
|
|
|
@ -1161,7 +1161,7 @@ static void __init init_mmcsd_host(struct mmc_davinci_host *host)
|
||||||
mmc_davinci_reset_ctrl(host, 0);
|
mmc_davinci_reset_ctrl(host, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_device_id davinci_mmc_devtype[] = {
|
static const struct platform_device_id davinci_mmc_devtype[] = {
|
||||||
{
|
{
|
||||||
.name = "dm6441-mmc",
|
.name = "dm6441-mmc",
|
||||||
.driver_data = MMC_CTLR_VERSION_1,
|
.driver_data = MMC_CTLR_VERSION_1,
|
||||||
|
|
|
@ -556,4 +556,4 @@ module_platform_driver(dw_mci_exynos_pltfm_driver);
|
||||||
MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
|
MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
|
||||||
MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
|
MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
MODULE_ALIAS("platform:dwmmc-exynos");
|
MODULE_ALIAS("platform:dwmmc_exynos");
|
||||||
|
|
|
@ -8,16 +8,30 @@
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/mfd/syscon.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/dw_mmc.h>
|
#include <linux/mmc/dw_mmc.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
|
||||||
#include "dw_mmc.h"
|
#include "dw_mmc.h"
|
||||||
#include "dw_mmc-pltfm.h"
|
#include "dw_mmc-pltfm.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hi6220 sd only support io voltage 1.8v and 3v
|
||||||
|
* Also need config AO_SCTRL_SEL18 accordingly
|
||||||
|
*/
|
||||||
|
#define AO_SCTRL_SEL18 BIT(10)
|
||||||
|
#define AO_SCTRL_CTRL3 0x40C
|
||||||
|
|
||||||
|
struct k3_priv {
|
||||||
|
struct regmap *reg;
|
||||||
|
};
|
||||||
|
|
||||||
static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -33,8 +47,93 @@ static const struct dw_mci_drv_data k3_drv_data = {
|
||||||
.set_ios = dw_mci_k3_set_ios,
|
.set_ios = dw_mci_k3_set_ios,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int dw_mci_hi6220_parse_dt(struct dw_mci *host)
|
||||||
|
{
|
||||||
|
struct k3_priv *priv;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv->reg = syscon_regmap_lookup_by_phandle(host->dev->of_node,
|
||||||
|
"hisilicon,peripheral-syscon");
|
||||||
|
if (IS_ERR(priv->reg))
|
||||||
|
priv->reg = NULL;
|
||||||
|
|
||||||
|
host->priv = priv;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dw_mci_hi6220_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
|
{
|
||||||
|
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||||
|
struct k3_priv *priv;
|
||||||
|
struct dw_mci *host;
|
||||||
|
int min_uv, max_uv;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
host = slot->host;
|
||||||
|
priv = host->priv;
|
||||||
|
|
||||||
|
if (!priv || !priv->reg)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
|
||||||
|
ret = regmap_update_bits(priv->reg, AO_SCTRL_CTRL3,
|
||||||
|
AO_SCTRL_SEL18, 0);
|
||||||
|
min_uv = 3000000;
|
||||||
|
max_uv = 3000000;
|
||||||
|
} else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
|
||||||
|
ret = regmap_update_bits(priv->reg, AO_SCTRL_CTRL3,
|
||||||
|
AO_SCTRL_SEL18, AO_SCTRL_SEL18);
|
||||||
|
min_uv = 1800000;
|
||||||
|
max_uv = 1800000;
|
||||||
|
} else {
|
||||||
|
dev_dbg(host->dev, "voltage not supported\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
dev_dbg(host->dev, "switch voltage failed\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_ERR_OR_NULL(mmc->supply.vqmmc))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv);
|
||||||
|
if (ret) {
|
||||||
|
dev_dbg(host->dev, "Regulator set error %d: %d - %d\n",
|
||||||
|
ret, min_uv, max_uv);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
unsigned int clock;
|
||||||
|
|
||||||
|
clock = (ios->clock <= 25000000) ? 25000000 : ios->clock;
|
||||||
|
|
||||||
|
ret = clk_set_rate(host->biu_clk, clock);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(host->dev, "failed to set rate %uHz\n", clock);
|
||||||
|
|
||||||
|
host->bus_hz = clk_get_rate(host->biu_clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dw_mci_drv_data hi6220_data = {
|
||||||
|
.switch_voltage = dw_mci_hi6220_switch_voltage,
|
||||||
|
.set_ios = dw_mci_hi6220_set_ios,
|
||||||
|
.parse_dt = dw_mci_hi6220_parse_dt,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id dw_mci_k3_match[] = {
|
static const struct of_device_id dw_mci_k3_match[] = {
|
||||||
{ .compatible = "hisilicon,hi4511-dw-mshc", .data = &k3_drv_data, },
|
{ .compatible = "hisilicon,hi4511-dw-mshc", .data = &k3_drv_data, },
|
||||||
|
{ .compatible = "hisilicon,hi6220-dw-mshc", .data = &hi6220_data, },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, dw_mci_k3_match);
|
MODULE_DEVICE_TABLE(of, dw_mci_k3_match);
|
||||||
|
@ -94,4 +193,4 @@ module_platform_driver(dw_mci_k3_pltfm_driver);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("K3 Specific DW-MSHC Driver Extension");
|
MODULE_DESCRIPTION("K3 Specific DW-MSHC Driver Extension");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
MODULE_ALIAS("platform:dwmmc-k3");
|
MODULE_ALIAS("platform:dwmmc_k3");
|
||||||
|
|
|
@ -153,5 +153,5 @@ module_platform_driver(dw_mci_rockchip_pltfm_driver);
|
||||||
|
|
||||||
MODULE_AUTHOR("Addy Ke <addy.ke@rock-chips.com>");
|
MODULE_AUTHOR("Addy Ke <addy.ke@rock-chips.com>");
|
||||||
MODULE_DESCRIPTION("Rockchip Specific DW-MSHC Driver Extension");
|
MODULE_DESCRIPTION("Rockchip Specific DW-MSHC Driver Extension");
|
||||||
MODULE_ALIAS("platform:dwmmc-rockchip");
|
MODULE_ALIAS("platform:dwmmc_rockchip");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
|
@ -1236,11 +1236,15 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||||
struct dw_mci *host = slot->host;
|
struct dw_mci *host = slot->host;
|
||||||
|
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||||
u32 uhs;
|
u32 uhs;
|
||||||
u32 v18 = SDMMC_UHS_18V << slot->id;
|
u32 v18 = SDMMC_UHS_18V << slot->id;
|
||||||
int min_uv, max_uv;
|
int min_uv, max_uv;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (drv_data && drv_data->switch_voltage)
|
||||||
|
return drv_data->switch_voltage(mmc, ios);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Program the voltage. Note that some instances of dw_mmc may use
|
* Program the voltage. Note that some instances of dw_mmc may use
|
||||||
* the UHS_REG for this. For other instances (like exynos) the UHS_REG
|
* the UHS_REG for this. For other instances (like exynos) the UHS_REG
|
||||||
|
@ -1278,10 +1282,7 @@ 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 (!IS_ERR_VALUE(gpio_ro))
|
||||||
(slot->host->quirks & DW_MCI_QUIRK_NO_WRITE_PROTECT))
|
|
||||||
read_only = 0;
|
|
||||||
else if (!IS_ERR_VALUE(gpio_ro))
|
|
||||||
read_only = gpio_ro;
|
read_only = gpio_ro;
|
||||||
else
|
else
|
||||||
read_only =
|
read_only =
|
||||||
|
@ -2280,9 +2281,10 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
/* given a slot id, find out the device node representing that slot */
|
/* given a slot, find out the device node representing that slot */
|
||||||
static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
|
static struct device_node *dw_mci_of_find_slot_node(struct dw_mci_slot *slot)
|
||||||
{
|
{
|
||||||
|
struct device *dev = slot->mmc->parent;
|
||||||
struct device_node *np;
|
struct device_node *np;
|
||||||
const __be32 *addr;
|
const __be32 *addr;
|
||||||
int len;
|
int len;
|
||||||
|
@ -2294,42 +2296,28 @@ static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
|
||||||
addr = of_get_property(np, "reg", &len);
|
addr = of_get_property(np, "reg", &len);
|
||||||
if (!addr || (len < sizeof(int)))
|
if (!addr || (len < sizeof(int)))
|
||||||
continue;
|
continue;
|
||||||
if (be32_to_cpup(addr) == slot)
|
if (be32_to_cpup(addr) == slot->id)
|
||||||
return np;
|
return np;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dw_mci_of_slot_quirks {
|
static void dw_mci_slot_of_parse(struct dw_mci_slot *slot)
|
||||||
char *quirk;
|
|
||||||
int id;
|
|
||||||
} of_slot_quirks[] = {
|
|
||||||
{
|
|
||||||
.quirk = "disable-wp",
|
|
||||||
.id = DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
|
|
||||||
{
|
{
|
||||||
struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
|
struct device_node *np = dw_mci_of_find_slot_node(slot);
|
||||||
int quirks = 0;
|
|
||||||
int idx;
|
|
||||||
|
|
||||||
/* get quirks */
|
if (!np)
|
||||||
for (idx = 0; idx < ARRAY_SIZE(of_slot_quirks); idx++)
|
return;
|
||||||
if (of_get_property(np, of_slot_quirks[idx].quirk, NULL)) {
|
|
||||||
dev_warn(dev, "Slot quirk %s is deprecated\n",
|
if (of_property_read_bool(np, "disable-wp")) {
|
||||||
of_slot_quirks[idx].quirk);
|
slot->mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
|
||||||
quirks |= of_slot_quirks[idx].id;
|
dev_warn(slot->mmc->parent,
|
||||||
|
"Slot quirk 'disable-wp' is deprecated\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return quirks;
|
|
||||||
}
|
}
|
||||||
#else /* CONFIG_OF */
|
#else /* CONFIG_OF */
|
||||||
static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
|
static void dw_mci_slot_of_parse(struct dw_mci_slot *slot)
|
||||||
{
|
{
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_OF */
|
#endif /* CONFIG_OF */
|
||||||
|
|
||||||
|
@ -2352,8 +2340,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
||||||
slot->host = host;
|
slot->host = host;
|
||||||
host->slot[id] = slot;
|
host->slot[id] = slot;
|
||||||
|
|
||||||
slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id);
|
|
||||||
|
|
||||||
mmc->ops = &dw_mci_ops;
|
mmc->ops = &dw_mci_ops;
|
||||||
if (of_property_read_u32_array(host->dev->of_node,
|
if (of_property_read_u32_array(host->dev->of_node,
|
||||||
"clock-freq-min-max", freq, 2)) {
|
"clock-freq-min-max", freq, 2)) {
|
||||||
|
@ -2391,6 +2377,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
||||||
if (host->pdata->caps2)
|
if (host->pdata->caps2)
|
||||||
mmc->caps2 = host->pdata->caps2;
|
mmc->caps2 = host->pdata->caps2;
|
||||||
|
|
||||||
|
dw_mci_slot_of_parse(slot);
|
||||||
|
|
||||||
ret = mmc_of_parse(mmc);
|
ret = mmc_of_parse(mmc);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_host_allocated;
|
goto err_host_allocated;
|
||||||
|
@ -2618,9 +2606,6 @@ 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,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2941,15 +2926,15 @@ void dw_mci_remove(struct dw_mci *host)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
mci_writel(host, RINTSTS, 0xFFFFFFFF);
|
|
||||||
mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
|
|
||||||
|
|
||||||
for (i = 0; i < host->num_slots; i++) {
|
for (i = 0; i < host->num_slots; i++) {
|
||||||
dev_dbg(host->dev, "remove slot %d\n", i);
|
dev_dbg(host->dev, "remove slot %d\n", i);
|
||||||
if (host->slot[i])
|
if (host->slot[i])
|
||||||
dw_mci_cleanup_slot(host->slot[i], i);
|
dw_mci_cleanup_slot(host->slot[i], i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mci_writel(host, RINTSTS, 0xFFFFFFFF);
|
||||||
|
mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
|
||||||
|
|
||||||
/* disable clock to CIU */
|
/* disable clock to CIU */
|
||||||
mci_writel(host, CLKENA, 0);
|
mci_writel(host, CLKENA, 0);
|
||||||
mci_writel(host, CLKSRC, 0);
|
mci_writel(host, CLKSRC, 0);
|
||||||
|
|
|
@ -227,7 +227,6 @@ extern int dw_mci_resume(struct dw_mci *host);
|
||||||
* struct dw_mci_slot - MMC slot state
|
* struct dw_mci_slot - MMC slot state
|
||||||
* @mmc: The mmc_host representing this slot.
|
* @mmc: The mmc_host representing this slot.
|
||||||
* @host: The MMC controller this slot is using.
|
* @host: The MMC controller this slot is using.
|
||||||
* @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX)
|
|
||||||
* @ctype: Card type for this slot.
|
* @ctype: Card type for this slot.
|
||||||
* @mrq: mmc_request currently being processed or waiting to be
|
* @mrq: mmc_request currently being processed or waiting to be
|
||||||
* processed, or NULL when the slot is idle.
|
* processed, or NULL when the slot is idle.
|
||||||
|
@ -245,8 +244,6 @@ struct dw_mci_slot {
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct dw_mci *host;
|
struct dw_mci *host;
|
||||||
|
|
||||||
int quirks;
|
|
||||||
|
|
||||||
u32 ctype;
|
u32 ctype;
|
||||||
|
|
||||||
struct mmc_request *mrq;
|
struct mmc_request *mrq;
|
||||||
|
@ -287,5 +284,7 @@ struct dw_mci_drv_data {
|
||||||
int (*execute_tuning)(struct dw_mci_slot *slot);
|
int (*execute_tuning)(struct dw_mci_slot *slot);
|
||||||
int (*prepare_hs400_tuning)(struct dw_mci *host,
|
int (*prepare_hs400_tuning)(struct dw_mci *host,
|
||||||
struct mmc_ios *ios);
|
struct mmc_ios *ios);
|
||||||
|
int (*switch_voltage)(struct mmc_host *mmc,
|
||||||
|
struct mmc_ios *ios);
|
||||||
};
|
};
|
||||||
#endif /* _DW_MMC_H_ */
|
#endif /* _DW_MMC_H_ */
|
||||||
|
|
1462
drivers/mmc/host/mtk-sd.c
Normal file
1462
drivers/mmc/host/mtk-sd.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -605,11 +605,7 @@ static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
|
||||||
mxcmci_writel(host, cpu_to_le32(tmp), MMC_REG_BUFFER_ACCESS);
|
mxcmci_writel(host, cpu_to_le32(tmp), MMC_REG_BUFFER_ACCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
|
return mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
|
||||||
if (stat)
|
|
||||||
return stat;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mxcmci_transfer_data(struct mxcmci_host *host)
|
static int mxcmci_transfer_data(struct mxcmci_host *host)
|
||||||
|
|
|
@ -549,7 +549,7 @@ static const struct mmc_host_ops mxs_mmc_ops = {
|
||||||
.enable_sdio_irq = mxs_mmc_enable_sdio_irq,
|
.enable_sdio_irq = mxs_mmc_enable_sdio_irq,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device_id mxs_ssp_ids[] = {
|
static const struct platform_device_id mxs_ssp_ids[] = {
|
||||||
{
|
{
|
||||||
.name = "imx23-mmc",
|
.name = "imx23-mmc",
|
||||||
.driver_data = IMX23_SSP,
|
.driver_data = IMX23_SSP,
|
||||||
|
|
|
@ -1474,7 +1474,7 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_device_id rtsx_pci_sdmmc_ids[] = {
|
static const struct platform_device_id rtsx_pci_sdmmc_ids[] = {
|
||||||
{
|
{
|
||||||
.name = DRV_NAME_RTSX_PCI_SDMMC,
|
.name = DRV_NAME_RTSX_PCI_SDMMC,
|
||||||
}, {
|
}, {
|
||||||
|
|
|
@ -1439,7 +1439,7 @@ static int rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_device_id rtsx_usb_sdmmc_ids[] = {
|
static const struct platform_device_id rtsx_usb_sdmmc_ids[] = {
|
||||||
{
|
{
|
||||||
.name = "rtsx_usb_sdmmc",
|
.name = "rtsx_usb_sdmmc",
|
||||||
}, {
|
}, {
|
||||||
|
|
|
@ -1856,7 +1856,7 @@ static int s3cmci_remove(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_device_id s3cmci_driver_ids[] = {
|
static const struct platform_device_id s3cmci_driver_ids[] = {
|
||||||
{
|
{
|
||||||
.name = "s3c2410-sdi",
|
.name = "s3c2410-sdi",
|
||||||
.driver_data = 0,
|
.driver_data = 0,
|
||||||
|
|
|
@ -172,9 +172,19 @@ static int bcm2835_sdhci_probe(struct platform_device *pdev)
|
||||||
ret = PTR_ERR(pltfm_host->clk);
|
ret = PTR_ERR(pltfm_host->clk);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
ret = clk_prepare_enable(pltfm_host->clk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "failed to enable host clk\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
return sdhci_add_host(host);
|
ret = sdhci_add_host(host);
|
||||||
|
if (ret)
|
||||||
|
goto err_clk;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err_clk:
|
||||||
|
clk_disable_unprepare(pltfm_host->clk);
|
||||||
err:
|
err:
|
||||||
sdhci_pltfm_free(pdev);
|
sdhci_pltfm_free(pdev);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* derived from the OF-version.
|
* derived from the OF-version.
|
||||||
*
|
*
|
||||||
* Copyright (c) 2010 Pengutronix e.K.
|
* Copyright (c) 2010 Pengutronix e.K.
|
||||||
* Author: Wolfram Sang <w.sang@pengutronix.de>
|
* Author: Wolfram Sang <kernel@pengutronix.de>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -112,6 +112,14 @@
|
||||||
#define ESDHC_FLAG_STD_TUNING BIT(5)
|
#define ESDHC_FLAG_STD_TUNING BIT(5)
|
||||||
/* The IP has SDHCI_CAPABILITIES_1 register */
|
/* The IP has SDHCI_CAPABILITIES_1 register */
|
||||||
#define ESDHC_FLAG_HAVE_CAP1 BIT(6)
|
#define ESDHC_FLAG_HAVE_CAP1 BIT(6)
|
||||||
|
/*
|
||||||
|
* The IP has errata ERR004536
|
||||||
|
* uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow,
|
||||||
|
* when reading data from the card
|
||||||
|
*/
|
||||||
|
#define ESDHC_FLAG_ERR004536 BIT(7)
|
||||||
|
/* The IP supports HS200 mode */
|
||||||
|
#define ESDHC_FLAG_HS200 BIT(8)
|
||||||
|
|
||||||
struct esdhc_soc_data {
|
struct esdhc_soc_data {
|
||||||
u32 flags;
|
u32 flags;
|
||||||
|
@ -139,7 +147,13 @@ static struct esdhc_soc_data usdhc_imx6q_data = {
|
||||||
|
|
||||||
static struct esdhc_soc_data usdhc_imx6sl_data = {
|
static struct esdhc_soc_data usdhc_imx6sl_data = {
|
||||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||||
| ESDHC_FLAG_HAVE_CAP1,
|
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
|
||||||
|
| ESDHC_FLAG_HS200,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct esdhc_soc_data usdhc_imx6sx_data = {
|
||||||
|
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||||
|
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pltfm_imx_data {
|
struct pltfm_imx_data {
|
||||||
|
@ -161,7 +175,7 @@ struct pltfm_imx_data {
|
||||||
u32 is_ddr;
|
u32 is_ddr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device_id imx_esdhc_devtype[] = {
|
static const struct platform_device_id imx_esdhc_devtype[] = {
|
||||||
{
|
{
|
||||||
.name = "sdhci-esdhc-imx25",
|
.name = "sdhci-esdhc-imx25",
|
||||||
.driver_data = (kernel_ulong_t) &esdhc_imx25_data,
|
.driver_data = (kernel_ulong_t) &esdhc_imx25_data,
|
||||||
|
@ -182,6 +196,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
|
||||||
{ .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
|
{ .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
|
||||||
{ .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
|
{ .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
|
||||||
{ .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
|
{ .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
|
||||||
|
{ .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
|
||||||
{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
|
{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
|
||||||
{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
|
{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
|
@ -298,7 +313,7 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
|
||||||
u32 data;
|
u32 data;
|
||||||
|
|
||||||
if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
|
if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
|
||||||
if (val & SDHCI_INT_CARD_INT) {
|
if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) {
|
||||||
/*
|
/*
|
||||||
* Clear and then set D3CD bit to avoid missing the
|
* Clear and then set D3CD bit to avoid missing the
|
||||||
* card interrupt. This is a eSDHC controller problem
|
* card interrupt. This is a eSDHC controller problem
|
||||||
|
@ -313,6 +328,11 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
|
||||||
data |= ESDHC_CTRL_D3CD;
|
data |= ESDHC_CTRL_D3CD;
|
||||||
writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
|
writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (val & SDHCI_INT_ADMA_ERROR) {
|
||||||
|
val &= ~SDHCI_INT_ADMA_ERROR;
|
||||||
|
val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
|
if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
|
||||||
|
@ -333,13 +353,6 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
|
|
||||||
if (val & SDHCI_INT_ADMA_ERROR) {
|
|
||||||
val &= ~SDHCI_INT_ADMA_ERROR;
|
|
||||||
val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writel(val, host->ioaddr + reg);
|
writel(val, host->ioaddr + reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -903,7 +916,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
||||||
|
|
||||||
mmc_of_parse_voltage(np, &host->ocr_mask);
|
mmc_of_parse_voltage(np, &host->ocr_mask);
|
||||||
|
|
||||||
return 0;
|
/* call to generic mmc_of_parse to support additional capabilities */
|
||||||
|
return mmc_of_parse(host->mmc);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static inline int
|
static inline int
|
||||||
|
@ -924,6 +938,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
||||||
struct esdhc_platform_data *boarddata;
|
struct esdhc_platform_data *boarddata;
|
||||||
int err;
|
int err;
|
||||||
struct pltfm_imx_data *imx_data;
|
struct pltfm_imx_data *imx_data;
|
||||||
|
bool dt = true;
|
||||||
|
|
||||||
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata, 0);
|
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata, 0);
|
||||||
if (IS_ERR(host))
|
if (IS_ERR(host))
|
||||||
|
@ -991,6 +1006,16 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
||||||
writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
|
writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
|
||||||
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
||||||
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||||
|
|
||||||
|
if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
|
||||||
|
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* errata ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
|
||||||
|
* TO1.1, it's harmless for MX6SL
|
||||||
|
*/
|
||||||
|
writel(readl(host->ioaddr + 0x6c) | BIT(7),
|
||||||
|
host->ioaddr + 0x6c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
|
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
|
||||||
|
@ -1002,6 +1027,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
||||||
ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP,
|
ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP,
|
||||||
host->ioaddr + ESDHC_TUNING_CTRL);
|
host->ioaddr + ESDHC_TUNING_CTRL);
|
||||||
|
|
||||||
|
if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
|
||||||
|
host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
|
||||||
|
|
||||||
boarddata = &imx_data->boarddata;
|
boarddata = &imx_data->boarddata;
|
||||||
if (sdhci_esdhc_imx_probe_dt(pdev, host, boarddata) < 0) {
|
if (sdhci_esdhc_imx_probe_dt(pdev, host, boarddata) < 0) {
|
||||||
if (!host->mmc->parent->platform_data) {
|
if (!host->mmc->parent->platform_data) {
|
||||||
|
@ -1011,11 +1039,44 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
imx_data->boarddata = *((struct esdhc_platform_data *)
|
imx_data->boarddata = *((struct esdhc_platform_data *)
|
||||||
host->mmc->parent->platform_data);
|
host->mmc->parent->platform_data);
|
||||||
|
dt = false;
|
||||||
|
}
|
||||||
|
/* write_protect */
|
||||||
|
if (boarddata->wp_type == ESDHC_WP_GPIO && !dt) {
|
||||||
|
err = mmc_gpio_request_ro(host->mmc, boarddata->wp_gpio);
|
||||||
|
if (err) {
|
||||||
|
dev_err(mmc_dev(host->mmc),
|
||||||
|
"failed to request write-protect gpio!\n");
|
||||||
|
goto disable_clk;
|
||||||
|
}
|
||||||
|
host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* card_detect */
|
/* card_detect */
|
||||||
if (boarddata->cd_type == ESDHC_CD_CONTROLLER)
|
switch (boarddata->cd_type) {
|
||||||
|
case ESDHC_CD_GPIO:
|
||||||
|
if (dt)
|
||||||
|
break;
|
||||||
|
err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio, 0);
|
||||||
|
if (err) {
|
||||||
|
dev_err(mmc_dev(host->mmc),
|
||||||
|
"failed to request card-detect gpio!\n");
|
||||||
|
goto disable_clk;
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
case ESDHC_CD_CONTROLLER:
|
||||||
|
/* we have a working card_detect back */
|
||||||
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESDHC_CD_PERMANENT:
|
||||||
|
host->mmc->caps |= MMC_CAP_NONREMOVABLE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESDHC_CD_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (boarddata->max_bus_width) {
|
switch (boarddata->max_bus_width) {
|
||||||
case 8:
|
case 8:
|
||||||
|
@ -1048,11 +1109,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
||||||
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
|
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* call to generic mmc_of_parse to support additional capabilities */
|
|
||||||
err = mmc_of_parse(host->mmc);
|
|
||||||
if (err)
|
|
||||||
goto disable_clk;
|
|
||||||
|
|
||||||
err = sdhci_add_host(host);
|
err = sdhci_add_host(host);
|
||||||
if (err)
|
if (err)
|
||||||
goto disable_clk;
|
goto disable_clk;
|
||||||
|
@ -1151,5 +1207,5 @@ static struct platform_driver sdhci_esdhc_imx_driver = {
|
||||||
module_platform_driver(sdhci_esdhc_imx_driver);
|
module_platform_driver(sdhci_esdhc_imx_driver);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
|
MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
|
||||||
MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
|
MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
#define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c
|
#define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c
|
||||||
|
@ -168,6 +169,11 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||||
goto clk_disable_all;
|
goto clk_disable_all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (of_device_is_compatible(pdev->dev.of_node, "arasan,sdhci-4.9a")) {
|
||||||
|
host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
|
||||||
|
host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23;
|
||||||
|
}
|
||||||
|
|
||||||
sdhci_get_of_property(pdev);
|
sdhci_get_of_property(pdev);
|
||||||
pltfm_host = sdhci_priv(host);
|
pltfm_host = sdhci_priv(host);
|
||||||
pltfm_host->priv = sdhci_arasan;
|
pltfm_host->priv = sdhci_arasan;
|
||||||
|
@ -208,6 +214,7 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
static const struct of_device_id sdhci_arasan_of_match[] = {
|
static const struct of_device_id sdhci_arasan_of_match[] = {
|
||||||
{ .compatible = "arasan,sdhci-8.9a" },
|
{ .compatible = "arasan,sdhci-8.9a" },
|
||||||
|
{ .compatible = "arasan,sdhci-4.9a" },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
|
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
|
||||||
|
|
|
@ -199,7 +199,7 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
|
||||||
|
|
||||||
static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||||
{
|
{
|
||||||
int pre_div = 2;
|
int pre_div = 1;
|
||||||
int div = 1;
|
int div = 1;
|
||||||
u32 temp;
|
u32 temp;
|
||||||
|
|
||||||
|
@ -229,7 +229,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||||
|
|
||||||
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
|
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
|
||||||
clock, host->max_clk / pre_div / div);
|
clock, host->max_clk / pre_div / div);
|
||||||
|
host->mmc->actual_clock = host->max_clk / pre_div / div;
|
||||||
pre_div >>= 1;
|
pre_div >>= 1;
|
||||||
div--;
|
div--;
|
||||||
|
|
||||||
|
@ -361,6 +361,13 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
|
||||||
sdhci_get_of_property(pdev);
|
sdhci_get_of_property(pdev);
|
||||||
|
|
||||||
np = pdev->dev.of_node;
|
np = pdev->dev.of_node;
|
||||||
|
if (of_device_is_compatible(np, "fsl,p5040-esdhc") ||
|
||||||
|
of_device_is_compatible(np, "fsl,p5020-esdhc") ||
|
||||||
|
of_device_is_compatible(np, "fsl,p4080-esdhc") ||
|
||||||
|
of_device_is_compatible(np, "fsl,p1020-esdhc") ||
|
||||||
|
of_device_is_compatible(np, "fsl,t1040-esdhc"))
|
||||||
|
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||||
|
|
||||||
if (of_device_is_compatible(np, "fsl,p2020-esdhc")) {
|
if (of_device_is_compatible(np, "fsl,p2020-esdhc")) {
|
||||||
/*
|
/*
|
||||||
* Freescale messed up with P2020 as it has a non-standard
|
* Freescale messed up with P2020 as it has a non-standard
|
||||||
|
|
|
@ -3,3 +3,6 @@
|
||||||
|
|
||||||
struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno);
|
struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno);
|
||||||
EXPORT_SYMBOL_GPL(sdhci_pci_get_data);
|
EXPORT_SYMBOL_GPL(sdhci_pci_get_data);
|
||||||
|
|
||||||
|
int sdhci_pci_spt_drive_strength;
|
||||||
|
EXPORT_SYMBOL_GPL(sdhci_pci_spt_drive_strength);
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
|
#include <linux/mmc/mmc.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
@ -266,6 +267,69 @@ static void sdhci_pci_int_hw_reset(struct sdhci_host *host)
|
||||||
usleep_range(300, 1000);
|
usleep_range(300, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int spt_select_drive_strength(struct sdhci_host *host,
|
||||||
|
struct mmc_card *card,
|
||||||
|
unsigned int max_dtr,
|
||||||
|
int host_drv, int card_drv, int *drv_type)
|
||||||
|
{
|
||||||
|
int drive_strength;
|
||||||
|
|
||||||
|
if (sdhci_pci_spt_drive_strength > 0)
|
||||||
|
drive_strength = sdhci_pci_spt_drive_strength & 0xf;
|
||||||
|
else
|
||||||
|
drive_strength = 1; /* 33-ohm */
|
||||||
|
|
||||||
|
if ((mmc_driver_type_mask(drive_strength) & card_drv) == 0)
|
||||||
|
drive_strength = 0; /* Default 50-ohm */
|
||||||
|
|
||||||
|
return drive_strength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to read the drive strength from the card */
|
||||||
|
static void spt_read_drive_strength(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
u32 val, i, t;
|
||||||
|
u16 m;
|
||||||
|
|
||||||
|
if (sdhci_pci_spt_drive_strength)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sdhci_pci_spt_drive_strength = -1;
|
||||||
|
|
||||||
|
m = sdhci_readw(host, SDHCI_HOST_CONTROL2) & 0x7;
|
||||||
|
if (m != 3 && m != 5)
|
||||||
|
return;
|
||||||
|
val = sdhci_readl(host, SDHCI_PRESENT_STATE);
|
||||||
|
if (val & 0x3)
|
||||||
|
return;
|
||||||
|
sdhci_writel(host, 0x007f0023, SDHCI_INT_ENABLE);
|
||||||
|
sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
|
||||||
|
sdhci_writew(host, 0x10, SDHCI_TRANSFER_MODE);
|
||||||
|
sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL);
|
||||||
|
sdhci_writew(host, 512, SDHCI_BLOCK_SIZE);
|
||||||
|
sdhci_writew(host, 1, SDHCI_BLOCK_COUNT);
|
||||||
|
sdhci_writel(host, 0, SDHCI_ARGUMENT);
|
||||||
|
sdhci_writew(host, 0x83b, SDHCI_COMMAND);
|
||||||
|
for (i = 0; i < 1000; i++) {
|
||||||
|
val = sdhci_readl(host, SDHCI_INT_STATUS);
|
||||||
|
if (val & 0xffff8000)
|
||||||
|
return;
|
||||||
|
if (val & 0x20)
|
||||||
|
break;
|
||||||
|
udelay(1);
|
||||||
|
}
|
||||||
|
val = sdhci_readl(host, SDHCI_PRESENT_STATE);
|
||||||
|
if (!(val & 0x800))
|
||||||
|
return;
|
||||||
|
for (i = 0; i < 47; i++)
|
||||||
|
val = sdhci_readl(host, SDHCI_BUFFER);
|
||||||
|
t = val & 0xf00;
|
||||||
|
if (t != 0x200 && t != 0x300)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sdhci_pci_spt_drive_strength = 0x10 | ((val >> 12) & 0xf);
|
||||||
|
}
|
||||||
|
|
||||||
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 |
|
||||||
|
@ -276,6 +340,10 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
slot->hw_reset = sdhci_pci_int_hw_reset;
|
slot->hw_reset = sdhci_pci_int_hw_reset;
|
||||||
if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC)
|
if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC)
|
||||||
slot->host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */
|
slot->host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */
|
||||||
|
if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_SPT_EMMC) {
|
||||||
|
spt_read_drive_strength(slot->host);
|
||||||
|
slot->select_drive_strength = spt_select_drive_strength;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,6 +370,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
|
||||||
.probe_slot = byt_emmc_probe_slot,
|
.probe_slot = byt_emmc_probe_slot,
|
||||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||||
|
SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 |
|
||||||
SDHCI_QUIRK2_STOP_WITH_TC,
|
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -655,14 +724,37 @@ static const struct sdhci_pci_fixes sdhci_rtsx = {
|
||||||
.probe_slot = rtsx_probe_slot,
|
.probe_slot = rtsx_probe_slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*AMD chipset generation*/
|
||||||
|
enum amd_chipset_gen {
|
||||||
|
AMD_CHIPSET_BEFORE_ML,
|
||||||
|
AMD_CHIPSET_CZ,
|
||||||
|
AMD_CHIPSET_NL,
|
||||||
|
AMD_CHIPSET_UNKNOWN,
|
||||||
|
};
|
||||||
|
|
||||||
static int amd_probe(struct sdhci_pci_chip *chip)
|
static int amd_probe(struct sdhci_pci_chip *chip)
|
||||||
{
|
{
|
||||||
struct pci_dev *smbus_dev;
|
struct pci_dev *smbus_dev;
|
||||||
|
enum amd_chipset_gen gen;
|
||||||
|
|
||||||
smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
|
smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
|
||||||
PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL);
|
PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL);
|
||||||
|
if (smbus_dev) {
|
||||||
|
gen = AMD_CHIPSET_BEFORE_ML;
|
||||||
|
} else {
|
||||||
|
smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
|
||||||
|
PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, NULL);
|
||||||
|
if (smbus_dev) {
|
||||||
|
if (smbus_dev->revision < 0x51)
|
||||||
|
gen = AMD_CHIPSET_CZ;
|
||||||
|
else
|
||||||
|
gen = AMD_CHIPSET_NL;
|
||||||
|
} else {
|
||||||
|
gen = AMD_CHIPSET_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (smbus_dev && (smbus_dev->revision < 0x51)) {
|
if ((gen == AMD_CHIPSET_BEFORE_ML) || (gen == AMD_CHIPSET_CZ)) {
|
||||||
chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD;
|
chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD;
|
||||||
chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
|
chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
|
||||||
}
|
}
|
||||||
|
@ -1203,6 +1295,20 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host)
|
||||||
slot->hw_reset(host);
|
slot->hw_reset(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sdhci_pci_select_drive_strength(struct sdhci_host *host,
|
||||||
|
struct mmc_card *card,
|
||||||
|
unsigned int max_dtr, int host_drv,
|
||||||
|
int card_drv, int *drv_type)
|
||||||
|
{
|
||||||
|
struct sdhci_pci_slot *slot = sdhci_priv(host);
|
||||||
|
|
||||||
|
if (!slot->select_drive_strength)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return slot->select_drive_strength(host, card, max_dtr, host_drv,
|
||||||
|
card_drv, drv_type);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct sdhci_ops sdhci_pci_ops = {
|
static const struct sdhci_ops sdhci_pci_ops = {
|
||||||
.set_clock = sdhci_set_clock,
|
.set_clock = sdhci_set_clock,
|
||||||
.enable_dma = sdhci_pci_enable_dma,
|
.enable_dma = sdhci_pci_enable_dma,
|
||||||
|
@ -1210,6 +1316,7 @@ static const struct sdhci_ops sdhci_pci_ops = {
|
||||||
.reset = sdhci_reset,
|
.reset = sdhci_reset,
|
||||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||||
.hw_reset = sdhci_pci_hw_reset,
|
.hw_reset = sdhci_pci_hw_reset,
|
||||||
|
.select_drive_strength = sdhci_pci_select_drive_strength,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************\
|
/*****************************************************************************\
|
||||||
|
|
|
@ -72,6 +72,10 @@ struct sdhci_pci_slot {
|
||||||
bool cd_override_level;
|
bool cd_override_level;
|
||||||
|
|
||||||
void (*hw_reset)(struct sdhci_host *host);
|
void (*hw_reset)(struct sdhci_host *host);
|
||||||
|
int (*select_drive_strength)(struct sdhci_host *host,
|
||||||
|
struct mmc_card *card,
|
||||||
|
unsigned int max_dtr, int host_drv,
|
||||||
|
int card_drv, int *drv_type);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sdhci_pci_chip {
|
struct sdhci_pci_chip {
|
||||||
|
|
|
@ -252,9 +252,7 @@ static int sdhci_pxav2_remove(struct platform_device *pdev)
|
||||||
static struct platform_driver sdhci_pxav2_driver = {
|
static struct platform_driver sdhci_pxav2_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-pxav2",
|
.name = "sdhci-pxav2",
|
||||||
#ifdef CONFIG_OF
|
.of_match_table = of_match_ptr(sdhci_pxav2_of_match),
|
||||||
.of_match_table = sdhci_pxav2_of_match,
|
|
||||||
#endif
|
|
||||||
.pm = SDHCI_PLTFM_PMOPS,
|
.pm = SDHCI_PLTFM_PMOPS,
|
||||||
},
|
},
|
||||||
.probe = sdhci_pxav2_probe,
|
.probe = sdhci_pxav2_probe,
|
||||||
|
|
|
@ -457,12 +457,8 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
platform_set_drvdata(pdev, host);
|
platform_set_drvdata(pdev, host);
|
||||||
|
|
||||||
if (host->mmc->pm_caps & MMC_PM_KEEP_POWER) {
|
if (host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ)
|
||||||
device_init_wakeup(&pdev->dev, 1);
|
device_init_wakeup(&pdev->dev, 1);
|
||||||
host->mmc->pm_flags |= MMC_PM_WAKE_SDIO_IRQ;
|
|
||||||
} else {
|
|
||||||
device_init_wakeup(&pdev->dev, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pm_runtime_put_autosuspend(&pdev->dev);
|
pm_runtime_put_autosuspend(&pdev->dev);
|
||||||
|
|
||||||
|
@ -578,9 +574,7 @@ static const struct dev_pm_ops sdhci_pxav3_pmops = {
|
||||||
static struct platform_driver sdhci_pxav3_driver = {
|
static struct platform_driver sdhci_pxav3_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-pxav3",
|
.name = "sdhci-pxav3",
|
||||||
#ifdef CONFIG_OF
|
.of_match_table = of_match_ptr(sdhci_pxav3_of_match),
|
||||||
.of_match_table = sdhci_pxav3_of_match,
|
|
||||||
#endif
|
|
||||||
.pm = SDHCI_PXAV3_PMOPS,
|
.pm = SDHCI_PXAV3_PMOPS,
|
||||||
},
|
},
|
||||||
.probe = sdhci_pxav3_probe,
|
.probe = sdhci_pxav3_probe,
|
||||||
|
|
|
@ -736,7 +736,7 @@ static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
|
||||||
#define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)NULL)
|
#define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)NULL)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static struct platform_device_id sdhci_s3c_driver_ids[] = {
|
static const struct platform_device_id sdhci_s3c_driver_ids[] = {
|
||||||
{
|
{
|
||||||
.name = "s3c-sdhci",
|
.name = "s3c-sdhci",
|
||||||
.driver_data = (kernel_ulong_t)NULL,
|
.driver_data = (kernel_ulong_t)NULL,
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
#define SDHCI_CLK_DELAY_SETTING 0x4C
|
#define SDHCI_CLK_DELAY_SETTING 0x4C
|
||||||
#define SDHCI_SIRF_8BITBUS BIT(3)
|
#define SDHCI_SIRF_8BITBUS BIT(3)
|
||||||
#define SIRF_TUNING_COUNT 128
|
#define SIRF_TUNING_COUNT 16384
|
||||||
|
|
||||||
struct sdhci_sirf_priv {
|
struct sdhci_sirf_priv {
|
||||||
int gpio_cd;
|
int gpio_cd;
|
||||||
|
@ -43,10 +43,43 @@ static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width)
|
||||||
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 sdhci_sirf_readl_le(struct sdhci_host *host, int reg)
|
||||||
|
{
|
||||||
|
u32 val = readl(host->ioaddr + reg);
|
||||||
|
|
||||||
|
if (unlikely((reg == SDHCI_CAPABILITIES_1) &&
|
||||||
|
(host->mmc->caps & MMC_CAP_UHS_SDR50))) {
|
||||||
|
/* fake CAP_1 register */
|
||||||
|
val = SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(reg == SDHCI_SLOT_INT_STATUS)) {
|
||||||
|
u32 prss = val;
|
||||||
|
/* fake chips as V3.0 host conreoller */
|
||||||
|
prss &= ~(0xFF << 16);
|
||||||
|
val = prss | (SDHCI_SPEC_300 << 16);
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u16 sdhci_sirf_readw_le(struct sdhci_host *host, int reg)
|
||||||
|
{
|
||||||
|
u16 ret = 0;
|
||||||
|
|
||||||
|
ret = readw(host->ioaddr + reg);
|
||||||
|
|
||||||
|
if (unlikely(reg == SDHCI_HOST_VERSION)) {
|
||||||
|
ret = readw(host->ioaddr + SDHCI_HOST_VERSION);
|
||||||
|
ret |= SDHCI_SPEC_300;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
|
static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
|
||||||
{
|
{
|
||||||
int tuning_seq_cnt = 3;
|
int tuning_seq_cnt = 3;
|
||||||
u8 phase, tuned_phases[SIRF_TUNING_COUNT];
|
int phase;
|
||||||
u8 tuned_phase_cnt = 0;
|
u8 tuned_phase_cnt = 0;
|
||||||
int rc = 0, longest_range = 0;
|
int rc = 0, longest_range = 0;
|
||||||
int start = -1, end = 0, tuning_value = -1, range = 0;
|
int start = -1, end = 0, tuning_value = -1, range = 0;
|
||||||
|
@ -58,6 +91,7 @@ static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
phase = 0;
|
phase = 0;
|
||||||
|
tuned_phase_cnt = 0;
|
||||||
do {
|
do {
|
||||||
sdhci_writel(host,
|
sdhci_writel(host,
|
||||||
clock_setting | phase,
|
clock_setting | phase,
|
||||||
|
@ -65,7 +99,7 @@ static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
|
||||||
|
|
||||||
if (!mmc_send_tuning(mmc)) {
|
if (!mmc_send_tuning(mmc)) {
|
||||||
/* Tuning is successful at this tuning point */
|
/* Tuning is successful at this tuning point */
|
||||||
tuned_phases[tuned_phase_cnt++] = phase;
|
tuned_phase_cnt++;
|
||||||
dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
|
dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
|
||||||
mmc_hostname(mmc), phase);
|
mmc_hostname(mmc), phase);
|
||||||
if (start == -1)
|
if (start == -1)
|
||||||
|
@ -85,7 +119,7 @@ static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
|
||||||
start = -1;
|
start = -1;
|
||||||
end = range = 0;
|
end = range = 0;
|
||||||
}
|
}
|
||||||
} while (++phase < ARRAY_SIZE(tuned_phases));
|
} while (++phase < SIRF_TUNING_COUNT);
|
||||||
|
|
||||||
if (tuned_phase_cnt && tuning_value > 0) {
|
if (tuned_phase_cnt && tuning_value > 0) {
|
||||||
/*
|
/*
|
||||||
|
@ -112,6 +146,8 @@ static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sdhci_ops sdhci_sirf_ops = {
|
static struct sdhci_ops sdhci_sirf_ops = {
|
||||||
|
.read_l = sdhci_sirf_readl_le,
|
||||||
|
.read_w = sdhci_sirf_readw_le,
|
||||||
.platform_execute_tuning = sdhci_sirf_execute_tuning,
|
.platform_execute_tuning = sdhci_sirf_execute_tuning,
|
||||||
.set_clock = sdhci_set_clock,
|
.set_clock = sdhci_set_clock,
|
||||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||||
|
|
|
@ -509,4 +509,4 @@ module_platform_driver(sdhci_st_driver);
|
||||||
MODULE_DESCRIPTION("SDHCI driver for STMicroelectronics SoCs");
|
MODULE_DESCRIPTION("SDHCI driver for STMicroelectronics SoCs");
|
||||||
MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
|
MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
MODULE_ALIAS("platform:st-sdhci");
|
MODULE_ALIAS("platform:sdhci-st");
|
||||||
|
|
|
@ -52,7 +52,6 @@ static void sdhci_finish_data(struct sdhci_host *);
|
||||||
|
|
||||||
static void sdhci_finish_command(struct sdhci_host *);
|
static void sdhci_finish_command(struct sdhci_host *);
|
||||||
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
|
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
|
||||||
static void sdhci_tuning_timer(unsigned long data);
|
|
||||||
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
|
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
|
||||||
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
||||||
struct mmc_data *data,
|
struct mmc_data *data,
|
||||||
|
@ -254,17 +253,6 @@ static void sdhci_init(struct sdhci_host *host, int soft)
|
||||||
static void sdhci_reinit(struct sdhci_host *host)
|
static void sdhci_reinit(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
sdhci_init(host, 0);
|
sdhci_init(host, 0);
|
||||||
/*
|
|
||||||
* Retuning stuffs are affected by different cards inserted and only
|
|
||||||
* applicable to UHS-I cards. So reset these fields to their initial
|
|
||||||
* value when card is removed.
|
|
||||||
*/
|
|
||||||
if (host->flags & SDHCI_USING_RETUNING_TIMER) {
|
|
||||||
host->flags &= ~SDHCI_USING_RETUNING_TIMER;
|
|
||||||
|
|
||||||
del_timer_sync(&host->tuning_timer);
|
|
||||||
host->flags &= ~SDHCI_NEEDS_RETUNING;
|
|
||||||
}
|
|
||||||
sdhci_enable_card_detection(host);
|
sdhci_enable_card_detection(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,8 +316,7 @@ static void sdhci_read_block_pio(struct sdhci_host *host)
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
|
|
||||||
while (blksize) {
|
while (blksize) {
|
||||||
if (!sg_miter_next(&host->sg_miter))
|
BUG_ON(!sg_miter_next(&host->sg_miter));
|
||||||
BUG();
|
|
||||||
|
|
||||||
len = min(host->sg_miter.length, blksize);
|
len = min(host->sg_miter.length, blksize);
|
||||||
|
|
||||||
|
@ -374,8 +361,7 @@ static void sdhci_write_block_pio(struct sdhci_host *host)
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
|
|
||||||
while (blksize) {
|
while (blksize) {
|
||||||
if (!sg_miter_next(&host->sg_miter))
|
BUG_ON(!sg_miter_next(&host->sg_miter));
|
||||||
BUG();
|
|
||||||
|
|
||||||
len = min(host->sg_miter.length, blksize);
|
len = min(host->sg_miter.length, blksize);
|
||||||
|
|
||||||
|
@ -848,7 +834,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
||||||
int sg_cnt;
|
int sg_cnt;
|
||||||
|
|
||||||
sg_cnt = sdhci_pre_dma_transfer(host, data, NULL);
|
sg_cnt = sdhci_pre_dma_transfer(host, data, NULL);
|
||||||
if (sg_cnt == 0) {
|
if (sg_cnt <= 0) {
|
||||||
/*
|
/*
|
||||||
* This only happens when someone fed
|
* This only happens when someone fed
|
||||||
* us an invalid request.
|
* us an invalid request.
|
||||||
|
@ -1353,7 +1339,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
struct sdhci_host *host;
|
struct sdhci_host *host;
|
||||||
int present;
|
int present;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
u32 tuning_opcode;
|
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
|
|
||||||
|
@ -1387,39 +1372,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
host->mrq->cmd->error = -ENOMEDIUM;
|
host->mrq->cmd->error = -ENOMEDIUM;
|
||||||
tasklet_schedule(&host->finish_tasklet);
|
tasklet_schedule(&host->finish_tasklet);
|
||||||
} else {
|
} else {
|
||||||
u32 present_state;
|
|
||||||
|
|
||||||
present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
|
|
||||||
/*
|
|
||||||
* Check if the re-tuning timer has already expired and there
|
|
||||||
* is no on-going data transfer and DAT0 is not busy. If so,
|
|
||||||
* we need to execute tuning procedure before sending command.
|
|
||||||
*/
|
|
||||||
if ((host->flags & SDHCI_NEEDS_RETUNING) &&
|
|
||||||
!(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ)) &&
|
|
||||||
(present_state & SDHCI_DATA_0_LVL_MASK)) {
|
|
||||||
if (mmc->card) {
|
|
||||||
/* eMMC uses cmd21 but sd and sdio use cmd19 */
|
|
||||||
tuning_opcode =
|
|
||||||
mmc->card->type == MMC_TYPE_MMC ?
|
|
||||||
MMC_SEND_TUNING_BLOCK_HS200 :
|
|
||||||
MMC_SEND_TUNING_BLOCK;
|
|
||||||
|
|
||||||
/* Here we need to set the host->mrq to NULL,
|
|
||||||
* in case the pending finish_tasklet
|
|
||||||
* finishes it incorrectly.
|
|
||||||
*/
|
|
||||||
host->mrq = NULL;
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
|
||||||
sdhci_execute_tuning(mmc, tuning_opcode);
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
|
||||||
|
|
||||||
/* Restore original mmc_request structure */
|
|
||||||
host->mrq = mrq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
|
if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
|
||||||
sdhci_send_command(host, mrq->sbc);
|
sdhci_send_command(host, mrq->sbc);
|
||||||
else
|
else
|
||||||
|
@ -1562,8 +1514,17 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
||||||
ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK;
|
ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK;
|
||||||
if (ios->drv_type == MMC_SET_DRIVER_TYPE_A)
|
if (ios->drv_type == MMC_SET_DRIVER_TYPE_A)
|
||||||
ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A;
|
ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A;
|
||||||
|
else if (ios->drv_type == MMC_SET_DRIVER_TYPE_B)
|
||||||
|
ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B;
|
||||||
else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C)
|
else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C)
|
||||||
ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C;
|
ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C;
|
||||||
|
else if (ios->drv_type == MMC_SET_DRIVER_TYPE_D)
|
||||||
|
ctrl_2 |= SDHCI_CTRL_DRV_TYPE_D;
|
||||||
|
else {
|
||||||
|
pr_warn("%s: invalid driver type, default to "
|
||||||
|
"driver type B\n", mmc_hostname(mmc));
|
||||||
|
ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B;
|
||||||
|
}
|
||||||
|
|
||||||
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2065,23 +2026,18 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
host->flags &= ~SDHCI_NEEDS_RETUNING;
|
|
||||||
|
|
||||||
if (tuning_count) {
|
if (tuning_count) {
|
||||||
host->flags |= SDHCI_USING_RETUNING_TIMER;
|
/*
|
||||||
mod_timer(&host->tuning_timer, jiffies + tuning_count * HZ);
|
* In case tuning fails, host controllers which support
|
||||||
|
* re-tuning can try tuning again at a later time, when the
|
||||||
|
* re-tuning timer expires. So for these controllers, we
|
||||||
|
* return 0. Since there might be other controllers who do not
|
||||||
|
* have this capability, we return error for them.
|
||||||
|
*/
|
||||||
|
err = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
host->mmc->retune_period = err ? 0 : tuning_count;
|
||||||
* In case tuning fails, host controllers which support re-tuning can
|
|
||||||
* try tuning again at a later time, when the re-tuning timer expires.
|
|
||||||
* So for these controllers, we return 0. Since there might be other
|
|
||||||
* controllers who do not have this capability, we return error for
|
|
||||||
* them. SDHCI_USING_RETUNING_TIMER means the host is currently using
|
|
||||||
* a retuning timer to do the retuning for the card.
|
|
||||||
*/
|
|
||||||
if (err && (host->flags & SDHCI_USING_RETUNING_TIMER))
|
|
||||||
err = 0;
|
|
||||||
|
|
||||||
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
||||||
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
||||||
|
@ -2092,6 +2048,18 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sdhci_select_drive_strength(struct mmc_card *card,
|
||||||
|
unsigned int max_dtr, int host_drv,
|
||||||
|
int card_drv, int *drv_type)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(card->host);
|
||||||
|
|
||||||
|
if (!host->ops->select_drive_strength)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return host->ops->select_drive_strength(host, card, max_dtr, host_drv,
|
||||||
|
card_drv, drv_type);
|
||||||
|
}
|
||||||
|
|
||||||
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
|
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
|
||||||
{
|
{
|
||||||
|
@ -2236,6 +2204,7 @@ static const struct mmc_host_ops sdhci_ops = {
|
||||||
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
|
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
|
||||||
.prepare_hs400_tuning = sdhci_prepare_hs400_tuning,
|
.prepare_hs400_tuning = sdhci_prepare_hs400_tuning,
|
||||||
.execute_tuning = sdhci_execute_tuning,
|
.execute_tuning = sdhci_execute_tuning,
|
||||||
|
.select_drive_strength = sdhci_select_drive_strength,
|
||||||
.card_event = sdhci_card_event,
|
.card_event = sdhci_card_event,
|
||||||
.card_busy = sdhci_card_busy,
|
.card_busy = sdhci_card_busy,
|
||||||
};
|
};
|
||||||
|
@ -2337,20 +2306,6 @@ static void sdhci_timeout_timer(unsigned long data)
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_tuning_timer(unsigned long data)
|
|
||||||
{
|
|
||||||
struct sdhci_host *host;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
host = (struct sdhci_host *)data;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
|
||||||
|
|
||||||
host->flags |= SDHCI_NEEDS_RETUNING;
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************\
|
/*****************************************************************************\
|
||||||
* *
|
* *
|
||||||
* Interrupt handling *
|
* Interrupt handling *
|
||||||
|
@ -2728,11 +2683,8 @@ int sdhci_suspend_host(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
sdhci_disable_card_detection(host);
|
sdhci_disable_card_detection(host);
|
||||||
|
|
||||||
/* Disable tuning since we are suspending */
|
mmc_retune_timer_stop(host->mmc);
|
||||||
if (host->flags & SDHCI_USING_RETUNING_TIMER) {
|
mmc_retune_needed(host->mmc);
|
||||||
del_timer_sync(&host->tuning_timer);
|
|
||||||
host->flags &= ~SDHCI_NEEDS_RETUNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!device_may_wakeup(mmc_dev(host->mmc))) {
|
if (!device_may_wakeup(mmc_dev(host->mmc))) {
|
||||||
host->ier = 0;
|
host->ier = 0;
|
||||||
|
@ -2782,10 +2734,6 @@ int sdhci_resume_host(struct sdhci_host *host)
|
||||||
|
|
||||||
sdhci_enable_card_detection(host);
|
sdhci_enable_card_detection(host);
|
||||||
|
|
||||||
/* Set the re-tuning expiration flag */
|
|
||||||
if (host->flags & SDHCI_USING_RETUNING_TIMER)
|
|
||||||
host->flags |= SDHCI_NEEDS_RETUNING;
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2822,11 +2770,8 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
/* Disable tuning since we are suspending */
|
mmc_retune_timer_stop(host->mmc);
|
||||||
if (host->flags & SDHCI_USING_RETUNING_TIMER) {
|
mmc_retune_needed(host->mmc);
|
||||||
del_timer_sync(&host->tuning_timer);
|
|
||||||
host->flags &= ~SDHCI_NEEDS_RETUNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
host->ier &= SDHCI_INT_CARD_INT;
|
host->ier &= SDHCI_INT_CARD_INT;
|
||||||
|
@ -2869,10 +2814,6 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the re-tuning expiration flag */
|
|
||||||
if (host->flags & SDHCI_USING_RETUNING_TIMER)
|
|
||||||
host->flags |= SDHCI_NEEDS_RETUNING;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
host->runtime_suspended = false;
|
host->runtime_suspended = false;
|
||||||
|
@ -3315,13 +3256,14 @@ 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 OCR set by host, use it instead. */
|
||||||
|
if (host->ocr_mask)
|
||||||
|
ocr_avail = host->ocr_mask;
|
||||||
|
|
||||||
|
/* If OCR set by external regulators, give it highest prio. */
|
||||||
if (mmc->ocr_avail)
|
if (mmc->ocr_avail)
|
||||||
ocr_avail = mmc->ocr_avail;
|
ocr_avail = mmc->ocr_avail;
|
||||||
|
|
||||||
if (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;
|
||||||
if (host->ocr_avail_sdio)
|
if (host->ocr_avail_sdio)
|
||||||
|
@ -3408,13 +3350,6 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
|
|
||||||
init_waitqueue_head(&host->buf_ready_int);
|
init_waitqueue_head(&host->buf_ready_int);
|
||||||
|
|
||||||
if (host->version >= SDHCI_SPEC_300) {
|
|
||||||
/* Initialize re-tuning timer */
|
|
||||||
init_timer(&host->tuning_timer);
|
|
||||||
host->tuning_timer.data = (unsigned long)host;
|
|
||||||
host->tuning_timer.function = sdhci_tuning_timer;
|
|
||||||
}
|
|
||||||
|
|
||||||
sdhci_init(host, 0);
|
sdhci_init(host, 0);
|
||||||
|
|
||||||
ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,
|
ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,
|
||||||
|
|
|
@ -432,13 +432,11 @@ struct sdhci_host {
|
||||||
#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
|
#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
|
||||||
#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
|
#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
|
||||||
#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
|
#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
|
||||||
#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */
|
|
||||||
#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
|
#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
|
||||||
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
|
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
|
||||||
#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
|
#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
|
||||||
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
|
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
|
||||||
#define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */
|
#define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */
|
||||||
#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
|
|
||||||
#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
|
#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
|
||||||
#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
|
#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
|
||||||
|
|
||||||
|
@ -504,7 +502,6 @@ struct sdhci_host {
|
||||||
unsigned int tuning_count; /* Timer count for re-tuning */
|
unsigned int tuning_count; /* Timer count for re-tuning */
|
||||||
unsigned int tuning_mode; /* Re-tuning mode supported by host */
|
unsigned int tuning_mode; /* Re-tuning mode supported by host */
|
||||||
#define SDHCI_TUNING_MODE_1 0
|
#define SDHCI_TUNING_MODE_1 0
|
||||||
struct timer_list tuning_timer; /* Timer for tuning */
|
|
||||||
|
|
||||||
struct sdhci_host_next next_data;
|
struct sdhci_host_next next_data;
|
||||||
unsigned long private[0] ____cacheline_aligned;
|
unsigned long private[0] ____cacheline_aligned;
|
||||||
|
@ -541,6 +538,10 @@ struct sdhci_ops {
|
||||||
void (*platform_init)(struct sdhci_host *host);
|
void (*platform_init)(struct sdhci_host *host);
|
||||||
void (*card_event)(struct sdhci_host *host);
|
void (*card_event)(struct sdhci_host *host);
|
||||||
void (*voltage_switch)(struct sdhci_host *host);
|
void (*voltage_switch)(struct sdhci_host *host);
|
||||||
|
int (*select_drive_strength)(struct sdhci_host *host,
|
||||||
|
struct mmc_card *card,
|
||||||
|
unsigned int max_dtr, int host_drv,
|
||||||
|
int card_drv, int *drv_type);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
||||||
|
|
|
@ -49,7 +49,7 @@ struct f_sdhost_priv {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
};
|
};
|
||||||
|
|
||||||
void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
|
static void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
struct f_sdhost_priv *priv = sdhci_priv(host);
|
struct f_sdhost_priv *priv = sdhci_priv(host);
|
||||||
u32 ctrl = 0;
|
u32 ctrl = 0;
|
||||||
|
@ -77,12 +77,12 @@ void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
|
||||||
sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
|
sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
|
static unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
return F_SDH30_MIN_CLOCK;
|
return F_SDH30_MIN_CLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
|
static void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
|
||||||
{
|
{
|
||||||
if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0)
|
if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0)
|
||||||
sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
|
sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
|
||||||
|
@ -114,8 +114,7 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
|
||||||
return irq;
|
return irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
host = sdhci_alloc_host(dev, sizeof(struct sdhci_host) +
|
host = sdhci_alloc_host(dev, sizeof(struct f_sdhost_priv));
|
||||||
sizeof(struct f_sdhost_priv));
|
|
||||||
if (IS_ERR(host))
|
if (IS_ERR(host))
|
||||||
return PTR_ERR(host);
|
return PTR_ERR(host);
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
#include <linux/mmc/slot-gpio.h>
|
#include <linux/mmc/slot-gpio.h>
|
||||||
#include <linux/mod_devicetable.h>
|
#include <linux/mod_devicetable.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include <linux/pagemap.h>
|
#include <linux/pagemap.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_qos.h>
|
#include <linux/pm_qos.h>
|
||||||
|
@ -205,14 +206,14 @@
|
||||||
#define CLKDEV_MMC_DATA 20000000 /* 20MHz */
|
#define CLKDEV_MMC_DATA 20000000 /* 20MHz */
|
||||||
#define CLKDEV_INIT 400000 /* 400 KHz */
|
#define CLKDEV_INIT 400000 /* 400 KHz */
|
||||||
|
|
||||||
enum mmcif_state {
|
enum sh_mmcif_state {
|
||||||
STATE_IDLE,
|
STATE_IDLE,
|
||||||
STATE_REQUEST,
|
STATE_REQUEST,
|
||||||
STATE_IOS,
|
STATE_IOS,
|
||||||
STATE_TIMEOUT,
|
STATE_TIMEOUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum mmcif_wait_for {
|
enum sh_mmcif_wait_for {
|
||||||
MMCIF_WAIT_FOR_REQUEST,
|
MMCIF_WAIT_FOR_REQUEST,
|
||||||
MMCIF_WAIT_FOR_CMD,
|
MMCIF_WAIT_FOR_CMD,
|
||||||
MMCIF_WAIT_FOR_MREAD,
|
MMCIF_WAIT_FOR_MREAD,
|
||||||
|
@ -224,12 +225,14 @@ enum mmcif_wait_for {
|
||||||
MMCIF_WAIT_FOR_STOP,
|
MMCIF_WAIT_FOR_STOP,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* difference for each SoC
|
||||||
|
*/
|
||||||
struct sh_mmcif_host {
|
struct sh_mmcif_host {
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct mmc_request *mrq;
|
struct mmc_request *mrq;
|
||||||
struct platform_device *pd;
|
struct platform_device *pd;
|
||||||
struct clk *hclk;
|
struct clk *clk;
|
||||||
unsigned int clk;
|
|
||||||
int bus_width;
|
int bus_width;
|
||||||
unsigned char timing;
|
unsigned char timing;
|
||||||
bool sd_error;
|
bool sd_error;
|
||||||
|
@ -238,8 +241,8 @@ struct sh_mmcif_host {
|
||||||
void __iomem *addr;
|
void __iomem *addr;
|
||||||
u32 *pio_ptr;
|
u32 *pio_ptr;
|
||||||
spinlock_t lock; /* protect sh_mmcif_host::state */
|
spinlock_t lock; /* protect sh_mmcif_host::state */
|
||||||
enum mmcif_state state;
|
enum sh_mmcif_state state;
|
||||||
enum mmcif_wait_for wait_for;
|
enum sh_mmcif_wait_for wait_for;
|
||||||
struct delayed_work timeout_work;
|
struct delayed_work timeout_work;
|
||||||
size_t blocksize;
|
size_t blocksize;
|
||||||
int sg_idx;
|
int sg_idx;
|
||||||
|
@ -249,6 +252,7 @@ struct sh_mmcif_host {
|
||||||
bool ccs_enable; /* Command Completion Signal support */
|
bool ccs_enable; /* Command Completion Signal support */
|
||||||
bool clk_ctrl2_enable;
|
bool clk_ctrl2_enable;
|
||||||
struct mutex thread_lock;
|
struct mutex thread_lock;
|
||||||
|
u32 clkdiv_map; /* see CE_CLK_CTRL::CLKDIV */
|
||||||
|
|
||||||
/* DMA support */
|
/* DMA support */
|
||||||
struct dma_chan *chan_rx;
|
struct dma_chan *chan_rx;
|
||||||
|
@ -257,6 +261,14 @@ struct sh_mmcif_host {
|
||||||
bool dma_active;
|
bool dma_active;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id sh_mmcif_of_match[] = {
|
||||||
|
{ .compatible = "renesas,sh-mmcif" },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, sh_mmcif_of_match);
|
||||||
|
|
||||||
|
#define sh_mmcif_host_to_dev(host) (&host->pd->dev)
|
||||||
|
|
||||||
static inline void sh_mmcif_bitset(struct sh_mmcif_host *host,
|
static inline void sh_mmcif_bitset(struct sh_mmcif_host *host,
|
||||||
unsigned int reg, u32 val)
|
unsigned int reg, u32 val)
|
||||||
{
|
{
|
||||||
|
@ -269,15 +281,16 @@ static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host,
|
||||||
writel(~val & readl(host->addr + reg), host->addr + reg);
|
writel(~val & readl(host->addr + reg), host->addr + reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mmcif_dma_complete(void *arg)
|
static void sh_mmcif_dma_complete(void *arg)
|
||||||
{
|
{
|
||||||
struct sh_mmcif_host *host = arg;
|
struct sh_mmcif_host *host = arg;
|
||||||
struct mmc_request *mrq = host->mrq;
|
struct mmc_request *mrq = host->mrq;
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
|
|
||||||
dev_dbg(&host->pd->dev, "Command completed\n");
|
dev_dbg(dev, "Command completed\n");
|
||||||
|
|
||||||
if (WARN(!mrq || !mrq->data, "%s: NULL data in DMA completion!\n",
|
if (WARN(!mrq || !mrq->data, "%s: NULL data in DMA completion!\n",
|
||||||
dev_name(&host->pd->dev)))
|
dev_name(dev)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
complete(&host->dma_complete);
|
complete(&host->dma_complete);
|
||||||
|
@ -289,6 +302,7 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
|
||||||
struct scatterlist *sg = data->sg;
|
struct scatterlist *sg = data->sg;
|
||||||
struct dma_async_tx_descriptor *desc = NULL;
|
struct dma_async_tx_descriptor *desc = NULL;
|
||||||
struct dma_chan *chan = host->chan_rx;
|
struct dma_chan *chan = host->chan_rx;
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
dma_cookie_t cookie = -EINVAL;
|
dma_cookie_t cookie = -EINVAL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -301,13 +315,13 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc) {
|
if (desc) {
|
||||||
desc->callback = mmcif_dma_complete;
|
desc->callback = sh_mmcif_dma_complete;
|
||||||
desc->callback_param = host;
|
desc->callback_param = host;
|
||||||
cookie = dmaengine_submit(desc);
|
cookie = dmaengine_submit(desc);
|
||||||
sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN);
|
sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN);
|
||||||
dma_async_issue_pending(chan);
|
dma_async_issue_pending(chan);
|
||||||
}
|
}
|
||||||
dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n",
|
dev_dbg(dev, "%s(): mapped %d -> %d, cookie %d\n",
|
||||||
__func__, data->sg_len, ret, cookie);
|
__func__, data->sg_len, ret, cookie);
|
||||||
|
|
||||||
if (!desc) {
|
if (!desc) {
|
||||||
|
@ -323,12 +337,12 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
|
||||||
host->chan_tx = NULL;
|
host->chan_tx = NULL;
|
||||||
dma_release_channel(chan);
|
dma_release_channel(chan);
|
||||||
}
|
}
|
||||||
dev_warn(&host->pd->dev,
|
dev_warn(dev,
|
||||||
"DMA failed: %d, falling back to PIO\n", ret);
|
"DMA failed: %d, falling back to PIO\n", ret);
|
||||||
sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
|
sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__,
|
dev_dbg(dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__,
|
||||||
desc, cookie, data->sg_len);
|
desc, cookie, data->sg_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,6 +352,7 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
|
||||||
struct scatterlist *sg = data->sg;
|
struct scatterlist *sg = data->sg;
|
||||||
struct dma_async_tx_descriptor *desc = NULL;
|
struct dma_async_tx_descriptor *desc = NULL;
|
||||||
struct dma_chan *chan = host->chan_tx;
|
struct dma_chan *chan = host->chan_tx;
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
dma_cookie_t cookie = -EINVAL;
|
dma_cookie_t cookie = -EINVAL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -350,13 +365,13 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc) {
|
if (desc) {
|
||||||
desc->callback = mmcif_dma_complete;
|
desc->callback = sh_mmcif_dma_complete;
|
||||||
desc->callback_param = host;
|
desc->callback_param = host;
|
||||||
cookie = dmaengine_submit(desc);
|
cookie = dmaengine_submit(desc);
|
||||||
sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAWEN);
|
sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAWEN);
|
||||||
dma_async_issue_pending(chan);
|
dma_async_issue_pending(chan);
|
||||||
}
|
}
|
||||||
dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n",
|
dev_dbg(dev, "%s(): mapped %d -> %d, cookie %d\n",
|
||||||
__func__, data->sg_len, ret, cookie);
|
__func__, data->sg_len, ret, cookie);
|
||||||
|
|
||||||
if (!desc) {
|
if (!desc) {
|
||||||
|
@ -372,12 +387,12 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
|
||||||
host->chan_rx = NULL;
|
host->chan_rx = NULL;
|
||||||
dma_release_channel(chan);
|
dma_release_channel(chan);
|
||||||
}
|
}
|
||||||
dev_warn(&host->pd->dev,
|
dev_warn(dev,
|
||||||
"DMA failed: %d, falling back to PIO\n", ret);
|
"DMA failed: %d, falling back to PIO\n", ret);
|
||||||
sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
|
sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d\n", __func__,
|
dev_dbg(dev, "%s(): desc %p, cookie %d\n", __func__,
|
||||||
desc, cookie);
|
desc, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,6 +405,7 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
|
||||||
struct dma_chan *chan;
|
struct dma_chan *chan;
|
||||||
void *slave_data = NULL;
|
void *slave_data = NULL;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
dma_cap_mask_t mask;
|
dma_cap_mask_t mask;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -402,10 +418,10 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
|
||||||
(void *)pdata->slave_id_rx;
|
(void *)pdata->slave_id_rx;
|
||||||
|
|
||||||
chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
|
chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
|
||||||
slave_data, &host->pd->dev,
|
slave_data, dev,
|
||||||
direction == DMA_MEM_TO_DEV ? "tx" : "rx");
|
direction == DMA_MEM_TO_DEV ? "tx" : "rx");
|
||||||
|
|
||||||
dev_dbg(&host->pd->dev, "%s: %s: got channel %p\n", __func__,
|
dev_dbg(dev, "%s: %s: got channel %p\n", __func__,
|
||||||
direction == DMA_MEM_TO_DEV ? "TX" : "RX", chan);
|
direction == DMA_MEM_TO_DEV ? "TX" : "RX", chan);
|
||||||
|
|
||||||
if (!chan)
|
if (!chan)
|
||||||
|
@ -435,12 +451,13 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
|
||||||
static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
|
static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
|
||||||
struct sh_mmcif_plat_data *pdata)
|
struct sh_mmcif_plat_data *pdata)
|
||||||
{
|
{
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
host->dma_active = false;
|
host->dma_active = false;
|
||||||
|
|
||||||
if (pdata) {
|
if (pdata) {
|
||||||
if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0)
|
if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0)
|
||||||
return;
|
return;
|
||||||
} else if (!host->pd->dev.of_node) {
|
} else if (!dev->of_node) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,21 +493,59 @@ static void sh_mmcif_release_dma(struct sh_mmcif_host *host)
|
||||||
|
|
||||||
static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk)
|
static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk)
|
||||||
{
|
{
|
||||||
struct sh_mmcif_plat_data *p = host->pd->dev.platform_data;
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
|
struct sh_mmcif_plat_data *p = dev->platform_data;
|
||||||
bool sup_pclk = p ? p->sup_pclk : false;
|
bool sup_pclk = p ? p->sup_pclk : false;
|
||||||
|
unsigned int current_clk = clk_get_rate(host->clk);
|
||||||
|
unsigned int clkdiv;
|
||||||
|
|
||||||
sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE);
|
sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE);
|
||||||
sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR);
|
sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR);
|
||||||
|
|
||||||
if (!clk)
|
if (!clk)
|
||||||
return;
|
return;
|
||||||
if (sup_pclk && clk == host->clk)
|
|
||||||
sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK);
|
|
||||||
else
|
|
||||||
sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR &
|
|
||||||
((fls(DIV_ROUND_UP(host->clk,
|
|
||||||
clk) - 1) - 1) << 16));
|
|
||||||
|
|
||||||
|
if (host->clkdiv_map) {
|
||||||
|
unsigned int freq, best_freq, myclk, div, diff_min, diff;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
clkdiv = 0;
|
||||||
|
diff_min = ~0;
|
||||||
|
best_freq = 0;
|
||||||
|
for (i = 31; i >= 0; i--) {
|
||||||
|
if (!((1 << i) & host->clkdiv_map))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* clk = parent_freq / div
|
||||||
|
* -> parent_freq = clk x div
|
||||||
|
*/
|
||||||
|
|
||||||
|
div = 1 << (i + 1);
|
||||||
|
freq = clk_round_rate(host->clk, clk * div);
|
||||||
|
myclk = freq / div;
|
||||||
|
diff = (myclk > clk) ? myclk - clk : clk - myclk;
|
||||||
|
|
||||||
|
if (diff <= diff_min) {
|
||||||
|
best_freq = freq;
|
||||||
|
clkdiv = i;
|
||||||
|
diff_min = diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(dev, "clk %u/%u (%u, 0x%x)\n",
|
||||||
|
(best_freq / (1 << (clkdiv + 1))), clk,
|
||||||
|
best_freq, clkdiv);
|
||||||
|
|
||||||
|
clk_set_rate(host->clk, best_freq);
|
||||||
|
clkdiv = clkdiv << 16;
|
||||||
|
} else if (sup_pclk && clk == current_clk) {
|
||||||
|
clkdiv = CLK_SUP_PCLK;
|
||||||
|
} else {
|
||||||
|
clkdiv = (fls(DIV_ROUND_UP(current_clk, clk) - 1) - 1) << 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR & clkdiv);
|
||||||
sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE);
|
sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,6 +569,7 @@ static void sh_mmcif_sync_reset(struct sh_mmcif_host *host)
|
||||||
|
|
||||||
static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
|
static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
|
||||||
{
|
{
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
u32 state1, state2;
|
u32 state1, state2;
|
||||||
int ret, timeout;
|
int ret, timeout;
|
||||||
|
|
||||||
|
@ -521,8 +577,8 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
|
||||||
|
|
||||||
state1 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1);
|
state1 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1);
|
||||||
state2 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS2);
|
state2 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS2);
|
||||||
dev_dbg(&host->pd->dev, "ERR HOST_STS1 = %08x\n", state1);
|
dev_dbg(dev, "ERR HOST_STS1 = %08x\n", state1);
|
||||||
dev_dbg(&host->pd->dev, "ERR HOST_STS2 = %08x\n", state2);
|
dev_dbg(dev, "ERR HOST_STS2 = %08x\n", state2);
|
||||||
|
|
||||||
if (state1 & STS1_CMDSEQ) {
|
if (state1 & STS1_CMDSEQ) {
|
||||||
sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK);
|
sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK);
|
||||||
|
@ -534,25 +590,25 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
|
||||||
mdelay(1);
|
mdelay(1);
|
||||||
}
|
}
|
||||||
if (!timeout) {
|
if (!timeout) {
|
||||||
dev_err(&host->pd->dev,
|
dev_err(dev,
|
||||||
"Forced end of command sequence timeout err\n");
|
"Forced end of command sequence timeout err\n");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
sh_mmcif_sync_reset(host);
|
sh_mmcif_sync_reset(host);
|
||||||
dev_dbg(&host->pd->dev, "Forced end of command sequence\n");
|
dev_dbg(dev, "Forced end of command sequence\n");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state2 & STS2_CRC_ERR) {
|
if (state2 & STS2_CRC_ERR) {
|
||||||
dev_err(&host->pd->dev, " CRC error: state %u, wait %u\n",
|
dev_err(dev, " CRC error: state %u, wait %u\n",
|
||||||
host->state, host->wait_for);
|
host->state, host->wait_for);
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
} else if (state2 & STS2_TIMEOUT_ERR) {
|
} else if (state2 & STS2_TIMEOUT_ERR) {
|
||||||
dev_err(&host->pd->dev, " Timeout: state %u, wait %u\n",
|
dev_err(dev, " Timeout: state %u, wait %u\n",
|
||||||
host->state, host->wait_for);
|
host->state, host->wait_for);
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
} else {
|
} else {
|
||||||
dev_dbg(&host->pd->dev, " End/Index error: state %u, wait %u\n",
|
dev_dbg(dev, " End/Index error: state %u, wait %u\n",
|
||||||
host->state, host->wait_for);
|
host->state, host->wait_for);
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
}
|
}
|
||||||
|
@ -593,13 +649,14 @@ static void sh_mmcif_single_read(struct sh_mmcif_host *host,
|
||||||
|
|
||||||
static bool sh_mmcif_read_block(struct sh_mmcif_host *host)
|
static bool sh_mmcif_read_block(struct sh_mmcif_host *host)
|
||||||
{
|
{
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
struct mmc_data *data = host->mrq->data;
|
struct mmc_data *data = host->mrq->data;
|
||||||
u32 *p = sg_virt(data->sg);
|
u32 *p = sg_virt(data->sg);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (host->sd_error) {
|
if (host->sd_error) {
|
||||||
data->error = sh_mmcif_error_manage(host);
|
data->error = sh_mmcif_error_manage(host);
|
||||||
dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
|
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,13 +691,14 @@ static void sh_mmcif_multi_read(struct sh_mmcif_host *host,
|
||||||
|
|
||||||
static bool sh_mmcif_mread_block(struct sh_mmcif_host *host)
|
static bool sh_mmcif_mread_block(struct sh_mmcif_host *host)
|
||||||
{
|
{
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
struct mmc_data *data = host->mrq->data;
|
struct mmc_data *data = host->mrq->data;
|
||||||
u32 *p = host->pio_ptr;
|
u32 *p = host->pio_ptr;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (host->sd_error) {
|
if (host->sd_error) {
|
||||||
data->error = sh_mmcif_error_manage(host);
|
data->error = sh_mmcif_error_manage(host);
|
||||||
dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
|
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -671,13 +729,14 @@ static void sh_mmcif_single_write(struct sh_mmcif_host *host,
|
||||||
|
|
||||||
static bool sh_mmcif_write_block(struct sh_mmcif_host *host)
|
static bool sh_mmcif_write_block(struct sh_mmcif_host *host)
|
||||||
{
|
{
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
struct mmc_data *data = host->mrq->data;
|
struct mmc_data *data = host->mrq->data;
|
||||||
u32 *p = sg_virt(data->sg);
|
u32 *p = sg_virt(data->sg);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (host->sd_error) {
|
if (host->sd_error) {
|
||||||
data->error = sh_mmcif_error_manage(host);
|
data->error = sh_mmcif_error_manage(host);
|
||||||
dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
|
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,13 +771,14 @@ static void sh_mmcif_multi_write(struct sh_mmcif_host *host,
|
||||||
|
|
||||||
static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host)
|
static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host)
|
||||||
{
|
{
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
struct mmc_data *data = host->mrq->data;
|
struct mmc_data *data = host->mrq->data;
|
||||||
u32 *p = host->pio_ptr;
|
u32 *p = host->pio_ptr;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (host->sd_error) {
|
if (host->sd_error) {
|
||||||
data->error = sh_mmcif_error_manage(host);
|
data->error = sh_mmcif_error_manage(host);
|
||||||
dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
|
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -756,6 +816,7 @@ static void sh_mmcif_get_cmd12response(struct sh_mmcif_host *host,
|
||||||
static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
|
static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
|
||||||
struct mmc_request *mrq)
|
struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
struct mmc_data *data = mrq->data;
|
struct mmc_data *data = mrq->data;
|
||||||
struct mmc_command *cmd = mrq->cmd;
|
struct mmc_command *cmd = mrq->cmd;
|
||||||
u32 opc = cmd->opcode;
|
u32 opc = cmd->opcode;
|
||||||
|
@ -775,7 +836,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
|
||||||
tmp |= CMD_SET_RTYP_17B;
|
tmp |= CMD_SET_RTYP_17B;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(&host->pd->dev, "Unsupported response type.\n");
|
dev_err(dev, "Unsupported response type.\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch (opc) {
|
switch (opc) {
|
||||||
|
@ -803,7 +864,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
|
||||||
tmp |= CMD_SET_DATW_8;
|
tmp |= CMD_SET_DATW_8;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(&host->pd->dev, "Unsupported bus width.\n");
|
dev_err(dev, "Unsupported bus width.\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch (host->timing) {
|
switch (host->timing) {
|
||||||
|
@ -846,6 +907,8 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
|
||||||
static int sh_mmcif_data_trans(struct sh_mmcif_host *host,
|
static int sh_mmcif_data_trans(struct sh_mmcif_host *host,
|
||||||
struct mmc_request *mrq, u32 opc)
|
struct mmc_request *mrq, u32 opc)
|
||||||
{
|
{
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
|
|
||||||
switch (opc) {
|
switch (opc) {
|
||||||
case MMC_READ_MULTIPLE_BLOCK:
|
case MMC_READ_MULTIPLE_BLOCK:
|
||||||
sh_mmcif_multi_read(host, mrq);
|
sh_mmcif_multi_read(host, mrq);
|
||||||
|
@ -861,7 +924,7 @@ static int sh_mmcif_data_trans(struct sh_mmcif_host *host,
|
||||||
sh_mmcif_single_read(host, mrq);
|
sh_mmcif_single_read(host, mrq);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
dev_err(&host->pd->dev, "Unsupported CMD%d\n", opc);
|
dev_err(dev, "Unsupported CMD%d\n", opc);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -918,6 +981,8 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
|
||||||
static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
|
static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
|
||||||
struct mmc_request *mrq)
|
struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
|
|
||||||
switch (mrq->cmd->opcode) {
|
switch (mrq->cmd->opcode) {
|
||||||
case MMC_READ_MULTIPLE_BLOCK:
|
case MMC_READ_MULTIPLE_BLOCK:
|
||||||
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE);
|
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE);
|
||||||
|
@ -926,7 +991,7 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
|
||||||
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE);
|
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(&host->pd->dev, "unsupported stop cmd\n");
|
dev_err(dev, "unsupported stop cmd\n");
|
||||||
mrq->stop->error = sh_mmcif_error_manage(host);
|
mrq->stop->error = sh_mmcif_error_manage(host);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -937,11 +1002,13 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
|
||||||
static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
struct sh_mmcif_host *host = mmc_priv(mmc);
|
struct sh_mmcif_host *host = mmc_priv(mmc);
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
if (host->state != STATE_IDLE) {
|
if (host->state != STATE_IDLE) {
|
||||||
dev_dbg(&host->pd->dev, "%s() rejected, state %u\n", __func__, host->state);
|
dev_dbg(dev, "%s() rejected, state %u\n",
|
||||||
|
__func__, host->state);
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
mrq->cmd->error = -EAGAIN;
|
mrq->cmd->error = -EAGAIN;
|
||||||
mmc_request_done(mmc, mrq);
|
mmc_request_done(mmc, mrq);
|
||||||
|
@ -972,17 +1039,37 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
sh_mmcif_start_cmd(host, mrq);
|
sh_mmcif_start_cmd(host, mrq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sh_mmcif_clk_update(struct sh_mmcif_host *host)
|
static void sh_mmcif_clk_setup(struct sh_mmcif_host *host)
|
||||||
{
|
{
|
||||||
int ret = clk_prepare_enable(host->hclk);
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
|
|
||||||
if (!ret) {
|
if (host->mmc->f_max) {
|
||||||
host->clk = clk_get_rate(host->hclk);
|
unsigned int f_max, f_min = 0, f_min_old;
|
||||||
host->mmc->f_max = host->clk / 2;
|
|
||||||
host->mmc->f_min = host->clk / 512;
|
f_max = host->mmc->f_max;
|
||||||
|
for (f_min_old = f_max; f_min_old > 2;) {
|
||||||
|
f_min = clk_round_rate(host->clk, f_min_old / 2);
|
||||||
|
if (f_min == f_min_old)
|
||||||
|
break;
|
||||||
|
f_min_old = f_min;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
/*
|
||||||
|
* This driver assumes this SoC is R-Car Gen2 or later
|
||||||
|
*/
|
||||||
|
host->clkdiv_map = 0x3ff;
|
||||||
|
|
||||||
|
host->mmc->f_max = f_max / (1 << ffs(host->clkdiv_map));
|
||||||
|
host->mmc->f_min = f_min / (1 << fls(host->clkdiv_map));
|
||||||
|
} else {
|
||||||
|
unsigned int clk = clk_get_rate(host->clk);
|
||||||
|
|
||||||
|
host->mmc->f_max = clk / 2;
|
||||||
|
host->mmc->f_min = clk / 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(dev, "clk max/min = %d/%d\n",
|
||||||
|
host->mmc->f_max, host->mmc->f_min);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios)
|
static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios)
|
||||||
|
@ -998,11 +1085,13 @@ static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios)
|
||||||
static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
struct sh_mmcif_host *host = mmc_priv(mmc);
|
struct sh_mmcif_host *host = mmc_priv(mmc);
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
if (host->state != STATE_IDLE) {
|
if (host->state != STATE_IDLE) {
|
||||||
dev_dbg(&host->pd->dev, "%s() rejected, state %u\n", __func__, host->state);
|
dev_dbg(dev, "%s() rejected, state %u\n",
|
||||||
|
__func__, host->state);
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1013,7 +1102,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
if (ios->power_mode == MMC_POWER_UP) {
|
if (ios->power_mode == MMC_POWER_UP) {
|
||||||
if (!host->card_present) {
|
if (!host->card_present) {
|
||||||
/* See if we also get DMA */
|
/* See if we also get DMA */
|
||||||
sh_mmcif_request_dma(host, host->pd->dev.platform_data);
|
sh_mmcif_request_dma(host, dev->platform_data);
|
||||||
host->card_present = true;
|
host->card_present = true;
|
||||||
}
|
}
|
||||||
sh_mmcif_set_power(host, ios);
|
sh_mmcif_set_power(host, ios);
|
||||||
|
@ -1027,8 +1116,8 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (host->power) {
|
if (host->power) {
|
||||||
pm_runtime_put_sync(&host->pd->dev);
|
pm_runtime_put_sync(dev);
|
||||||
clk_disable_unprepare(host->hclk);
|
clk_disable_unprepare(host->clk);
|
||||||
host->power = false;
|
host->power = false;
|
||||||
if (ios->power_mode == MMC_POWER_OFF)
|
if (ios->power_mode == MMC_POWER_OFF)
|
||||||
sh_mmcif_set_power(host, ios);
|
sh_mmcif_set_power(host, ios);
|
||||||
|
@ -1039,8 +1128,9 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
|
|
||||||
if (ios->clock) {
|
if (ios->clock) {
|
||||||
if (!host->power) {
|
if (!host->power) {
|
||||||
sh_mmcif_clk_update(host);
|
clk_prepare_enable(host->clk);
|
||||||
pm_runtime_get_sync(&host->pd->dev);
|
|
||||||
|
pm_runtime_get_sync(dev);
|
||||||
host->power = true;
|
host->power = true;
|
||||||
sh_mmcif_sync_reset(host);
|
sh_mmcif_sync_reset(host);
|
||||||
}
|
}
|
||||||
|
@ -1055,7 +1145,8 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
static int sh_mmcif_get_cd(struct mmc_host *mmc)
|
static int sh_mmcif_get_cd(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
struct sh_mmcif_host *host = mmc_priv(mmc);
|
struct sh_mmcif_host *host = mmc_priv(mmc);
|
||||||
struct sh_mmcif_plat_data *p = host->pd->dev.platform_data;
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
|
struct sh_mmcif_plat_data *p = dev->platform_data;
|
||||||
int ret = mmc_gpio_get_cd(mmc);
|
int ret = mmc_gpio_get_cd(mmc);
|
||||||
|
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
|
@ -1077,6 +1168,7 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host)
|
||||||
{
|
{
|
||||||
struct mmc_command *cmd = host->mrq->cmd;
|
struct mmc_command *cmd = host->mrq->cmd;
|
||||||
struct mmc_data *data = host->mrq->data;
|
struct mmc_data *data = host->mrq->data;
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
long time;
|
long time;
|
||||||
|
|
||||||
if (host->sd_error) {
|
if (host->sd_error) {
|
||||||
|
@ -1090,7 +1182,7 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host)
|
||||||
cmd->error = sh_mmcif_error_manage(host);
|
cmd->error = sh_mmcif_error_manage(host);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dev_dbg(&host->pd->dev, "CMD%d error %d\n",
|
dev_dbg(dev, "CMD%d error %d\n",
|
||||||
cmd->opcode, cmd->error);
|
cmd->opcode, cmd->error);
|
||||||
host->sd_error = false;
|
host->sd_error = false;
|
||||||
return false;
|
return false;
|
||||||
|
@ -1170,6 +1262,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct sh_mmcif_host *host = dev_id;
|
struct sh_mmcif_host *host = dev_id;
|
||||||
struct mmc_request *mrq;
|
struct mmc_request *mrq;
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
bool wait = false;
|
bool wait = false;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int wait_work;
|
int wait_work;
|
||||||
|
@ -1184,7 +1277,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
|
||||||
|
|
||||||
mrq = host->mrq;
|
mrq = host->mrq;
|
||||||
if (!mrq) {
|
if (!mrq) {
|
||||||
dev_dbg(&host->pd->dev, "IRQ thread state %u, wait %u: NULL mrq!\n",
|
dev_dbg(dev, "IRQ thread state %u, wait %u: NULL mrq!\n",
|
||||||
host->state, host->wait_for);
|
host->state, host->wait_for);
|
||||||
mutex_unlock(&host->thread_lock);
|
mutex_unlock(&host->thread_lock);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
|
@ -1222,7 +1315,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
|
||||||
case MMCIF_WAIT_FOR_STOP:
|
case MMCIF_WAIT_FOR_STOP:
|
||||||
if (host->sd_error) {
|
if (host->sd_error) {
|
||||||
mrq->stop->error = sh_mmcif_error_manage(host);
|
mrq->stop->error = sh_mmcif_error_manage(host);
|
||||||
dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->stop->error);
|
dev_dbg(dev, "%s(): %d\n", __func__, mrq->stop->error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
sh_mmcif_get_cmd12response(host, mrq->stop);
|
sh_mmcif_get_cmd12response(host, mrq->stop);
|
||||||
|
@ -1232,7 +1325,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
|
||||||
case MMCIF_WAIT_FOR_WRITE_END:
|
case MMCIF_WAIT_FOR_WRITE_END:
|
||||||
if (host->sd_error) {
|
if (host->sd_error) {
|
||||||
mrq->data->error = sh_mmcif_error_manage(host);
|
mrq->data->error = sh_mmcif_error_manage(host);
|
||||||
dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->data->error);
|
dev_dbg(dev, "%s(): %d\n", __func__, mrq->data->error);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1275,6 +1368,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
|
||||||
static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
|
static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct sh_mmcif_host *host = dev_id;
|
struct sh_mmcif_host *host = dev_id;
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
u32 state, mask;
|
u32 state, mask;
|
||||||
|
|
||||||
state = sh_mmcif_readl(host->addr, MMCIF_CE_INT);
|
state = sh_mmcif_readl(host->addr, MMCIF_CE_INT);
|
||||||
|
@ -1286,32 +1380,33 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
|
||||||
sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN);
|
sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN);
|
||||||
|
|
||||||
if (state & ~MASK_CLEAN)
|
if (state & ~MASK_CLEAN)
|
||||||
dev_dbg(&host->pd->dev, "IRQ state = 0x%08x incompletely cleared\n",
|
dev_dbg(dev, "IRQ state = 0x%08x incompletely cleared\n",
|
||||||
state);
|
state);
|
||||||
|
|
||||||
if (state & INT_ERR_STS || state & ~INT_ALL) {
|
if (state & INT_ERR_STS || state & ~INT_ALL) {
|
||||||
host->sd_error = true;
|
host->sd_error = true;
|
||||||
dev_dbg(&host->pd->dev, "int err state = 0x%08x\n", state);
|
dev_dbg(dev, "int err state = 0x%08x\n", state);
|
||||||
}
|
}
|
||||||
if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) {
|
if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) {
|
||||||
if (!host->mrq)
|
if (!host->mrq)
|
||||||
dev_dbg(&host->pd->dev, "NULL IRQ state = 0x%08x\n", state);
|
dev_dbg(dev, "NULL IRQ state = 0x%08x\n", state);
|
||||||
if (!host->dma_active)
|
if (!host->dma_active)
|
||||||
return IRQ_WAKE_THREAD;
|
return IRQ_WAKE_THREAD;
|
||||||
else if (host->sd_error)
|
else if (host->sd_error)
|
||||||
mmcif_dma_complete(host);
|
sh_mmcif_dma_complete(host);
|
||||||
} else {
|
} else {
|
||||||
dev_dbg(&host->pd->dev, "Unexpected IRQ 0x%x\n", state);
|
dev_dbg(dev, "Unexpected IRQ 0x%x\n", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mmcif_timeout_work(struct work_struct *work)
|
static void sh_mmcif_timeout_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct delayed_work *d = container_of(work, struct delayed_work, work);
|
struct delayed_work *d = container_of(work, struct delayed_work, work);
|
||||||
struct sh_mmcif_host *host = container_of(d, struct sh_mmcif_host, timeout_work);
|
struct sh_mmcif_host *host = container_of(d, struct sh_mmcif_host, timeout_work);
|
||||||
struct mmc_request *mrq = host->mrq;
|
struct mmc_request *mrq = host->mrq;
|
||||||
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
if (host->dying)
|
if (host->dying)
|
||||||
|
@ -1324,7 +1419,7 @@ static void mmcif_timeout_work(struct work_struct *work)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_err(&host->pd->dev, "Timeout waiting for %u on CMD%u\n",
|
dev_err(dev, "Timeout waiting for %u on CMD%u\n",
|
||||||
host->wait_for, mrq->cmd->opcode);
|
host->wait_for, mrq->cmd->opcode);
|
||||||
|
|
||||||
host->state = STATE_TIMEOUT;
|
host->state = STATE_TIMEOUT;
|
||||||
|
@ -1361,7 +1456,8 @@ static void mmcif_timeout_work(struct work_struct *work)
|
||||||
|
|
||||||
static void sh_mmcif_init_ocr(struct sh_mmcif_host *host)
|
static void sh_mmcif_init_ocr(struct sh_mmcif_host *host)
|
||||||
{
|
{
|
||||||
struct sh_mmcif_plat_data *pd = host->pd->dev.platform_data;
|
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||||
|
struct sh_mmcif_plat_data *pd = dev->platform_data;
|
||||||
struct mmc_host *mmc = host->mmc;
|
struct mmc_host *mmc = host->mmc;
|
||||||
|
|
||||||
mmc_regulator_get_supply(mmc);
|
mmc_regulator_get_supply(mmc);
|
||||||
|
@ -1380,7 +1476,8 @@ static int sh_mmcif_probe(struct platform_device *pdev)
|
||||||
int ret = 0, irq[2];
|
int ret = 0, irq[2];
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct sh_mmcif_host *host;
|
struct sh_mmcif_host *host;
|
||||||
struct sh_mmcif_plat_data *pd = pdev->dev.platform_data;
|
struct device *dev = &pdev->dev;
|
||||||
|
struct sh_mmcif_plat_data *pd = dev->platform_data;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
void __iomem *reg;
|
void __iomem *reg;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
@ -1388,16 +1485,16 @@ static int sh_mmcif_probe(struct platform_device *pdev)
|
||||||
irq[0] = platform_get_irq(pdev, 0);
|
irq[0] = platform_get_irq(pdev, 0);
|
||||||
irq[1] = platform_get_irq(pdev, 1);
|
irq[1] = platform_get_irq(pdev, 1);
|
||||||
if (irq[0] < 0) {
|
if (irq[0] < 0) {
|
||||||
dev_err(&pdev->dev, "Get irq error\n");
|
dev_err(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);
|
||||||
reg = devm_ioremap_resource(&pdev->dev, res);
|
reg = devm_ioremap_resource(dev, res);
|
||||||
if (IS_ERR(reg))
|
if (IS_ERR(reg))
|
||||||
return PTR_ERR(reg);
|
return PTR_ERR(reg);
|
||||||
|
|
||||||
mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), &pdev->dev);
|
mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), dev);
|
||||||
if (!mmc)
|
if (!mmc)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -1430,41 +1527,44 @@ static int sh_mmcif_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
platform_set_drvdata(pdev, host);
|
platform_set_drvdata(pdev, host);
|
||||||
|
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(dev);
|
||||||
host->power = false;
|
host->power = false;
|
||||||
|
|
||||||
host->hclk = devm_clk_get(&pdev->dev, NULL);
|
host->clk = devm_clk_get(dev, NULL);
|
||||||
if (IS_ERR(host->hclk)) {
|
if (IS_ERR(host->clk)) {
|
||||||
ret = PTR_ERR(host->hclk);
|
ret = PTR_ERR(host->clk);
|
||||||
dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
|
dev_err(dev, "cannot get clock: %d\n", ret);
|
||||||
goto err_pm;
|
goto err_pm;
|
||||||
}
|
}
|
||||||
ret = sh_mmcif_clk_update(host);
|
|
||||||
|
ret = clk_prepare_enable(host->clk);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_pm;
|
goto err_pm;
|
||||||
|
|
||||||
ret = pm_runtime_resume(&pdev->dev);
|
sh_mmcif_clk_setup(host);
|
||||||
|
|
||||||
|
ret = pm_runtime_resume(dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_clk;
|
goto err_clk;
|
||||||
|
|
||||||
INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work);
|
INIT_DELAYED_WORK(&host->timeout_work, sh_mmcif_timeout_work);
|
||||||
|
|
||||||
sh_mmcif_sync_reset(host);
|
sh_mmcif_sync_reset(host);
|
||||||
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(dev) : "sh_mmc:error";
|
||||||
ret = devm_request_threaded_irq(&pdev->dev, irq[0], sh_mmcif_intr,
|
ret = devm_request_threaded_irq(dev, irq[0], sh_mmcif_intr,
|
||||||
sh_mmcif_irqt, 0, name, host);
|
sh_mmcif_irqt, 0, name, host);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "request_irq error (%s)\n", name);
|
dev_err(dev, "request_irq error (%s)\n", name);
|
||||||
goto err_clk;
|
goto err_clk;
|
||||||
}
|
}
|
||||||
if (irq[1] >= 0) {
|
if (irq[1] >= 0) {
|
||||||
ret = devm_request_threaded_irq(&pdev->dev, irq[1],
|
ret = devm_request_threaded_irq(dev, irq[1],
|
||||||
sh_mmcif_intr, sh_mmcif_irqt,
|
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(dev, "request_irq error (sh_mmc:int)\n");
|
||||||
goto err_clk;
|
goto err_clk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1481,19 +1581,19 @@ static int sh_mmcif_probe(struct platform_device *pdev)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_clk;
|
goto err_clk;
|
||||||
|
|
||||||
dev_pm_qos_expose_latency_limit(&pdev->dev, 100);
|
dev_pm_qos_expose_latency_limit(dev, 100);
|
||||||
|
|
||||||
dev_info(&pdev->dev, "Chip version 0x%04x, clock rate %luMHz\n",
|
dev_info(dev, "Chip version 0x%04x, clock rate %luMHz\n",
|
||||||
sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0xffff,
|
sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0xffff,
|
||||||
clk_get_rate(host->hclk) / 1000000UL);
|
clk_get_rate(host->clk) / 1000000UL);
|
||||||
|
|
||||||
clk_disable_unprepare(host->hclk);
|
clk_disable_unprepare(host->clk);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
err_clk:
|
err_clk:
|
||||||
clk_disable_unprepare(host->hclk);
|
clk_disable_unprepare(host->clk);
|
||||||
err_pm:
|
err_pm:
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(dev);
|
||||||
err_host:
|
err_host:
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1504,7 +1604,7 @@ 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);
|
||||||
|
|
||||||
host->dying = true;
|
host->dying = true;
|
||||||
clk_prepare_enable(host->hclk);
|
clk_prepare_enable(host->clk);
|
||||||
pm_runtime_get_sync(&pdev->dev);
|
pm_runtime_get_sync(&pdev->dev);
|
||||||
|
|
||||||
dev_pm_qos_hide_latency_limit(&pdev->dev);
|
dev_pm_qos_hide_latency_limit(&pdev->dev);
|
||||||
|
@ -1519,7 +1619,7 @@ static int sh_mmcif_remove(struct platform_device *pdev)
|
||||||
*/
|
*/
|
||||||
cancel_delayed_work_sync(&host->timeout_work);
|
cancel_delayed_work_sync(&host->timeout_work);
|
||||||
|
|
||||||
clk_disable_unprepare(host->hclk);
|
clk_disable_unprepare(host->clk);
|
||||||
mmc_free_host(host->mmc);
|
mmc_free_host(host->mmc);
|
||||||
pm_runtime_put_sync(&pdev->dev);
|
pm_runtime_put_sync(&pdev->dev);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
@ -1543,12 +1643,6 @@ static int sh_mmcif_resume(struct device *dev)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct of_device_id mmcif_of_match[] = {
|
|
||||||
{ .compatible = "renesas,sh-mmcif" },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, mmcif_of_match);
|
|
||||||
|
|
||||||
static const struct dev_pm_ops sh_mmcif_dev_pm_ops = {
|
static const struct dev_pm_ops sh_mmcif_dev_pm_ops = {
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume)
|
SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume)
|
||||||
};
|
};
|
||||||
|
@ -1559,7 +1653,7 @@ static struct platform_driver sh_mmcif_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
.pm = &sh_mmcif_dev_pm_ops,
|
.pm = &sh_mmcif_dev_pm_ops,
|
||||||
.of_match_table = mmcif_of_match,
|
.of_match_table = sh_mmcif_of_match,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -85,8 +85,10 @@ static int tmio_mmc_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!res)
|
if (!res) {
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto cell_disable;
|
||||||
|
}
|
||||||
|
|
||||||
pdata->flags |= TMIO_MMC_HAVE_HIGH_REG;
|
pdata->flags |= TMIO_MMC_HAVE_HIGH_REG;
|
||||||
|
|
||||||
|
@ -101,7 +103,8 @@ static int tmio_mmc_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto host_free;
|
goto host_free;
|
||||||
|
|
||||||
ret = request_irq(irq, tmio_mmc_irq, IRQF_TRIGGER_FALLING,
|
ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq,
|
||||||
|
IRQF_TRIGGER_FALLING,
|
||||||
dev_name(&pdev->dev), host);
|
dev_name(&pdev->dev), host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto host_remove;
|
goto host_remove;
|
||||||
|
@ -129,7 +132,6 @@ static int tmio_mmc_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
if (mmc) {
|
if (mmc) {
|
||||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||||
free_irq(platform_get_irq(pdev, 0), host);
|
|
||||||
tmio_mmc_host_remove(host);
|
tmio_mmc_host_remove(host);
|
||||||
if (cell->disable)
|
if (cell->disable)
|
||||||
cell->disable(pdev);
|
cell->disable(pdev);
|
||||||
|
|
|
@ -1108,7 +1108,8 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto host_free;
|
goto host_free;
|
||||||
|
|
||||||
_host->ctl = ioremap(res_ctl->start, resource_size(res_ctl));
|
_host->ctl = devm_ioremap(&pdev->dev,
|
||||||
|
res_ctl->start, resource_size(res_ctl));
|
||||||
if (!_host->ctl) {
|
if (!_host->ctl) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto host_free;
|
goto host_free;
|
||||||
|
@ -1230,8 +1231,6 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
|
||||||
|
|
||||||
pm_runtime_put_sync(&pdev->dev);
|
pm_runtime_put_sync(&pdev->dev);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
iounmap(host->ctl);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(tmio_mmc_host_remove);
|
EXPORT_SYMBOL(tmio_mmc_host_remove);
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,7 @@ struct mmc_ext_csd {
|
||||||
u8 raw_erased_mem_count; /* 181 */
|
u8 raw_erased_mem_count; /* 181 */
|
||||||
u8 raw_ext_csd_structure; /* 194 */
|
u8 raw_ext_csd_structure; /* 194 */
|
||||||
u8 raw_card_type; /* 196 */
|
u8 raw_card_type; /* 196 */
|
||||||
|
u8 raw_driver_strength; /* 197 */
|
||||||
u8 out_of_int_time; /* 198 */
|
u8 out_of_int_time; /* 198 */
|
||||||
u8 raw_pwr_cl_52_195; /* 200 */
|
u8 raw_pwr_cl_52_195; /* 200 */
|
||||||
u8 raw_pwr_cl_26_195; /* 201 */
|
u8 raw_pwr_cl_26_195; /* 201 */
|
||||||
|
@ -305,6 +306,7 @@ struct mmc_card {
|
||||||
|
|
||||||
unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */
|
unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */
|
||||||
unsigned int mmc_avail_type; /* supported device type by both host and card */
|
unsigned int mmc_avail_type; /* supported device type by both host and card */
|
||||||
|
unsigned int drive_strength; /* for UHS-I, HS200 or HS400 */
|
||||||
|
|
||||||
struct dentry *debugfs_root;
|
struct dentry *debugfs_root;
|
||||||
struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
|
struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
|
||||||
|
|
|
@ -121,6 +121,7 @@ struct mmc_data {
|
||||||
struct mmc_request *mrq; /* associated request */
|
struct mmc_request *mrq; /* associated request */
|
||||||
|
|
||||||
unsigned int sg_len; /* size of scatter list */
|
unsigned int sg_len; /* size of scatter list */
|
||||||
|
int sg_count; /* mapped sg entries */
|
||||||
struct scatterlist *sg; /* I/O scatter list */
|
struct scatterlist *sg; /* I/O scatter list */
|
||||||
s32 host_cookie; /* host private data */
|
s32 host_cookie; /* host private data */
|
||||||
};
|
};
|
||||||
|
|
|
@ -226,12 +226,6 @@ 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 */
|
|
||||||
/* This slot has no write protect */
|
|
||||||
#define DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT BIT(0)
|
|
||||||
|
|
||||||
struct dma_pdata;
|
struct dma_pdata;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/timer.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/fault-inject.h>
|
#include <linux/fault-inject.h>
|
||||||
|
@ -131,7 +132,9 @@ struct mmc_host_ops {
|
||||||
|
|
||||||
/* Prepare HS400 target operating frequency depending host driver */
|
/* Prepare HS400 target operating frequency depending host driver */
|
||||||
int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
|
int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
|
||||||
int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
|
int (*select_drive_strength)(struct mmc_card *card,
|
||||||
|
unsigned int max_dtr, int host_drv,
|
||||||
|
int card_drv, int *drv_type);
|
||||||
void (*hw_reset)(struct mmc_host *host);
|
void (*hw_reset)(struct mmc_host *host);
|
||||||
void (*card_event)(struct mmc_host *host);
|
void (*card_event)(struct mmc_host *host);
|
||||||
|
|
||||||
|
@ -285,6 +288,7 @@ struct mmc_host {
|
||||||
MMC_CAP2_HS400_1_2V)
|
MMC_CAP2_HS400_1_2V)
|
||||||
#define MMC_CAP2_HSX00_1_2V (MMC_CAP2_HS200_1_2V_SDR | MMC_CAP2_HS400_1_2V)
|
#define MMC_CAP2_HSX00_1_2V (MMC_CAP2_HS200_1_2V_SDR | MMC_CAP2_HS400_1_2V)
|
||||||
#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
|
#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
|
||||||
|
#define MMC_CAP2_NO_WRITE_PROTECT (1 << 18) /* No physical write protect pin, assume that card is always read-write */
|
||||||
|
|
||||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||||
|
|
||||||
|
@ -321,10 +325,18 @@ struct mmc_host {
|
||||||
#ifdef CONFIG_MMC_DEBUG
|
#ifdef CONFIG_MMC_DEBUG
|
||||||
unsigned int removed:1; /* host is being removed */
|
unsigned int removed:1; /* host is being removed */
|
||||||
#endif
|
#endif
|
||||||
|
unsigned int can_retune:1; /* re-tuning can be used */
|
||||||
|
unsigned int doing_retune:1; /* re-tuning in progress */
|
||||||
|
unsigned int retune_now:1; /* do re-tuning at next req */
|
||||||
|
|
||||||
int rescan_disable; /* disable card detection */
|
int rescan_disable; /* disable card detection */
|
||||||
int rescan_entered; /* used with nonremovable devices */
|
int rescan_entered; /* used with nonremovable devices */
|
||||||
|
|
||||||
|
int need_retune; /* re-tuning is needed */
|
||||||
|
int hold_retune; /* hold off re-tuning */
|
||||||
|
unsigned int retune_period; /* re-tuning period in secs */
|
||||||
|
struct timer_list retune_timer; /* for periodic re-tuning */
|
||||||
|
|
||||||
bool trigger_card_event; /* card_event necessary */
|
bool trigger_card_event; /* card_event necessary */
|
||||||
|
|
||||||
struct mmc_card *card; /* device attached to this host */
|
struct mmc_card *card; /* device attached to this host */
|
||||||
|
@ -513,4 +525,18 @@ static inline bool mmc_card_hs400(struct mmc_card *card)
|
||||||
return card->host->ios.timing == MMC_TIMING_MMC_HS400;
|
return card->host->ios.timing == MMC_TIMING_MMC_HS400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mmc_retune_timer_stop(struct mmc_host *host);
|
||||||
|
|
||||||
|
static inline void mmc_retune_needed(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
if (host->can_retune)
|
||||||
|
host->need_retune = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mmc_retune_recheck(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
if (host->hold_retune <= 1)
|
||||||
|
host->retune_now = 1;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* LINUX_MMC_HOST_H */
|
#endif /* LINUX_MMC_HOST_H */
|
||||||
|
|
|
@ -302,6 +302,7 @@ struct _mmc_csd {
|
||||||
#define EXT_CSD_REV 192 /* RO */
|
#define EXT_CSD_REV 192 /* RO */
|
||||||
#define EXT_CSD_STRUCTURE 194 /* RO */
|
#define EXT_CSD_STRUCTURE 194 /* RO */
|
||||||
#define EXT_CSD_CARD_TYPE 196 /* RO */
|
#define EXT_CSD_CARD_TYPE 196 /* RO */
|
||||||
|
#define EXT_CSD_DRIVER_STRENGTH 197 /* RO */
|
||||||
#define EXT_CSD_OUT_OF_INTERRUPT_TIME 198 /* RO */
|
#define EXT_CSD_OUT_OF_INTERRUPT_TIME 198 /* RO */
|
||||||
#define EXT_CSD_PART_SWITCH_TIME 199 /* RO */
|
#define EXT_CSD_PART_SWITCH_TIME 199 /* RO */
|
||||||
#define EXT_CSD_PWR_CL_52_195 200 /* RO */
|
#define EXT_CSD_PWR_CL_52_195 200 /* RO */
|
||||||
|
@ -390,6 +391,7 @@ struct _mmc_csd {
|
||||||
#define EXT_CSD_TIMING_HS 1 /* High speed */
|
#define EXT_CSD_TIMING_HS 1 /* High speed */
|
||||||
#define EXT_CSD_TIMING_HS200 2 /* HS200 */
|
#define EXT_CSD_TIMING_HS200 2 /* HS200 */
|
||||||
#define EXT_CSD_TIMING_HS400 3 /* HS400 */
|
#define EXT_CSD_TIMING_HS400 3 /* HS400 */
|
||||||
|
#define EXT_CSD_DRV_STR_SHIFT 4 /* Driver Strength shift */
|
||||||
|
|
||||||
#define EXT_CSD_SEC_ER_EN BIT(0)
|
#define EXT_CSD_SEC_ER_EN BIT(0)
|
||||||
#define EXT_CSD_SEC_BD_BLK_EN BIT(2)
|
#define EXT_CSD_SEC_BD_BLK_EN BIT(2)
|
||||||
|
@ -441,4 +443,6 @@ struct _mmc_csd {
|
||||||
#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */
|
#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */
|
||||||
#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */
|
#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */
|
||||||
|
|
||||||
|
#define mmc_driver_type_mask(n) (1 << (n))
|
||||||
|
|
||||||
#endif /* LINUX_MMC_MMC_H */
|
#endif /* LINUX_MMC_MMC_H */
|
||||||
|
|
|
@ -15,4 +15,6 @@ struct sdhci_pci_data {
|
||||||
extern struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev,
|
extern struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev,
|
||||||
int slotno);
|
int slotno);
|
||||||
|
|
||||||
|
extern int sdhci_pci_spt_drive_strength;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -579,6 +579,7 @@
|
||||||
#define PCI_DEVICE_ID_AMD_HUDSON2_SATA_IDE 0x7800
|
#define PCI_DEVICE_ID_AMD_HUDSON2_SATA_IDE 0x7800
|
||||||
#define PCI_DEVICE_ID_AMD_HUDSON2_SMBUS 0x780b
|
#define PCI_DEVICE_ID_AMD_HUDSON2_SMBUS 0x780b
|
||||||
#define PCI_DEVICE_ID_AMD_HUDSON2_IDE 0x780c
|
#define PCI_DEVICE_ID_AMD_HUDSON2_IDE 0x780c
|
||||||
|
#define PCI_DEVICE_ID_AMD_KERNCZ_SMBUS 0x790b
|
||||||
|
|
||||||
#define PCI_VENDOR_ID_TRIDENT 0x1023
|
#define PCI_VENDOR_ID_TRIDENT 0x1023
|
||||||
#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000
|
#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000
|
||||||
|
|
Loading…
Reference in a new issue