mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-13 11:51:32 +00:00
MMC highlights for 3.15:
Core: - CONFIG_MMC_UNSAFE_RESUME=y is now default behavior. - DT bindings for SDHCI UHS, eMMC HS200, high-speed DDR, at 1.8/1.2V. - Add GPIO descriptor based slot-gpio card detect API. Drivers: - dw_mmc: Refactor SOCFPGA support as a variant inside dw_mmc-pltfm.c. - mmci: Support HW busy detection on ux500. - omap: Support MMC_ERASE. - omap_hsmmc: Support MMC_PM_KEEP_POWER, MMC_PM_WAKE_SDIO_IRQ, (a)cmd23. - rtsx: Support pre-req/post-req async. - sdhci: Add support for Realtek RTS5250 controllers. - sdhci-acpi: Add support for 80860F16, fix 80860F14/SDIO card detect. - sdhci-msm: Add new driver for Qualcomm SDHCI chipset support. - sdhci-pxav3: Add support for Marvell Armada 380 and 385 SoCs. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.14 (GNU/Linux) iQIcBAABAgAGBQJTRLHLAAoJEHNBYZ7TNxYMoqEQAOULXl1SHt0aHn5I0cfdVnRm J3i56MqarwXQOse/qJrg8/uKsggAu0ivTlQ7x1h6bpXmzHqvOtZhSoO9BqGEvxOU WNeA9ouaKMx3gCpIAwl9Odox+d2E+91nRfxU3fZTDITy554fREXmIpWiidjFPR7n 2oHT0yvGuLjunTC8MhxSB0OsggoIDXDTVPxrcf2k+AcAZAMlCMDNirN9+JbhiVM9 PNESapMyQAbFy18BGzCt5lO2o6aRileaSdX4BFTW4lx2LSPryUVV3cnfIH4zlytW joVDWyU5kAtQgfhoEhTsWJld+cwHsMUrl/FOfhMvBWbPMxLJnbFx8b459nKJDM5j NUo29KQxxHgWblGYx+F5SYuTloqWtX5iQWsez9g38Z/3UtjHR++o3+auwTFsZFRe 7EusZqsXdKggx1iiW/5afgb+tFOiCe5WOOQv29YdqWurPhaSK2Nr1aprD4RRiMeT IG9qBLhHFLl8Pv0nTdEGbJHhAhihja6w2ul+i/8JSaDOYAGFbEn47MC8JfrKAnpw WovxkSqMroMhjI+51cwJnVtdczQWx5kpjqDY0VaJlKvOfcwyOuyTU+s2vrHVDMZS a0HgaXeVxr5IcDTz2zo1f6UbM4k2z/Ka0LOOSPqyOYOpFuT6VkXhgOVq6fsRpnaN /9CUirULwF5ej0oz38hk =6S8w -----END PGP SIGNATURE----- Merge tag 'mmc-updates-for-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc Pull MMC updates from Chris Ball: "MMC highlights for 3.15: Core: - CONFIG_MMC_UNSAFE_RESUME=y is now default behavior - DT bindings for SDHCI UHS, eMMC HS200, high-speed DDR, at 1.8/1.2V - Add GPIO descriptor based slot-gpio card detect API Drivers: - dw_mmc: Refactor SOCFPGA support as a variant inside dw_mmc-pltfm.c - mmci: Support HW busy detection on ux500 - omap: Support MMC_ERASE - omap_hsmmc: Support MMC_PM_KEEP_POWER, MMC_PM_WAKE_SDIO_IRQ, (a)cmd23 - rtsx: Support pre-req/post-req async - sdhci: Add support for Realtek RTS5250 controllers - sdhci-acpi: Add support for 80860F16, fix 80860F14/SDIO card detect - sdhci-msm: Add new driver for Qualcomm SDHCI chipset support - sdhci-pxav3: Add support for Marvell Armada 380 and 385 SoCs" * tag 'mmc-updates-for-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (102 commits) mmc: sdhci-acpi: Intel SDIO has broken card detect mmc: sdhci-pxav3: add support for the Armada 38x SDHCI controller mmc: sdhci-msm: Add platform_execute_tuning implementation mmc: sdhci-msm: Initial support for Qualcomm chipsets mmc: sdhci-msm: Qualcomm SDHCI binding documentation sdhci: only reprogram retuning timer when flag is set mmc: rename ARCH_BCM to ARCH_BCM_MOBILE mmc: sdhci: Allow for irq being shared mmc: sdhci-acpi: Add device id 80860F16 mmc: sdhci-acpi: Fix broken card detect for ACPI HID 80860F14 mmc: slot-gpio: Add GPIO descriptor based CD GPIO API mmc: slot-gpio: Split out CD IRQ request into a separate function mmc: slot-gpio: Record GPIO descriptors instead of GPIO numbers Revert "dts: socfpga: Add support for SD/MMC on the SOCFPGA platform" mmc: sdhci-spear: use generic card detection gpio support mmc: sdhci-spear: remove support for power gpio mmc: sdhci-spear: simplify resource handling mmc: sdhci-spear: fix platform_data usage mmc: sdhci-spear: fix error handling paths for DT mmc: sdhci-bcm-kona: fix build errors when built-in ...
This commit is contained in:
commit
97e18dc007
@ -26,9 +26,18 @@ Optional properties:
|
||||
this system, even if the controller claims it is.
|
||||
- cap-sd-highspeed: SD high-speed timing is supported
|
||||
- cap-mmc-highspeed: MMC high-speed timing is supported
|
||||
- sd-uhs-sdr12: SD UHS SDR12 speed is supported
|
||||
- sd-uhs-sdr25: SD UHS SDR25 speed is supported
|
||||
- sd-uhs-sdr50: SD UHS SDR50 speed is supported
|
||||
- sd-uhs-sdr104: SD UHS SDR104 speed is supported
|
||||
- sd-uhs-ddr50: SD UHS DDR50 speed is supported
|
||||
- cap-power-off-card: powering off the card is safe
|
||||
- cap-sdio-irq: enable SDIO IRQ signalling on this interface
|
||||
- full-pwr-cycle: full power cycle of the card is supported
|
||||
- mmc-highspeed-ddr-1_8v: eMMC high-speed DDR mode(1.8V I/O) is supported
|
||||
- mmc-highspeed-ddr-1_2v: eMMC high-speed DDR mode(1.2V I/O) is supported
|
||||
- mmc-hs200-1_8v: eMMC HS200 mode(1.8V I/O) is supported
|
||||
- mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported
|
||||
|
||||
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
|
||||
polarity properties, we have to fix the meaning of the "normal" and "inverted"
|
||||
|
55
Documentation/devicetree/bindings/mmc/sdhci-msm.txt
Normal file
55
Documentation/devicetree/bindings/mmc/sdhci-msm.txt
Normal file
@ -0,0 +1,55 @@
|
||||
* Qualcomm SDHCI controller (sdhci-msm)
|
||||
|
||||
This file documents differences between the core properties in mmc.txt
|
||||
and the properties used by the sdhci-msm driver.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain "qcom,sdhci-msm-v4".
|
||||
- reg: Base address and length of the register in the following order:
|
||||
- Host controller register map (required)
|
||||
- SD Core register map (required)
|
||||
- interrupts: Should contain an interrupt-specifiers for the interrupts:
|
||||
- Host controller interrupt (required)
|
||||
- pinctrl-names: Should contain only one value - "default".
|
||||
- pinctrl-0: Should specify pin control groups used for this controller.
|
||||
- clocks: A list of phandle + clock-specifier pairs for the clocks listed in clock-names.
|
||||
- clock-names: Should contain the following:
|
||||
"iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required)
|
||||
"core" - SDC MMC clock (MCLK) (required)
|
||||
"bus" - SDCC bus voter clock (optional)
|
||||
|
||||
Example:
|
||||
|
||||
sdhc_1: sdhci@f9824900 {
|
||||
compatible = "qcom,sdhci-msm-v4";
|
||||
reg = <0xf9824900 0x11c>, <0xf9824000 0x800>;
|
||||
interrupts = <0 123 0>;
|
||||
bus-width = <8>;
|
||||
non-removable;
|
||||
|
||||
vmmc = <&pm8941_l20>;
|
||||
vqmmc = <&pm8941_s3>;
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&sdc1_clk &sdc1_cmd &sdc1_data>;
|
||||
|
||||
clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>;
|
||||
clock-names = "core", "iface";
|
||||
};
|
||||
|
||||
sdhc_2: sdhci@f98a4900 {
|
||||
compatible = "qcom,sdhci-msm-v4";
|
||||
reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>;
|
||||
interrupts = <0 125 0>;
|
||||
bus-width = <4>;
|
||||
cd-gpios = <&msmgpio 62 0x1>;
|
||||
|
||||
vmmc = <&pm8941_l21>;
|
||||
vqmmc = <&pm8941_l13>;
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&sdc2_clk &sdc2_cmd &sdc2_data>;
|
||||
|
||||
clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>;
|
||||
clock-names = "core", "iface";
|
||||
};
|
@ -4,7 +4,14 @@ This file documents differences between the core properties in mmc.txt
|
||||
and the properties used by the sdhci-pxav2 and sdhci-pxav3 drivers.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "mrvl,pxav2-mmc" or "mrvl,pxav3-mmc".
|
||||
- compatible: Should be "mrvl,pxav2-mmc", "mrvl,pxav3-mmc" or
|
||||
"marvell,armada-380-sdhci".
|
||||
- reg:
|
||||
* for "mrvl,pxav2-mmc" and "mrvl,pxav3-mmc", one register area for
|
||||
the SDHCI registers.
|
||||
* for "marvell,armada-380-sdhci", two register areas. The first one
|
||||
for the SDHCI registers themselves, and the second one for the
|
||||
AXI/Mbus bridge registers of the SDHCI unit.
|
||||
|
||||
Optional properties:
|
||||
- mrvl,clk-delay-cycles: Specify a number of cycles to delay for tuning.
|
||||
@ -19,3 +26,11 @@ sdhci@d4280800 {
|
||||
non-removable;
|
||||
mrvl,clk-delay-cycles = <31>;
|
||||
};
|
||||
|
||||
sdhci@d8000 {
|
||||
compatible = "marvell,armada-380-sdhci";
|
||||
reg = <0xd8000 0x1000>, <0xdc000 0x100>;
|
||||
interrupts = <0 25 0x4>;
|
||||
clocks = <&gateclk 17>;
|
||||
mrvl,clk-delay-cycles = <0x1F>;
|
||||
};
|
||||
|
@ -10,6 +10,7 @@ Required properties:
|
||||
- compatible:
|
||||
Should be "ti,omap2-hsmmc", for OMAP2 controllers
|
||||
Should be "ti,omap3-hsmmc", for OMAP3 controllers
|
||||
Should be "ti,omap3-pre-es3-hsmmc" for OMAP3 controllers pre ES3.0
|
||||
Should be "ti,omap4-hsmmc", for OMAP4 controllers
|
||||
- ti,hwmods: Must be "mmc<n>", n is controller instance starting 1
|
||||
|
||||
|
@ -0,0 +1,27 @@
|
||||
PBIAS internal regulator for SD card dual voltage i/o pads on OMAP SoCs.
|
||||
|
||||
Required properties:
|
||||
- compatible:
|
||||
- "ti,pbias-omap" for OMAP2, OMAP3, OMAP4, OMAP5, DRA7.
|
||||
- reg: pbias register offset from syscon base and size of pbias register.
|
||||
- syscon : phandle of the system control module
|
||||
- regulator-name : should be
|
||||
pbias_mmc_omap2430 for OMAP2430, OMAP3 SoCs
|
||||
pbias_sim_omap3 for OMAP3 SoCs
|
||||
pbias_mmc_omap4 for OMAP4 SoCs
|
||||
pbias_mmc_omap5 for OMAP5 and DRA7 SoC
|
||||
|
||||
Optional properties:
|
||||
- Any optional property defined in bindings/regulator/regulator.txt
|
||||
|
||||
Example:
|
||||
|
||||
pbias_regulator: pbias_regulator {
|
||||
compatible = "ti,pbias-omap";
|
||||
reg = <0 0x4>;
|
||||
syscon = <&omap5_padconf_global>;
|
||||
pbias_mmc_reg: pbias_mmc_omap5 {
|
||||
regulator-name = "pbias_mmc_omap5";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3000000>;
|
||||
};
|
@ -5930,6 +5930,7 @@ F: include/linux/mfd/
|
||||
|
||||
MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
|
||||
M: Chris Ball <chris@printf.net>
|
||||
M: Ulf Hansson <ulf.hansson@linaro.org>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
|
||||
S: Maintained
|
||||
|
@ -154,6 +154,22 @@
|
||||
ti,hwmods = "counter_32k";
|
||||
};
|
||||
|
||||
dra7_ctrl_general: tisyscon@4a002e00 {
|
||||
compatible = "syscon";
|
||||
reg = <0x4a002e00 0x7c>;
|
||||
};
|
||||
|
||||
pbias_regulator: pbias_regulator {
|
||||
compatible = "ti,pbias-omap";
|
||||
reg = <0 0x4>;
|
||||
syscon = <&dra7_ctrl_general>;
|
||||
pbias_mmc_reg: pbias_mmc_omap5 {
|
||||
regulator-name = "pbias_mmc_omap5";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3000000>;
|
||||
};
|
||||
};
|
||||
|
||||
dra7_pmx_core: pinmux@4a003400 {
|
||||
compatible = "pinctrl-single";
|
||||
reg = <0x4a003400 0x0464>;
|
||||
@ -543,6 +559,7 @@
|
||||
dmas = <&sdma 61>, <&sdma 62>;
|
||||
dma-names = "tx", "rx";
|
||||
status = "disabled";
|
||||
pbias-supply = <&pbias_mmc_reg>;
|
||||
};
|
||||
|
||||
mmc2: mmc@480b4000 {
|
||||
|
@ -29,6 +29,22 @@
|
||||
pinctrl-single,function-mask = <0x3f>;
|
||||
};
|
||||
|
||||
omap2_scm_general: tisyscon@49002270 {
|
||||
compatible = "syscon";
|
||||
reg = <0x49002270 0x240>;
|
||||
};
|
||||
|
||||
pbias_regulator: pbias_regulator {
|
||||
compatible = "ti,pbias-omap";
|
||||
reg = <0x230 0x4>;
|
||||
syscon = <&omap2_scm_general>;
|
||||
pbias_mmc_reg: pbias_mmc_omap2430 {
|
||||
regulator-name = "pbias_mmc_omap2430";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3000000>;
|
||||
};
|
||||
};
|
||||
|
||||
gpio1: gpio@4900c000 {
|
||||
compatible = "ti,omap2-gpio";
|
||||
reg = <0x4900c000 0x200>;
|
||||
@ -188,6 +204,7 @@
|
||||
ti,dual-volt;
|
||||
dmas = <&sdma 61>, <&sdma 62>;
|
||||
dma-names = "tx", "rx";
|
||||
pbias-supply = <&pbias_mmc_reg>;
|
||||
};
|
||||
|
||||
mmc2: mmc@480b4000 {
|
||||
|
@ -174,8 +174,20 @@
|
||||
};
|
||||
|
||||
&mmc1 {
|
||||
/* See 35xx errata 2.1.1.128 in SPRZ278F */
|
||||
compatible = "ti,omap3-pre-es3-hsmmc";
|
||||
vmmc-supply = <&vmmc1>;
|
||||
bus-width = <4>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&mmc1_pins>;
|
||||
};
|
||||
|
||||
&mmc2 {
|
||||
status="disabled";
|
||||
};
|
||||
|
||||
&mmc3 {
|
||||
status="disabled";
|
||||
};
|
||||
|
||||
&omap3_pmx_core {
|
||||
@ -209,6 +221,17 @@
|
||||
0x174 (PIN_OUTPUT | MUX_MODE0) /* hsusb0_stp.hsusb0_stp */
|
||||
>;
|
||||
};
|
||||
|
||||
mmc1_pins: pinmux_mmc1_pins {
|
||||
pinctrl-single,pins = <
|
||||
OMAP3_CORE1_IOPAD(0x2144, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc1_clk.mmc1_clk */
|
||||
OMAP3_CORE1_IOPAD(0x2146, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc1_cmd.mmc1_cmd */
|
||||
OMAP3_CORE1_IOPAD(0x2148, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc1_dat0.mmc1_dat0 */
|
||||
OMAP3_CORE1_IOPAD(0x214A, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc1_dat1.mmc1_dat1 */
|
||||
OMAP3_CORE1_IOPAD(0x214C, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc1_dat2.mmc1_dat2 */
|
||||
OMAP3_CORE1_IOPAD(0x214e, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc1_dat3.mmc1_dat3 */
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
&usb_otg_hs {
|
||||
|
@ -181,6 +181,22 @@
|
||||
pinctrl-single,function-mask = <0xff1f>;
|
||||
};
|
||||
|
||||
omap3_scm_general: tisyscon@48002270 {
|
||||
compatible = "syscon";
|
||||
reg = <0x48002270 0x2f0>;
|
||||
};
|
||||
|
||||
pbias_regulator: pbias_regulator {
|
||||
compatible = "ti,pbias-omap";
|
||||
reg = <0x2b0 0x4>;
|
||||
syscon = <&omap3_scm_general>;
|
||||
pbias_mmc_reg: pbias_mmc_omap2430 {
|
||||
regulator-name = "pbias_mmc_omap2430";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3000000>;
|
||||
};
|
||||
};
|
||||
|
||||
gpio1: gpio@48310000 {
|
||||
compatible = "ti,omap3-gpio";
|
||||
reg = <0x48310000 0x200>;
|
||||
@ -395,6 +411,7 @@
|
||||
ti,dual-volt;
|
||||
dmas = <&sdma 61>, <&sdma 62>;
|
||||
dma-names = "tx", "rx";
|
||||
pbias-supply = <&pbias_mmc_reg>;
|
||||
};
|
||||
|
||||
mmc2: mmc@480b4000 {
|
||||
|
@ -191,6 +191,22 @@
|
||||
pinctrl-single,function-mask = <0x7fff>;
|
||||
};
|
||||
|
||||
omap4_padconf_global: tisyscon@4a1005a0 {
|
||||
compatible = "syscon";
|
||||
reg = <0x4a1005a0 0x170>;
|
||||
};
|
||||
|
||||
pbias_regulator: pbias_regulator {
|
||||
compatible = "ti,pbias-omap";
|
||||
reg = <0x60 0x4>;
|
||||
syscon = <&omap4_padconf_global>;
|
||||
pbias_mmc_reg: pbias_mmc_omap4 {
|
||||
regulator-name = "pbias_mmc_omap4";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3000000>;
|
||||
};
|
||||
};
|
||||
|
||||
sdma: dma-controller@4a056000 {
|
||||
compatible = "ti,omap4430-sdma";
|
||||
reg = <0x4a056000 0x1000>;
|
||||
@ -427,6 +443,7 @@
|
||||
ti,needs-special-reset;
|
||||
dmas = <&sdma 61>, <&sdma 62>;
|
||||
dma-names = "tx", "rx";
|
||||
pbias-supply = <&pbias_mmc_reg>;
|
||||
};
|
||||
|
||||
mmc2: mmc@480b4000 {
|
||||
|
@ -198,6 +198,22 @@
|
||||
pinctrl-single,function-mask = <0x7fff>;
|
||||
};
|
||||
|
||||
omap5_padconf_global: tisyscon@4a002da0 {
|
||||
compatible = "syscon";
|
||||
reg = <0x4A002da0 0xec>;
|
||||
};
|
||||
|
||||
pbias_regulator: pbias_regulator {
|
||||
compatible = "ti,pbias-omap";
|
||||
reg = <0x60 0x4>;
|
||||
syscon = <&omap5_padconf_global>;
|
||||
pbias_mmc_reg: pbias_mmc_omap5 {
|
||||
regulator-name = "pbias_mmc_omap5";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3000000>;
|
||||
};
|
||||
};
|
||||
|
||||
sdma: dma-controller@4a056000 {
|
||||
compatible = "ti,omap4430-sdma";
|
||||
reg = <0x4a056000 0x1000>;
|
||||
@ -480,6 +496,7 @@
|
||||
ti,needs-special-reset;
|
||||
dmas = <&sdma 61>, <&sdma 62>;
|
||||
dma-names = "tx", "rx";
|
||||
pbias-supply = <&pbias_mmc_reg>;
|
||||
};
|
||||
|
||||
mmc2: mmc@480b4000 {
|
||||
|
@ -170,6 +170,7 @@ CONFIG_DRA752_THERMAL=y
|
||||
CONFIG_WATCHDOG=y
|
||||
CONFIG_OMAP_WATCHDOG=y
|
||||
CONFIG_TWL4030_WATCHDOG=y
|
||||
CONFIG_MFD_SYSCON=y
|
||||
CONFIG_MFD_PALMAS=y
|
||||
CONFIG_MFD_TPS65217=y
|
||||
CONFIG_MFD_TPS65910=y
|
||||
@ -181,6 +182,7 @@ CONFIG_REGULATOR_TPS6507X=y
|
||||
CONFIG_REGULATOR_TPS65217=y
|
||||
CONFIG_REGULATOR_TPS65910=y
|
||||
CONFIG_REGULATOR_TWL4030=y
|
||||
CONFIG_REGULATOR_PBIAS=y
|
||||
CONFIG_FB=y
|
||||
CONFIG_FIRMWARE_EDID=y
|
||||
CONFIG_FB_MODE_HELPERS=y
|
||||
|
@ -338,58 +338,28 @@ int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist,
|
||||
int num_sg, bool read, int timeout)
|
||||
{
|
||||
struct completion trans_done;
|
||||
u8 dir;
|
||||
int err = 0, i, count;
|
||||
int err = 0, count;
|
||||
long timeleft;
|
||||
unsigned long flags;
|
||||
struct scatterlist *sg;
|
||||
enum dma_data_direction dma_dir;
|
||||
u32 val;
|
||||
dma_addr_t addr;
|
||||
unsigned int len;
|
||||
|
||||
dev_dbg(&(pcr->pci->dev), "--> %s: num_sg = %d\n", __func__, num_sg);
|
||||
|
||||
/* don't transfer data during abort processing */
|
||||
if (pcr->remove_pci)
|
||||
return -EINVAL;
|
||||
|
||||
if ((sglist == NULL) || (num_sg <= 0))
|
||||
return -EINVAL;
|
||||
|
||||
if (read) {
|
||||
dir = DEVICE_TO_HOST;
|
||||
dma_dir = DMA_FROM_DEVICE;
|
||||
} else {
|
||||
dir = HOST_TO_DEVICE;
|
||||
dma_dir = DMA_TO_DEVICE;
|
||||
}
|
||||
|
||||
count = dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir);
|
||||
count = rtsx_pci_dma_map_sg(pcr, sglist, num_sg, read);
|
||||
if (count < 1) {
|
||||
dev_err(&(pcr->pci->dev), "scatterlist map failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
dev_dbg(&(pcr->pci->dev), "DMA mapping count: %d\n", count);
|
||||
|
||||
val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE;
|
||||
pcr->sgi = 0;
|
||||
for_each_sg(sglist, sg, count, i) {
|
||||
addr = sg_dma_address(sg);
|
||||
len = sg_dma_len(sg);
|
||||
rtsx_pci_add_sg_tbl(pcr, addr, len, i == count - 1);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&pcr->lock, flags);
|
||||
|
||||
pcr->done = &trans_done;
|
||||
pcr->trans_result = TRANS_NOT_READY;
|
||||
init_completion(&trans_done);
|
||||
rtsx_pci_writel(pcr, RTSX_HDBAR, pcr->host_sg_tbl_addr);
|
||||
rtsx_pci_writel(pcr, RTSX_HDBCTLR, val);
|
||||
|
||||
spin_unlock_irqrestore(&pcr->lock, flags);
|
||||
|
||||
rtsx_pci_dma_transfer(pcr, sglist, count, read);
|
||||
|
||||
timeleft = wait_for_completion_interruptible_timeout(
|
||||
&trans_done, msecs_to_jiffies(timeout));
|
||||
if (timeleft <= 0) {
|
||||
@ -413,7 +383,7 @@ out:
|
||||
pcr->done = NULL;
|
||||
spin_unlock_irqrestore(&pcr->lock, flags);
|
||||
|
||||
dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir);
|
||||
rtsx_pci_dma_unmap_sg(pcr, sglist, num_sg, read);
|
||||
|
||||
if ((err < 0) && (err != -ENODEV))
|
||||
rtsx_pci_stop_cmd(pcr);
|
||||
@ -425,6 +395,73 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rtsx_pci_transfer_data);
|
||||
|
||||
int rtsx_pci_dma_map_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist,
|
||||
int num_sg, bool read)
|
||||
{
|
||||
enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||
|
||||
if (pcr->remove_pci)
|
||||
return -EINVAL;
|
||||
|
||||
if ((sglist == NULL) || num_sg < 1)
|
||||
return -EINVAL;
|
||||
|
||||
return dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dir);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rtsx_pci_dma_map_sg);
|
||||
|
||||
int rtsx_pci_dma_unmap_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist,
|
||||
int num_sg, bool read)
|
||||
{
|
||||
enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||
|
||||
if (pcr->remove_pci)
|
||||
return -EINVAL;
|
||||
|
||||
if (sglist == NULL || num_sg < 1)
|
||||
return -EINVAL;
|
||||
|
||||
dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dir);
|
||||
return num_sg;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rtsx_pci_dma_unmap_sg);
|
||||
|
||||
int rtsx_pci_dma_transfer(struct rtsx_pcr *pcr, struct scatterlist *sglist,
|
||||
int sg_count, bool read)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
dma_addr_t addr;
|
||||
unsigned int len;
|
||||
int i;
|
||||
u32 val;
|
||||
u8 dir = read ? DEVICE_TO_HOST : HOST_TO_DEVICE;
|
||||
unsigned long flags;
|
||||
|
||||
if (pcr->remove_pci)
|
||||
return -EINVAL;
|
||||
|
||||
if ((sglist == NULL) || (sg_count < 1))
|
||||
return -EINVAL;
|
||||
|
||||
val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE;
|
||||
pcr->sgi = 0;
|
||||
for_each_sg(sglist, sg, sg_count, i) {
|
||||
addr = sg_dma_address(sg);
|
||||
len = sg_dma_len(sg);
|
||||
rtsx_pci_add_sg_tbl(pcr, addr, len, i == sg_count - 1);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&pcr->lock, flags);
|
||||
|
||||
rtsx_pci_writel(pcr, RTSX_HDBAR, pcr->host_sg_tbl_addr);
|
||||
rtsx_pci_writel(pcr, RTSX_HDBCTLR, val);
|
||||
|
||||
spin_unlock_irqrestore(&pcr->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rtsx_pci_dma_transfer);
|
||||
|
||||
int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len)
|
||||
{
|
||||
int err;
|
||||
@ -836,6 +873,8 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
|
||||
int_reg = rtsx_pci_readl(pcr, RTSX_BIPR);
|
||||
/* Clear interrupt flag */
|
||||
rtsx_pci_writel(pcr, RTSX_BIPR, int_reg);
|
||||
dev_dbg(&pcr->pci->dev, "=========== BIPR 0x%8x ==========\n", int_reg);
|
||||
|
||||
if ((int_reg & pcr->bier) == 0) {
|
||||
spin_unlock(&pcr->lock);
|
||||
return IRQ_NONE;
|
||||
@ -866,17 +905,28 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
|
||||
}
|
||||
|
||||
if (int_reg & (NEED_COMPLETE_INT | DELINK_INT)) {
|
||||
if (int_reg & (TRANS_FAIL_INT | DELINK_INT)) {
|
||||
if (int_reg & (TRANS_FAIL_INT | DELINK_INT))
|
||||
pcr->trans_result = TRANS_RESULT_FAIL;
|
||||
if (pcr->done)
|
||||
complete(pcr->done);
|
||||
} else if (int_reg & TRANS_OK_INT) {
|
||||
else if (int_reg & TRANS_OK_INT)
|
||||
pcr->trans_result = TRANS_RESULT_OK;
|
||||
if (pcr->done)
|
||||
complete(pcr->done);
|
||||
|
||||
if (pcr->done)
|
||||
complete(pcr->done);
|
||||
|
||||
if (int_reg & SD_EXIST) {
|
||||
struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD];
|
||||
if (slot && slot->done_transfer)
|
||||
slot->done_transfer(slot->p_dev);
|
||||
}
|
||||
|
||||
if (int_reg & MS_EXIST) {
|
||||
struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD];
|
||||
if (slot && slot->done_transfer)
|
||||
slot->done_transfer(slot->p_dev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (pcr->card_inserted || pcr->card_removed)
|
||||
schedule_delayed_work(&pcr->carddet_work,
|
||||
msecs_to_jiffies(200));
|
||||
|
@ -415,8 +415,7 @@ static int ioctl_do_sanitize(struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!(mmc_can_sanitize(card) &&
|
||||
(card->host->caps2 & MMC_CAP2_SANITIZE))) {
|
||||
if (!mmc_can_sanitize(card)) {
|
||||
pr_warn("%s: %s - SANITIZE is not supported\n",
|
||||
mmc_hostname(card->host), __func__);
|
||||
err = -EOPNOTSUPP;
|
||||
@ -722,19 +721,6 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
|
||||
return result;
|
||||
}
|
||||
|
||||
static int send_stop(struct mmc_card *card, u32 *status)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
int err;
|
||||
|
||||
cmd.opcode = MMC_STOP_TRANSMISSION;
|
||||
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, 5);
|
||||
if (err == 0)
|
||||
*status = cmd.resp[0];
|
||||
return err;
|
||||
}
|
||||
|
||||
static int get_card_status(struct mmc_card *card, u32 *status, int retries)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
@ -750,6 +736,99 @@ static int get_card_status(struct mmc_card *card, u32 *status, int retries)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
|
||||
bool hw_busy_detect, struct request *req, int *gen_err)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
|
||||
int err = 0;
|
||||
u32 status;
|
||||
|
||||
do {
|
||||
err = get_card_status(card, &status, 5);
|
||||
if (err) {
|
||||
pr_err("%s: error %d requesting status\n",
|
||||
req->rq_disk->disk_name, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (status & R1_ERROR) {
|
||||
pr_err("%s: %s: error sending status cmd, status %#x\n",
|
||||
req->rq_disk->disk_name, __func__, status);
|
||||
*gen_err = 1;
|
||||
}
|
||||
|
||||
/* We may rely on the host hw to handle busy detection.*/
|
||||
if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) &&
|
||||
hw_busy_detect)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Timeout if the device never becomes ready for data and never
|
||||
* leaves the program state.
|
||||
*/
|
||||
if (time_after(jiffies, timeout)) {
|
||||
pr_err("%s: Card stuck in programming state! %s %s\n",
|
||||
mmc_hostname(card->host),
|
||||
req->rq_disk->disk_name, __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some cards mishandle the status bits,
|
||||
* so make sure to check both the busy
|
||||
* indication and the card state.
|
||||
*/
|
||||
} while (!(status & R1_READY_FOR_DATA) ||
|
||||
(R1_CURRENT_STATE(status) == R1_STATE_PRG));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int send_stop(struct mmc_card *card, unsigned int timeout_ms,
|
||||
struct request *req, int *gen_err, u32 *stop_status)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
struct mmc_command cmd = {0};
|
||||
int err;
|
||||
bool use_r1b_resp = rq_data_dir(req) == WRITE;
|
||||
|
||||
/*
|
||||
* Normally we use R1B responses for WRITE, but in cases where the host
|
||||
* has specified a max_busy_timeout we need to validate it. A failure
|
||||
* means we need to prevent the host from doing hw busy detection, which
|
||||
* is done by converting to a R1 response instead.
|
||||
*/
|
||||
if (host->max_busy_timeout && (timeout_ms > host->max_busy_timeout))
|
||||
use_r1b_resp = false;
|
||||
|
||||
cmd.opcode = MMC_STOP_TRANSMISSION;
|
||||
if (use_r1b_resp) {
|
||||
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
|
||||
cmd.busy_timeout = timeout_ms;
|
||||
} else {
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||
}
|
||||
|
||||
err = mmc_wait_for_cmd(host, &cmd, 5);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*stop_status = cmd.resp[0];
|
||||
|
||||
/* No need to check card status in case of READ. */
|
||||
if (rq_data_dir(req) == READ)
|
||||
return 0;
|
||||
|
||||
if (!mmc_host_is_spi(host) &&
|
||||
(*stop_status & R1_ERROR)) {
|
||||
pr_err("%s: %s: general error sending stop command, resp %#x\n",
|
||||
req->rq_disk->disk_name, __func__, *stop_status);
|
||||
*gen_err = 1;
|
||||
}
|
||||
|
||||
return card_busy_detect(card, timeout_ms, use_r1b_resp, req, gen_err);
|
||||
}
|
||||
|
||||
#define ERR_NOMEDIUM 3
|
||||
#define ERR_RETRY 2
|
||||
#define ERR_ABORT 1
|
||||
@ -866,26 +945,21 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
|
||||
*/
|
||||
if (R1_CURRENT_STATE(status) == R1_STATE_DATA ||
|
||||
R1_CURRENT_STATE(status) == R1_STATE_RCV) {
|
||||
err = send_stop(card, &stop_status);
|
||||
if (err)
|
||||
err = send_stop(card,
|
||||
DIV_ROUND_UP(brq->data.timeout_ns, 1000000),
|
||||
req, gen_err, &stop_status);
|
||||
if (err) {
|
||||
pr_err("%s: error %d sending stop command\n",
|
||||
req->rq_disk->disk_name, err);
|
||||
|
||||
/*
|
||||
* If the stop cmd also timed out, the card is probably
|
||||
* not present, so abort. Other errors are bad news too.
|
||||
*/
|
||||
if (err)
|
||||
/*
|
||||
* If the stop cmd also timed out, the card is probably
|
||||
* not present, so abort. Other errors are bad news too.
|
||||
*/
|
||||
return ERR_ABORT;
|
||||
}
|
||||
|
||||
if (stop_status & R1_CARD_ECC_FAILED)
|
||||
*ecc_err = 1;
|
||||
if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ)
|
||||
if (stop_status & R1_ERROR) {
|
||||
pr_err("%s: %s: general error sending stop command, stop cmd response %#x\n",
|
||||
req->rq_disk->disk_name, __func__,
|
||||
stop_status);
|
||||
*gen_err = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for set block count errors */
|
||||
@ -1157,8 +1231,7 @@ static int mmc_blk_err_check(struct mmc_card *card,
|
||||
* program mode, which we have to wait for it to complete.
|
||||
*/
|
||||
if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
|
||||
u32 status;
|
||||
unsigned long timeout;
|
||||
int err;
|
||||
|
||||
/* Check stop command response */
|
||||
if (brq->stop.resp[0] & R1_ERROR) {
|
||||
@ -1168,39 +1241,10 @@ static int mmc_blk_err_check(struct mmc_card *card,
|
||||
gen_err = 1;
|
||||
}
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(MMC_BLK_TIMEOUT_MS);
|
||||
do {
|
||||
int err = get_card_status(card, &status, 5);
|
||||
if (err) {
|
||||
pr_err("%s: error %d requesting status\n",
|
||||
req->rq_disk->disk_name, err);
|
||||
return MMC_BLK_CMD_ERR;
|
||||
}
|
||||
|
||||
if (status & R1_ERROR) {
|
||||
pr_err("%s: %s: general error sending status command, card status %#x\n",
|
||||
req->rq_disk->disk_name, __func__,
|
||||
status);
|
||||
gen_err = 1;
|
||||
}
|
||||
|
||||
/* Timeout if the device never becomes ready for data
|
||||
* and never leaves the program state.
|
||||
*/
|
||||
if (time_after(jiffies, timeout)) {
|
||||
pr_err("%s: Card stuck in programming state!"\
|
||||
" %s %s\n", mmc_hostname(card->host),
|
||||
req->rq_disk->disk_name, __func__);
|
||||
|
||||
return MMC_BLK_CMD_ERR;
|
||||
}
|
||||
/*
|
||||
* Some cards mishandle the status bits,
|
||||
* so make sure to check both the busy
|
||||
* indication and the card state.
|
||||
*/
|
||||
} while (!(status & R1_READY_FOR_DATA) ||
|
||||
(R1_CURRENT_STATE(status) == R1_STATE_PRG));
|
||||
err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, false, req,
|
||||
&gen_err);
|
||||
if (err)
|
||||
return MMC_BLK_CMD_ERR;
|
||||
}
|
||||
|
||||
/* if general error occurs, retry the write operation. */
|
||||
@ -1335,7 +1379,6 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||
brq->data.blksz = 512;
|
||||
brq->stop.opcode = MMC_STOP_TRANSMISSION;
|
||||
brq->stop.arg = 0;
|
||||
brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
|
||||
brq->data.blocks = blk_rq_sectors(req);
|
||||
|
||||
/*
|
||||
@ -1378,9 +1421,15 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||
if (rq_data_dir(req) == READ) {
|
||||
brq->cmd.opcode = readcmd;
|
||||
brq->data.flags |= MMC_DATA_READ;
|
||||
if (brq->mrq.stop)
|
||||
brq->stop.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 |
|
||||
MMC_CMD_AC;
|
||||
} else {
|
||||
brq->cmd.opcode = writecmd;
|
||||
brq->data.flags |= MMC_DATA_WRITE;
|
||||
if (brq->mrq.stop)
|
||||
brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B |
|
||||
MMC_CMD_AC;
|
||||
}
|
||||
|
||||
if (do_rel_wr)
|
||||
|
@ -2,21 +2,6 @@
|
||||
# MMC core configuration
|
||||
#
|
||||
|
||||
config MMC_UNSAFE_RESUME
|
||||
bool "Assume MMC/SD cards are non-removable (DANGEROUS)"
|
||||
help
|
||||
If you say Y here, the MMC layer will assume that all cards
|
||||
stayed in their respective slots during the suspend. The
|
||||
normal behaviour is to remove them at suspend and
|
||||
redetecting them at resume. Breaking this assumption will
|
||||
in most cases result in data corruption.
|
||||
|
||||
This option is usually just for embedded systems which use
|
||||
a MMC/SD card for rootfs. Most people should say N here.
|
||||
|
||||
This option sets a default which can be overridden by the
|
||||
module parameter "removable=0" or "removable=1".
|
||||
|
||||
config MMC_CLKGATE
|
||||
bool "MMC host clock gating"
|
||||
help
|
||||
|
@ -185,24 +185,16 @@ static int mmc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
struct mmc_host *host = card->host;
|
||||
int ret = 0;
|
||||
|
||||
if (host->bus_ops->runtime_suspend)
|
||||
ret = host->bus_ops->runtime_suspend(host);
|
||||
|
||||
return ret;
|
||||
return host->bus_ops->runtime_suspend(host);
|
||||
}
|
||||
|
||||
static int mmc_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
struct mmc_host *host = card->host;
|
||||
int ret = 0;
|
||||
|
||||
if (host->bus_ops->runtime_resume)
|
||||
ret = host->bus_ops->runtime_resume(host);
|
||||
|
||||
return ret;
|
||||
return host->bus_ops->runtime_resume(host);
|
||||
}
|
||||
|
||||
static int mmc_runtime_idle(struct device *dev)
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/sd.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "bus.h"
|
||||
@ -64,23 +65,6 @@ static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
|
||||
bool use_spi_crc = 1;
|
||||
module_param(use_spi_crc, bool, 0);
|
||||
|
||||
/*
|
||||
* We normally treat cards as removed during suspend if they are not
|
||||
* known to be on a non-removable bus, to avoid the risk of writing
|
||||
* back data to a different card after resume. Allow this to be
|
||||
* overridden if necessary.
|
||||
*/
|
||||
#ifdef CONFIG_MMC_UNSAFE_RESUME
|
||||
bool mmc_assume_removable;
|
||||
#else
|
||||
bool mmc_assume_removable = 1;
|
||||
#endif
|
||||
EXPORT_SYMBOL(mmc_assume_removable);
|
||||
module_param_named(removable, mmc_assume_removable, bool, 0644);
|
||||
MODULE_PARM_DESC(
|
||||
removable,
|
||||
"MMC/SD cards are removable and may be removed during suspend");
|
||||
|
||||
/*
|
||||
* Internal function. Schedule delayed work in the MMC work queue.
|
||||
*/
|
||||
@ -302,7 +286,8 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
|
||||
}
|
||||
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal, true);
|
||||
EXT_CSD_BKOPS_START, 1, timeout,
|
||||
use_busy_signal, true, false);
|
||||
if (err) {
|
||||
pr_warn("%s: Error %d starting bkops\n",
|
||||
mmc_hostname(card->host), err);
|
||||
@ -1950,7 +1935,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
||||
cmd.opcode = MMC_ERASE;
|
||||
cmd.arg = arg;
|
||||
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
|
||||
cmd.cmd_timeout_ms = mmc_erase_timeout(card, arg, qty);
|
||||
cmd.busy_timeout = mmc_erase_timeout(card, arg, qty);
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
||||
if (err) {
|
||||
pr_err("mmc_erase: erase error %d, status %#x\n",
|
||||
@ -2137,7 +2122,7 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,
|
||||
y = 0;
|
||||
for (x = 1; x && x <= max_qty && max_qty - x >= qty; x <<= 1) {
|
||||
timeout = mmc_erase_timeout(card, arg, qty + x);
|
||||
if (timeout > host->max_discard_to)
|
||||
if (timeout > host->max_busy_timeout)
|
||||
break;
|
||||
if (timeout < last_timeout)
|
||||
break;
|
||||
@ -2169,7 +2154,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
|
||||
struct mmc_host *host = card->host;
|
||||
unsigned int max_discard, max_trim;
|
||||
|
||||
if (!host->max_discard_to)
|
||||
if (!host->max_busy_timeout)
|
||||
return UINT_MAX;
|
||||
|
||||
/*
|
||||
@ -2189,7 +2174,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
|
||||
max_discard = 0;
|
||||
}
|
||||
pr_debug("%s: calculated max. discard sectors %u for timeout %u ms\n",
|
||||
mmc_hostname(host), max_discard, host->max_discard_to);
|
||||
mmc_hostname(host), max_discard, host->max_busy_timeout);
|
||||
return max_discard;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_calc_max_discard);
|
||||
@ -2248,9 +2233,6 @@ static int mmc_do_hw_reset(struct mmc_host *host, int check)
|
||||
{
|
||||
struct mmc_card *card = host->card;
|
||||
|
||||
if (!host->bus_ops->power_restore)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
@ -2352,7 +2334,7 @@ int _mmc_detect_card_removed(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((host->caps & MMC_CAP_NONREMOVABLE) || !host->bus_ops->alive)
|
||||
if (host->caps & MMC_CAP_NONREMOVABLE)
|
||||
return 0;
|
||||
|
||||
if (!host->card || mmc_card_removed(host->card))
|
||||
@ -2435,7 +2417,7 @@ void mmc_rescan(struct work_struct *work)
|
||||
* if there is a _removable_ card registered, check whether it is
|
||||
* still present
|
||||
*/
|
||||
if (host->bus_ops && host->bus_ops->detect && !host->bus_dead
|
||||
if (host->bus_ops && !host->bus_dead
|
||||
&& !(host->caps & MMC_CAP_NONREMOVABLE))
|
||||
host->bus_ops->detect(host);
|
||||
|
||||
@ -2490,6 +2472,7 @@ void mmc_start_host(struct mmc_host *host)
|
||||
mmc_power_off(host);
|
||||
else
|
||||
mmc_power_up(host, host->ocr_avail);
|
||||
mmc_gpiod_request_cd_irq(host);
|
||||
_mmc_detect_change(host, 0, false);
|
||||
}
|
||||
|
||||
@ -2501,6 +2484,8 @@ void mmc_stop_host(struct mmc_host *host)
|
||||
host->removed = 1;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
#endif
|
||||
if (host->slot.cd_irq >= 0)
|
||||
disable_irq(host->slot.cd_irq);
|
||||
|
||||
host->rescan_disable = 1;
|
||||
cancel_delayed_work_sync(&host->detect);
|
||||
@ -2537,7 +2522,7 @@ int mmc_power_save_host(struct mmc_host *host)
|
||||
|
||||
mmc_bus_get(host);
|
||||
|
||||
if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
|
||||
if (!host->bus_ops || host->bus_dead) {
|
||||
mmc_bus_put(host);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -2563,7 +2548,7 @@ int mmc_power_restore_host(struct mmc_host *host)
|
||||
|
||||
mmc_bus_get(host);
|
||||
|
||||
if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
|
||||
if (!host->bus_ops || host->bus_dead) {
|
||||
mmc_bus_put(host);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -2582,12 +2567,8 @@ EXPORT_SYMBOL(mmc_power_restore_host);
|
||||
*/
|
||||
int mmc_flush_cache(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
int err = 0;
|
||||
|
||||
if (!(host->caps2 & MMC_CAP2_CACHE_CTRL))
|
||||
return err;
|
||||
|
||||
if (mmc_card_mmc(card) &&
|
||||
(card->ext_csd.cache_size > 0) &&
|
||||
(card->ext_csd.cache_ctrl & 1)) {
|
||||
@ -2602,44 +2583,6 @@ int mmc_flush_cache(struct mmc_card *card)
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_flush_cache);
|
||||
|
||||
/*
|
||||
* Turn the cache ON/OFF.
|
||||
* Turning the cache OFF shall trigger flushing of the data
|
||||
* to the non-volatile storage.
|
||||
* This function should be called with host claimed
|
||||
*/
|
||||
int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
|
||||
{
|
||||
struct mmc_card *card = host->card;
|
||||
unsigned int timeout;
|
||||
int err = 0;
|
||||
|
||||
if (!(host->caps2 & MMC_CAP2_CACHE_CTRL) ||
|
||||
mmc_card_is_removable(host))
|
||||
return err;
|
||||
|
||||
if (card && mmc_card_mmc(card) &&
|
||||
(card->ext_csd.cache_size > 0)) {
|
||||
enable = !!enable;
|
||||
|
||||
if (card->ext_csd.cache_ctrl ^ enable) {
|
||||
timeout = enable ? card->ext_csd.generic_cmd6_time : 0;
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_CACHE_CTRL, enable, timeout);
|
||||
if (err)
|
||||
pr_err("%s: cache %s error %d\n",
|
||||
mmc_hostname(card->host),
|
||||
enable ? "on" : "off",
|
||||
err);
|
||||
else
|
||||
card->ext_csd.cache_ctrl = enable;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_cache_ctrl);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/* Do the card removal on suspend if card is assumed removeable
|
||||
@ -2668,7 +2611,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
|
||||
/* Validate prerequisites for suspend */
|
||||
if (host->bus_ops->pre_suspend)
|
||||
err = host->bus_ops->pre_suspend(host);
|
||||
if (!err && host->bus_ops->suspend)
|
||||
if (!err)
|
||||
break;
|
||||
|
||||
/* Calling bus_ops->remove() with a claimed host can deadlock */
|
||||
|
@ -419,6 +419,16 @@ int mmc_of_parse(struct mmc_host *host)
|
||||
host->caps |= MMC_CAP_SD_HIGHSPEED;
|
||||
if (of_find_property(np, "cap-mmc-highspeed", &len))
|
||||
host->caps |= MMC_CAP_MMC_HIGHSPEED;
|
||||
if (of_find_property(np, "sd-uhs-sdr12", &len))
|
||||
host->caps |= MMC_CAP_UHS_SDR12;
|
||||
if (of_find_property(np, "sd-uhs-sdr25", &len))
|
||||
host->caps |= MMC_CAP_UHS_SDR25;
|
||||
if (of_find_property(np, "sd-uhs-sdr50", &len))
|
||||
host->caps |= MMC_CAP_UHS_SDR50;
|
||||
if (of_find_property(np, "sd-uhs-sdr104", &len))
|
||||
host->caps |= MMC_CAP_UHS_SDR104;
|
||||
if (of_find_property(np, "sd-uhs-ddr50", &len))
|
||||
host->caps |= MMC_CAP_UHS_DDR50;
|
||||
if (of_find_property(np, "cap-power-off-card", &len))
|
||||
host->caps |= MMC_CAP_POWER_OFF_CARD;
|
||||
if (of_find_property(np, "cap-sdio-irq", &len))
|
||||
@ -429,6 +439,14 @@ int mmc_of_parse(struct mmc_host *host)
|
||||
host->pm_caps |= MMC_PM_KEEP_POWER;
|
||||
if (of_find_property(np, "enable-sdio-wakeup", &len))
|
||||
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
||||
if (of_find_property(np, "mmc-ddr-1_8v", &len))
|
||||
host->caps |= MMC_CAP_1_8V_DDR;
|
||||
if (of_find_property(np, "mmc-ddr-1_2v", &len))
|
||||
host->caps |= MMC_CAP_1_2V_DDR;
|
||||
if (of_find_property(np, "mmc-hs200-1_8v", &len))
|
||||
host->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
|
||||
if (of_find_property(np, "mmc-hs200-1_2v", &len))
|
||||
host->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -856,8 +856,10 @@ static int mmc_select_hs200(struct mmc_card *card)
|
||||
|
||||
/* switch to HS200 mode if bus width set successfully */
|
||||
if (!err)
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, 2, 0);
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, 2,
|
||||
card->ext_csd.generic_cmd6_time,
|
||||
true, true, true);
|
||||
err:
|
||||
return err;
|
||||
}
|
||||
@ -1074,9 +1076,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
host->caps2 & MMC_CAP2_HS200)
|
||||
err = mmc_select_hs200(card);
|
||||
else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, 1,
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, 1,
|
||||
card->ext_csd.generic_cmd6_time,
|
||||
true, true, true);
|
||||
|
||||
if (err && err != -EBADMSG)
|
||||
goto free_card;
|
||||
@ -1287,8 +1290,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
* If cache size is higher than 0, this indicates
|
||||
* the existence of cache and it can be turned on.
|
||||
*/
|
||||
if ((host->caps2 & MMC_CAP2_CACHE_CTRL) &&
|
||||
card->ext_csd.cache_size > 0) {
|
||||
if (card->ext_csd.cache_size > 0) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_CACHE_CTRL, 1,
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
@ -1356,11 +1358,9 @@ static int mmc_sleep(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_card *card = host->card;
|
||||
unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
|
||||
int err;
|
||||
|
||||
if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD)
|
||||
return 0;
|
||||
|
||||
err = mmc_deselect_cards(host);
|
||||
if (err)
|
||||
return err;
|
||||
@ -1369,7 +1369,19 @@ static int mmc_sleep(struct mmc_host *host)
|
||||
cmd.arg = card->rca << 16;
|
||||
cmd.arg |= 1 << 15;
|
||||
|
||||
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
|
||||
/*
|
||||
* If the max_busy_timeout of the host is specified, validate it against
|
||||
* the sleep cmd timeout. A failure means we need to prevent the host
|
||||
* from doing hw busy detection, which is done by converting to a R1
|
||||
* response instead of a R1B.
|
||||
*/
|
||||
if (host->max_busy_timeout && (timeout_ms > host->max_busy_timeout)) {
|
||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||
} else {
|
||||
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
|
||||
cmd.busy_timeout = timeout_ms;
|
||||
}
|
||||
|
||||
err = mmc_wait_for_cmd(host, &cmd, 0);
|
||||
if (err)
|
||||
return err;
|
||||
@ -1380,8 +1392,8 @@ static int mmc_sleep(struct mmc_host *host)
|
||||
* SEND_STATUS command to poll the status because that command (and most
|
||||
* others) is invalid while the card sleeps.
|
||||
*/
|
||||
if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
|
||||
mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000));
|
||||
if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
|
||||
mmc_delay(timeout_ms);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -1404,7 +1416,7 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
|
||||
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_POWER_OFF_NOTIFICATION,
|
||||
notify_type, timeout, true, false);
|
||||
notify_type, timeout, true, false, false);
|
||||
if (err)
|
||||
pr_err("%s: Power Off Notification timed out, %u\n",
|
||||
mmc_hostname(card->host), timeout);
|
||||
@ -1484,7 +1496,7 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = mmc_cache_ctrl(host, 0);
|
||||
err = mmc_flush_cache(host->card);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@ -1634,16 +1646,6 @@ static int mmc_power_restore(struct mmc_host *host)
|
||||
}
|
||||
|
||||
static const struct mmc_bus_ops mmc_ops = {
|
||||
.remove = mmc_remove,
|
||||
.detect = mmc_detect,
|
||||
.suspend = NULL,
|
||||
.resume = NULL,
|
||||
.power_restore = mmc_power_restore,
|
||||
.alive = mmc_alive,
|
||||
.shutdown = mmc_shutdown,
|
||||
};
|
||||
|
||||
static const struct mmc_bus_ops mmc_ops_unsafe = {
|
||||
.remove = mmc_remove,
|
||||
.detect = mmc_detect,
|
||||
.suspend = mmc_suspend,
|
||||
@ -1655,17 +1657,6 @@ static const struct mmc_bus_ops mmc_ops_unsafe = {
|
||||
.shutdown = mmc_shutdown,
|
||||
};
|
||||
|
||||
static void mmc_attach_bus_ops(struct mmc_host *host)
|
||||
{
|
||||
const struct mmc_bus_ops *bus_ops;
|
||||
|
||||
if (!mmc_card_is_removable(host))
|
||||
bus_ops = &mmc_ops_unsafe;
|
||||
else
|
||||
bus_ops = &mmc_ops;
|
||||
mmc_attach_bus(host, bus_ops);
|
||||
}
|
||||
|
||||
/*
|
||||
* Starting point for MMC card init.
|
||||
*/
|
||||
@ -1685,7 +1676,7 @@ int mmc_attach_mmc(struct mmc_host *host)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mmc_attach_bus_ops(host);
|
||||
mmc_attach_bus(host, &mmc_ops);
|
||||
if (host->ocr_avail_mmc)
|
||||
host->ocr_avail = host->ocr_avail_mmc;
|
||||
|
||||
|
@ -405,20 +405,30 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
|
||||
* timeout of zero implies maximum possible timeout
|
||||
* @use_busy_signal: use the busy signal as response type
|
||||
* @send_status: send status cmd to poll for busy
|
||||
* @ignore_crc: ignore CRC errors when sending status cmd to poll for busy
|
||||
*
|
||||
* Modifies the EXT_CSD register for selected card.
|
||||
*/
|
||||
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
unsigned int timeout_ms, bool use_busy_signal, bool send_status)
|
||||
unsigned int timeout_ms, bool use_busy_signal, bool send_status,
|
||||
bool ignore_crc)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
unsigned long timeout;
|
||||
u32 status = 0;
|
||||
bool ignore_crc = false;
|
||||
bool use_r1b_resp = use_busy_signal;
|
||||
|
||||
BUG_ON(!card);
|
||||
BUG_ON(!card->host);
|
||||
/*
|
||||
* 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
|
||||
* the host from doing hw busy detection, which is done by converting
|
||||
* to a R1 response instead of a R1B.
|
||||
*/
|
||||
if (timeout_ms && host->max_busy_timeout &&
|
||||
(timeout_ms > host->max_busy_timeout))
|
||||
use_r1b_resp = false;
|
||||
|
||||
cmd.opcode = MMC_SWITCH;
|
||||
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
|
||||
@ -426,17 +436,21 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
(value << 8) |
|
||||
set;
|
||||
cmd.flags = MMC_CMD_AC;
|
||||
if (use_busy_signal)
|
||||
if (use_r1b_resp) {
|
||||
cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
|
||||
else
|
||||
/*
|
||||
* A busy_timeout of zero means the host can decide to use
|
||||
* whatever value it finds suitable.
|
||||
*/
|
||||
cmd.busy_timeout = timeout_ms;
|
||||
} else {
|
||||
cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1;
|
||||
}
|
||||
|
||||
|
||||
cmd.cmd_timeout_ms = timeout_ms;
|
||||
if (index == EXT_CSD_SANITIZE_START)
|
||||
cmd.sanitize_busy = true;
|
||||
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
|
||||
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -445,24 +459,27 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Must check status to be sure of no errors
|
||||
* If CMD13 is to check the busy completion of the timing change,
|
||||
* disable the check of CRC error.
|
||||
* CRC errors shall only be ignored in cases were CMD13 is used to poll
|
||||
* to detect busy completion.
|
||||
*/
|
||||
if (index == EXT_CSD_HS_TIMING &&
|
||||
!(card->host->caps & MMC_CAP_WAIT_WHILE_BUSY))
|
||||
ignore_crc = true;
|
||||
if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
|
||||
ignore_crc = false;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS);
|
||||
/* We have an unspecified cmd timeout, use the fallback value. */
|
||||
if (!timeout_ms)
|
||||
timeout_ms = MMC_OPS_TIMEOUT_MS;
|
||||
|
||||
/* Must check status to be sure of no errors. */
|
||||
timeout = jiffies + msecs_to_jiffies(timeout_ms);
|
||||
do {
|
||||
if (send_status) {
|
||||
err = __mmc_send_status(card, &status, ignore_crc);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
|
||||
if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
|
||||
break;
|
||||
if (mmc_host_is_spi(card->host))
|
||||
if (mmc_host_is_spi(host))
|
||||
break;
|
||||
|
||||
/*
|
||||
@ -478,18 +495,18 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
/* Timeout if the device never leaves the program state. */
|
||||
if (time_after(jiffies, timeout)) {
|
||||
pr_err("%s: Card stuck in programming state! %s\n",
|
||||
mmc_hostname(card->host), __func__);
|
||||
mmc_hostname(host), __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
|
||||
|
||||
if (mmc_host_is_spi(card->host)) {
|
||||
if (mmc_host_is_spi(host)) {
|
||||
if (status & R1_SPI_ILLEGAL_COMMAND)
|
||||
return -EBADMSG;
|
||||
} else {
|
||||
if (status & 0xFDFFA000)
|
||||
pr_warning("%s: unexpected status %#x after "
|
||||
"switch", mmc_hostname(card->host), status);
|
||||
pr_warn("%s: unexpected status %#x after switch\n",
|
||||
mmc_hostname(host), status);
|
||||
if (status & R1_SWITCH_ERROR)
|
||||
return -EBADMSG;
|
||||
}
|
||||
@ -501,7 +518,8 @@ EXPORT_SYMBOL_GPL(__mmc_switch);
|
||||
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
unsigned int timeout_ms)
|
||||
{
|
||||
return __mmc_switch(card, set, index, value, timeout_ms, true, true);
|
||||
return __mmc_switch(card, set, index, value, timeout_ms, true, true,
|
||||
false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_switch);
|
||||
|
||||
|
@ -1207,16 +1207,6 @@ static int mmc_sd_power_restore(struct mmc_host *host)
|
||||
}
|
||||
|
||||
static const struct mmc_bus_ops mmc_sd_ops = {
|
||||
.remove = mmc_sd_remove,
|
||||
.detect = mmc_sd_detect,
|
||||
.suspend = NULL,
|
||||
.resume = NULL,
|
||||
.power_restore = mmc_sd_power_restore,
|
||||
.alive = mmc_sd_alive,
|
||||
.shutdown = mmc_sd_suspend,
|
||||
};
|
||||
|
||||
static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
|
||||
.remove = mmc_sd_remove,
|
||||
.detect = mmc_sd_detect,
|
||||
.runtime_suspend = mmc_sd_runtime_suspend,
|
||||
@ -1228,17 +1218,6 @@ static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
|
||||
.shutdown = mmc_sd_suspend,
|
||||
};
|
||||
|
||||
static void mmc_sd_attach_bus_ops(struct mmc_host *host)
|
||||
{
|
||||
const struct mmc_bus_ops *bus_ops;
|
||||
|
||||
if (!mmc_card_is_removable(host))
|
||||
bus_ops = &mmc_sd_ops_unsafe;
|
||||
else
|
||||
bus_ops = &mmc_sd_ops;
|
||||
mmc_attach_bus(host, bus_ops);
|
||||
}
|
||||
|
||||
/*
|
||||
* Starting point for SD card init.
|
||||
*/
|
||||
@ -1254,7 +1233,7 @@ int mmc_attach_sd(struct mmc_host *host)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mmc_sd_attach_bus_ops(host);
|
||||
mmc_attach_bus(host, &mmc_sd_ops);
|
||||
if (host->ocr_avail_sd)
|
||||
host->ocr_avail = host->ocr_avail_sd;
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/mmc/host.h>
|
||||
@ -18,8 +19,10 @@
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct mmc_gpio {
|
||||
int ro_gpio;
|
||||
int cd_gpio;
|
||||
struct gpio_desc *ro_gpio;
|
||||
struct gpio_desc *cd_gpio;
|
||||
bool override_ro_active_level;
|
||||
bool override_cd_active_level;
|
||||
char *ro_label;
|
||||
char cd_label[0];
|
||||
};
|
||||
@ -57,8 +60,6 @@ static int mmc_gpio_alloc(struct mmc_host *host)
|
||||
ctx->ro_label = ctx->cd_label + len;
|
||||
snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));
|
||||
snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent));
|
||||
ctx->cd_gpio = -EINVAL;
|
||||
ctx->ro_gpio = -EINVAL;
|
||||
host->slot.handler_priv = ctx;
|
||||
}
|
||||
}
|
||||
@ -72,11 +73,14 @@ int mmc_gpio_get_ro(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
|
||||
if (!ctx || !gpio_is_valid(ctx->ro_gpio))
|
||||
if (!ctx || !ctx->ro_gpio)
|
||||
return -ENOSYS;
|
||||
|
||||
return !gpio_get_value_cansleep(ctx->ro_gpio) ^
|
||||
!!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
|
||||
if (ctx->override_ro_active_level)
|
||||
return !gpiod_get_raw_value_cansleep(ctx->ro_gpio) ^
|
||||
!!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
|
||||
|
||||
return gpiod_get_value_cansleep(ctx->ro_gpio);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_get_ro);
|
||||
|
||||
@ -84,11 +88,14 @@ int mmc_gpio_get_cd(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
|
||||
if (!ctx || !gpio_is_valid(ctx->cd_gpio))
|
||||
if (!ctx || !ctx->cd_gpio)
|
||||
return -ENOSYS;
|
||||
|
||||
return !gpio_get_value_cansleep(ctx->cd_gpio) ^
|
||||
!!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
|
||||
if (ctx->override_cd_active_level)
|
||||
return !gpiod_get_raw_value_cansleep(ctx->cd_gpio) ^
|
||||
!!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
|
||||
|
||||
return gpiod_get_value_cansleep(ctx->cd_gpio);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_get_cd);
|
||||
|
||||
@ -125,12 +132,47 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx->ro_gpio = gpio;
|
||||
ctx->override_ro_active_level = true;
|
||||
ctx->ro_gpio = gpio_to_desc(gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_request_ro);
|
||||
|
||||
void mmc_gpiod_request_cd_irq(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
int ret, irq;
|
||||
|
||||
if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio)
|
||||
return;
|
||||
|
||||
irq = gpiod_to_irq(ctx->cd_gpio);
|
||||
|
||||
/*
|
||||
* Even if gpiod_to_irq() returns a valid IRQ number, the platform might
|
||||
* still prefer to poll, e.g., because that IRQ number is already used
|
||||
* by another unit and cannot be shared.
|
||||
*/
|
||||
if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL)
|
||||
irq = -EINVAL;
|
||||
|
||||
if (irq >= 0) {
|
||||
ret = devm_request_threaded_irq(&host->class_dev, irq,
|
||||
NULL, mmc_gpio_cd_irqt,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
ctx->cd_label, host);
|
||||
if (ret < 0)
|
||||
irq = ret;
|
||||
}
|
||||
|
||||
host->slot.cd_irq = irq;
|
||||
|
||||
if (irq < 0)
|
||||
host->caps |= MMC_CAP_NEEDS_POLL;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
|
||||
|
||||
/**
|
||||
* mmc_gpio_request_cd - request a gpio for card-detection
|
||||
* @host: mmc host
|
||||
@ -154,7 +196,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
|
||||
unsigned int debounce)
|
||||
{
|
||||
struct mmc_gpio *ctx;
|
||||
int irq = gpio_to_irq(gpio);
|
||||
int ret;
|
||||
|
||||
ret = mmc_gpio_alloc(host);
|
||||
@ -179,29 +220,10 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Even if gpio_to_irq() returns a valid IRQ number, the platform might
|
||||
* still prefer to poll, e.g., because that IRQ number is already used
|
||||
* by another unit and cannot be shared.
|
||||
*/
|
||||
if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL)
|
||||
irq = -EINVAL;
|
||||
ctx->override_cd_active_level = true;
|
||||
ctx->cd_gpio = gpio_to_desc(gpio);
|
||||
|
||||
if (irq >= 0) {
|
||||
ret = devm_request_threaded_irq(&host->class_dev, irq,
|
||||
NULL, mmc_gpio_cd_irqt,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
ctx->cd_label, host);
|
||||
if (ret < 0)
|
||||
irq = ret;
|
||||
}
|
||||
|
||||
host->slot.cd_irq = irq;
|
||||
|
||||
if (irq < 0)
|
||||
host->caps |= MMC_CAP_NEEDS_POLL;
|
||||
|
||||
ctx->cd_gpio = gpio;
|
||||
mmc_gpiod_request_cd_irq(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -219,11 +241,11 @@ void mmc_gpio_free_ro(struct mmc_host *host)
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
int gpio;
|
||||
|
||||
if (!ctx || !gpio_is_valid(ctx->ro_gpio))
|
||||
if (!ctx || !ctx->ro_gpio)
|
||||
return;
|
||||
|
||||
gpio = ctx->ro_gpio;
|
||||
ctx->ro_gpio = -EINVAL;
|
||||
gpio = desc_to_gpio(ctx->ro_gpio);
|
||||
ctx->ro_gpio = NULL;
|
||||
|
||||
devm_gpio_free(&host->class_dev, gpio);
|
||||
}
|
||||
@ -241,7 +263,7 @@ void mmc_gpio_free_cd(struct mmc_host *host)
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
int gpio;
|
||||
|
||||
if (!ctx || !gpio_is_valid(ctx->cd_gpio))
|
||||
if (!ctx || !ctx->cd_gpio)
|
||||
return;
|
||||
|
||||
if (host->slot.cd_irq >= 0) {
|
||||
@ -249,9 +271,87 @@ void mmc_gpio_free_cd(struct mmc_host *host)
|
||||
host->slot.cd_irq = -EINVAL;
|
||||
}
|
||||
|
||||
gpio = ctx->cd_gpio;
|
||||
ctx->cd_gpio = -EINVAL;
|
||||
gpio = desc_to_gpio(ctx->cd_gpio);
|
||||
ctx->cd_gpio = NULL;
|
||||
|
||||
devm_gpio_free(&host->class_dev, gpio);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_free_cd);
|
||||
|
||||
/**
|
||||
* mmc_gpiod_request_cd - request a gpio descriptor for card-detection
|
||||
* @host: mmc host
|
||||
* @con_id: function within the GPIO consumer
|
||||
* @idx: index of the GPIO to obtain in the consumer
|
||||
* @override_active_level: ignore %GPIO_ACTIVE_LOW flag
|
||||
* @debounce: debounce time in microseconds
|
||||
*
|
||||
* Use this function in place of mmc_gpio_request_cd() to use the GPIO
|
||||
* descriptor API. Note that it is paired with mmc_gpiod_free_cd() not
|
||||
* mmc_gpio_free_cd(). Note also that it must be called prior to mmc_add_host()
|
||||
* otherwise the caller must also call mmc_gpiod_request_cd_irq().
|
||||
*
|
||||
* Returns zero on success, else an error.
|
||||
*/
|
||||
int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
||||
unsigned int idx, bool override_active_level,
|
||||
unsigned int debounce)
|
||||
{
|
||||
struct mmc_gpio *ctx;
|
||||
struct gpio_desc *desc;
|
||||
int ret;
|
||||
|
||||
ret = mmc_gpio_alloc(host);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx = host->slot.handler_priv;
|
||||
|
||||
if (!con_id)
|
||||
con_id = ctx->cd_label;
|
||||
|
||||
desc = devm_gpiod_get_index(host->parent, con_id, idx);
|
||||
if (IS_ERR(desc))
|
||||
return PTR_ERR(desc);
|
||||
|
||||
ret = gpiod_direction_input(desc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (debounce) {
|
||||
ret = gpiod_set_debounce(desc, debounce);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->override_cd_active_level = override_active_level;
|
||||
ctx->cd_gpio = desc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpiod_request_cd);
|
||||
|
||||
/**
|
||||
* mmc_gpiod_free_cd - free the card-detection gpio descriptor
|
||||
* @host: mmc host
|
||||
*
|
||||
* It's provided only for cases that client drivers need to manually free
|
||||
* up the card-detection gpio requested by mmc_gpiod_request_cd().
|
||||
*/
|
||||
void mmc_gpiod_free_cd(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
|
||||
if (!ctx || !ctx->cd_gpio)
|
||||
return;
|
||||
|
||||
if (host->slot.cd_irq >= 0) {
|
||||
devm_free_irq(&host->class_dev, host->slot.cd_irq, host);
|
||||
host->slot.cd_irq = -EINVAL;
|
||||
}
|
||||
|
||||
devm_gpiod_put(&host->class_dev, ctx->cd_gpio);
|
||||
|
||||
ctx->cd_gpio = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpiod_free_cd);
|
||||
|
@ -263,7 +263,7 @@ config MMC_SDHCI_S3C_DMA
|
||||
|
||||
config MMC_SDHCI_BCM_KONA
|
||||
tristate "SDHCI support on Broadcom KONA platform"
|
||||
depends on ARCH_BCM
|
||||
depends on ARCH_BCM_MOBILE
|
||||
select MMC_SDHCI_PLTFM
|
||||
help
|
||||
This selects the Broadcom Kona Secure Digital Host Controller
|
||||
@ -334,6 +334,19 @@ config MMC_ATMELMCI
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_MSM
|
||||
tristate "Qualcomm SDHCI Controller Support"
|
||||
depends on ARCH_QCOM
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
help
|
||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||
support present in Qualcomm SOCs. The controller supports
|
||||
SD/MMC/SDIO devices.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_MSM
|
||||
tristate "Qualcomm SDCC Controller Support"
|
||||
depends on MMC && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50)
|
||||
@ -580,14 +593,6 @@ config MMC_DW_EXYNOS
|
||||
Synopsys DesignWare Memory Card Interface driver. Select this option
|
||||
for platforms based on Exynos4 and Exynos5 SoC's.
|
||||
|
||||
config MMC_DW_SOCFPGA
|
||||
tristate "SOCFPGA specific extensions for Synopsys DW Memory Card Interface"
|
||||
depends on MMC_DW && MFD_SYSCON
|
||||
select MMC_DW_PLTFM
|
||||
help
|
||||
This selects support for Altera SoCFPGA specific extensions to the
|
||||
Synopsys DesignWare Memory Card Interface driver.
|
||||
|
||||
config MMC_DW_K3
|
||||
tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
|
||||
depends on MMC_DW
|
||||
|
@ -43,7 +43,6 @@ obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
|
||||
obj-$(CONFIG_MMC_DW) += dw_mmc.o
|
||||
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
|
||||
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
|
||||
obj-$(CONFIG_MMC_DW_SOCFPGA) += dw_mmc-socfpga.o
|
||||
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
|
||||
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
|
||||
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
|
||||
@ -64,6 +63,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
|
||||
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
|
||||
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
|
||||
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
|
||||
|
||||
ifeq ($(CONFIG_CB710_DEBUG),y)
|
||||
CFLAGS-cb710-mmc += -DDEBUG
|
||||
|
@ -1192,7 +1192,7 @@ static struct davinci_mmc_config
|
||||
struct device_node *np;
|
||||
struct davinci_mmc_config *pdata = pdev->dev.platform_data;
|
||||
const struct of_device_id *match =
|
||||
of_match_device(of_match_ptr(davinci_mmc_dt_ids), &pdev->dev);
|
||||
of_match_device(davinci_mmc_dt_ids, &pdev->dev);
|
||||
u32 data;
|
||||
|
||||
np = pdev->dev.of_node;
|
||||
@ -1468,7 +1468,7 @@ static struct platform_driver davinci_mmcsd_driver = {
|
||||
.name = "davinci_mmc",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = davinci_mmcsd_pm_ops,
|
||||
.of_match_table = of_match_ptr(davinci_mmc_dt_ids),
|
||||
.of_match_table = davinci_mmc_dt_ids,
|
||||
},
|
||||
.remove = __exit_p(davinci_mmcsd_remove),
|
||||
.id_table = davinci_mmc_devtype,
|
||||
|
@ -50,6 +50,7 @@ static int dw_mci_k3_probe(struct platform_device *pdev)
|
||||
return dw_mci_pltfm_register(pdev, drv_data);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dw_mci_k3_suspend(struct device *dev)
|
||||
{
|
||||
struct dw_mci *host = dev_get_drvdata(dev);
|
||||
@ -75,6 +76,7 @@ static int dw_mci_k3_resume(struct device *dev)
|
||||
|
||||
return dw_mci_resume(host);
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(dw_mci_k3_pmops, dw_mci_k3_suspend, dw_mci_k3_resume);
|
||||
|
||||
|
@ -25,13 +25,17 @@
|
||||
#include "dw_mmc.h"
|
||||
#include "dw_mmc-pltfm.h"
|
||||
|
||||
static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
|
||||
static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr)
|
||||
{
|
||||
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
|
||||
}
|
||||
|
||||
static const struct dw_mci_drv_data rockchip_drv_data = {
|
||||
.prepare_command = dw_mci_rockchip_prepare_command,
|
||||
.prepare_command = dw_mci_pltfm_prepare_command,
|
||||
};
|
||||
|
||||
static const struct dw_mci_drv_data socfpga_drv_data = {
|
||||
.prepare_command = dw_mci_pltfm_prepare_command,
|
||||
};
|
||||
|
||||
int dw_mci_pltfm_register(struct platform_device *pdev,
|
||||
@ -92,6 +96,8 @@ static const struct of_device_id dw_mci_pltfm_match[] = {
|
||||
{ .compatible = "snps,dw-mshc", },
|
||||
{ .compatible = "rockchip,rk2928-dw-mshc",
|
||||
.data = &rockchip_drv_data },
|
||||
{ .compatible = "altr,socfpga-dw-mshc",
|
||||
.data = &socfpga_drv_data },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
|
||||
@ -123,7 +129,7 @@ static struct platform_driver dw_mci_pltfm_driver = {
|
||||
.remove = dw_mci_pltfm_remove,
|
||||
.driver = {
|
||||
.name = "dw_mmc",
|
||||
.of_match_table = of_match_ptr(dw_mci_pltfm_match),
|
||||
.of_match_table = dw_mci_pltfm_match,
|
||||
.pm = &dw_mci_pltfm_pmops,
|
||||
},
|
||||
};
|
||||
|
@ -1,138 +0,0 @@
|
||||
/*
|
||||
* Altera SoCFPGA Specific Extensions for Synopsys DW Multimedia Card Interface
|
||||
* driver
|
||||
*
|
||||
* Copyright (C) 2012, Samsung Electronics Co., Ltd.
|
||||
* Copyright (C) 2013 Altera Corporation
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Taken from dw_mmc-exynos.c
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/dw_mmc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "dw_mmc.h"
|
||||
#include "dw_mmc-pltfm.h"
|
||||
|
||||
#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108
|
||||
#define DRV_CLK_PHASE_SHIFT_SEL_MASK 0x7
|
||||
#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \
|
||||
((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0))
|
||||
|
||||
/* SOCFPGA implementation specific driver private data */
|
||||
struct dw_mci_socfpga_priv_data {
|
||||
u8 ciu_div; /* card interface unit divisor */
|
||||
u32 hs_timing; /* bitmask for CIU clock phase shift */
|
||||
struct regmap *sysreg; /* regmap for system manager register */
|
||||
};
|
||||
|
||||
static int dw_mci_socfpga_priv_init(struct dw_mci *host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_socfpga_setup_clock(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_socfpga_priv_data *priv = host->priv;
|
||||
|
||||
clk_disable_unprepare(host->ciu_clk);
|
||||
regmap_write(priv->sysreg, SYSMGR_SDMMCGRP_CTRL_OFFSET,
|
||||
priv->hs_timing);
|
||||
clk_prepare_enable(host->ciu_clk);
|
||||
|
||||
host->bus_hz /= (priv->ciu_div + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dw_mci_socfpga_prepare_command(struct dw_mci *host, u32 *cmdr)
|
||||
{
|
||||
struct dw_mci_socfpga_priv_data *priv = host->priv;
|
||||
|
||||
if (priv->hs_timing & DRV_CLK_PHASE_SHIFT_SEL_MASK)
|
||||
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
|
||||
}
|
||||
|
||||
static int dw_mci_socfpga_parse_dt(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_socfpga_priv_data *priv;
|
||||
struct device_node *np = host->dev->of_node;
|
||||
u32 timing[2];
|
||||
u32 div = 0;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(host->dev, "mem alloc failed for private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
|
||||
if (IS_ERR(priv->sysreg)) {
|
||||
dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n");
|
||||
return PTR_ERR(priv->sysreg);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "altr,dw-mshc-ciu-div", &div);
|
||||
if (ret)
|
||||
dev_info(host->dev, "No dw-mshc-ciu-div specified, assuming 1");
|
||||
priv->ciu_div = div;
|
||||
|
||||
ret = of_property_read_u32_array(np,
|
||||
"altr,dw-mshc-sdr-timing", timing, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]);
|
||||
host->priv = priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dw_mci_drv_data socfpga_drv_data = {
|
||||
.init = dw_mci_socfpga_priv_init,
|
||||
.setup_clock = dw_mci_socfpga_setup_clock,
|
||||
.prepare_command = dw_mci_socfpga_prepare_command,
|
||||
.parse_dt = dw_mci_socfpga_parse_dt,
|
||||
};
|
||||
|
||||
static const struct of_device_id dw_mci_socfpga_match[] = {
|
||||
{ .compatible = "altr,socfpga-dw-mshc",
|
||||
.data = &socfpga_drv_data, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_mci_socfpga_match);
|
||||
|
||||
static int dw_mci_socfpga_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct dw_mci_drv_data *drv_data;
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_node(dw_mci_socfpga_match, pdev->dev.of_node);
|
||||
drv_data = match->data;
|
||||
return dw_mci_pltfm_register(pdev, drv_data);
|
||||
}
|
||||
|
||||
static struct platform_driver dw_mci_socfpga_pltfm_driver = {
|
||||
.probe = dw_mci_socfpga_probe,
|
||||
.remove = __exit_p(dw_mci_pltfm_remove),
|
||||
.driver = {
|
||||
.name = "dwmmc_socfpga",
|
||||
.of_match_table = dw_mci_socfpga_match,
|
||||
.pm = &dw_mci_pltfm_pmops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dw_mci_socfpga_pltfm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Altera SOCFPGA Specific DW-MSHC Driver Extension");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:dwmmc-socfpga");
|
@ -1345,7 +1345,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
||||
|
||||
if (!err) {
|
||||
if (!data->stop || mrq->sbc) {
|
||||
if (mrq->sbc)
|
||||
if (mrq->sbc && data->stop)
|
||||
data->stop->error = 0;
|
||||
dw_mci_request_end(host, mrq);
|
||||
goto unlock;
|
||||
|
@ -185,7 +185,7 @@
|
||||
|
||||
extern int dw_mci_probe(struct dw_mci *host);
|
||||
extern void dw_mci_remove(struct dw_mci *host);
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
extern int dw_mci_suspend(struct dw_mci *host);
|
||||
extern int dw_mci_resume(struct dw_mci *host);
|
||||
#endif
|
||||
@ -244,6 +244,7 @@ struct dw_mci_tuning_data {
|
||||
* @prepare_command: handle CMD register extensions.
|
||||
* @set_ios: handle bus specific extensions.
|
||||
* @parse_dt: parse implementation specific device tree properties.
|
||||
* @execute_tuning: implementation specific tuning procedure.
|
||||
*
|
||||
* Provide controller implementation specific extensions. The usage of this
|
||||
* data structure is fully optional and usage of each member in this structure
|
||||
|
@ -921,6 +921,29 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
|
||||
{
|
||||
void __iomem *base = host->base;
|
||||
bool sbc = (cmd == host->mrq->sbc);
|
||||
bool busy_resp = host->variant->busy_detect &&
|
||||
(cmd->flags & MMC_RSP_BUSY);
|
||||
|
||||
/* Check if we need to wait for busy completion. */
|
||||
if (host->busy_status && (status & MCI_ST_CARDBUSY))
|
||||
return;
|
||||
|
||||
/* Enable busy completion if needed and supported. */
|
||||
if (!host->busy_status && busy_resp &&
|
||||
!(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
|
||||
(readl(base + MMCISTATUS) & MCI_ST_CARDBUSY)) {
|
||||
writel(readl(base + MMCIMASK0) | MCI_ST_BUSYEND,
|
||||
base + MMCIMASK0);
|
||||
host->busy_status = status & (MCI_CMDSENT|MCI_CMDRESPEND);
|
||||
return;
|
||||
}
|
||||
|
||||
/* At busy completion, mask the IRQ and complete the request. */
|
||||
if (host->busy_status) {
|
||||
writel(readl(base + MMCIMASK0) & ~MCI_ST_BUSYEND,
|
||||
base + MMCIMASK0);
|
||||
host->busy_status = 0;
|
||||
}
|
||||
|
||||
host->cmd = NULL;
|
||||
|
||||
@ -1139,20 +1162,30 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
|
||||
status &= ~MCI_IRQ1MASK;
|
||||
}
|
||||
|
||||
/*
|
||||
* We intentionally clear the MCI_ST_CARDBUSY IRQ here (if it's
|
||||
* enabled) since the HW seems to be triggering the IRQ on both
|
||||
* edges while monitoring DAT0 for busy completion.
|
||||
*/
|
||||
status &= readl(host->base + MMCIMASK0);
|
||||
writel(status, host->base + MMCICLEAR);
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);
|
||||
|
||||
cmd = host->cmd;
|
||||
if ((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|
|
||||
MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
|
||||
mmci_cmd_irq(host, cmd, status);
|
||||
|
||||
data = host->data;
|
||||
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR|
|
||||
MCI_TXUNDERRUN|MCI_RXOVERRUN|MCI_DATAEND|
|
||||
MCI_DATABLOCKEND) && data)
|
||||
mmci_data_irq(host, data, status);
|
||||
|
||||
cmd = host->cmd;
|
||||
if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
|
||||
mmci_cmd_irq(host, cmd, status);
|
||||
/* Don't poll for busy completion in irq context. */
|
||||
if (host->busy_status)
|
||||
status &= ~MCI_ST_CARDBUSY;
|
||||
|
||||
ret = 1;
|
||||
} while (status);
|
||||
@ -1503,12 +1536,6 @@ static int mmci_probe(struct amba_device *dev,
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
if (variant->busy_detect) {
|
||||
mmci_ops.card_busy = mmci_card_busy;
|
||||
mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
|
||||
}
|
||||
|
||||
mmc->ops = &mmci_ops;
|
||||
/*
|
||||
* The ARM and ST versions of the block have slightly different
|
||||
* clock divider equations which means that the minimum divider
|
||||
@ -1542,6 +1569,15 @@ static int mmci_probe(struct amba_device *dev,
|
||||
mmc->caps = plat->capabilities;
|
||||
mmc->caps2 = plat->capabilities2;
|
||||
|
||||
if (variant->busy_detect) {
|
||||
mmci_ops.card_busy = mmci_card_busy;
|
||||
mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
|
||||
mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
|
||||
mmc->max_busy_timeout = 0;
|
||||
}
|
||||
|
||||
mmc->ops = &mmci_ops;
|
||||
|
||||
/* We support these PM capabilities. */
|
||||
mmc->pm_caps = MMC_PM_KEEP_POWER;
|
||||
|
||||
|
@ -140,6 +140,7 @@
|
||||
/* Extended status bits for the ST Micro variants */
|
||||
#define MCI_ST_SDIOITMASK (1 << 22)
|
||||
#define MCI_ST_CEATAENDMASK (1 << 23)
|
||||
#define MCI_ST_BUSYEND (1 << 24)
|
||||
|
||||
#define MMCIMASK1 0x040
|
||||
#define MMCIFIFOCNT 0x048
|
||||
@ -187,6 +188,7 @@ struct mmci_host {
|
||||
u32 pwr_reg;
|
||||
u32 clk_reg;
|
||||
u32 datactrl_reg;
|
||||
u32 busy_status;
|
||||
bool vqmmc_enabled;
|
||||
struct mmci_platform_data *plat;
|
||||
struct variant_data *variant;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/omap-dma.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
@ -130,7 +131,6 @@ struct mmc_omap_host {
|
||||
u32 dma_rx_burst;
|
||||
struct dma_chan *dma_tx;
|
||||
u32 dma_tx_burst;
|
||||
struct resource *mem_res;
|
||||
void __iomem *virt_base;
|
||||
unsigned int phys_base;
|
||||
int irq;
|
||||
@ -153,7 +153,6 @@ struct mmc_omap_host {
|
||||
u32 total_bytes_left;
|
||||
|
||||
unsigned features;
|
||||
unsigned use_dma:1;
|
||||
unsigned brs_received:1, dma_done:1;
|
||||
unsigned dma_in_use:1;
|
||||
spinlock_t dma_lock;
|
||||
@ -338,6 +337,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
|
||||
u32 cmdreg;
|
||||
u32 resptype;
|
||||
u32 cmdtype;
|
||||
u16 irq_mask;
|
||||
|
||||
host->cmd = cmd;
|
||||
|
||||
@ -390,12 +390,14 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
|
||||
OMAP_MMC_WRITE(host, CTO, 200);
|
||||
OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
|
||||
OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16);
|
||||
OMAP_MMC_WRITE(host, IE,
|
||||
OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL |
|
||||
OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT |
|
||||
OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT |
|
||||
OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR |
|
||||
OMAP_MMC_STAT_END_OF_DATA);
|
||||
irq_mask = OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL |
|
||||
OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT |
|
||||
OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT |
|
||||
OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR |
|
||||
OMAP_MMC_STAT_END_OF_DATA;
|
||||
if (cmd->opcode == MMC_ERASE)
|
||||
irq_mask &= ~OMAP_MMC_STAT_DATA_TOUT;
|
||||
OMAP_MMC_WRITE(host, IE, irq_mask);
|
||||
OMAP_MMC_WRITE(host, CMD, cmdreg);
|
||||
}
|
||||
|
||||
@ -945,7 +947,7 @@ static void
|
||||
mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
|
||||
{
|
||||
struct mmc_data *data = req->data;
|
||||
int i, use_dma, block_size;
|
||||
int i, use_dma = 1, block_size;
|
||||
unsigned sg_len;
|
||||
|
||||
host->data = data;
|
||||
@ -970,13 +972,10 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
|
||||
sg_len = (data->blocks == 1) ? 1 : data->sg_len;
|
||||
|
||||
/* Only do DMA for entire blocks */
|
||||
use_dma = host->use_dma;
|
||||
if (use_dma) {
|
||||
for (i = 0; i < sg_len; i++) {
|
||||
if ((data->sg[i].length % block_size) != 0) {
|
||||
use_dma = 0;
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < sg_len; i++) {
|
||||
if ((data->sg[i].length % block_size) != 0) {
|
||||
use_dma = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1239,7 +1238,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
|
||||
|
||||
mmc->caps = 0;
|
||||
if (host->pdata->slots[id].wires >= 4)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_ERASE;
|
||||
|
||||
mmc->ops = &mmc_omap_ops;
|
||||
mmc->f_min = 400000;
|
||||
@ -1262,6 +1261,13 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
mmc->max_seg_size = mmc->max_req_size;
|
||||
|
||||
if (slot->pdata->get_cover_state != NULL) {
|
||||
setup_timer(&slot->cover_timer, mmc_omap_cover_timer,
|
||||
(unsigned long)slot);
|
||||
tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,
|
||||
(unsigned long)slot);
|
||||
}
|
||||
|
||||
r = mmc_add_host(mmc);
|
||||
if (r < 0)
|
||||
goto err_remove_host;
|
||||
@ -1278,11 +1284,6 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
|
||||
&dev_attr_cover_switch);
|
||||
if (r < 0)
|
||||
goto err_remove_slot_name;
|
||||
|
||||
setup_timer(&slot->cover_timer, mmc_omap_cover_timer,
|
||||
(unsigned long)slot);
|
||||
tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,
|
||||
(unsigned long)slot);
|
||||
tasklet_schedule(&slot->cover_tasklet);
|
||||
}
|
||||
|
||||
@ -1333,21 +1334,19 @@ static int mmc_omap_probe(struct platform_device *pdev)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host = devm_kzalloc(&pdev->dev, sizeof(struct mmc_omap_host),
|
||||
GFP_KERNEL);
|
||||
if (host == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (res == NULL || irq < 0)
|
||||
if (irq < 0)
|
||||
return -ENXIO;
|
||||
|
||||
res = request_mem_region(res->start, resource_size(res),
|
||||
pdev->name);
|
||||
if (res == NULL)
|
||||
return -EBUSY;
|
||||
|
||||
host = kzalloc(sizeof(struct mmc_omap_host), GFP_KERNEL);
|
||||
if (host == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_mem_region;
|
||||
}
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->virt_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(host->virt_base))
|
||||
return PTR_ERR(host->virt_base);
|
||||
|
||||
INIT_WORK(&host->slot_release_work, mmc_omap_slot_release_work);
|
||||
INIT_WORK(&host->send_stop_work, mmc_omap_send_stop_work);
|
||||
@ -1369,20 +1368,11 @@ static int mmc_omap_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
host->id = pdev->id;
|
||||
host->mem_res = res;
|
||||
host->irq = irq;
|
||||
host->use_dma = 1;
|
||||
host->irq = irq;
|
||||
host->phys_base = host->mem_res->start;
|
||||
host->virt_base = ioremap(res->start, resource_size(res));
|
||||
if (!host->virt_base)
|
||||
goto err_ioremap;
|
||||
|
||||
host->phys_base = res->start;
|
||||
host->iclk = clk_get(&pdev->dev, "ick");
|
||||
if (IS_ERR(host->iclk)) {
|
||||
ret = PTR_ERR(host->iclk);
|
||||
goto err_free_mmc_host;
|
||||
}
|
||||
if (IS_ERR(host->iclk))
|
||||
return PTR_ERR(host->iclk);
|
||||
clk_enable(host->iclk);
|
||||
|
||||
host->fclk = clk_get(&pdev->dev, "fck");
|
||||
@ -1460,12 +1450,6 @@ err_free_dma:
|
||||
err_free_iclk:
|
||||
clk_disable(host->iclk);
|
||||
clk_put(host->iclk);
|
||||
err_free_mmc_host:
|
||||
iounmap(host->virt_base);
|
||||
err_ioremap:
|
||||
kfree(host);
|
||||
err_free_mem_region:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1493,13 +1477,8 @@ static int mmc_omap_remove(struct platform_device *pdev)
|
||||
if (host->dma_rx)
|
||||
dma_release_channel(host->dma_rx);
|
||||
|
||||
iounmap(host->virt_base);
|
||||
release_mem_region(pdev->resource[0].start,
|
||||
pdev->resource[0].end - pdev->resource[0].start + 1);
|
||||
destroy_workqueue(host->mmc_omap_wq);
|
||||
|
||||
kfree(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,7 @@
|
||||
/* OMAP HSMMC Host Controller Registers */
|
||||
#define OMAP_HSMMC_SYSSTATUS 0x0014
|
||||
#define OMAP_HSMMC_CON 0x002C
|
||||
#define OMAP_HSMMC_SDMASA 0x0100
|
||||
#define OMAP_HSMMC_BLK 0x0104
|
||||
#define OMAP_HSMMC_ARG 0x0108
|
||||
#define OMAP_HSMMC_CMD 0x010C
|
||||
@ -58,6 +59,7 @@
|
||||
#define OMAP_HSMMC_STAT 0x0130
|
||||
#define OMAP_HSMMC_IE 0x0134
|
||||
#define OMAP_HSMMC_ISE 0x0138
|
||||
#define OMAP_HSMMC_AC12 0x013C
|
||||
#define OMAP_HSMMC_CAPA 0x0140
|
||||
|
||||
#define VS18 (1 << 26)
|
||||
@ -81,6 +83,7 @@
|
||||
#define DTO_MASK 0x000F0000
|
||||
#define DTO_SHIFT 16
|
||||
#define INIT_STREAM (1 << 1)
|
||||
#define ACEN_ACMD23 (2 << 2)
|
||||
#define DP_SELECT (1 << 21)
|
||||
#define DDIR (1 << 4)
|
||||
#define DMAE 0x1
|
||||
@ -97,7 +100,6 @@
|
||||
#define SRC (1 << 25)
|
||||
#define SRD (1 << 26)
|
||||
#define SOFTRESET (1 << 1)
|
||||
#define RESETDONE (1 << 0)
|
||||
|
||||
/* Interrupt masks for IE and ISE register */
|
||||
#define CC_EN (1 << 0)
|
||||
@ -112,13 +114,21 @@
|
||||
#define DTO_EN (1 << 20)
|
||||
#define DCRC_EN (1 << 21)
|
||||
#define DEB_EN (1 << 22)
|
||||
#define ACE_EN (1 << 24)
|
||||
#define CERR_EN (1 << 28)
|
||||
#define BADA_EN (1 << 29)
|
||||
|
||||
#define INT_EN_MASK (BADA_EN | CERR_EN | DEB_EN | DCRC_EN |\
|
||||
#define INT_EN_MASK (BADA_EN | CERR_EN | ACE_EN | DEB_EN | DCRC_EN |\
|
||||
DTO_EN | CIE_EN | CEB_EN | CCRC_EN | CTO_EN | \
|
||||
BRR_EN | BWR_EN | TC_EN | CC_EN)
|
||||
|
||||
#define CNI (1 << 7)
|
||||
#define ACIE (1 << 4)
|
||||
#define ACEB (1 << 3)
|
||||
#define ACCE (1 << 2)
|
||||
#define ACTO (1 << 1)
|
||||
#define ACNE (1 << 0)
|
||||
|
||||
#define MMC_AUTOSUSPEND_DELAY 100
|
||||
#define MMC_TIMEOUT_MS 20 /* 20 mSec */
|
||||
#define MMC_TIMEOUT_US 20000 /* 20000 micro Sec */
|
||||
@ -126,6 +136,11 @@
|
||||
#define OMAP_MMC_MAX_CLOCK 52000000
|
||||
#define DRIVER_NAME "omap_hsmmc"
|
||||
|
||||
#define VDD_1V8 1800000 /* 180000 uV */
|
||||
#define VDD_3V0 3000000 /* 300000 uV */
|
||||
#define VDD_165_195 (ffs(MMC_VDD_165_195) - 1)
|
||||
|
||||
#define AUTO_CMD23 (1 << 1) /* Auto CMD23 support */
|
||||
/*
|
||||
* One controller can have multiple slots, like on some omap boards using
|
||||
* omap.c controller driver. Luckily this is not currently done on any known
|
||||
@ -164,7 +179,8 @@ struct omap_hsmmc_host {
|
||||
*/
|
||||
struct regulator *vcc;
|
||||
struct regulator *vcc_aux;
|
||||
int pbias_disable;
|
||||
struct regulator *pbias;
|
||||
bool pbias_enabled;
|
||||
void __iomem *base;
|
||||
resource_size_t mapbase;
|
||||
spinlock_t irq_lock; /* Prevent races with irq handler */
|
||||
@ -188,10 +204,19 @@ struct omap_hsmmc_host {
|
||||
int reqs_blocked;
|
||||
int use_reg;
|
||||
int req_in_progress;
|
||||
unsigned long clk_rate;
|
||||
unsigned int flags;
|
||||
struct omap_hsmmc_next next_data;
|
||||
struct omap_mmc_platform_data *pdata;
|
||||
};
|
||||
|
||||
struct omap_mmc_of_data {
|
||||
u32 reg_offset;
|
||||
u8 controller_flags;
|
||||
};
|
||||
|
||||
static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host);
|
||||
|
||||
static int omap_hsmmc_card_detect(struct device *dev, int slot)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||
@ -261,17 +286,19 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
|
||||
*/
|
||||
if (!host->vcc)
|
||||
return 0;
|
||||
/*
|
||||
* With DT, never turn OFF the regulator for MMC1. This is because
|
||||
* the pbias cell programming support is still missing when
|
||||
* booting with Device tree
|
||||
*/
|
||||
if (host->pbias_disable && !vdd)
|
||||
return 0;
|
||||
|
||||
if (mmc_slot(host).before_set_reg)
|
||||
mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
|
||||
|
||||
if (host->pbias) {
|
||||
if (host->pbias_enabled == 1) {
|
||||
ret = regulator_disable(host->pbias);
|
||||
if (!ret)
|
||||
host->pbias_enabled = 0;
|
||||
}
|
||||
regulator_set_voltage(host->pbias, VDD_3V0, VDD_3V0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Assume Vcc regulator is used only to power the card ... OMAP
|
||||
* VDDS is used to power the pins, optionally with a transceiver to
|
||||
@ -286,11 +313,12 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
|
||||
* chips/cards need an interface voltage rail too.
|
||||
*/
|
||||
if (power_on) {
|
||||
ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
|
||||
if (host->vcc)
|
||||
ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
|
||||
/* Enable interface voltage rail, if needed */
|
||||
if (ret == 0 && host->vcc_aux) {
|
||||
ret = regulator_enable(host->vcc_aux);
|
||||
if (ret < 0)
|
||||
if (ret < 0 && host->vcc)
|
||||
ret = mmc_regulator_set_ocr(host->mmc,
|
||||
host->vcc, 0);
|
||||
}
|
||||
@ -298,16 +326,34 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
|
||||
/* Shut down the rail */
|
||||
if (host->vcc_aux)
|
||||
ret = regulator_disable(host->vcc_aux);
|
||||
if (!ret) {
|
||||
if (host->vcc) {
|
||||
/* Then proceed to shut down the local regulator */
|
||||
ret = mmc_regulator_set_ocr(host->mmc,
|
||||
host->vcc, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (host->pbias) {
|
||||
if (vdd <= VDD_165_195)
|
||||
ret = regulator_set_voltage(host->pbias, VDD_1V8,
|
||||
VDD_1V8);
|
||||
else
|
||||
ret = regulator_set_voltage(host->pbias, VDD_3V0,
|
||||
VDD_3V0);
|
||||
if (ret < 0)
|
||||
goto error_set_power;
|
||||
|
||||
if (host->pbias_enabled == 0) {
|
||||
ret = regulator_enable(host->pbias);
|
||||
if (!ret)
|
||||
host->pbias_enabled = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (mmc_slot(host).after_set_reg)
|
||||
mmc_slot(host).after_set_reg(dev, slot, power_on, vdd);
|
||||
|
||||
error_set_power:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -316,12 +362,12 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
|
||||
struct regulator *reg;
|
||||
int ocr_value = 0;
|
||||
|
||||
reg = regulator_get(host->dev, "vmmc");
|
||||
reg = devm_regulator_get(host->dev, "vmmc");
|
||||
if (IS_ERR(reg)) {
|
||||
dev_err(host->dev, "vmmc regulator missing\n");
|
||||
dev_err(host->dev, "unable to get vmmc regulator %ld\n",
|
||||
PTR_ERR(reg));
|
||||
return PTR_ERR(reg);
|
||||
} else {
|
||||
mmc_slot(host).set_power = omap_hsmmc_set_power;
|
||||
host->vcc = reg;
|
||||
ocr_value = mmc_regulator_get_ocrmask(reg);
|
||||
if (!mmc_slot(host).ocr_mask) {
|
||||
@ -334,31 +380,29 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
mmc_slot(host).set_power = omap_hsmmc_set_power;
|
||||
|
||||
/* Allow an aux regulator */
|
||||
reg = regulator_get(host->dev, "vmmc_aux");
|
||||
host->vcc_aux = IS_ERR(reg) ? NULL : reg;
|
||||
/* Allow an aux regulator */
|
||||
reg = devm_regulator_get_optional(host->dev, "vmmc_aux");
|
||||
host->vcc_aux = IS_ERR(reg) ? NULL : reg;
|
||||
|
||||
/* For eMMC do not power off when not in sleep state */
|
||||
if (mmc_slot(host).no_regulator_off_init)
|
||||
return 0;
|
||||
/*
|
||||
* UGLY HACK: workaround regulator framework bugs.
|
||||
* When the bootloader leaves a supply active, it's
|
||||
* initialized with zero usecount ... and we can't
|
||||
* disable it without first enabling it. Until the
|
||||
* framework is fixed, we need a workaround like this
|
||||
* (which is safe for MMC, but not in general).
|
||||
*/
|
||||
if (regulator_is_enabled(host->vcc) > 0 ||
|
||||
(host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
|
||||
int vdd = ffs(mmc_slot(host).ocr_mask) - 1;
|
||||
reg = devm_regulator_get_optional(host->dev, "pbias");
|
||||
host->pbias = IS_ERR(reg) ? NULL : reg;
|
||||
|
||||
mmc_slot(host).set_power(host->dev, host->slot_id,
|
||||
1, vdd);
|
||||
mmc_slot(host).set_power(host->dev, host->slot_id,
|
||||
0, 0);
|
||||
}
|
||||
/* For eMMC do not power off when not in sleep state */
|
||||
if (mmc_slot(host).no_regulator_off_init)
|
||||
return 0;
|
||||
/*
|
||||
* To disable boot_on regulator, enable regulator
|
||||
* to increase usecount and then disable it.
|
||||
*/
|
||||
if ((host->vcc && regulator_is_enabled(host->vcc) > 0) ||
|
||||
(host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
|
||||
int vdd = ffs(mmc_slot(host).ocr_mask) - 1;
|
||||
|
||||
mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd);
|
||||
mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -366,8 +410,6 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
|
||||
|
||||
static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)
|
||||
{
|
||||
regulator_put(host->vcc);
|
||||
regulator_put(host->vcc_aux);
|
||||
mmc_slot(host).set_power = NULL;
|
||||
}
|
||||
|
||||
@ -605,9 +647,6 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
|
||||
u32 hctl, capa;
|
||||
unsigned long timeout;
|
||||
|
||||
if (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE)
|
||||
return 1;
|
||||
|
||||
if (host->con == OMAP_HSMMC_READ(host->base, CON) &&
|
||||
host->hctl == OMAP_HSMMC_READ(host->base, HCTL) &&
|
||||
host->sysctl == OMAP_HSMMC_READ(host->base, SYSCTL) &&
|
||||
@ -787,6 +826,11 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
|
||||
|
||||
cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22);
|
||||
|
||||
if ((host->flags & AUTO_CMD23) && mmc_op_multi(cmd->opcode) &&
|
||||
host->mrq->sbc) {
|
||||
cmdreg |= ACEN_ACMD23;
|
||||
OMAP_HSMMC_WRITE(host->base, SDMASA, host->mrq->sbc->arg);
|
||||
}
|
||||
if (data) {
|
||||
cmdreg |= DP_SELECT | MSBS | BCE;
|
||||
if (data->flags & MMC_DATA_READ)
|
||||
@ -864,11 +908,10 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
|
||||
else
|
||||
data->bytes_xfered = 0;
|
||||
|
||||
if (!data->stop) {
|
||||
if (data->stop && (data->error || !host->mrq->sbc))
|
||||
omap_hsmmc_start_command(host, data->stop, NULL);
|
||||
else
|
||||
omap_hsmmc_request_done(host, data->mrq);
|
||||
return;
|
||||
}
|
||||
omap_hsmmc_start_command(host, data->stop, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -879,6 +922,14 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
|
||||
{
|
||||
host->cmd = NULL;
|
||||
|
||||
if (host->mrq->sbc && (host->cmd == host->mrq->sbc) &&
|
||||
!host->mrq->sbc->error && !(host->flags & AUTO_CMD23)) {
|
||||
omap_hsmmc_start_dma_transfer(host);
|
||||
omap_hsmmc_start_command(host, host->mrq->cmd,
|
||||
host->mrq->data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd->flags & MMC_RSP_PRESENT) {
|
||||
if (cmd->flags & MMC_RSP_136) {
|
||||
/* response type 2 */
|
||||
@ -892,7 +943,7 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
|
||||
}
|
||||
}
|
||||
if ((host->data == NULL && !host->response_busy) || cmd->error)
|
||||
omap_hsmmc_request_done(host, cmd->mrq);
|
||||
omap_hsmmc_request_done(host, host->mrq);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1015,6 +1066,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
|
||||
{
|
||||
struct mmc_data *data;
|
||||
int end_cmd = 0, end_trans = 0;
|
||||
int error = 0;
|
||||
|
||||
data = host->data;
|
||||
dev_vdbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
|
||||
@ -1029,6 +1081,20 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
|
||||
else if (status & (CCRC_EN | DCRC_EN))
|
||||
hsmmc_command_incomplete(host, -EILSEQ, end_cmd);
|
||||
|
||||
if (status & ACE_EN) {
|
||||
u32 ac12;
|
||||
ac12 = OMAP_HSMMC_READ(host->base, AC12);
|
||||
if (!(ac12 & ACNE) && host->mrq->sbc) {
|
||||
end_cmd = 1;
|
||||
if (ac12 & ACTO)
|
||||
error = -ETIMEDOUT;
|
||||
else if (ac12 & (ACCE | ACEB | ACIE))
|
||||
error = -EILSEQ;
|
||||
host->mrq->sbc->error = error;
|
||||
hsmmc_command_incomplete(host, error, end_cmd);
|
||||
}
|
||||
dev_dbg(mmc_dev(host->mmc), "AC12 err: 0x%x\n", ac12);
|
||||
}
|
||||
if (host->data || host->response_busy) {
|
||||
end_trans = !end_cmd;
|
||||
host->response_busy = 0;
|
||||
@ -1236,8 +1302,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
|
||||
}
|
||||
|
||||
/* Check if next job is already prepared */
|
||||
if (next ||
|
||||
(!next && data->host_cookie != host->next_data.cookie)) {
|
||||
if (next || data->host_cookie != host->next_data.cookie) {
|
||||
dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
|
||||
omap_hsmmc_get_dma_dir(host, data));
|
||||
|
||||
@ -1262,7 +1327,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
|
||||
/*
|
||||
* Routine to configure and start DMA for the MMC card
|
||||
*/
|
||||
static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
|
||||
static int omap_hsmmc_setup_dma_transfer(struct omap_hsmmc_host *host,
|
||||
struct mmc_request *req)
|
||||
{
|
||||
struct dma_slave_config cfg;
|
||||
@ -1321,8 +1386,6 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
|
||||
|
||||
host->dma_ch = 1;
|
||||
|
||||
dma_async_issue_pending(chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1338,7 +1401,7 @@ static void set_data_timeout(struct omap_hsmmc_host *host,
|
||||
if (clkd == 0)
|
||||
clkd = 1;
|
||||
|
||||
cycle_ns = 1000000000 / (clk_get_rate(host->fclk) / clkd);
|
||||
cycle_ns = 1000000000 / (host->clk_rate / clkd);
|
||||
timeout = timeout_ns / cycle_ns;
|
||||
timeout += timeout_clks;
|
||||
if (timeout) {
|
||||
@ -1363,6 +1426,21 @@ static void set_data_timeout(struct omap_hsmmc_host *host,
|
||||
OMAP_HSMMC_WRITE(host->base, SYSCTL, reg);
|
||||
}
|
||||
|
||||
static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host)
|
||||
{
|
||||
struct mmc_request *req = host->mrq;
|
||||
struct dma_chan *chan;
|
||||
|
||||
if (!req->data)
|
||||
return;
|
||||
OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz)
|
||||
| (req->data->blocks << 16));
|
||||
set_data_timeout(host, req->data->timeout_ns,
|
||||
req->data->timeout_clks);
|
||||
chan = omap_hsmmc_get_dma_chan(host, req->data);
|
||||
dma_async_issue_pending(chan);
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure block length for MMC/SD cards and initiate the transfer.
|
||||
*/
|
||||
@ -1383,12 +1461,8 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
|
||||
return 0;
|
||||
}
|
||||
|
||||
OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz)
|
||||
| (req->data->blocks << 16));
|
||||
set_data_timeout(host, req->data->timeout_ns, req->data->timeout_clks);
|
||||
|
||||
if (host->use_dma) {
|
||||
ret = omap_hsmmc_start_dma_transfer(host, req);
|
||||
ret = omap_hsmmc_setup_dma_transfer(host, req);
|
||||
if (ret != 0) {
|
||||
dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");
|
||||
return ret;
|
||||
@ -1462,6 +1536,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
|
||||
host->reqs_blocked = 0;
|
||||
WARN_ON(host->mrq != NULL);
|
||||
host->mrq = req;
|
||||
host->clk_rate = clk_get_rate(host->fclk);
|
||||
err = omap_hsmmc_prepare_data(host, req);
|
||||
if (err) {
|
||||
req->cmd->error = err;
|
||||
@ -1471,7 +1546,12 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
|
||||
mmc_request_done(mmc, req);
|
||||
return;
|
||||
}
|
||||
if (req->sbc && !(host->flags & AUTO_CMD23)) {
|
||||
omap_hsmmc_start_command(host, req->sbc, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
omap_hsmmc_start_dma_transfer(host);
|
||||
omap_hsmmc_start_command(host, req->cmd, req->data);
|
||||
}
|
||||
|
||||
@ -1509,13 +1589,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
* of external transceiver; but they all handle 1.8V.
|
||||
*/
|
||||
if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) &&
|
||||
(ios->vdd == DUAL_VOLT_OCR_BIT) &&
|
||||
/*
|
||||
* With pbias cell programming missing, this
|
||||
* can't be allowed on MMC1 when booting with device
|
||||
* tree.
|
||||
*/
|
||||
!host->pbias_disable) {
|
||||
(ios->vdd == DUAL_VOLT_OCR_BIT)) {
|
||||
/*
|
||||
* The mmc_select_voltage fn of the core does
|
||||
* not seem to set the power_mode to
|
||||
@ -1678,18 +1752,29 @@ static void omap_hsmmc_debugfs(struct mmc_host *mmc)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static u16 omap4_reg_offset = 0x100;
|
||||
static const struct omap_mmc_of_data omap3_pre_es3_mmc_of_data = {
|
||||
/* See 35xx errata 2.1.1.128 in SPRZ278F */
|
||||
.controller_flags = OMAP_HSMMC_BROKEN_MULTIBLOCK_READ,
|
||||
};
|
||||
|
||||
static const struct omap_mmc_of_data omap4_mmc_of_data = {
|
||||
.reg_offset = 0x100,
|
||||
};
|
||||
|
||||
static const struct of_device_id omap_mmc_of_match[] = {
|
||||
{
|
||||
.compatible = "ti,omap2-hsmmc",
|
||||
},
|
||||
{
|
||||
.compatible = "ti,omap3-pre-es3-hsmmc",
|
||||
.data = &omap3_pre_es3_mmc_of_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,omap3-hsmmc",
|
||||
},
|
||||
{
|
||||
.compatible = "ti,omap4-hsmmc",
|
||||
.data = &omap4_reg_offset,
|
||||
.data = &omap4_mmc_of_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
@ -1709,7 +1794,7 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return NULL; /* out of memory */
|
||||
return ERR_PTR(-ENOMEM); /* out of memory */
|
||||
|
||||
if (of_find_property(np, "ti,dual-volt", NULL))
|
||||
pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
|
||||
@ -1738,13 +1823,19 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
|
||||
if (of_find_property(np, "ti,needs-special-hs-handling", NULL))
|
||||
pdata->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT;
|
||||
|
||||
if (of_find_property(np, "keep-power-in-suspend", NULL))
|
||||
pdata->slots[0].pm_caps |= MMC_PM_KEEP_POWER;
|
||||
|
||||
if (of_find_property(np, "enable-sdio-wakeup", NULL))
|
||||
pdata->slots[0].pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
static inline struct omap_mmc_platform_data
|
||||
*of_get_hsmmc_pdata(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1759,6 +1850,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||
dma_cap_mask_t mask;
|
||||
unsigned tx_req, rx_req;
|
||||
struct pinctrl *pinctrl;
|
||||
const struct omap_mmc_of_data *data;
|
||||
|
||||
match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
|
||||
if (match) {
|
||||
@ -1768,8 +1860,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(pdata);
|
||||
|
||||
if (match->data) {
|
||||
const u16 *offsetp = match->data;
|
||||
pdata->reg_offset = *offsetp;
|
||||
data = match->data;
|
||||
pdata->reg_offset = data->reg_offset;
|
||||
pdata->controller_flags |= data->controller_flags;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1814,6 +1907,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||
host->base = ioremap(host->mapbase, SZ_4K);
|
||||
host->power_mode = MMC_POWER_OFF;
|
||||
host->next_data.cookie = 1;
|
||||
host->pbias_enabled = 0;
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
@ -1847,10 +1941,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||
|
||||
omap_hsmmc_context_save(host);
|
||||
|
||||
/* This can be removed once we support PBIAS with DT */
|
||||
if (host->dev->of_node && res->start == 0x4809c000)
|
||||
host->pbias_disable = 1;
|
||||
|
||||
host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
|
||||
/*
|
||||
* MMC can still work without debounce clock.
|
||||
|
@ -31,14 +31,9 @@
|
||||
#include <linux/mfd/rtsx_pci.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/* SD Tuning Data Structure
|
||||
* Record continuous timing phase path
|
||||
*/
|
||||
struct timing_phase_path {
|
||||
int start;
|
||||
int end;
|
||||
int mid;
|
||||
int len;
|
||||
struct realtek_next {
|
||||
unsigned int sg_count;
|
||||
s32 cookie;
|
||||
};
|
||||
|
||||
struct realtek_pci_sdmmc {
|
||||
@ -46,9 +41,18 @@ struct realtek_pci_sdmmc {
|
||||
struct rtsx_pcr *pcr;
|
||||
struct mmc_host *mmc;
|
||||
struct mmc_request *mrq;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
|
||||
struct mutex host_mutex;
|
||||
spinlock_t lock;
|
||||
struct timer_list timer;
|
||||
struct tasklet_struct cmd_tasklet;
|
||||
struct tasklet_struct data_tasklet;
|
||||
struct tasklet_struct finish_tasklet;
|
||||
|
||||
u8 rsp_type;
|
||||
u8 rsp_len;
|
||||
int sg_count;
|
||||
u8 ssc_depth;
|
||||
unsigned int clock;
|
||||
bool vpclk;
|
||||
@ -58,8 +62,13 @@ struct realtek_pci_sdmmc {
|
||||
int power_state;
|
||||
#define SDMMC_POWER_ON 1
|
||||
#define SDMMC_POWER_OFF 0
|
||||
|
||||
struct realtek_next next_data;
|
||||
};
|
||||
|
||||
static int sd_start_multi_rw(struct realtek_pci_sdmmc *host,
|
||||
struct mmc_request *mrq);
|
||||
|
||||
static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host)
|
||||
{
|
||||
return &(host->pdev->dev);
|
||||
@ -96,6 +105,95 @@ static void sd_print_debug_regs(struct realtek_pci_sdmmc *host)
|
||||
#define sd_print_debug_regs(host)
|
||||
#endif /* DEBUG */
|
||||
|
||||
static void sd_isr_done_transfer(struct platform_device *pdev)
|
||||
{
|
||||
struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
|
||||
|
||||
spin_lock(&host->lock);
|
||||
if (host->cmd)
|
||||
tasklet_schedule(&host->cmd_tasklet);
|
||||
if (host->data)
|
||||
tasklet_schedule(&host->data_tasklet);
|
||||
spin_unlock(&host->lock);
|
||||
}
|
||||
|
||||
static void sd_request_timeout(unsigned long host_addr)
|
||||
{
|
||||
struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
if (!host->mrq) {
|
||||
dev_err(sdmmc_dev(host), "error: no request exist\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (host->cmd)
|
||||
host->cmd->error = -ETIMEDOUT;
|
||||
if (host->data)
|
||||
host->data->error = -ETIMEDOUT;
|
||||
|
||||
dev_dbg(sdmmc_dev(host), "timeout for request\n");
|
||||
|
||||
out:
|
||||
tasklet_schedule(&host->finish_tasklet);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
static void sd_finish_request(unsigned long host_addr)
|
||||
{
|
||||
struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr;
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
struct mmc_request *mrq;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
unsigned long flags;
|
||||
bool any_error;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
del_timer(&host->timer);
|
||||
mrq = host->mrq;
|
||||
if (!mrq) {
|
||||
dev_err(sdmmc_dev(host), "error: no request need finish\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
cmd = mrq->cmd;
|
||||
data = mrq->data;
|
||||
|
||||
any_error = (mrq->sbc && mrq->sbc->error) ||
|
||||
(mrq->stop && mrq->stop->error) ||
|
||||
(cmd && cmd->error) || (data && data->error);
|
||||
|
||||
if (any_error) {
|
||||
rtsx_pci_stop_cmd(pcr);
|
||||
sd_clear_error(host);
|
||||
}
|
||||
|
||||
if (data) {
|
||||
if (any_error)
|
||||
data->bytes_xfered = 0;
|
||||
else
|
||||
data->bytes_xfered = data->blocks * data->blksz;
|
||||
|
||||
if (!data->host_cookie)
|
||||
rtsx_pci_dma_unmap_sg(pcr, data->sg, data->sg_len,
|
||||
data->flags & MMC_DATA_READ);
|
||||
|
||||
}
|
||||
|
||||
host->mrq = NULL;
|
||||
host->cmd = NULL;
|
||||
host->data = NULL;
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
mutex_unlock(&pcr->pcr_mutex);
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
}
|
||||
|
||||
static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,
|
||||
u8 *buf, int buf_len, int timeout)
|
||||
{
|
||||
@ -213,8 +311,7 @@ static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
|
||||
struct mmc_command *cmd)
|
||||
static void sd_send_cmd(struct realtek_pci_sdmmc *host, struct mmc_command *cmd)
|
||||
{
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
u8 cmd_idx = (u8)cmd->opcode;
|
||||
@ -222,11 +319,14 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
|
||||
int err = 0;
|
||||
int timeout = 100;
|
||||
int i;
|
||||
u8 *ptr;
|
||||
int stat_idx = 0;
|
||||
u8 rsp_type;
|
||||
int rsp_len = 5;
|
||||
bool clock_toggled = false;
|
||||
unsigned long flags;
|
||||
|
||||
if (host->cmd)
|
||||
dev_err(sdmmc_dev(host), "error: cmd already exist\n");
|
||||
|
||||
host->cmd = cmd;
|
||||
|
||||
dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
|
||||
__func__, cmd_idx, arg);
|
||||
@ -261,6 +361,8 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
host->rsp_type = rsp_type;
|
||||
host->rsp_len = rsp_len;
|
||||
|
||||
if (rsp_type == SD_RSP_TYPE_R1b)
|
||||
timeout = 3000;
|
||||
@ -270,8 +372,6 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
|
||||
0xFF, SD_CLK_TOGGLE_EN);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
clock_toggled = true;
|
||||
}
|
||||
|
||||
rtsx_pci_init_cmd(pcr);
|
||||
@ -295,25 +395,60 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
|
||||
/* Read data from ping-pong buffer */
|
||||
for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++)
|
||||
rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
|
||||
stat_idx = 16;
|
||||
} else if (rsp_type != SD_RSP_TYPE_R0) {
|
||||
/* Read data from SD_CMDx registers */
|
||||
for (i = SD_CMD0; i <= SD_CMD4; i++)
|
||||
rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
|
||||
stat_idx = 5;
|
||||
}
|
||||
|
||||
rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0);
|
||||
|
||||
err = rtsx_pci_send_cmd(pcr, timeout);
|
||||
if (err < 0) {
|
||||
sd_print_debug_regs(host);
|
||||
sd_clear_error(host);
|
||||
dev_dbg(sdmmc_dev(host),
|
||||
"rtsx_pci_send_cmd error (err = %d)\n", err);
|
||||
mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout));
|
||||
|
||||
spin_lock_irqsave(&pcr->lock, flags);
|
||||
pcr->trans_result = TRANS_NOT_READY;
|
||||
rtsx_pci_send_cmd_no_wait(pcr);
|
||||
spin_unlock_irqrestore(&pcr->lock, flags);
|
||||
|
||||
return;
|
||||
|
||||
out:
|
||||
cmd->error = err;
|
||||
tasklet_schedule(&host->finish_tasklet);
|
||||
}
|
||||
|
||||
static void sd_get_rsp(unsigned long host_addr)
|
||||
{
|
||||
struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr;
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
struct mmc_command *cmd;
|
||||
int i, err = 0, stat_idx;
|
||||
u8 *ptr, rsp_type;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
cmd = host->cmd;
|
||||
host->cmd = NULL;
|
||||
|
||||
if (!cmd) {
|
||||
dev_err(sdmmc_dev(host), "error: cmd not exist\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock(&pcr->lock);
|
||||
if (pcr->trans_result == TRANS_NO_DEVICE)
|
||||
err = -ENODEV;
|
||||
else if (pcr->trans_result != TRANS_RESULT_OK)
|
||||
err = -EINVAL;
|
||||
spin_unlock(&pcr->lock);
|
||||
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
rsp_type = host->rsp_type;
|
||||
stat_idx = host->rsp_len;
|
||||
|
||||
if (rsp_type == SD_RSP_TYPE_R0) {
|
||||
err = 0;
|
||||
goto out;
|
||||
@ -350,26 +485,106 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
|
||||
cmd->resp[0]);
|
||||
}
|
||||
|
||||
if (cmd == host->mrq->sbc) {
|
||||
sd_send_cmd(host, host->mrq->cmd);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd == host->mrq->stop)
|
||||
goto out;
|
||||
|
||||
if (cmd->data) {
|
||||
sd_start_multi_rw(host, host->mrq);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
out:
|
||||
cmd->error = err;
|
||||
|
||||
if (err && clock_toggled)
|
||||
rtsx_pci_write_register(pcr, SD_BUS_STAT,
|
||||
SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
|
||||
tasklet_schedule(&host->finish_tasklet);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
|
||||
static int sd_pre_dma_transfer(struct realtek_pci_sdmmc *host,
|
||||
struct mmc_data *data, struct realtek_next *next)
|
||||
{
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
int read = data->flags & MMC_DATA_READ;
|
||||
int sg_count = 0;
|
||||
|
||||
if (!next && data->host_cookie &&
|
||||
data->host_cookie != host->next_data.cookie) {
|
||||
dev_err(sdmmc_dev(host),
|
||||
"error: invalid cookie data[%d] host[%d]\n",
|
||||
data->host_cookie, host->next_data.cookie);
|
||||
data->host_cookie = 0;
|
||||
}
|
||||
|
||||
if (next || (!next && data->host_cookie != host->next_data.cookie))
|
||||
sg_count = rtsx_pci_dma_map_sg(pcr,
|
||||
data->sg, data->sg_len, read);
|
||||
else
|
||||
sg_count = host->next_data.sg_count;
|
||||
|
||||
if (next) {
|
||||
next->sg_count = sg_count;
|
||||
if (++next->cookie < 0)
|
||||
next->cookie = 1;
|
||||
data->host_cookie = next->cookie;
|
||||
}
|
||||
|
||||
return sg_count;
|
||||
}
|
||||
|
||||
static void sdmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
||||
bool is_first_req)
|
||||
{
|
||||
struct realtek_pci_sdmmc *host = mmc_priv(mmc);
|
||||
struct mmc_data *data = mrq->data;
|
||||
|
||||
if (data->host_cookie) {
|
||||
dev_err(sdmmc_dev(host),
|
||||
"error: descard already cookie data[%d]\n",
|
||||
data->host_cookie);
|
||||
data->host_cookie = 0;
|
||||
}
|
||||
|
||||
dev_dbg(sdmmc_dev(host), "dma sg prepared: %d\n",
|
||||
sd_pre_dma_transfer(host, data, &host->next_data));
|
||||
}
|
||||
|
||||
static void sdmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
||||
int err)
|
||||
{
|
||||
struct realtek_pci_sdmmc *host = mmc_priv(mmc);
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
struct mmc_data *data = mrq->data;
|
||||
int read = data->flags & MMC_DATA_READ;
|
||||
|
||||
rtsx_pci_dma_unmap_sg(pcr, data->sg, data->sg_len, read);
|
||||
data->host_cookie = 0;
|
||||
}
|
||||
|
||||
static int sd_start_multi_rw(struct realtek_pci_sdmmc *host,
|
||||
struct mmc_request *mrq)
|
||||
{
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
struct mmc_card *card = mmc->card;
|
||||
struct mmc_data *data = mrq->data;
|
||||
int uhs = mmc_card_uhs(card);
|
||||
int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
|
||||
int read = data->flags & MMC_DATA_READ;
|
||||
u8 cfg2, trans_mode;
|
||||
int err;
|
||||
size_t data_len = data->blksz * data->blocks;
|
||||
|
||||
if (host->data)
|
||||
dev_err(sdmmc_dev(host), "error: data already exist\n");
|
||||
|
||||
host->data = data;
|
||||
|
||||
if (read) {
|
||||
cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
|
||||
SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0;
|
||||
@ -420,15 +635,54 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
|
||||
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
|
||||
SD_TRANSFER_END, SD_TRANSFER_END);
|
||||
|
||||
mod_timer(&host->timer, jiffies + 10 * HZ);
|
||||
rtsx_pci_send_cmd_no_wait(pcr);
|
||||
|
||||
err = rtsx_pci_transfer_data(pcr, data->sg, data->sg_len, read, 10000);
|
||||
err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, read);
|
||||
if (err < 0) {
|
||||
sd_clear_error(host);
|
||||
return err;
|
||||
data->error = err;
|
||||
tasklet_schedule(&host->finish_tasklet);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sd_finish_multi_rw(unsigned long host_addr)
|
||||
{
|
||||
struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr;
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
struct mmc_data *data;
|
||||
int err = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
if (!host->data) {
|
||||
dev_err(sdmmc_dev(host), "error: no data exist\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
data = host->data;
|
||||
host->data = NULL;
|
||||
|
||||
if (pcr->trans_result == TRANS_NO_DEVICE)
|
||||
err = -ENODEV;
|
||||
else if (pcr->trans_result != TRANS_RESULT_OK)
|
||||
err = -EINVAL;
|
||||
|
||||
if (err < 0) {
|
||||
data->error = err;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!host->mrq->sbc && data->stop) {
|
||||
sd_send_cmd(host, data->stop);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
out:
|
||||
tasklet_schedule(&host->finish_tasklet);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host)
|
||||
@ -511,85 +765,47 @@ static int sd_change_phase(struct realtek_pci_sdmmc *host,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 test_phase_bit(u32 phase_map, unsigned int bit)
|
||||
{
|
||||
bit %= RTSX_PHASE_MAX;
|
||||
return phase_map & (1 << bit);
|
||||
}
|
||||
|
||||
static int sd_get_phase_len(u32 phase_map, unsigned int start_bit)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < RTSX_PHASE_MAX; i++) {
|
||||
if (test_phase_bit(phase_map, start_bit + i) == 0)
|
||||
return i;
|
||||
}
|
||||
return RTSX_PHASE_MAX;
|
||||
}
|
||||
|
||||
static u8 sd_search_final_phase(struct realtek_pci_sdmmc *host, u32 phase_map)
|
||||
{
|
||||
struct timing_phase_path path[MAX_PHASE + 1];
|
||||
int i, j, cont_path_cnt;
|
||||
int new_block, max_len, final_path_idx;
|
||||
int start = 0, len = 0;
|
||||
int start_final = 0, len_final = 0;
|
||||
u8 final_phase = 0xFF;
|
||||
|
||||
/* Parse phase_map, take it as a bit-ring */
|
||||
cont_path_cnt = 0;
|
||||
new_block = 1;
|
||||
j = 0;
|
||||
for (i = 0; i < MAX_PHASE + 1; i++) {
|
||||
if (phase_map & (1 << i)) {
|
||||
if (new_block) {
|
||||
new_block = 0;
|
||||
j = cont_path_cnt++;
|
||||
path[j].start = i;
|
||||
path[j].end = i;
|
||||
} else {
|
||||
path[j].end = i;
|
||||
}
|
||||
} else {
|
||||
new_block = 1;
|
||||
if (cont_path_cnt) {
|
||||
/* Calculate path length and middle point */
|
||||
int idx = cont_path_cnt - 1;
|
||||
path[idx].len =
|
||||
path[idx].end - path[idx].start + 1;
|
||||
path[idx].mid =
|
||||
path[idx].start + path[idx].len / 2;
|
||||
}
|
||||
if (phase_map == 0) {
|
||||
dev_err(sdmmc_dev(host), "phase error: [map:%x]\n", phase_map);
|
||||
return final_phase;
|
||||
}
|
||||
|
||||
while (start < RTSX_PHASE_MAX) {
|
||||
len = sd_get_phase_len(phase_map, start);
|
||||
if (len_final < len) {
|
||||
start_final = start;
|
||||
len_final = len;
|
||||
}
|
||||
start += len ? len : 1;
|
||||
}
|
||||
|
||||
if (cont_path_cnt == 0) {
|
||||
dev_dbg(sdmmc_dev(host), "No continuous phase path\n");
|
||||
goto finish;
|
||||
} else {
|
||||
/* Calculate last continuous path length and middle point */
|
||||
int idx = cont_path_cnt - 1;
|
||||
path[idx].len = path[idx].end - path[idx].start + 1;
|
||||
path[idx].mid = path[idx].start + path[idx].len / 2;
|
||||
}
|
||||
final_phase = (start_final + len_final / 2) % RTSX_PHASE_MAX;
|
||||
dev_dbg(sdmmc_dev(host), "phase: [map:%x] [maxlen:%d] [final:%d]\n",
|
||||
phase_map, len_final, final_phase);
|
||||
|
||||
/* Connect the first and last continuous paths if they are adjacent */
|
||||
if (!path[0].start && (path[cont_path_cnt - 1].end == MAX_PHASE)) {
|
||||
/* Using negative index */
|
||||
path[0].start = path[cont_path_cnt - 1].start - MAX_PHASE - 1;
|
||||
path[0].len += path[cont_path_cnt - 1].len;
|
||||
path[0].mid = path[0].start + path[0].len / 2;
|
||||
/* Convert negative middle point index to positive one */
|
||||
if (path[0].mid < 0)
|
||||
path[0].mid += MAX_PHASE + 1;
|
||||
cont_path_cnt--;
|
||||
}
|
||||
|
||||
/* Choose the longest continuous phase path */
|
||||
max_len = 0;
|
||||
final_phase = 0;
|
||||
final_path_idx = 0;
|
||||
for (i = 0; i < cont_path_cnt; i++) {
|
||||
if (path[i].len > max_len) {
|
||||
max_len = path[i].len;
|
||||
final_phase = (u8)path[i].mid;
|
||||
final_path_idx = i;
|
||||
}
|
||||
|
||||
dev_dbg(sdmmc_dev(host), "path[%d].start = %d\n",
|
||||
i, path[i].start);
|
||||
dev_dbg(sdmmc_dev(host), "path[%d].end = %d\n",
|
||||
i, path[i].end);
|
||||
dev_dbg(sdmmc_dev(host), "path[%d].len = %d\n",
|
||||
i, path[i].len);
|
||||
dev_dbg(sdmmc_dev(host), "path[%d].mid = %d\n",
|
||||
i, path[i].mid);
|
||||
}
|
||||
|
||||
finish:
|
||||
dev_dbg(sdmmc_dev(host), "Final chosen phase: %d\n", final_phase);
|
||||
return final_phase;
|
||||
}
|
||||
|
||||
@ -635,7 +851,7 @@ static int sd_tuning_phase(struct realtek_pci_sdmmc *host,
|
||||
int err, i;
|
||||
u32 raw_phase_map = 0;
|
||||
|
||||
for (i = MAX_PHASE; i >= 0; i--) {
|
||||
for (i = 0; i < RTSX_PHASE_MAX; i++) {
|
||||
err = sd_tuning_rx_cmd(host, opcode, (u8)i);
|
||||
if (err == 0)
|
||||
raw_phase_map |= 1 << i;
|
||||
@ -685,6 +901,13 @@ static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool sd_use_muti_rw(struct mmc_command *cmd)
|
||||
{
|
||||
return mmc_op_multi(cmd->opcode) ||
|
||||
(cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
|
||||
(cmd->opcode == MMC_WRITE_BLOCK);
|
||||
}
|
||||
|
||||
static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
struct realtek_pci_sdmmc *host = mmc_priv(mmc);
|
||||
@ -693,6 +916,14 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
struct mmc_data *data = mrq->data;
|
||||
unsigned int data_size = 0;
|
||||
int err;
|
||||
unsigned long flags;
|
||||
|
||||
mutex_lock(&pcr->pcr_mutex);
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
if (host->mrq)
|
||||
dev_err(sdmmc_dev(host), "error: request already exist\n");
|
||||
host->mrq = mrq;
|
||||
|
||||
if (host->eject) {
|
||||
cmd->error = -ENOMEDIUM;
|
||||
@ -705,8 +936,6 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
mutex_lock(&pcr->pcr_mutex);
|
||||
|
||||
rtsx_pci_start_run(pcr);
|
||||
|
||||
rtsx_pci_switch_clock(pcr, host->clock, host->ssc_depth,
|
||||
@ -715,46 +944,28 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
rtsx_pci_write_register(pcr, CARD_SHARE_MODE,
|
||||
CARD_SHARE_MASK, CARD_SHARE_48_SD);
|
||||
|
||||
mutex_lock(&host->host_mutex);
|
||||
host->mrq = mrq;
|
||||
mutex_unlock(&host->host_mutex);
|
||||
|
||||
if (mrq->data)
|
||||
data_size = data->blocks * data->blksz;
|
||||
|
||||
if (!data_size || mmc_op_multi(cmd->opcode) ||
|
||||
(cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
|
||||
(cmd->opcode == MMC_WRITE_BLOCK)) {
|
||||
sd_send_cmd_get_rsp(host, cmd);
|
||||
if (sd_use_muti_rw(cmd))
|
||||
host->sg_count = sd_pre_dma_transfer(host, data, NULL);
|
||||
|
||||
if (!cmd->error && data_size) {
|
||||
sd_rw_multi(host, mrq);
|
||||
|
||||
if (mmc_op_multi(cmd->opcode) && mrq->stop)
|
||||
sd_send_cmd_get_rsp(host, mrq->stop);
|
||||
}
|
||||
} else {
|
||||
sd_normal_rw(host, mrq);
|
||||
}
|
||||
|
||||
if (mrq->data) {
|
||||
if (cmd->error || data->error)
|
||||
data->bytes_xfered = 0;
|
||||
if (!data_size || sd_use_muti_rw(cmd)) {
|
||||
if (mrq->sbc)
|
||||
sd_send_cmd(host, mrq->sbc);
|
||||
else
|
||||
data->bytes_xfered = data->blocks * data->blksz;
|
||||
sd_send_cmd(host, cmd);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
sd_normal_rw(host, mrq);
|
||||
tasklet_schedule(&host->finish_tasklet);
|
||||
}
|
||||
|
||||
mutex_unlock(&pcr->pcr_mutex);
|
||||
return;
|
||||
|
||||
finish:
|
||||
if (cmd->error)
|
||||
dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error);
|
||||
|
||||
mutex_lock(&host->host_mutex);
|
||||
host->mrq = NULL;
|
||||
mutex_unlock(&host->host_mutex);
|
||||
|
||||
mmc_request_done(mmc, mrq);
|
||||
tasklet_schedule(&host->finish_tasklet);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
static int sd_set_bus_width(struct realtek_pci_sdmmc *host,
|
||||
@ -1189,6 +1400,8 @@ out:
|
||||
}
|
||||
|
||||
static const struct mmc_host_ops realtek_pci_sdmmc_ops = {
|
||||
.pre_req = sdmmc_pre_req,
|
||||
.post_req = sdmmc_post_req,
|
||||
.request = sdmmc_request,
|
||||
.set_ios = sdmmc_set_ios,
|
||||
.get_ro = sdmmc_get_ro,
|
||||
@ -1252,6 +1465,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
|
||||
struct realtek_pci_sdmmc *host;
|
||||
struct rtsx_pcr *pcr;
|
||||
struct pcr_handle *handle = pdev->dev.platform_data;
|
||||
unsigned long host_addr;
|
||||
|
||||
if (!handle)
|
||||
return -ENXIO;
|
||||
@ -1275,8 +1489,15 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
|
||||
pcr->slots[RTSX_SD_CARD].p_dev = pdev;
|
||||
pcr->slots[RTSX_SD_CARD].card_event = rtsx_pci_sdmmc_card_event;
|
||||
|
||||
mutex_init(&host->host_mutex);
|
||||
host_addr = (unsigned long)host;
|
||||
host->next_data.cookie = 1;
|
||||
setup_timer(&host->timer, sd_request_timeout, host_addr);
|
||||
tasklet_init(&host->cmd_tasklet, sd_get_rsp, host_addr);
|
||||
tasklet_init(&host->data_tasklet, sd_finish_multi_rw, host_addr);
|
||||
tasklet_init(&host->finish_tasklet, sd_finish_request, host_addr);
|
||||
spin_lock_init(&host->lock);
|
||||
|
||||
pcr->slots[RTSX_SD_CARD].done_transfer = sd_isr_done_transfer;
|
||||
realtek_init_host(host);
|
||||
|
||||
mmc_add_host(mmc);
|
||||
@ -1289,6 +1510,8 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
|
||||
struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
|
||||
struct rtsx_pcr *pcr;
|
||||
struct mmc_host *mmc;
|
||||
struct mmc_request *mrq;
|
||||
unsigned long flags;
|
||||
|
||||
if (!host)
|
||||
return 0;
|
||||
@ -1296,25 +1519,37 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
|
||||
pcr = host->pcr;
|
||||
pcr->slots[RTSX_SD_CARD].p_dev = NULL;
|
||||
pcr->slots[RTSX_SD_CARD].card_event = NULL;
|
||||
pcr->slots[RTSX_SD_CARD].done_transfer = NULL;
|
||||
mmc = host->mmc;
|
||||
host->eject = true;
|
||||
mrq = host->mrq;
|
||||
|
||||
mutex_lock(&host->host_mutex);
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
if (host->mrq) {
|
||||
dev_dbg(&(pdev->dev),
|
||||
"%s: Controller removed during transfer\n",
|
||||
mmc_hostname(mmc));
|
||||
|
||||
rtsx_pci_complete_unfinished_transfer(pcr);
|
||||
if (mrq->sbc)
|
||||
mrq->sbc->error = -ENOMEDIUM;
|
||||
if (mrq->cmd)
|
||||
mrq->cmd->error = -ENOMEDIUM;
|
||||
if (mrq->stop)
|
||||
mrq->stop->error = -ENOMEDIUM;
|
||||
if (mrq->data)
|
||||
mrq->data->error = -ENOMEDIUM;
|
||||
|
||||
host->mrq->cmd->error = -ENOMEDIUM;
|
||||
if (host->mrq->stop)
|
||||
host->mrq->stop->error = -ENOMEDIUM;
|
||||
mmc_request_done(mmc, host->mrq);
|
||||
tasklet_schedule(&host->finish_tasklet);
|
||||
}
|
||||
mutex_unlock(&host->host_mutex);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
del_timer_sync(&host->timer);
|
||||
tasklet_kill(&host->cmd_tasklet);
|
||||
tasklet_kill(&host->data_tasklet);
|
||||
tasklet_kill(&host->finish_tasklet);
|
||||
|
||||
mmc_remove_host(mmc);
|
||||
host->eject = true;
|
||||
|
||||
mmc_free_host(mmc);
|
||||
|
||||
dev_dbg(&(pdev->dev),
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pm.h>
|
||||
@ -40,13 +39,15 @@
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/pm.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/mmc/sdhci.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
|
||||
enum {
|
||||
SDHCI_ACPI_SD_CD = BIT(0),
|
||||
SDHCI_ACPI_RUNTIME_PM = BIT(1),
|
||||
SDHCI_ACPI_SD_CD = BIT(0),
|
||||
SDHCI_ACPI_RUNTIME_PM = BIT(1),
|
||||
SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL = BIT(2),
|
||||
};
|
||||
|
||||
struct sdhci_acpi_chip {
|
||||
@ -121,6 +122,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
|
||||
};
|
||||
|
||||
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION,
|
||||
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
|
||||
.caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD,
|
||||
.flags = SDHCI_ACPI_RUNTIME_PM,
|
||||
@ -128,7 +130,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
|
||||
};
|
||||
|
||||
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
|
||||
.flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_RUNTIME_PM,
|
||||
.flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL |
|
||||
SDHCI_ACPI_RUNTIME_PM,
|
||||
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,
|
||||
};
|
||||
|
||||
@ -141,6 +144,7 @@ struct sdhci_acpi_uid_slot {
|
||||
static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
|
||||
{ "80860F14" , "1" , &sdhci_acpi_slot_int_emmc },
|
||||
{ "80860F14" , "3" , &sdhci_acpi_slot_int_sd },
|
||||
{ "80860F16" , NULL, &sdhci_acpi_slot_int_sd },
|
||||
{ "INT33BB" , "2" , &sdhci_acpi_slot_int_sdio },
|
||||
{ "INT33C6" , NULL, &sdhci_acpi_slot_int_sdio },
|
||||
{ "INT3436" , NULL, &sdhci_acpi_slot_int_sdio },
|
||||
@ -150,6 +154,7 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
|
||||
|
||||
static const struct acpi_device_id sdhci_acpi_ids[] = {
|
||||
{ "80860F14" },
|
||||
{ "80860F16" },
|
||||
{ "INT33BB" },
|
||||
{ "INT33C6" },
|
||||
{ "INT3436" },
|
||||
@ -192,59 +197,6 @@ static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(acpi_handle handle,
|
||||
return slot;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static irqreturn_t sdhci_acpi_sd_cd(int irq, void *dev_id)
|
||||
{
|
||||
mmc_detect_change(dev_id, msecs_to_jiffies(200));
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sdhci_acpi_add_own_cd(struct device *dev, struct mmc_host *mmc)
|
||||
{
|
||||
struct gpio_desc *desc;
|
||||
unsigned long flags;
|
||||
int err, irq;
|
||||
|
||||
desc = devm_gpiod_get_index(dev, "sd_cd", 0);
|
||||
if (IS_ERR(desc)) {
|
||||
err = PTR_ERR(desc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = gpiod_direction_input(desc);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
irq = gpiod_to_irq(desc);
|
||||
if (irq < 0) {
|
||||
err = irq;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
|
||||
err = devm_request_irq(dev, irq, sdhci_acpi_sd_cd, flags, "sd_cd", mmc);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
devm_gpiod_put(dev, desc);
|
||||
out:
|
||||
dev_warn(dev, "failed to setup card detect wake up\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int sdhci_acpi_add_own_cd(struct device *dev, struct mmc_host *mmc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int sdhci_acpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -332,15 +284,19 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
|
||||
|
||||
host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
|
||||
|
||||
if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) {
|
||||
bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL);
|
||||
|
||||
if (mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0)) {
|
||||
dev_warn(dev, "failed to setup card detect gpio\n");
|
||||
c->use_runtime_pm = false;
|
||||
}
|
||||
}
|
||||
|
||||
err = sdhci_add_host(host);
|
||||
if (err)
|
||||
goto err_free;
|
||||
|
||||
if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) {
|
||||
if (sdhci_acpi_add_own_cd(dev, host->mmc))
|
||||
c->use_runtime_pm = false;
|
||||
}
|
||||
|
||||
if (c->use_runtime_pm) {
|
||||
pm_runtime_set_active(dev);
|
||||
pm_suspend_ignore_children(dev, 1);
|
||||
|
@ -54,6 +54,7 @@
|
||||
|
||||
struct sdhci_bcm_kona_dev {
|
||||
struct mutex write_lock; /* protect back to back writes */
|
||||
struct clk *external_clk;
|
||||
};
|
||||
|
||||
|
||||
@ -257,6 +258,24 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
/* Get and enable the external clock */
|
||||
kona_dev->external_clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(kona_dev->external_clk)) {
|
||||
dev_err(dev, "Failed to get external clock\n");
|
||||
ret = PTR_ERR(kona_dev->external_clk);
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
if (clk_set_rate(kona_dev->external_clk, host->mmc->f_max) != 0) {
|
||||
dev_err(dev, "Failed to set rate external clock\n");
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
if (clk_prepare_enable(kona_dev->external_clk) != 0) {
|
||||
dev_err(dev, "Failed to enable external clock\n");
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "non-removable=%c\n",
|
||||
(host->mmc->caps & MMC_CAP_NONREMOVABLE) ? 'Y' : 'N');
|
||||
dev_dbg(dev, "cd_gpio %c, wp_gpio %c\n",
|
||||
@ -271,7 +290,7 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
|
||||
|
||||
ret = sdhci_bcm_kona_sd_reset(host);
|
||||
if (ret)
|
||||
goto err_pltfm_free;
|
||||
goto err_clk_disable;
|
||||
|
||||
sdhci_bcm_kona_sd_init(host);
|
||||
|
||||
@ -307,6 +326,9 @@ err_remove_host:
|
||||
err_reset:
|
||||
sdhci_bcm_kona_sd_reset(host);
|
||||
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(kona_dev->external_clk);
|
||||
|
||||
err_pltfm_free:
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
@ -314,9 +336,20 @@ err_pltfm_free:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev)
|
||||
static int sdhci_bcm_kona_remove(struct platform_device *pdev)
|
||||
{
|
||||
return sdhci_pltfm_unregister(pdev);
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
|
||||
struct sdhci_bcm_kona_dev *kona_dev = sdhci_pltfm_priv(pltfm_priv);
|
||||
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
|
||||
|
||||
sdhci_remove_host(host, dead);
|
||||
|
||||
clk_disable_unprepare(kona_dev->external_clk);
|
||||
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sdhci_bcm_kona_driver = {
|
||||
|
@ -208,7 +208,7 @@ static struct platform_driver sdhci_dove_driver = {
|
||||
.name = "sdhci-dove",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
.of_match_table = of_match_ptr(sdhci_dove_of_match_table),
|
||||
.of_match_table = sdhci_dove_of_match_table,
|
||||
},
|
||||
.probe = sdhci_dove_probe,
|
||||
.remove = sdhci_dove_remove,
|
||||
|
618
drivers/mmc/host/sdhci-msm.c
Normal file
618
drivers/mmc/host/sdhci-msm.c
Normal file
@ -0,0 +1,618 @@
|
||||
/*
|
||||
* drivers/mmc/host/sdhci-msm.c - Qualcomm SDHCI Platform driver
|
||||
*
|
||||
* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
#define CORE_HC_MODE 0x78
|
||||
#define HC_MODE_EN 0x1
|
||||
#define CORE_POWER 0x0
|
||||
#define CORE_SW_RST BIT(7)
|
||||
|
||||
#define MAX_PHASES 16
|
||||
#define CORE_DLL_LOCK BIT(7)
|
||||
#define CORE_DLL_EN BIT(16)
|
||||
#define CORE_CDR_EN BIT(17)
|
||||
#define CORE_CK_OUT_EN BIT(18)
|
||||
#define CORE_CDR_EXT_EN BIT(19)
|
||||
#define CORE_DLL_PDN BIT(29)
|
||||
#define CORE_DLL_RST BIT(30)
|
||||
#define CORE_DLL_CONFIG 0x100
|
||||
#define CORE_DLL_STATUS 0x108
|
||||
|
||||
#define CORE_VENDOR_SPEC 0x10c
|
||||
#define CORE_CLK_PWRSAVE BIT(1)
|
||||
|
||||
#define CDR_SELEXT_SHIFT 20
|
||||
#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)
|
||||
#define CMUX_SHIFT_PHASE_SHIFT 24
|
||||
#define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT)
|
||||
|
||||
static const u32 tuning_block_64[] = {
|
||||
0x00ff0fff, 0xccc3ccff, 0xffcc3cc3, 0xeffefffe,
|
||||
0xddffdfff, 0xfbfffbff, 0xff7fffbf, 0xefbdf777,
|
||||
0xf0fff0ff, 0x3cccfc0f, 0xcfcc33cc, 0xeeffefff,
|
||||
0xfdfffdff, 0xffbfffdf, 0xfff7ffbb, 0xde7b7ff7
|
||||
};
|
||||
|
||||
static const u32 tuning_block_128[] = {
|
||||
0xff00ffff, 0x0000ffff, 0xccccffff, 0xcccc33cc,
|
||||
0xcc3333cc, 0xffffcccc, 0xffffeeff, 0xffeeeeff,
|
||||
0xffddffff, 0xddddffff, 0xbbffffff, 0xbbffffff,
|
||||
0xffffffbb, 0xffffff77, 0x77ff7777, 0xffeeddbb,
|
||||
0x00ffffff, 0x00ffffff, 0xccffff00, 0xcc33cccc,
|
||||
0x3333cccc, 0xffcccccc, 0xffeeffff, 0xeeeeffff,
|
||||
0xddffffff, 0xddffffff, 0xffffffdd, 0xffffffbb,
|
||||
0xffffbbbb, 0xffff77ff, 0xff7777ff, 0xeeddbb77
|
||||
};
|
||||
|
||||
struct sdhci_msm_host {
|
||||
struct platform_device *pdev;
|
||||
void __iomem *core_mem; /* MSM SDCC mapped address */
|
||||
struct clk *clk; /* main SD/MMC bus clock */
|
||||
struct clk *pclk; /* SDHC peripheral bus clock */
|
||||
struct clk *bus_clk; /* SDHC bus voter clock */
|
||||
struct mmc_host *mmc;
|
||||
struct sdhci_pltfm_data sdhci_msm_pdata;
|
||||
};
|
||||
|
||||
/* Platform specific tuning */
|
||||
static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll)
|
||||
{
|
||||
u32 wait_cnt = 50;
|
||||
u8 ck_out_en;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
/* Poll for CK_OUT_EN bit. max. poll time = 50us */
|
||||
ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
|
||||
CORE_CK_OUT_EN);
|
||||
|
||||
while (ck_out_en != poll) {
|
||||
if (--wait_cnt == 0) {
|
||||
dev_err(mmc_dev(mmc), "%s: CK_OUT_EN bit is not %d\n",
|
||||
mmc_hostname(mmc), poll);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
udelay(1);
|
||||
|
||||
ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
|
||||
CORE_CK_OUT_EN);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
|
||||
{
|
||||
int rc;
|
||||
static const u8 grey_coded_phase_table[] = {
|
||||
0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4,
|
||||
0xc, 0xd, 0xf, 0xe, 0xa, 0xb, 0x9, 0x8
|
||||
};
|
||||
unsigned long flags;
|
||||
u32 config;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
|
||||
config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN);
|
||||
config |= (CORE_CDR_EXT_EN | CORE_DLL_EN);
|
||||
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
|
||||
|
||||
/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */
|
||||
rc = msm_dll_poll_ck_out_en(host, 0);
|
||||
if (rc)
|
||||
goto err_out;
|
||||
|
||||
/*
|
||||
* Write the selected DLL clock output phase (0 ... 15)
|
||||
* to CDR_SELEXT bit field of DLL_CONFIG register.
|
||||
*/
|
||||
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
|
||||
config &= ~CDR_SELEXT_MASK;
|
||||
config |= grey_coded_phase_table[phase] << CDR_SELEXT_SHIFT;
|
||||
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
|
||||
|
||||
/* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */
|
||||
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
|
||||
| CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
|
||||
|
||||
/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */
|
||||
rc = msm_dll_poll_ck_out_en(host, 1);
|
||||
if (rc)
|
||||
goto err_out;
|
||||
|
||||
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
|
||||
config |= CORE_CDR_EN;
|
||||
config &= ~CORE_CDR_EXT_EN;
|
||||
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
|
||||
goto out;
|
||||
|
||||
err_out:
|
||||
dev_err(mmc_dev(mmc), "%s: Failed to set DLL phase: %d\n",
|
||||
mmc_hostname(mmc), phase);
|
||||
out:
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find out the greatest range of consecuitive selected
|
||||
* DLL clock output phases that can be used as sampling
|
||||
* setting for SD3.0 UHS-I card read operation (in SDR104
|
||||
* timing mode) or for eMMC4.5 card read operation (in HS200
|
||||
* timing mode).
|
||||
* Select the 3/4 of the range and configure the DLL with the
|
||||
* selected DLL clock output phase.
|
||||
*/
|
||||
|
||||
static int msm_find_most_appropriate_phase(struct sdhci_host *host,
|
||||
u8 *phase_table, u8 total_phases)
|
||||
{
|
||||
int ret;
|
||||
u8 ranges[MAX_PHASES][MAX_PHASES] = { {0}, {0} };
|
||||
u8 phases_per_row[MAX_PHASES] = { 0 };
|
||||
int row_index = 0, col_index = 0, selected_row_index = 0, curr_max = 0;
|
||||
int i, cnt, phase_0_raw_index = 0, phase_15_raw_index = 0;
|
||||
bool phase_0_found = false, phase_15_found = false;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
if (!total_phases || (total_phases > MAX_PHASES)) {
|
||||
dev_err(mmc_dev(mmc), "%s: Invalid argument: total_phases=%d\n",
|
||||
mmc_hostname(mmc), total_phases);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (cnt = 0; cnt < total_phases; cnt++) {
|
||||
ranges[row_index][col_index] = phase_table[cnt];
|
||||
phases_per_row[row_index] += 1;
|
||||
col_index++;
|
||||
|
||||
if ((cnt + 1) == total_phases) {
|
||||
continue;
|
||||
/* check if next phase in phase_table is consecutive or not */
|
||||
} else if ((phase_table[cnt] + 1) != phase_table[cnt + 1]) {
|
||||
row_index++;
|
||||
col_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (row_index >= MAX_PHASES)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check if phase-0 is present in first valid window? */
|
||||
if (!ranges[0][0]) {
|
||||
phase_0_found = true;
|
||||
phase_0_raw_index = 0;
|
||||
/* Check if cycle exist between 2 valid windows */
|
||||
for (cnt = 1; cnt <= row_index; cnt++) {
|
||||
if (phases_per_row[cnt]) {
|
||||
for (i = 0; i < phases_per_row[cnt]; i++) {
|
||||
if (ranges[cnt][i] == 15) {
|
||||
phase_15_found = true;
|
||||
phase_15_raw_index = cnt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If 2 valid windows form cycle then merge them as single window */
|
||||
if (phase_0_found && phase_15_found) {
|
||||
/* number of phases in raw where phase 0 is present */
|
||||
u8 phases_0 = phases_per_row[phase_0_raw_index];
|
||||
/* number of phases in raw where phase 15 is present */
|
||||
u8 phases_15 = phases_per_row[phase_15_raw_index];
|
||||
|
||||
if (phases_0 + phases_15 >= MAX_PHASES)
|
||||
/*
|
||||
* If there are more than 1 phase windows then total
|
||||
* number of phases in both the windows should not be
|
||||
* more than or equal to MAX_PHASES.
|
||||
*/
|
||||
return -EINVAL;
|
||||
|
||||
/* Merge 2 cyclic windows */
|
||||
i = phases_15;
|
||||
for (cnt = 0; cnt < phases_0; cnt++) {
|
||||
ranges[phase_15_raw_index][i] =
|
||||
ranges[phase_0_raw_index][cnt];
|
||||
if (++i >= MAX_PHASES)
|
||||
break;
|
||||
}
|
||||
|
||||
phases_per_row[phase_0_raw_index] = 0;
|
||||
phases_per_row[phase_15_raw_index] = phases_15 + phases_0;
|
||||
}
|
||||
|
||||
for (cnt = 0; cnt <= row_index; cnt++) {
|
||||
if (phases_per_row[cnt] > curr_max) {
|
||||
curr_max = phases_per_row[cnt];
|
||||
selected_row_index = cnt;
|
||||
}
|
||||
}
|
||||
|
||||
i = (curr_max * 3) / 4;
|
||||
if (i)
|
||||
i--;
|
||||
|
||||
ret = ranges[selected_row_index][i];
|
||||
|
||||
if (ret >= MAX_PHASES) {
|
||||
ret = -EINVAL;
|
||||
dev_err(mmc_dev(mmc), "%s: Invalid phase selected=%d\n",
|
||||
mmc_hostname(mmc), ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void msm_cm_dll_set_freq(struct sdhci_host *host)
|
||||
{
|
||||
u32 mclk_freq = 0, config;
|
||||
|
||||
/* Program the MCLK value to MCLK_FREQ bit field */
|
||||
if (host->clock <= 112000000)
|
||||
mclk_freq = 0;
|
||||
else if (host->clock <= 125000000)
|
||||
mclk_freq = 1;
|
||||
else if (host->clock <= 137000000)
|
||||
mclk_freq = 2;
|
||||
else if (host->clock <= 150000000)
|
||||
mclk_freq = 3;
|
||||
else if (host->clock <= 162000000)
|
||||
mclk_freq = 4;
|
||||
else if (host->clock <= 175000000)
|
||||
mclk_freq = 5;
|
||||
else if (host->clock <= 187000000)
|
||||
mclk_freq = 6;
|
||||
else if (host->clock <= 200000000)
|
||||
mclk_freq = 7;
|
||||
|
||||
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
|
||||
config &= ~CMUX_SHIFT_PHASE_MASK;
|
||||
config |= mclk_freq << CMUX_SHIFT_PHASE_SHIFT;
|
||||
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
|
||||
}
|
||||
|
||||
/* Initialize the DLL (Programmable Delay Line) */
|
||||
static int msm_init_cm_dll(struct sdhci_host *host)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
int wait_cnt = 50;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
/*
|
||||
* Make sure that clock is always enabled when DLL
|
||||
* tuning is in progress. Keeping PWRSAVE ON may
|
||||
* turn off the clock.
|
||||
*/
|
||||
writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
|
||||
& ~CORE_CLK_PWRSAVE), host->ioaddr + CORE_VENDOR_SPEC);
|
||||
|
||||
/* Write 1 to DLL_RST bit of DLL_CONFIG register */
|
||||
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
|
||||
| CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
|
||||
|
||||
/* Write 1 to DLL_PDN bit of DLL_CONFIG register */
|
||||
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
|
||||
| CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
|
||||
msm_cm_dll_set_freq(host);
|
||||
|
||||
/* Write 0 to DLL_RST bit of DLL_CONFIG register */
|
||||
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
|
||||
& ~CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
|
||||
|
||||
/* Write 0 to DLL_PDN bit of DLL_CONFIG register */
|
||||
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
|
||||
& ~CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
|
||||
|
||||
/* Set DLL_EN bit to 1. */
|
||||
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
|
||||
| CORE_DLL_EN), host->ioaddr + CORE_DLL_CONFIG);
|
||||
|
||||
/* Set CK_OUT_EN bit to 1. */
|
||||
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
|
||||
| CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
|
||||
|
||||
/* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
|
||||
while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) &
|
||||
CORE_DLL_LOCK)) {
|
||||
/* max. wait for 50us sec for LOCK bit to be set */
|
||||
if (--wait_cnt == 0) {
|
||||
dev_err(mmc_dev(mmc), "%s: DLL failed to LOCK\n",
|
||||
mmc_hostname(mmc));
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
|
||||
{
|
||||
int tuning_seq_cnt = 3;
|
||||
u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
|
||||
const u32 *tuning_block_pattern = tuning_block_64;
|
||||
int size = sizeof(tuning_block_64); /* Pattern size in bytes */
|
||||
int rc;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
struct mmc_ios ios = host->mmc->ios;
|
||||
|
||||
/*
|
||||
* Tuning is required for SDR104, HS200 and HS400 cards and
|
||||
* if clock frequency is greater than 100MHz in these modes.
|
||||
*/
|
||||
if (host->clock <= 100 * 1000 * 1000 ||
|
||||
!((ios.timing == MMC_TIMING_MMC_HS200) ||
|
||||
(ios.timing == MMC_TIMING_UHS_SDR104)))
|
||||
return 0;
|
||||
|
||||
if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
|
||||
(mmc->ios.bus_width == MMC_BUS_WIDTH_8)) {
|
||||
tuning_block_pattern = tuning_block_128;
|
||||
size = sizeof(tuning_block_128);
|
||||
}
|
||||
|
||||
data_buf = kmalloc(size, GFP_KERNEL);
|
||||
if (!data_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
retry:
|
||||
/* First of all reset the tuning block */
|
||||
rc = msm_init_cm_dll(host);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
phase = 0;
|
||||
do {
|
||||
struct mmc_command cmd = { 0 };
|
||||
struct mmc_data data = { 0 };
|
||||
struct mmc_request mrq = {
|
||||
.cmd = &cmd,
|
||||
.data = &data
|
||||
};
|
||||
struct scatterlist sg;
|
||||
|
||||
/* Set the phase in delay line hw block */
|
||||
rc = msm_config_cm_dll_phase(host, phase);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
cmd.opcode = opcode;
|
||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
|
||||
|
||||
data.blksz = size;
|
||||
data.blocks = 1;
|
||||
data.flags = MMC_DATA_READ;
|
||||
data.timeout_ns = NSEC_PER_SEC; /* 1 second */
|
||||
|
||||
data.sg = &sg;
|
||||
data.sg_len = 1;
|
||||
sg_init_one(&sg, data_buf, size);
|
||||
memset(data_buf, 0, size);
|
||||
mmc_wait_for_req(mmc, &mrq);
|
||||
|
||||
if (!cmd.error && !data.error &&
|
||||
!memcmp(data_buf, tuning_block_pattern, size)) {
|
||||
/* Tuning is successful at this tuning point */
|
||||
tuned_phases[tuned_phase_cnt++] = phase;
|
||||
dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
|
||||
mmc_hostname(mmc), phase);
|
||||
}
|
||||
} while (++phase < ARRAY_SIZE(tuned_phases));
|
||||
|
||||
if (tuned_phase_cnt) {
|
||||
rc = msm_find_most_appropriate_phase(host, tuned_phases,
|
||||
tuned_phase_cnt);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
else
|
||||
phase = rc;
|
||||
|
||||
/*
|
||||
* Finally set the selected phase in delay
|
||||
* line hw block.
|
||||
*/
|
||||
rc = msm_config_cm_dll_phase(host, phase);
|
||||
if (rc)
|
||||
goto out;
|
||||
dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
|
||||
mmc_hostname(mmc), phase);
|
||||
} else {
|
||||
if (--tuning_seq_cnt)
|
||||
goto retry;
|
||||
/* Tuning failed */
|
||||
dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n",
|
||||
mmc_hostname(mmc));
|
||||
rc = -EIO;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(data_buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct of_device_id sdhci_msm_dt_match[] = {
|
||||
{ .compatible = "qcom,sdhci-msm-v4" },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
|
||||
|
||||
static struct sdhci_ops sdhci_msm_ops = {
|
||||
.platform_execute_tuning = sdhci_msm_execute_tuning,
|
||||
};
|
||||
|
||||
static int sdhci_msm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_msm_host *msm_host;
|
||||
struct resource *core_memres;
|
||||
int ret;
|
||||
u16 host_version;
|
||||
|
||||
msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL);
|
||||
if (!msm_host)
|
||||
return -ENOMEM;
|
||||
|
||||
msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops;
|
||||
host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0);
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = msm_host;
|
||||
msm_host->mmc = host->mmc;
|
||||
msm_host->pdev = pdev;
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret)
|
||||
goto pltfm_free;
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
/* Setup SDCC bus voter clock. */
|
||||
msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus");
|
||||
if (!IS_ERR(msm_host->bus_clk)) {
|
||||
/* Vote for max. clk rate for max. performance */
|
||||
ret = clk_set_rate(msm_host->bus_clk, INT_MAX);
|
||||
if (ret)
|
||||
goto pltfm_free;
|
||||
ret = clk_prepare_enable(msm_host->bus_clk);
|
||||
if (ret)
|
||||
goto pltfm_free;
|
||||
}
|
||||
|
||||
/* Setup main peripheral bus clock */
|
||||
msm_host->pclk = devm_clk_get(&pdev->dev, "iface");
|
||||
if (IS_ERR(msm_host->pclk)) {
|
||||
ret = PTR_ERR(msm_host->pclk);
|
||||
dev_err(&pdev->dev, "Perpheral clk setup failed (%d)\n", ret);
|
||||
goto bus_clk_disable;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(msm_host->pclk);
|
||||
if (ret)
|
||||
goto bus_clk_disable;
|
||||
|
||||
/* Setup SDC MMC clock */
|
||||
msm_host->clk = devm_clk_get(&pdev->dev, "core");
|
||||
if (IS_ERR(msm_host->clk)) {
|
||||
ret = PTR_ERR(msm_host->clk);
|
||||
dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret);
|
||||
goto pclk_disable;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(msm_host->clk);
|
||||
if (ret)
|
||||
goto pclk_disable;
|
||||
|
||||
core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres);
|
||||
|
||||
if (IS_ERR(msm_host->core_mem)) {
|
||||
dev_err(&pdev->dev, "Failed to remap registers\n");
|
||||
ret = PTR_ERR(msm_host->core_mem);
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
/* Reset the core and Enable SDHC mode */
|
||||
writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) |
|
||||
CORE_SW_RST, msm_host->core_mem + CORE_POWER);
|
||||
|
||||
/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
|
||||
usleep_range(1000, 5000);
|
||||
if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) {
|
||||
dev_err(&pdev->dev, "Stuck in reset\n");
|
||||
ret = -ETIMEDOUT;
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
/* Set HC_MODE_EN bit in HC_MODE register */
|
||||
writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
|
||||
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||
host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE;
|
||||
|
||||
host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
|
||||
dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n",
|
||||
host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
|
||||
SDHCI_VENDOR_VER_SHIFT));
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto clk_disable;
|
||||
|
||||
return 0;
|
||||
|
||||
clk_disable:
|
||||
clk_disable_unprepare(msm_host->clk);
|
||||
pclk_disable:
|
||||
clk_disable_unprepare(msm_host->pclk);
|
||||
bus_clk_disable:
|
||||
if (!IS_ERR(msm_host->bus_clk))
|
||||
clk_disable_unprepare(msm_host->bus_clk);
|
||||
pltfm_free:
|
||||
sdhci_pltfm_free(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_msm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = pltfm_host->priv;
|
||||
int dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) ==
|
||||
0xffffffff);
|
||||
|
||||
sdhci_remove_host(host, dead);
|
||||
sdhci_pltfm_free(pdev);
|
||||
clk_disable_unprepare(msm_host->clk);
|
||||
clk_disable_unprepare(msm_host->pclk);
|
||||
if (!IS_ERR(msm_host->bus_clk))
|
||||
clk_disable_unprepare(msm_host->bus_clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sdhci_msm_driver = {
|
||||
.probe = sdhci_msm_probe,
|
||||
.remove = sdhci_msm_remove,
|
||||
.driver = {
|
||||
.name = "sdhci_msm",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = sdhci_msm_dt_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_msm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Qualcomm Secure Digital Host Controller Interface driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -610,6 +610,18 @@ static const struct sdhci_pci_fixes sdhci_via = {
|
||||
.probe = via_probe,
|
||||
};
|
||||
|
||||
static int rtsx_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
slot->host->mmc->caps2 |= MMC_CAP2_HS200;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_rtsx = {
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_BROKEN_DDR50,
|
||||
.probe_slot = rtsx_probe_slot,
|
||||
};
|
||||
|
||||
static const struct pci_device_id pci_ids[] = {
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_RICOH,
|
||||
@ -731,6 +743,14 @@ static const struct pci_device_id pci_ids[] = {
|
||||
.driver_data = (kernel_ulong_t)&sdhci_via,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_REALTEK,
|
||||
.device = 0x5250,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (kernel_ulong_t)&sdhci_rtsx,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = PCI_DEVICE_ID_INTEL_MRST_SD0,
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/mbus.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
#include "sdhci-pltfm.h"
|
||||
@ -57,6 +58,60 @@
|
||||
#define SDCE_MISC_INT (1<<2)
|
||||
#define SDCE_MISC_INT_EN (1<<1)
|
||||
|
||||
/*
|
||||
* These registers are relative to the second register region, for the
|
||||
* MBus bridge.
|
||||
*/
|
||||
#define SDHCI_WINDOW_CTRL(i) (0x80 + ((i) << 3))
|
||||
#define SDHCI_WINDOW_BASE(i) (0x84 + ((i) << 3))
|
||||
#define SDHCI_MAX_WIN_NUM 8
|
||||
|
||||
static int mv_conf_mbus_windows(struct platform_device *pdev,
|
||||
const struct mbus_dram_target_info *dram)
|
||||
{
|
||||
int i;
|
||||
void __iomem *regs;
|
||||
struct resource *res;
|
||||
|
||||
if (!dram) {
|
||||
dev_err(&pdev->dev, "no mbus dram info\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "cannot get mbus registers\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regs = ioremap(res->start, resource_size(res));
|
||||
if (!regs) {
|
||||
dev_err(&pdev->dev, "cannot map mbus registers\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < SDHCI_MAX_WIN_NUM; i++) {
|
||||
writel(0, regs + SDHCI_WINDOW_CTRL(i));
|
||||
writel(0, regs + SDHCI_WINDOW_BASE(i));
|
||||
}
|
||||
|
||||
for (i = 0; i < dram->num_cs; i++) {
|
||||
const struct mbus_dram_window *cs = dram->cs + i;
|
||||
|
||||
/* Write size, attributes and target id to control register */
|
||||
writel(((cs->size - 1) & 0xffff0000) |
|
||||
(cs->mbus_attr << 8) |
|
||||
(dram->mbus_dram_target_id << 4) | 1,
|
||||
regs + SDHCI_WINDOW_CTRL(i));
|
||||
/* Write base address to base register */
|
||||
writel(cs->base, regs + SDHCI_WINDOW_BASE(i));
|
||||
}
|
||||
|
||||
iounmap(regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pxav3_set_private_registers(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
|
||||
@ -187,6 +242,9 @@ static const struct of_device_id sdhci_pxav3_of_match[] = {
|
||||
{
|
||||
.compatible = "mrvl,pxav3-mmc",
|
||||
},
|
||||
{
|
||||
.compatible = "marvell,armada-380-sdhci",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_pxav3_of_match);
|
||||
@ -219,6 +277,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct sdhci_host *host = NULL;
|
||||
struct sdhci_pxa *pxa = NULL;
|
||||
const struct of_device_id *match;
|
||||
@ -235,6 +294,14 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||
kfree(pxa);
|
||||
return PTR_ERR(host);
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) {
|
||||
ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info());
|
||||
if (ret < 0)
|
||||
goto err_mbus_win;
|
||||
}
|
||||
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = pxa;
|
||||
|
||||
@ -321,6 +388,7 @@ err_add_host:
|
||||
clk_disable_unprepare(clk);
|
||||
clk_put(clk);
|
||||
err_clk_get:
|
||||
err_mbus_win:
|
||||
sdhci_pltfm_free(pdev);
|
||||
kfree(pxa);
|
||||
return ret;
|
||||
|
@ -51,12 +51,13 @@ struct sdhci_s3c {
|
||||
struct platform_device *pdev;
|
||||
struct resource *ioarea;
|
||||
struct s3c_sdhci_platdata *pdata;
|
||||
unsigned int cur_clk;
|
||||
int cur_clk;
|
||||
int ext_cd_irq;
|
||||
int ext_cd_gpio;
|
||||
|
||||
struct clk *clk_io;
|
||||
struct clk *clk_bus[MAX_BUS_CLK];
|
||||
unsigned long clk_rates[MAX_BUS_CLK];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -76,32 +77,6 @@ static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host)
|
||||
return sdhci_priv(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_curclk - convert ctrl2 register to clock source number
|
||||
* @ctrl2: Control2 register value.
|
||||
*/
|
||||
static u32 get_curclk(u32 ctrl2)
|
||||
{
|
||||
ctrl2 &= S3C_SDHCI_CTRL2_SELBASECLK_MASK;
|
||||
ctrl2 >>= S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
|
||||
|
||||
return ctrl2;
|
||||
}
|
||||
|
||||
static void sdhci_s3c_check_sclk(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_s3c *ourhost = to_s3c(host);
|
||||
u32 tmp = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
|
||||
|
||||
if (get_curclk(tmp) != ourhost->cur_clk) {
|
||||
dev_dbg(&ourhost->pdev->dev, "restored ctrl2 clock setting\n");
|
||||
|
||||
tmp &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
|
||||
tmp |= ourhost->cur_clk << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
|
||||
writel(tmp, host->ioaddr + S3C_SDHCI_CONTROL2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sdhci_s3c_get_max_clk - callback to get maximum clock frequency.
|
||||
* @host: The SDHCI host instance.
|
||||
@ -111,20 +86,11 @@ static void sdhci_s3c_check_sclk(struct sdhci_host *host)
|
||||
static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_s3c *ourhost = to_s3c(host);
|
||||
struct clk *busclk;
|
||||
unsigned int rate, max;
|
||||
int clk;
|
||||
unsigned long rate, max = 0;
|
||||
int src;
|
||||
|
||||
/* note, a reset will reset the clock source */
|
||||
|
||||
sdhci_s3c_check_sclk(host);
|
||||
|
||||
for (max = 0, clk = 0; clk < MAX_BUS_CLK; clk++) {
|
||||
busclk = ourhost->clk_bus[clk];
|
||||
if (!busclk)
|
||||
continue;
|
||||
|
||||
rate = clk_get_rate(busclk);
|
||||
for (src = 0; src < MAX_BUS_CLK; src++) {
|
||||
rate = ourhost->clk_rates[src];
|
||||
if (rate > max)
|
||||
max = rate;
|
||||
}
|
||||
@ -144,9 +110,9 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
|
||||
{
|
||||
unsigned long rate;
|
||||
struct clk *clksrc = ourhost->clk_bus[src];
|
||||
int div;
|
||||
int shift;
|
||||
|
||||
if (!clksrc)
|
||||
if (IS_ERR(clksrc))
|
||||
return UINT_MAX;
|
||||
|
||||
/*
|
||||
@ -158,17 +124,24 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
|
||||
return wanted - rate;
|
||||
}
|
||||
|
||||
rate = clk_get_rate(clksrc);
|
||||
rate = ourhost->clk_rates[src];
|
||||
|
||||
for (div = 1; div < 256; div *= 2) {
|
||||
if ((rate / div) <= wanted)
|
||||
for (shift = 0; shift <= 8; ++shift) {
|
||||
if ((rate >> shift) <= wanted)
|
||||
break;
|
||||
}
|
||||
|
||||
dev_dbg(&ourhost->pdev->dev, "clk %d: rate %ld, want %d, got %ld\n",
|
||||
src, rate, wanted, rate / div);
|
||||
if (shift > 8) {
|
||||
dev_dbg(&ourhost->pdev->dev,
|
||||
"clk %d: rate %ld, min rate %lu > wanted %u\n",
|
||||
src, rate, rate / 256, wanted);
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
return wanted - (rate / div);
|
||||
dev_dbg(&ourhost->pdev->dev, "clk %d: rate %ld, want %d, got %ld\n",
|
||||
src, rate, wanted, rate >> shift);
|
||||
|
||||
return wanted - (rate >> shift);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -209,20 +182,22 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
struct clk *clk = ourhost->clk_bus[best_src];
|
||||
|
||||
clk_prepare_enable(clk);
|
||||
clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
|
||||
|
||||
/* turn clock off to card before changing clock source */
|
||||
writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
|
||||
if (ourhost->cur_clk >= 0)
|
||||
clk_disable_unprepare(
|
||||
ourhost->clk_bus[ourhost->cur_clk]);
|
||||
|
||||
ourhost->cur_clk = best_src;
|
||||
host->max_clk = clk_get_rate(clk);
|
||||
|
||||
ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
|
||||
ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
|
||||
ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
|
||||
writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
|
||||
host->max_clk = ourhost->clk_rates[best_src];
|
||||
}
|
||||
|
||||
/* turn clock off to card before changing clock source */
|
||||
writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
|
||||
|
||||
ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
|
||||
ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
|
||||
ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
|
||||
writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
|
||||
|
||||
/* reprogram default hardware configuration */
|
||||
writel(S3C64XX_SDHCI_CONTROL4_DRIVE_9mA,
|
||||
host->ioaddr + S3C64XX_SDHCI_CONTROL4);
|
||||
@ -254,17 +229,17 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_s3c *ourhost = to_s3c(host);
|
||||
unsigned int delta, min = UINT_MAX;
|
||||
unsigned long rate, min = ULONG_MAX;
|
||||
int src;
|
||||
|
||||
for (src = 0; src < MAX_BUS_CLK; src++) {
|
||||
delta = sdhci_s3c_consider_clock(ourhost, src, 0);
|
||||
if (delta == UINT_MAX)
|
||||
rate = ourhost->clk_rates[src] / 256;
|
||||
if (!rate)
|
||||
continue;
|
||||
/* delta is a negative value in this case */
|
||||
if (-delta < min)
|
||||
min = -delta;
|
||||
if (rate < min)
|
||||
min = rate;
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
@ -272,20 +247,44 @@ static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)
|
||||
static unsigned int sdhci_cmu_get_max_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_s3c *ourhost = to_s3c(host);
|
||||
unsigned long rate, max = 0;
|
||||
int src;
|
||||
|
||||
return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], UINT_MAX);
|
||||
for (src = 0; src < MAX_BUS_CLK; src++) {
|
||||
struct clk *clk;
|
||||
|
||||
clk = ourhost->clk_bus[src];
|
||||
if (IS_ERR(clk))
|
||||
continue;
|
||||
|
||||
rate = clk_round_rate(clk, ULONG_MAX);
|
||||
if (rate > max)
|
||||
max = rate;
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
/* sdhci_cmu_get_min_clock - callback to get minimal supported clock value. */
|
||||
static unsigned int sdhci_cmu_get_min_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_s3c *ourhost = to_s3c(host);
|
||||
unsigned long rate, min = ULONG_MAX;
|
||||
int src;
|
||||
|
||||
/*
|
||||
* initial clock can be in the frequency range of
|
||||
* 100KHz-400KHz, so we set it as max value.
|
||||
*/
|
||||
return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], 400000);
|
||||
for (src = 0; src < MAX_BUS_CLK; src++) {
|
||||
struct clk *clk;
|
||||
|
||||
clk = ourhost->clk_bus[src];
|
||||
if (IS_ERR(clk))
|
||||
continue;
|
||||
|
||||
rate = clk_round_rate(clk, 0);
|
||||
if (rate < min)
|
||||
min = rate;
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
/* sdhci_cmu_set_clock - callback on clock change.*/
|
||||
@ -552,6 +551,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
||||
sc->host = host;
|
||||
sc->pdev = pdev;
|
||||
sc->pdata = pdata;
|
||||
sc->cur_clk = -1;
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
@ -566,25 +566,18 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
||||
clk_prepare_enable(sc->clk_io);
|
||||
|
||||
for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
|
||||
struct clk *clk;
|
||||
char name[14];
|
||||
|
||||
snprintf(name, 14, "mmc_busclk.%d", ptr);
|
||||
clk = devm_clk_get(dev, name);
|
||||
if (IS_ERR(clk))
|
||||
sc->clk_bus[ptr] = devm_clk_get(dev, name);
|
||||
if (IS_ERR(sc->clk_bus[ptr]))
|
||||
continue;
|
||||
|
||||
clks++;
|
||||
sc->clk_bus[ptr] = clk;
|
||||
|
||||
/*
|
||||
* save current clock index to know which clock bus
|
||||
* is used later in overriding functions.
|
||||
*/
|
||||
sc->cur_clk = ptr;
|
||||
sc->clk_rates[ptr] = clk_get_rate(sc->clk_bus[ptr]);
|
||||
|
||||
dev_info(dev, "clock source %d: %s (%ld Hz)\n",
|
||||
ptr, name, clk_get_rate(clk));
|
||||
ptr, name, sc->clk_rates[ptr]);
|
||||
}
|
||||
|
||||
if (clks == 0) {
|
||||
@ -593,10 +586,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
||||
goto err_no_busclks;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_PM_RUNTIME
|
||||
clk_prepare_enable(sc->clk_bus[sc->cur_clk]);
|
||||
#endif
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->ioaddr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(host->ioaddr)) {
|
||||
@ -709,10 +698,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
err_req_regs:
|
||||
#ifndef CONFIG_PM_RUNTIME
|
||||
clk_disable_unprepare(sc->clk_bus[sc->cur_clk]);
|
||||
#endif
|
||||
|
||||
err_no_busclks:
|
||||
clk_disable_unprepare(sc->clk_io);
|
||||
|
||||
@ -743,9 +728,6 @@ static int sdhci_s3c_remove(struct platform_device *pdev)
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
#ifndef CONFIG_PM_RUNTIME
|
||||
clk_disable_unprepare(sc->clk_bus[sc->cur_clk]);
|
||||
#endif
|
||||
clk_disable_unprepare(sc->clk_io);
|
||||
|
||||
sdhci_free_host(host);
|
||||
@ -779,7 +761,8 @@ static int sdhci_s3c_runtime_suspend(struct device *dev)
|
||||
|
||||
ret = sdhci_runtime_suspend_host(host);
|
||||
|
||||
clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
|
||||
if (ourhost->cur_clk >= 0)
|
||||
clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
|
||||
clk_disable_unprepare(busclk);
|
||||
return ret;
|
||||
}
|
||||
@ -792,7 +775,8 @@ static int sdhci_s3c_runtime_resume(struct device *dev)
|
||||
int ret;
|
||||
|
||||
clk_prepare_enable(busclk);
|
||||
clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]);
|
||||
if (ourhost->cur_clk >= 0)
|
||||
clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]);
|
||||
ret = sdhci_runtime_resume_host(host);
|
||||
return ret;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/sdhci-spear.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include "sdhci.h"
|
||||
|
||||
@ -40,36 +41,6 @@ static const struct sdhci_ops sdhci_pltfm_ops = {
|
||||
/* Nothing to do for now. */
|
||||
};
|
||||
|
||||
/* gpio card detection interrupt handler */
|
||||
static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct platform_device *pdev = dev_id;
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
|
||||
unsigned long gpio_irq_type;
|
||||
int val;
|
||||
|
||||
val = gpio_get_value(sdhci->data->card_int_gpio);
|
||||
|
||||
/* val == 1 -> card removed, val == 0 -> card inserted */
|
||||
/* if card removed - set irq for low level, else vice versa */
|
||||
gpio_irq_type = val ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH;
|
||||
irq_set_irq_type(irq, gpio_irq_type);
|
||||
|
||||
if (sdhci->data->card_power_gpio >= 0) {
|
||||
if (!sdhci->data->power_always_enb) {
|
||||
/* if card inserted, give power, otherwise remove it */
|
||||
val = sdhci->data->power_active_high ? !val : val ;
|
||||
gpio_set_value(sdhci->data->card_power_gpio, val);
|
||||
}
|
||||
}
|
||||
|
||||
/* inform sdhci driver about card insertion/removal */
|
||||
tasklet_schedule(&host->card_tasklet);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev)
|
||||
{
|
||||
@ -84,14 +55,12 @@ static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pde
|
||||
/* If pdata is required */
|
||||
if (cd_gpio != -1) {
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
if (!pdata)
|
||||
dev_err(&pdev->dev, "DT: kzalloc failed\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
else
|
||||
pdata->card_int_gpio = cd_gpio;
|
||||
}
|
||||
|
||||
pdata->card_int_gpio = cd_gpio;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
@ -107,41 +76,44 @@ static int sdhci_probe(struct platform_device *pdev)
|
||||
struct sdhci_host *host;
|
||||
struct resource *iomem;
|
||||
struct spear_sdhci *sdhci;
|
||||
struct device *dev;
|
||||
int ret;
|
||||
|
||||
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!iomem) {
|
||||
ret = -ENOMEM;
|
||||
dev_dbg(&pdev->dev, "memory resource not defined\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!devm_request_mem_region(&pdev->dev, iomem->start,
|
||||
resource_size(iomem), "spear-sdhci")) {
|
||||
ret = -EBUSY;
|
||||
dev_dbg(&pdev->dev, "cannot request region\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
sdhci = devm_kzalloc(&pdev->dev, sizeof(*sdhci), GFP_KERNEL);
|
||||
if (!sdhci) {
|
||||
ret = -ENOMEM;
|
||||
dev = pdev->dev.parent ? pdev->dev.parent : &pdev->dev;
|
||||
host = sdhci_alloc_host(dev, sizeof(*sdhci));
|
||||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
|
||||
if (IS_ERR(host->ioaddr)) {
|
||||
ret = PTR_ERR(host->ioaddr);
|
||||
dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret);
|
||||
goto err_host;
|
||||
}
|
||||
|
||||
host->hw_name = "sdhci";
|
||||
host->ops = &sdhci_pltfm_ops;
|
||||
host->irq = platform_get_irq(pdev, 0);
|
||||
host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
|
||||
|
||||
sdhci = sdhci_priv(host);
|
||||
|
||||
/* clk enable */
|
||||
sdhci->clk = clk_get(&pdev->dev, NULL);
|
||||
sdhci->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(sdhci->clk)) {
|
||||
ret = PTR_ERR(sdhci->clk);
|
||||
dev_dbg(&pdev->dev, "Error getting clock\n");
|
||||
goto err;
|
||||
goto err_host;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sdhci->clk);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "Error enabling clock\n");
|
||||
goto put_clk;
|
||||
goto err_host;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(sdhci->clk, 50000000);
|
||||
@ -153,118 +125,42 @@ static int sdhci_probe(struct platform_device *pdev)
|
||||
sdhci->data = sdhci_probe_config_dt(pdev);
|
||||
if (IS_ERR(sdhci->data)) {
|
||||
dev_err(&pdev->dev, "DT: Failed to get pdata\n");
|
||||
return -ENODEV;
|
||||
goto disable_clk;
|
||||
}
|
||||
} else {
|
||||
sdhci->data = dev_get_platdata(&pdev->dev);
|
||||
}
|
||||
|
||||
pdev->dev.platform_data = sdhci;
|
||||
|
||||
if (pdev->dev.parent)
|
||||
host = sdhci_alloc_host(pdev->dev.parent, 0);
|
||||
else
|
||||
host = sdhci_alloc_host(&pdev->dev, 0);
|
||||
|
||||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
dev_dbg(&pdev->dev, "error allocating host\n");
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
host->hw_name = "sdhci";
|
||||
host->ops = &sdhci_pltfm_ops;
|
||||
host->irq = platform_get_irq(pdev, 0);
|
||||
host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
|
||||
|
||||
host->ioaddr = devm_ioremap(&pdev->dev, iomem->start,
|
||||
resource_size(iomem));
|
||||
if (!host->ioaddr) {
|
||||
ret = -ENOMEM;
|
||||
dev_dbg(&pdev->dev, "failed to remap registers\n");
|
||||
goto free_host;
|
||||
/*
|
||||
* It is optional to use GPIOs for sdhci card detection. If
|
||||
* sdhci->data is NULL, then use original sdhci lines otherwise
|
||||
* GPIO lines. We use the built-in GPIO support for this.
|
||||
*/
|
||||
if (sdhci->data && sdhci->data->card_int_gpio >= 0) {
|
||||
ret = mmc_gpio_request_cd(host->mmc,
|
||||
sdhci->data->card_int_gpio, 0);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"failed to request card-detect gpio%d\n",
|
||||
sdhci->data->card_int_gpio);
|
||||
goto disable_clk;
|
||||
}
|
||||
}
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "error adding host\n");
|
||||
goto free_host;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
/*
|
||||
* It is optional to use GPIOs for sdhci Power control & sdhci card
|
||||
* interrupt detection. If sdhci->data is NULL, then use original sdhci
|
||||
* lines otherwise GPIO lines.
|
||||
* If GPIO is selected for power control, then power should be disabled
|
||||
* after card removal and should be enabled when card insertion
|
||||
* interrupt occurs
|
||||
*/
|
||||
if (!sdhci->data)
|
||||
return 0;
|
||||
|
||||
if (sdhci->data->card_power_gpio >= 0) {
|
||||
int val = 0;
|
||||
|
||||
ret = devm_gpio_request(&pdev->dev,
|
||||
sdhci->data->card_power_gpio, "sdhci");
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev, "gpio request fail: %d\n",
|
||||
sdhci->data->card_power_gpio);
|
||||
goto set_drvdata;
|
||||
}
|
||||
|
||||
if (sdhci->data->power_always_enb)
|
||||
val = sdhci->data->power_active_high;
|
||||
else
|
||||
val = !sdhci->data->power_active_high;
|
||||
|
||||
ret = gpio_direction_output(sdhci->data->card_power_gpio, val);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
|
||||
sdhci->data->card_power_gpio);
|
||||
goto set_drvdata;
|
||||
}
|
||||
}
|
||||
|
||||
if (sdhci->data->card_int_gpio >= 0) {
|
||||
ret = devm_gpio_request(&pdev->dev, sdhci->data->card_int_gpio,
|
||||
"sdhci");
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev, "gpio request fail: %d\n",
|
||||
sdhci->data->card_int_gpio);
|
||||
goto set_drvdata;
|
||||
}
|
||||
|
||||
ret = gpio_direction_input(sdhci->data->card_int_gpio);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
|
||||
sdhci->data->card_int_gpio);
|
||||
goto set_drvdata;
|
||||
}
|
||||
ret = devm_request_irq(&pdev->dev,
|
||||
gpio_to_irq(sdhci->data->card_int_gpio),
|
||||
sdhci_gpio_irq, IRQF_TRIGGER_LOW,
|
||||
mmc_hostname(host->mmc), pdev);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "gpio request irq fail: %d\n",
|
||||
sdhci->data->card_int_gpio);
|
||||
goto set_drvdata;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
set_drvdata:
|
||||
sdhci_remove_host(host, 1);
|
||||
free_host:
|
||||
sdhci_free_host(host);
|
||||
disable_clk:
|
||||
clk_disable_unprepare(sdhci->clk);
|
||||
put_clk:
|
||||
clk_put(sdhci->clk);
|
||||
err_host:
|
||||
sdhci_free_host(host);
|
||||
err:
|
||||
dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);
|
||||
return ret;
|
||||
@ -273,7 +169,7 @@ err:
|
||||
static int sdhci_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
|
||||
struct spear_sdhci *sdhci = sdhci_priv(host);
|
||||
int dead = 0;
|
||||
u32 scratch;
|
||||
|
||||
@ -282,9 +178,8 @@ static int sdhci_remove(struct platform_device *pdev)
|
||||
dead = 1;
|
||||
|
||||
sdhci_remove_host(host, dead);
|
||||
sdhci_free_host(host);
|
||||
clk_disable_unprepare(sdhci->clk);
|
||||
clk_put(sdhci->clk);
|
||||
sdhci_free_host(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -293,7 +188,7 @@ static int sdhci_remove(struct platform_device *pdev)
|
||||
static int sdhci_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct spear_sdhci *sdhci = dev_get_platdata(dev);
|
||||
struct spear_sdhci *sdhci = sdhci_priv(host);
|
||||
int ret;
|
||||
|
||||
ret = sdhci_suspend_host(host);
|
||||
@ -306,7 +201,7 @@ static int sdhci_suspend(struct device *dev)
|
||||
static int sdhci_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct spear_sdhci *sdhci = dev_get_platdata(dev);
|
||||
struct spear_sdhci *sdhci = sdhci_priv(host);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(sdhci->clk);
|
||||
|
@ -675,12 +675,12 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
return 0xE;
|
||||
|
||||
/* Unspecified timeout, assume max */
|
||||
if (!data && !cmd->cmd_timeout_ms)
|
||||
if (!data && !cmd->busy_timeout)
|
||||
return 0xE;
|
||||
|
||||
/* timeout in us */
|
||||
if (!data)
|
||||
target_timeout = cmd->cmd_timeout_ms * 1000;
|
||||
target_timeout = cmd->busy_timeout * 1000;
|
||||
else {
|
||||
target_timeout = data->timeout_ns / 1000;
|
||||
if (host->clock)
|
||||
@ -1019,8 +1019,8 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
}
|
||||
|
||||
timeout = jiffies;
|
||||
if (!cmd->data && cmd->cmd_timeout_ms > 9000)
|
||||
timeout += DIV_ROUND_UP(cmd->cmd_timeout_ms, 1000) * HZ + HZ;
|
||||
if (!cmd->data && cmd->busy_timeout > 9000)
|
||||
timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
|
||||
else
|
||||
timeout += 10 * HZ;
|
||||
mod_timer(&host->timer, timeout);
|
||||
@ -2026,12 +2026,11 @@ out:
|
||||
host->tuning_count * HZ);
|
||||
/* Tuning mode 1 limits the maximum data length to 4MB */
|
||||
mmc->max_blk_count = (4 * 1024 * 1024) / mmc->max_blk_size;
|
||||
} else {
|
||||
} else if (host->flags & SDHCI_USING_RETUNING_TIMER) {
|
||||
host->flags &= ~SDHCI_NEEDS_RETUNING;
|
||||
/* Reload the new initial value for timer */
|
||||
if (host->tuning_mode == SDHCI_TUNING_MODE_1)
|
||||
mod_timer(&host->tuning_timer, jiffies +
|
||||
host->tuning_count * HZ);
|
||||
mod_timer(&host->tuning_timer, jiffies +
|
||||
host->tuning_count * HZ);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2434,9 +2433,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
|
||||
|
||||
if (host->runtime_suspended) {
|
||||
spin_unlock(&host->lock);
|
||||
pr_warning("%s: got irq while runtime suspended\n",
|
||||
mmc_hostname(host->mmc));
|
||||
return IRQ_HANDLED;
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
|
||||
@ -2941,7 +2938,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||
if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
|
||||
host->timeout_clk = mmc->f_max / 1000;
|
||||
|
||||
mmc->max_discard_to = (1 << 27) / host->timeout_clk;
|
||||
mmc->max_busy_timeout = (1 << 27) / host->timeout_clk;
|
||||
|
||||
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
|
||||
|
||||
@ -3020,7 +3017,8 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||
} else if (caps[1] & SDHCI_SUPPORT_SDR50)
|
||||
mmc->caps |= MMC_CAP_UHS_SDR50;
|
||||
|
||||
if (caps[1] & SDHCI_SUPPORT_DDR50)
|
||||
if ((caps[1] & SDHCI_SUPPORT_DDR50) &&
|
||||
!(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50))
|
||||
mmc->caps |= MMC_CAP_UHS_DDR50;
|
||||
|
||||
/* Does the host need tuning for SDR50? */
|
||||
|
@ -37,6 +37,8 @@
|
||||
|
||||
struct sh_mobile_sdhi_of_data {
|
||||
unsigned long tmio_flags;
|
||||
unsigned long capabilities;
|
||||
unsigned long capabilities2;
|
||||
};
|
||||
|
||||
static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
|
||||
@ -45,6 +47,31 @@ static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct sh_mobile_sdhi_of_data of_rcar_gen1_compatible = {
|
||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE,
|
||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
|
||||
};
|
||||
|
||||
static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = {
|
||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE,
|
||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
|
||||
.capabilities2 = MMC_CAP2_NO_MULTI_READ,
|
||||
};
|
||||
|
||||
static const struct of_device_id sh_mobile_sdhi_of_match[] = {
|
||||
{ .compatible = "renesas,sdhi-shmobile" },
|
||||
{ .compatible = "renesas,sdhi-sh7372" },
|
||||
{ .compatible = "renesas,sdhi-sh73a0", .data = &sh_mobile_sdhi_of_cfg[0], },
|
||||
{ .compatible = "renesas,sdhi-r8a73a4", .data = &sh_mobile_sdhi_of_cfg[0], },
|
||||
{ .compatible = "renesas,sdhi-r8a7740", .data = &sh_mobile_sdhi_of_cfg[0], },
|
||||
{ .compatible = "renesas,sdhi-r8a7778", .data = &of_rcar_gen1_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7779", .data = &of_rcar_gen1_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7790", .data = &of_rcar_gen2_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7791", .data = &of_rcar_gen2_compatible, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
|
||||
|
||||
struct sh_mobile_sdhi {
|
||||
struct clk *clk;
|
||||
struct tmio_mmc_data mmc_data;
|
||||
@ -114,19 +141,6 @@ static const struct sh_mobile_sdhi_ops sdhi_ops = {
|
||||
.cd_wakeup = sh_mobile_sdhi_cd_wakeup,
|
||||
};
|
||||
|
||||
static const struct of_device_id sh_mobile_sdhi_of_match[] = {
|
||||
{ .compatible = "renesas,sdhi-shmobile" },
|
||||
{ .compatible = "renesas,sdhi-sh7372" },
|
||||
{ .compatible = "renesas,sdhi-sh73a0", .data = &sh_mobile_sdhi_of_cfg[0], },
|
||||
{ .compatible = "renesas,sdhi-r8a73a4", .data = &sh_mobile_sdhi_of_cfg[0], },
|
||||
{ .compatible = "renesas,sdhi-r8a7740", .data = &sh_mobile_sdhi_of_cfg[0], },
|
||||
{ .compatible = "renesas,sdhi-r8a7778", .data = &sh_mobile_sdhi_of_cfg[0], },
|
||||
{ .compatible = "renesas,sdhi-r8a7779", .data = &sh_mobile_sdhi_of_cfg[0], },
|
||||
{ .compatible = "renesas,sdhi-r8a7790", .data = &sh_mobile_sdhi_of_cfg[0], },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
|
||||
|
||||
static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *of_id =
|
||||
@ -212,6 +226,8 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||
if (of_id && of_id->data) {
|
||||
const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
|
||||
mmc_data->flags |= of_data->tmio_flags;
|
||||
mmc_data->capabilities |= of_data->capabilities;
|
||||
mmc_data->capabilities2 |= of_data->capabilities2;
|
||||
}
|
||||
|
||||
/* SD control register space size is 0x100, 0x200 for bus_shift=1 */
|
||||
@ -316,10 +332,10 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
|
||||
.suspend = tmio_mmc_host_suspend,
|
||||
.resume = tmio_mmc_host_resume,
|
||||
.runtime_suspend = tmio_mmc_host_runtime_suspend,
|
||||
.runtime_resume = tmio_mmc_host_runtime_resume,
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_host_suspend, tmio_mmc_host_resume)
|
||||
SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
|
||||
tmio_mmc_host_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver sh_mobile_sdhi_driver = {
|
||||
|
@ -23,38 +23,37 @@
|
||||
|
||||
#include "tmio_mmc.h"
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tmio_mmc_suspend(struct device *dev)
|
||||
{
|
||||
const struct mfd_cell *cell = mfd_get_cell(dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
const struct mfd_cell *cell = mfd_get_cell(pdev);
|
||||
int ret;
|
||||
|
||||
ret = tmio_mmc_host_suspend(&dev->dev);
|
||||
ret = tmio_mmc_host_suspend(dev);
|
||||
|
||||
/* Tell MFD core it can disable us now.*/
|
||||
if (!ret && cell->disable)
|
||||
cell->disable(dev);
|
||||
cell->disable(pdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tmio_mmc_resume(struct platform_device *dev)
|
||||
static int tmio_mmc_resume(struct device *dev)
|
||||
{
|
||||
const struct mfd_cell *cell = mfd_get_cell(dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
const struct mfd_cell *cell = mfd_get_cell(pdev);
|
||||
int ret = 0;
|
||||
|
||||
/* Tell the MFD core we are ready to be enabled */
|
||||
if (cell->resume)
|
||||
ret = cell->resume(dev);
|
||||
ret = cell->resume(pdev);
|
||||
|
||||
if (!ret)
|
||||
ret = tmio_mmc_host_resume(&dev->dev);
|
||||
ret = tmio_mmc_host_resume(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#define tmio_mmc_suspend NULL
|
||||
#define tmio_mmc_resume NULL
|
||||
#endif
|
||||
|
||||
static int tmio_mmc_probe(struct platform_device *pdev)
|
||||
@ -134,15 +133,18 @@ static int tmio_mmc_remove(struct platform_device *pdev)
|
||||
|
||||
/* ------------------- device registration ----------------------- */
|
||||
|
||||
static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_suspend, tmio_mmc_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver tmio_mmc_driver = {
|
||||
.driver = {
|
||||
.name = "tmio-mmc",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &tmio_mmc_dev_pm_ops,
|
||||
},
|
||||
.probe = tmio_mmc_probe,
|
||||
.remove = tmio_mmc_remove,
|
||||
.suspend = tmio_mmc_suspend,
|
||||
.resume = tmio_mmc_resume,
|
||||
};
|
||||
|
||||
module_platform_driver(tmio_mmc_driver);
|
||||
|
@ -162,16 +162,15 @@ static inline void tmio_mmc_abort_dma(struct tmio_mmc_host *host)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
int tmio_mmc_host_suspend(struct device *dev);
|
||||
int tmio_mmc_host_resume(struct device *dev);
|
||||
#else
|
||||
#define tmio_mmc_host_suspend NULL
|
||||
#define tmio_mmc_host_resume NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
int tmio_mmc_host_runtime_suspend(struct device *dev);
|
||||
int tmio_mmc_host_runtime_resume(struct device *dev);
|
||||
#endif
|
||||
|
||||
static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
|
||||
{
|
||||
|
@ -1142,7 +1142,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_remove);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
int tmio_mmc_host_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
@ -1165,9 +1165,9 @@ int tmio_mmc_host_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_resume);
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
int tmio_mmc_host_runtime_suspend(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
@ -1184,5 +1184,6 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_runtime_resume);
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -504,7 +504,7 @@ static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
ushc->csw = kzalloc(sizeof(struct ushc_cbw), GFP_KERNEL);
|
||||
ushc->csw = kzalloc(sizeof(struct ushc_csw), GFP_KERNEL);
|
||||
if (ushc->csw == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
|
@ -757,7 +757,7 @@ static int wmt_mci_probe(struct platform_device *pdev)
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(wmt_mci_dt_ids, &pdev->dev);
|
||||
const struct wmt_mci_caps *wmt_caps = of_id->data;
|
||||
const struct wmt_mci_caps *wmt_caps;
|
||||
int ret;
|
||||
int regular_irq, dma_irq;
|
||||
|
||||
@ -766,6 +766,8 @@ static int wmt_mci_probe(struct platform_device *pdev)
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
wmt_caps = of_id->data;
|
||||
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "Missing SDMMC description in devicetree\n");
|
||||
return -EFAULT;
|
||||
|
@ -392,6 +392,15 @@ config REGULATOR_PALMAS
|
||||
on the muxing. This is handled automatically in the driver by
|
||||
reading the mux info from OTP.
|
||||
|
||||
config REGULATOR_PBIAS
|
||||
tristate "PBIAS OMAP regulator driver"
|
||||
depends on (ARCH_OMAP || COMPILE_TEST) && MFD_SYSCON
|
||||
help
|
||||
Say y here to support pbias regulator for mmc1:SD card i/o
|
||||
on OMAP SoCs.
|
||||
This driver provides support for OMAP pbias modelled
|
||||
regulators.
|
||||
|
||||
config REGULATOR_PCAP
|
||||
tristate "Motorola PCAP2 regulator driver"
|
||||
depends on EZX_PCAP
|
||||
|
@ -55,6 +55,7 @@ obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
|
||||
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o
|
||||
|
255
drivers/regulator/pbias-regulator.c
Normal file
255
drivers/regulator/pbias-regulator.c
Normal file
@ -0,0 +1,255 @@
|
||||
/*
|
||||
* pbias-regulator.c
|
||||
*
|
||||
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Author: Balaji T K <balajitk@ti.com>
|
||||
*
|
||||
* 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 the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
struct pbias_reg_info {
|
||||
u32 enable;
|
||||
u32 enable_mask;
|
||||
u32 vmode;
|
||||
unsigned int enable_time;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct pbias_regulator_data {
|
||||
struct regulator_desc desc;
|
||||
void __iomem *pbias_addr;
|
||||
unsigned int pbias_reg;
|
||||
struct regulator_dev *dev;
|
||||
struct regmap *syscon;
|
||||
const struct pbias_reg_info *info;
|
||||
int voltage;
|
||||
};
|
||||
|
||||
static int pbias_regulator_set_voltage(struct regulator_dev *dev,
|
||||
int min_uV, int max_uV, unsigned *selector)
|
||||
{
|
||||
struct pbias_regulator_data *data = rdev_get_drvdata(dev);
|
||||
const struct pbias_reg_info *info = data->info;
|
||||
int ret, vmode;
|
||||
|
||||
if (min_uV <= 1800000)
|
||||
vmode = 0;
|
||||
else if (min_uV > 1800000)
|
||||
vmode = info->vmode;
|
||||
|
||||
ret = regmap_update_bits(data->syscon, data->pbias_reg,
|
||||
info->vmode, vmode);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pbias_regulator_get_voltage(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
|
||||
const struct pbias_reg_info *info = data->info;
|
||||
int value, voltage;
|
||||
|
||||
regmap_read(data->syscon, data->pbias_reg, &value);
|
||||
value &= info->vmode;
|
||||
|
||||
voltage = value ? 3000000 : 1800000;
|
||||
|
||||
return voltage;
|
||||
}
|
||||
|
||||
static int pbias_regulator_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
|
||||
const struct pbias_reg_info *info = data->info;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(data->syscon, data->pbias_reg,
|
||||
info->enable_mask, info->enable);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pbias_regulator_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
|
||||
const struct pbias_reg_info *info = data->info;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(data->syscon, data->pbias_reg,
|
||||
info->enable_mask, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pbias_regulator_is_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
|
||||
const struct pbias_reg_info *info = data->info;
|
||||
int value;
|
||||
|
||||
regmap_read(data->syscon, data->pbias_reg, &value);
|
||||
|
||||
return (value & info->enable_mask) == info->enable_mask;
|
||||
}
|
||||
|
||||
static struct regulator_ops pbias_regulator_voltage_ops = {
|
||||
.set_voltage = pbias_regulator_set_voltage,
|
||||
.get_voltage = pbias_regulator_get_voltage,
|
||||
.enable = pbias_regulator_enable,
|
||||
.disable = pbias_regulator_disable,
|
||||
.is_enabled = pbias_regulator_is_enable,
|
||||
};
|
||||
|
||||
static const struct pbias_reg_info pbias_mmc_omap2430 = {
|
||||
.enable = BIT(1),
|
||||
.enable_mask = BIT(1),
|
||||
.vmode = BIT(0),
|
||||
.enable_time = 100,
|
||||
.name = "pbias_mmc_omap2430"
|
||||
};
|
||||
|
||||
static const struct pbias_reg_info pbias_sim_omap3 = {
|
||||
.enable = BIT(9),
|
||||
.enable_mask = BIT(9),
|
||||
.vmode = BIT(8),
|
||||
.enable_time = 100,
|
||||
.name = "pbias_sim_omap3"
|
||||
};
|
||||
|
||||
static const struct pbias_reg_info pbias_mmc_omap4 = {
|
||||
.enable = BIT(26) | BIT(22),
|
||||
.enable_mask = BIT(26) | BIT(25) | BIT(22),
|
||||
.vmode = BIT(21),
|
||||
.enable_time = 100,
|
||||
.name = "pbias_mmc_omap4"
|
||||
};
|
||||
|
||||
static const struct pbias_reg_info pbias_mmc_omap5 = {
|
||||
.enable = BIT(27) | BIT(26),
|
||||
.enable_mask = BIT(27) | BIT(25) | BIT(26),
|
||||
.vmode = BIT(21),
|
||||
.enable_time = 100,
|
||||
.name = "pbias_mmc_omap5"
|
||||
};
|
||||
|
||||
static struct of_regulator_match pbias_matches[] = {
|
||||
{ .name = "pbias_mmc_omap2430", .driver_data = (void *)&pbias_mmc_omap2430},
|
||||
{ .name = "pbias_sim_omap3", .driver_data = (void *)&pbias_sim_omap3},
|
||||
{ .name = "pbias_mmc_omap4", .driver_data = (void *)&pbias_mmc_omap4},
|
||||
{ .name = "pbias_mmc_omap5", .driver_data = (void *)&pbias_mmc_omap5},
|
||||
};
|
||||
#define PBIAS_NUM_REGS ARRAY_SIZE(pbias_matches)
|
||||
|
||||
static const struct of_device_id pbias_of_match[] = {
|
||||
{ .compatible = "ti,pbias-omap", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pbias_of_match);
|
||||
|
||||
static int pbias_regulator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct pbias_regulator_data *drvdata;
|
||||
struct resource *res;
|
||||
struct regulator_config cfg = { };
|
||||
struct regmap *syscon;
|
||||
const struct pbias_reg_info *info;
|
||||
int ret = 0;
|
||||
int count, idx, data_idx = 0;
|
||||
|
||||
count = of_regulator_match(&pdev->dev, np, pbias_matches,
|
||||
PBIAS_NUM_REGS);
|
||||
if (count < 0)
|
||||
return count;
|
||||
|
||||
drvdata = devm_kzalloc(&pdev->dev, sizeof(struct pbias_regulator_data)
|
||||
* count, GFP_KERNEL);
|
||||
if (drvdata == NULL) {
|
||||
dev_err(&pdev->dev, "Failed to allocate device data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
|
||||
if (IS_ERR(syscon))
|
||||
return PTR_ERR(syscon);
|
||||
|
||||
cfg.dev = &pdev->dev;
|
||||
|
||||
for (idx = 0; idx < PBIAS_NUM_REGS && data_idx < count; idx++) {
|
||||
if (!pbias_matches[idx].init_data ||
|
||||
!pbias_matches[idx].of_node)
|
||||
continue;
|
||||
|
||||
info = pbias_matches[idx].driver_data;
|
||||
if (!info)
|
||||
return -ENODEV;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
drvdata[data_idx].pbias_reg = res->start;
|
||||
drvdata[data_idx].syscon = syscon;
|
||||
drvdata[data_idx].info = info;
|
||||
drvdata[data_idx].desc.name = info->name;
|
||||
drvdata[data_idx].desc.owner = THIS_MODULE;
|
||||
drvdata[data_idx].desc.type = REGULATOR_VOLTAGE;
|
||||
drvdata[data_idx].desc.ops = &pbias_regulator_voltage_ops;
|
||||
drvdata[data_idx].desc.n_voltages = 2;
|
||||
drvdata[data_idx].desc.enable_time = info->enable_time;
|
||||
|
||||
cfg.init_data = pbias_matches[idx].init_data;
|
||||
cfg.driver_data = &drvdata[data_idx];
|
||||
cfg.of_node = pbias_matches[idx].of_node;
|
||||
|
||||
drvdata[data_idx].dev = devm_regulator_register(&pdev->dev,
|
||||
&drvdata[data_idx].desc, &cfg);
|
||||
if (IS_ERR(drvdata[data_idx].dev)) {
|
||||
ret = PTR_ERR(drvdata[data_idx].dev);
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to register regulator: %d\n", ret);
|
||||
goto err_regulator;
|
||||
}
|
||||
data_idx++;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
|
||||
err_regulator:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver pbias_regulator_driver = {
|
||||
.probe = pbias_regulator_probe,
|
||||
.driver = {
|
||||
.name = "pbias-regulator",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(pbias_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(pbias_regulator_driver);
|
||||
|
||||
MODULE_AUTHOR("Balaji T K <balajitk@ti.com>");
|
||||
MODULE_DESCRIPTION("pbias voltage regulator");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:pbias-regulator");
|
@ -45,6 +45,7 @@ struct platform_device;
|
||||
struct rtsx_slot {
|
||||
struct platform_device *p_dev;
|
||||
void (*card_event)(struct platform_device *p_dev);
|
||||
void (*done_transfer)(struct platform_device *p_dev);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -144,7 +144,7 @@
|
||||
#define HOST_TO_DEVICE 0
|
||||
#define DEVICE_TO_HOST 1
|
||||
|
||||
#define MAX_PHASE 31
|
||||
#define RTSX_PHASE_MAX 32
|
||||
#define RX_TUNING_CNT 3
|
||||
|
||||
/* SG descriptor */
|
||||
@ -943,6 +943,12 @@ void rtsx_pci_send_cmd_no_wait(struct rtsx_pcr *pcr);
|
||||
int rtsx_pci_send_cmd(struct rtsx_pcr *pcr, int timeout);
|
||||
int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist,
|
||||
int num_sg, bool read, int timeout);
|
||||
int rtsx_pci_dma_map_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist,
|
||||
int num_sg, bool read);
|
||||
int rtsx_pci_dma_unmap_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist,
|
||||
int num_sg, bool read);
|
||||
int rtsx_pci_dma_transfer(struct rtsx_pcr *pcr, struct scatterlist *sglist,
|
||||
int sg_count, bool read);
|
||||
int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len);
|
||||
int rtsx_pci_write_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len);
|
||||
int rtsx_pci_card_pull_ctl_enable(struct rtsx_pcr *pcr, int card);
|
||||
|
@ -95,7 +95,7 @@ struct mmc_command {
|
||||
* actively failing requests
|
||||
*/
|
||||
|
||||
unsigned int cmd_timeout_ms; /* in milliseconds */
|
||||
unsigned int busy_timeout; /* busy detect timeout in ms */
|
||||
/* Set this flag only for blocking sanitize request */
|
||||
bool sanitize_busy;
|
||||
|
||||
@ -152,7 +152,7 @@ extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
|
||||
struct mmc_command *, int);
|
||||
extern void mmc_start_bkops(struct mmc_card *card, bool from_exception);
|
||||
extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool,
|
||||
bool);
|
||||
bool, bool);
|
||||
extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
|
||||
extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
|
||||
|
||||
|
@ -264,15 +264,12 @@ struct mmc_host {
|
||||
u32 caps2; /* More host capabilities */
|
||||
|
||||
#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */
|
||||
#define MMC_CAP2_CACHE_CTRL (1 << 1) /* Allow cache control */
|
||||
#define MMC_CAP2_FULL_PWR_CYCLE (1 << 2) /* Can do full power cycle */
|
||||
#define MMC_CAP2_NO_MULTI_READ (1 << 3) /* Multiblock reads don't work */
|
||||
#define MMC_CAP2_NO_SLEEP_CMD (1 << 4) /* Don't allow sleep command */
|
||||
#define MMC_CAP2_HS200_1_8V_SDR (1 << 5) /* can support */
|
||||
#define MMC_CAP2_HS200_1_2V_SDR (1 << 6) /* can support */
|
||||
#define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \
|
||||
MMC_CAP2_HS200_1_2V_SDR)
|
||||
#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */
|
||||
#define MMC_CAP2_HC_ERASE_SZ (1 << 9) /* High-capacity erase size */
|
||||
#define MMC_CAP2_CD_ACTIVE_HIGH (1 << 10) /* Card-detect signal active high */
|
||||
#define MMC_CAP2_RO_ACTIVE_HIGH (1 << 11) /* Write-protect signal active high */
|
||||
@ -281,7 +278,6 @@ struct mmc_host {
|
||||
#define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \
|
||||
MMC_CAP2_PACKED_WR)
|
||||
#define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */
|
||||
#define MMC_CAP2_SANITIZE (1 << 15) /* Support Sanitize */
|
||||
|
||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||
|
||||
@ -304,7 +300,7 @@ struct mmc_host {
|
||||
unsigned int max_req_size; /* maximum number of bytes in one req */
|
||||
unsigned int max_blk_size; /* maximum size of one mmc block */
|
||||
unsigned int max_blk_count; /* maximum number of blocks in one req */
|
||||
unsigned int max_discard_to; /* max. discard timeout in ms */
|
||||
unsigned int max_busy_timeout; /* max busy timeout in ms */
|
||||
|
||||
/* private data */
|
||||
spinlock_t lock; /* lock for claim and bus ops */
|
||||
@ -388,8 +384,6 @@ int mmc_power_restore_host(struct mmc_host *host);
|
||||
void mmc_detect_change(struct mmc_host *, unsigned long delay);
|
||||
void mmc_request_done(struct mmc_host *, struct mmc_request *);
|
||||
|
||||
int mmc_cache_ctrl(struct mmc_host *, u8);
|
||||
|
||||
static inline void mmc_signal_sdio_irq(struct mmc_host *host)
|
||||
{
|
||||
host->ops->enable_sdio_irq(host, 0);
|
||||
@ -424,12 +418,9 @@ static inline int mmc_regulator_get_supply(struct mmc_host *mmc)
|
||||
|
||||
int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *);
|
||||
|
||||
/* Module parameter */
|
||||
extern bool mmc_assume_removable;
|
||||
|
||||
static inline int mmc_card_is_removable(struct mmc_host *host)
|
||||
{
|
||||
return !(host->caps & MMC_CAP_NONREMOVABLE) && mmc_assume_removable;
|
||||
return !(host->caps & MMC_CAP_NONREMOVABLE);
|
||||
}
|
||||
|
||||
static inline int mmc_card_keep_power(struct mmc_host *host)
|
||||
|
@ -18,17 +18,9 @@
|
||||
/*
|
||||
* struct sdhci_plat_data: spear sdhci platform data structure
|
||||
*
|
||||
* @card_power_gpio: gpio pin for enabling/disabling power to sdhci socket
|
||||
* @power_active_high: if set, enable power to sdhci socket by setting
|
||||
* card_power_gpio
|
||||
* @power_always_enb: If set, then enable power on probe, otherwise enable only
|
||||
* on card insertion and disable on card removal.
|
||||
* card_int_gpio: gpio pin used for card detection
|
||||
*/
|
||||
struct sdhci_plat_data {
|
||||
int card_power_gpio;
|
||||
int power_active_high;
|
||||
int power_always_enb;
|
||||
int card_int_gpio;
|
||||
};
|
||||
|
||||
|
@ -100,6 +100,8 @@ struct sdhci_host {
|
||||
#define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5)
|
||||
/* Controller does not support HS200 */
|
||||
#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6)
|
||||
/* Controller does not support DDR50 */
|
||||
#define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7)
|
||||
|
||||
int irq; /* Device IRQ */
|
||||
void __iomem *ioaddr; /* Mapped address */
|
||||
|
@ -22,4 +22,10 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
|
||||
unsigned int debounce);
|
||||
void mmc_gpio_free_cd(struct mmc_host *host);
|
||||
|
||||
int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
||||
unsigned int idx, bool override_active_level,
|
||||
unsigned int debounce);
|
||||
void mmc_gpiod_free_cd(struct mmc_host *host);
|
||||
void mmc_gpiod_request_cd_irq(struct mmc_host *host);
|
||||
|
||||
#endif
|
||||
|
@ -1,8 +1,5 @@
|
||||
/*
|
||||
* arch/arm/include/asm/mach/mmc.h
|
||||
*/
|
||||
#ifndef ASMARM_MACH_MMC_H
|
||||
#define ASMARM_MACH_MMC_H
|
||||
#ifndef __MMC_MSM_SDCC_H
|
||||
#define __MMC_MSM_SDCC_H
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
|
@ -1,13 +1,11 @@
|
||||
/*
|
||||
* arch/arm/plat-orion/include/plat/mvsdio.h
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#ifndef __MACH_MVSDIO_H
|
||||
#define __MACH_MVSDIO_H
|
||||
#ifndef __MMC_MVSDIO_H
|
||||
#define __MMC_MVSDIO_H
|
||||
|
||||
#include <linux/mbus.h>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user