whiterose

linux unikernel
Log | Files | Refs | README | LICENSE | git clone https://git.ne02ptzero.me/git/whiterose

commit 00d59fde8532b2d42e80909d2e58678755e04da9
parent 75f95da078b2891cd186f074ffc15a8e7c3f082d
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Fri, 28 Dec 2018 16:52:18 -0800

Merge tag 'mmc-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc

Pull MMC updates from Ulf Hansson:
 "This time, this pull request contains changes crossing subsystems and
  archs/platforms, which is mainly because of a bigger modernization of
  moving from legacy GPIO to GPIO descriptors for MMC (by Linus
  Walleij).

  Additionally, once again, I am funneling changes to
  drivers/misc/cardreader/* and drivers/memstick/* through my MMC tree,
  mostly due to that we lack a maintainer for these.

  Summary:

  MMC core:
   - Cleanup BKOPS support
   - Introduce MMC_CAP_SYNC_RUNTIME_PM
   - slot-gpio: Delete legacy slot GPIO handling

  MMC host:
   - alcor: Add new mmc host driver for Alcor Micro PCI based cardreader
   - bcm2835: Several improvements to better recover from errors
   - jz4740: Rework and fixup pre|post_req support
   - mediatek: Add support for SDIO IRQs
   - meson-gx: Improve clock phase management
   - meson-gx: Stop descriptor on errors
   - mmci: Complete the sbc error path by sending a stop command
   - renesas_sdhi/tmio: Fixup reset/resume operations
   - renesas_sdhi: Add support for r8a774c0 and R7S9210
   - renesas_sdhi: Whitelist R8A77990 SDHI
   - renesas_sdhi: Fixup eMMC HS400 compatibility issues for H3 and M3-W
   - rtsx_usb_sdmmc: Re-work card detection/removal support
   - rtsx_usb_sdmmc: Re-work runtime PM support
   - sdhci: Fix timeout loops for some variant drivers
   - sdhci: Improve support for error handling due to failing commands
   - sdhci-acpi/pci: Disable LED control for Intel BYT-based controllers
   - sdhci_am654: Add new SDHCI variant driver to support TI's AM654 SOCs
   - sdhci-of-esdhc: Add support for eMMC HS400 mode
   - sdhci-omap: Fixup reset support
   - sdhci-omap: Workaround errata regarding SDR104/HS200 tuning failures
   - sdhci-msm: Fixup sporadic write transfers issues for SDR104/HS200
   - sdhci-msm: Fixup dynamical clock gating issues
   - various: Complete converting all hosts into using slot GPIO descriptors

  Other:
   - Move GPIO mmc platform data for mips/sh/arm to GPIO descriptors
   - Add new Alcor Micro cardreader PCI driver
   - Support runtime power management for memstick rtsx_usb_ms driver
   - Use USB remote wakeups for card detection for rtsx_usb misc driver"

* tag 'mmc-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (99 commits)
  mmc: mediatek: Add MMC_CAP_SDIO_IRQ support
  mmc: renesas_sdhi_internal_dmac: Whitelist r8a774c0
  dt-bindings: mmc: renesas_sdhi: Add r8a774c0 support
  mmc: core: Cleanup BKOPS support
  mmc: core: Drop redundant check in mmc_send_hpi_cmd()
  mmc: sdhci-omap: Workaround errata regarding SDR104/HS200 tuning failures (i929)
  dt-bindings: sdhci-omap: Add note for cpu_thermal
  mmc: sdhci-acpi: Disable LED control for Intel BYT-based controllers
  mmc: sdhci-pci: Disable LED control for Intel BYT-based controllers
  mmc: sdhci: Add quirk to disable LED control
  mmc: mmci: add variant property to set command stop bit
  misc: alcor_pci: fix spelling mistake "invailid" -> "invalid"
  mmc: meson-gx: add signal resampling
  mmc: meson-gx: align default phase on soc vendor tree
  mmc: meson-gx: remove useless lock
  mmc: meson-gx: make sure the descriptor is stopped on errors
  mmc: sdhci_am654: Add Initial Support for AM654 SDHCI driver
  dt-bindings: mmc: sdhci-of-arasan: Add deprecated message for AM65
  dt-bindings: mmc: sdhci-am654: Document bindings for the host controllers on TI's AM654 SOCs
  mmc: sdhci-msm: avoid unused function warning
  ...

Diffstat:
MDocumentation/devicetree/bindings/mmc/arasan,sdhci.txt | 4++++
MDocumentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt | 1+
ADocumentation/devicetree/bindings/mmc/sdhci-am654.txt | 36++++++++++++++++++++++++++++++++++++
MDocumentation/devicetree/bindings/mmc/sdhci-msm.txt | 23++++++++++++++++++-----
MDocumentation/devicetree/bindings/mmc/sdhci-omap.txt | 2++
MDocumentation/devicetree/bindings/mmc/tmio_mmc.txt | 4+++-
March/arm/mach-ep93xx/simone.c | 14+++++++++++---
March/arm/mach-ep93xx/vision_ep9307.c | 17+++++++++++++----
March/arm/mach-imx/mach-pcm043.c | 17+++++++++++++----
March/arm/mach-pxa/balloon3.c | 3---
March/arm/mach-pxa/cm-x270.c | 18++++++++++++++----
March/arm/mach-pxa/cm-x300.c | 18++++++++++++------
March/arm/mach-pxa/colibri-evalboard.c | 40+++++++++++++++++++++++++++++++---------
March/arm/mach-pxa/colibri-pxa270-income.c | 19+++++++++++++++----
March/arm/mach-pxa/corgi.c | 20+++++++++++++++++---
March/arm/mach-pxa/csb726.c | 19+++++++++++++++----
March/arm/mach-pxa/em-x270.c | 34+++++++++++++---------------------
March/arm/mach-pxa/gumstix.c | 3---
March/arm/mach-pxa/idp.c | 3---
March/arm/mach-pxa/littleton.c | 18++++++++++++------
March/arm/mach-pxa/lubbock.c | 3---
March/arm/mach-pxa/magician.c | 26+++++++++++++++++++++++---
March/arm/mach-pxa/mainstone.c | 3---
March/arm/mach-pxa/mioa701.c | 21++++++++++++++++++---
March/arm/mach-pxa/mxm8x10.c | 19+++++++++++++++----
March/arm/mach-pxa/palm27x.c | 10+++-------
March/arm/mach-pxa/palm27x.h | 6++----
March/arm/mach-pxa/palmld.c | 16++++++++++++++--
March/arm/mach-pxa/palmt5.c | 16++++++++++++++--
March/arm/mach-pxa/palmtc.c | 19+++++++++++++++----
March/arm/mach-pxa/palmte2.c | 18+++++++++++++++---
March/arm/mach-pxa/palmtreo.c | 31+++++++++++++++++++++++++++----
March/arm/mach-pxa/palmtx.c | 16++++++++++++++--
March/arm/mach-pxa/palmz72.c | 16++++++++++++++--
March/arm/mach-pxa/pcm990-baseboard.c | 3---
March/arm/mach-pxa/poodle.c | 15++++++++++++---
March/arm/mach-pxa/raumfeld.c | 3---
March/arm/mach-pxa/spitz.c | 16+++++++++++++---
March/arm/mach-pxa/stargate2.c | 3---
March/arm/mach-pxa/tosa.c | 18+++++++++++++++---
March/arm/mach-pxa/trizeps4.c | 3---
March/arm/mach-pxa/vpac270.c | 16+++++++++++++---
March/arm/mach-pxa/z2.c | 14+++++++++++---
March/arm/mach-pxa/zeus.c | 15++++++++++++---
March/arm/mach-pxa/zylonite.c | 57++++++++++++++++++++++++++++++++++++++++++++++-----------
March/arm/mach-pxa/zylonite_pxa300.c | 2++
March/arm/mach-s3c24xx/mach-at2440evb.c | 14++++++++++++--
March/arm/mach-s3c24xx/mach-h1940.c | 15+++++++++++++--
March/arm/mach-s3c24xx/mach-mini2440.c | 15+++++++++++++--
March/arm/mach-s3c24xx/mach-n30.c | 15+++++++++++++--
March/arm/mach-s3c24xx/mach-rx1950.c | 15+++++++++++++--
March/mips/include/asm/mach-jz4740/jz4740_mmc.h | 4----
March/mips/jz4740/board-qi_lb60.c | 18+++++++++++-------
March/sh/boards/mach-ecovec24/setup.c | 43++++++++++++++++++++++++++++++++++++-------
Mdrivers/gpio/gpio-pca953x.c | 2+-
Mdrivers/memstick/core/memstick.c | 3+++
Mdrivers/memstick/host/rtsx_usb_ms.c | 170++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mdrivers/misc/Makefile | 2+-
Mdrivers/misc/cardreader/Kconfig | 11+++++++++++
Mdrivers/misc/cardreader/Makefile | 4++--
Adrivers/misc/cardreader/alcor_pci.c | 371+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/misc/cardreader/rtsx_usb.c | 8++++++++
Mdrivers/mmc/core/block.c | 2+-
Mdrivers/mmc/core/card.h | 6+-----
Mdrivers/mmc/core/core.c | 19++++---------------
Mdrivers/mmc/core/core.h | 2--
Mdrivers/mmc/core/mmc.c | 9+++------
Mdrivers/mmc/core/mmc_ops.c | 93++++++++++++++++---------------------------------------------------------------
Mdrivers/mmc/core/mmc_ops.h | 3+--
Mdrivers/mmc/core/mmc_test.c | 14++------------
Mdrivers/mmc/core/slot-gpio.c | 97+++++++++----------------------------------------------------------------------
Mdrivers/mmc/host/Kconfig | 28+++++++++++++++++++++++++---
Mdrivers/mmc/host/Makefile | 2++
Adrivers/mmc/host/alcor.c | 1162+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/mmc/host/atmel-mci.c | 34+++++++---------------------------
Mdrivers/mmc/host/bcm2835.c | 71+++++++++++++++++++++++++++++++++++++++--------------------------------
Mdrivers/mmc/host/dw_mmc-bluefield.c | 11+----------
Mdrivers/mmc/host/jz4740_mmc.c | 202+++++++++++++++++++++++++++++++------------------------------------------------
Mdrivers/mmc/host/meson-gx-mmc.c | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mdrivers/mmc/host/meson-mx-sdio.c | 6++++++
Mdrivers/mmc/host/mmc_spi.c | 27+++++++++++++++------------
Mdrivers/mmc/host/mmci.c | 11++++++++---
Mdrivers/mmc/host/mmci.h | 2++
Mdrivers/mmc/host/mtk-sd.c | 53++++++++++++++++++++++++++++++++++++++++++++++++++---
Mdrivers/mmc/host/of_mmc_spi.c | 34----------------------------------
Mdrivers/mmc/host/omap_hsmmc.c | 14++------------
Mdrivers/mmc/host/pxamci.c | 68++++++++++++++++++++++++++------------------------------------------
Mdrivers/mmc/host/renesas_sdhi_core.c | 50+++++++++++++++++++++++++++++++++++++++++---------
Mdrivers/mmc/host/renesas_sdhi_internal_dmac.c | 28+++++++++++++++++++---------
Mdrivers/mmc/host/renesas_sdhi_sys_dmac.c | 20+++-----------------
Mdrivers/mmc/host/rtsx_usb_sdmmc.c | 44++++++++++++++++++++++++++++++++++++--------
Mdrivers/mmc/host/s3cmci.c | 59+++++++++++++++--------------------------------------------
Mdrivers/mmc/host/sdhci-acpi.c | 7+++++--
Mdrivers/mmc/host/sdhci-cadence.c | 2+-
Mdrivers/mmc/host/sdhci-esdhc-imx.c | 26++++++++++++++------------
Mdrivers/mmc/host/sdhci-esdhc.h | 24++++++++++++++++++++++++
Mdrivers/mmc/host/sdhci-msm.c | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mdrivers/mmc/host/sdhci-of-arasan.c | 46----------------------------------------------
Mdrivers/mmc/host/sdhci-of-esdhc.c | 151+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mdrivers/mmc/host/sdhci-omap.c | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mdrivers/mmc/host/sdhci-pci-core.c | 19++++++++++++++-----
Mdrivers/mmc/host/sdhci-xenon-phy.c | 10+++++++---
Mdrivers/mmc/host/sdhci-xenon.c | 10+++++++---
Mdrivers/mmc/host/sdhci.c | 106+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mdrivers/mmc/host/sdhci.h | 13++++++++++---
Adrivers/mmc/host/sdhci_am654.c | 374+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/mmc/host/tmio_mmc.h | 6++++++
Mdrivers/mmc/host/tmio_mmc_core.c | 41++++++++++++++++++++++-------------------
Ainclude/linux/alcor_pci.h | 286+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/linux/mfd/tmio.h | 9++-------
Minclude/linux/mmc/host.h | 5++++-
Minclude/linux/mmc/slot-gpio.h | 5-----
Minclude/linux/platform_data/mmc-esdhc-imx.h | 4----
Minclude/linux/platform_data/mmc-pxamci.h | 4----
Minclude/linux/platform_data/mmc-s3cmci.h | 4----
Minclude/linux/spi/mmc_spi.h | 15---------------
Minclude/uapi/linux/mmc/ioctl.h | 5++++-
117 files changed, 4010 insertions(+), 1097 deletions(-)

diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt @@ -16,6 +16,10 @@ Required Properties: - "rockchip,rk3399-sdhci-5.1", "arasan,sdhci-5.1": rk3399 eMMC PHY For this device it is strongly suggested to include arasan,soc-ctl-syscon. - "ti,am654-sdhci-5.1", "arasan,sdhci-5.1": TI AM654 MMC PHY + Note: This binding has been deprecated and moved to [5]. + + [5] Documentation/devicetree/bindings/mmc/sdhci-am654.txt + - reg: From mmc bindings: Register location and length. - clocks: From clock bindings: Handles to clock inputs. - clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb" diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt @@ -16,6 +16,7 @@ Required properties: "fsl,imx6sl-usdhc" "fsl,imx6sx-usdhc" "fsl,imx7d-usdhc" + "fsl,imx8qxp-usdhc" Optional properties: - fsl,wp-controller : Indicate to use controller internal write protection diff --git a/Documentation/devicetree/bindings/mmc/sdhci-am654.txt b/Documentation/devicetree/bindings/mmc/sdhci-am654.txt @@ -0,0 +1,36 @@ +Device Tree Bindings for the SDHCI Controllers present on TI's AM654 SOCs + +The bindings follow the mmc[1], clock[2] and interrupt[3] bindings. +Only deviations are documented here. + + [1] Documentation/devicetree/bindings/mmc/mmc.txt + [2] Documentation/devicetree/bindings/clock/clock-bindings.txt + [3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt + +Required Properties: + - compatible: should be "ti,am654-sdhci-5.1" + - reg: Must be two entries. + - The first should be the sdhci register space + - The second should the subsystem/phy register space + - clocks: Handles to the clock inputs. + - clock-names: Tuple including "clk_xin" and "clk_ahb" + - interrupts: Interrupt specifiers + - ti,otap-del-sel: Output Tap Delay select + - ti,trm-icp: DLL trim select + - ti,driver-strength-ohm: driver strength in ohms. + Valid values are 33, 40, 50, 66 and 100 ohms. + +Example: + + sdhci0: sdhci@4f80000 { + compatible = "ti,am654-sdhci-5.1"; + reg = <0x0 0x4f80000 0x0 0x260>, <0x0 0x4f90000 0x0 0x134>; + power-domains = <&k3_pds 47>; + clocks = <&k3_clks 47 0>, <&k3_clks 47 1>; + clock-names = "clk_ahb", "clk_xin"; + interrupts = <GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>; + sdhci-caps-mask = <0x80000007 0x0>; + mmc-ddr-1_8v; + ti,otap-del-sel = <0x2>; + ti,trm-icp = <0x8>; + }; diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -4,15 +4,28 @@ 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: +- compatible: Should contain a SoC-specific string and a IP version string: + version strings: "qcom,sdhci-msm-v4" for sdcc versions less than 5.0 - "qcom,sdhci-msm-v5" for sdcc versions >= 5.0 + "qcom,sdhci-msm-v5" for sdcc version 5.0 For SDCC version 5.0.0, MCI registers are removed from SDCC interface and some registers are moved to HC. New compatible string is added to support this change - "qcom,sdhci-msm-v5". + full compatible strings with SoC and version: + "qcom,apq8084-sdhci", "qcom,sdhci-msm-v4" + "qcom,msm8974-sdhci", "qcom,sdhci-msm-v4" + "qcom,msm8916-sdhci", "qcom,sdhci-msm-v4" + "qcom,msm8992-sdhci", "qcom,sdhci-msm-v4" + "qcom,msm8996-sdhci", "qcom,sdhci-msm-v4" + "qcom,sdm845-sdhci", "qcom,sdhci-msm-v5" + "qcom,qcs404-sdhci", "qcom,sdhci-msm-v5" + NOTE that some old device tree files may be floating around that only + have the string "qcom,sdhci-msm-v4" without the SoC compatible string + but doing that should be considered a deprecated practice. + - reg: Base address and length of the register in the following order: - Host controller register map (required) - - SD Core register map (required) + - SD Core register map (required for msm-v4 and below) - interrupts: Should contain an interrupt-specifiers for the interrupts: - Host controller interrupt (required) - pinctrl-names: Should contain only one value - "default". @@ -29,7 +42,7 @@ Required properties: Example: sdhc_1: sdhci@f9824900 { - compatible = "qcom,sdhci-msm-v4"; + compatible = "qcom,msm8974-sdhci", "qcom,sdhci-msm-v4"; reg = <0xf9824900 0x11c>, <0xf9824000 0x800>; interrupts = <0 123 0>; bus-width = <8>; @@ -46,7 +59,7 @@ Example: }; sdhc_2: sdhci@f98a4900 { - compatible = "qcom,sdhci-msm-v4"; + compatible = "qcom,msm8974-sdhci", "qcom,sdhci-msm-v4"; reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>; interrupts = <0 125 0>; bus-width = <4>; diff --git a/Documentation/devicetree/bindings/mmc/sdhci-omap.txt b/Documentation/devicetree/bindings/mmc/sdhci-omap.txt @@ -2,6 +2,8 @@ Refer to mmc.txt for standard MMC bindings. +For UHS devices which require tuning, the device tree should have a "cpu_thermal" node which maps to the appropriate thermal zone. This is used to get the temperature of the zone during tuning. + Required properties: - compatible: Should be "ti,dra7-sdhci" for DRA7 and DRA72 controllers Should be "ti,k2g-sdhci" for K2G diff --git a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt @@ -13,12 +13,14 @@ Required properties: - compatible: should contain one or more of the following: "renesas,sdhi-sh73a0" - SDHI IP on SH73A0 SoC "renesas,sdhi-r7s72100" - SDHI IP on R7S72100 SoC + "renesas,sdhi-r7s9210" - SDHI IP on R7S9210 SoC "renesas,sdhi-r8a73a4" - SDHI IP on R8A73A4 SoC "renesas,sdhi-r8a7740" - SDHI IP on R8A7740 SoC "renesas,sdhi-r8a7743" - SDHI IP on R8A7743 SoC "renesas,sdhi-r8a7744" - SDHI IP on R8A7744 SoC "renesas,sdhi-r8a7745" - SDHI IP on R8A7745 SoC "renesas,sdhi-r8a774a1" - SDHI IP on R8A774A1 SoC + "renesas,sdhi-r8a774c0" - SDHI IP on R8A774C0 SoC "renesas,sdhi-r8a77470" - SDHI IP on R8A77470 SoC "renesas,sdhi-mmc-r8a77470" - SDHI/MMC IP on R8A77470 SoC "renesas,sdhi-r8a7778" - SDHI IP on R8A7778 SoC @@ -56,7 +58,7 @@ Required properties: "core" and "cd". If the controller only has 1 clock, naming is not required. Devices which have more than 1 clock are listed below: - 2: R7S72100 + 2: R7S72100, R7S9210 Optional properties: - pinctrl-names: should be "default", "state_uhs" diff --git a/arch/arm/mach-ep93xx/simone.c b/arch/arm/mach-ep93xx/simone.c @@ -25,6 +25,7 @@ #include <linux/platform_data/video-ep93xx.h> #include <linux/platform_data/spi-ep93xx.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <mach/hardware.h> #include <mach/gpio-ep93xx.h> @@ -45,9 +46,15 @@ static struct ep93xxfb_mach_info __initdata simone_fb_info = { static struct mmc_spi_platform_data simone_mmc_spi_data = { .detect_delay = 500, .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, - .flags = MMC_SPI_USE_CD_GPIO, - .cd_gpio = EP93XX_GPIO_LINE_EGPIO0, - .cd_debounce = 1, +}; + +static struct gpiod_lookup_table simone_mmc_spi_gpio_table = { + .dev_id = "mmc_spi.0", /* "mmc_spi" @ CS0 */ + .table = { + /* Card detect */ + GPIO_LOOKUP_IDX("A", 0, NULL, 0, GPIO_ACTIVE_LOW), + { }, + }, }; static struct spi_board_info simone_spi_devices[] __initdata = { @@ -105,6 +112,7 @@ static void __init simone_init_machine(void) ep93xx_register_fb(&simone_fb_info); ep93xx_register_i2c(simone_i2c_board_info, ARRAY_SIZE(simone_i2c_board_info)); + gpiod_add_lookup_table(&simone_mmc_spi_gpio_table); ep93xx_register_spi(&simone_spi_info, simone_spi_devices, ARRAY_SIZE(simone_spi_devices)); simone_register_audio(); diff --git a/arch/arm/mach-ep93xx/vision_ep9307.c b/arch/arm/mach-ep93xx/vision_ep9307.c @@ -18,6 +18,7 @@ #include <linux/platform_device.h> #include <linux/irq.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/fb.h> #include <linux/io.h> #include <linux/mtd/partitions.h> @@ -202,13 +203,20 @@ static struct mmc_spi_platform_data vision_spi_mmc_data = { .detect_delay = 100, .powerup_msecs = 100, .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, - .flags = MMC_SPI_USE_CD_GPIO | MMC_SPI_USE_RO_GPIO, - .cd_gpio = EP93XX_GPIO_LINE_EGPIO15, - .cd_debounce = 1, - .ro_gpio = EP93XX_GPIO_LINE_F(0), .caps2 = MMC_CAP2_RO_ACTIVE_HIGH, }; +static struct gpiod_lookup_table vision_spi_mmc_gpio_table = { + .dev_id = "mmc_spi.2", /* "mmc_spi @ CS2 */ + .table = { + /* Card detect */ + GPIO_LOOKUP_IDX("B", 7, NULL, 0, GPIO_ACTIVE_LOW), + /* Write protect */ + GPIO_LOOKUP_IDX("F", 0, NULL, 1, GPIO_ACTIVE_HIGH), + { }, + }, +}; + /************************************************************************* * SPI Bus *************************************************************************/ @@ -286,6 +294,7 @@ static void __init vision_init_machine(void) ep93xx_register_i2c(vision_i2c_info, ARRAY_SIZE(vision_i2c_info)); + gpiod_add_lookup_table(&vision_spi_mmc_gpio_table); ep93xx_register_spi(&vision_spi_master, vision_spi_board_info, ARRAY_SIZE(vision_spi_board_info)); vision_register_i2s(); diff --git a/arch/arm/mach-imx/mach-pcm043.c b/arch/arm/mach-imx/mach-pcm043.c @@ -20,6 +20,7 @@ #include <linux/mtd/plat-ram.h> #include <linux/memory.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/smc911x.h> #include <linux/interrupt.h> #include <linux/delay.h> @@ -214,8 +215,6 @@ static const iomux_v3_cfg_t pcm043_pads[] __initconst = { #define AC97_GPIO_TXFS IMX_GPIO_NR(2, 31) #define AC97_GPIO_TXD IMX_GPIO_NR(2, 28) #define AC97_GPIO_RESET IMX_GPIO_NR(2, 0) -#define SD1_GPIO_WP IMX_GPIO_NR(2, 23) -#define SD1_GPIO_CD IMX_GPIO_NR(2, 24) static void pcm043_ac97_warm_reset(struct snd_ac97 *ac97) { @@ -341,12 +340,21 @@ static int __init pcm043_otg_mode(char *options) __setup("otg_mode=", pcm043_otg_mode); static struct esdhc_platform_data sd1_pdata = { - .wp_gpio = SD1_GPIO_WP, - .cd_gpio = SD1_GPIO_CD, .wp_type = ESDHC_WP_GPIO, .cd_type = ESDHC_CD_GPIO, }; +static struct gpiod_lookup_table sd1_gpio_table = { + .dev_id = "sdhci-esdhc-imx35.0", + .table = { + /* Card detect: bank 2 offset 24 */ + GPIO_LOOKUP("imx35-gpio.2", 24, "cd", GPIO_ACTIVE_LOW), + /* Write protect: bank 2 offset 23 */ + GPIO_LOOKUP("imx35-gpio.2", 23, "wp", GPIO_ACTIVE_LOW), + { }, + }, +}; + /* * Board specific initialization. */ @@ -391,6 +399,7 @@ static void __init pcm043_late_init(void) { imx35_add_imx_ssi(0, &pcm043_ssi_pdata); + gpiod_add_lookup_table(&sd1_gpio_table); imx35_add_sdhci_esdhc_imx(0, &sd1_pdata); } diff --git a/arch/arm/mach-pxa/balloon3.c b/arch/arm/mach-pxa/balloon3.c @@ -290,9 +290,6 @@ static unsigned long balloon3_mmc_pin_config[] __initdata = { static struct pxamci_platform_data balloon3_mci_platform_data = { .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, - .gpio_card_detect = -1, - .gpio_card_ro = -1, - .gpio_power = -1, .detect_delay_ms = 200, }; diff --git a/arch/arm/mach-pxa/cm-x270.c b/arch/arm/mach-pxa/cm-x270.c @@ -12,6 +12,7 @@ #include <linux/platform_device.h> #include <linux/irq.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/delay.h> #include <linux/platform_data/rtc-v3020.h> @@ -288,14 +289,23 @@ static inline void cmx270_init_ohci(void) {} #if defined(CONFIG_MMC) || defined(CONFIG_MMC_MODULE) static struct pxamci_platform_data cmx270_mci_platform_data = { .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, - .gpio_card_detect = GPIO83_MMC_IRQ, - .gpio_card_ro = -1, - .gpio_power = GPIO105_MMC_POWER, - .gpio_power_invert = 1, +}; + +static struct gpiod_lookup_table cmx270_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + /* Card detect on GPIO 83 */ + GPIO_LOOKUP("gpio-pxa", GPIO83_MMC_IRQ, "cd", GPIO_ACTIVE_LOW), + /* Power on GPIO 105 */ + GPIO_LOOKUP("gpio-pxa", GPIO105_MMC_POWER, + "power", GPIO_ACTIVE_LOW), + { }, + }, }; static void __init cmx270_init_mmc(void) { + gpiod_add_lookup_table(&cmx270_mci_gpio_table); pxa_set_mci_info(&cmx270_mci_platform_data); } #else diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c @@ -459,9 +459,17 @@ static inline void cm_x300_init_nand(void) {} static struct pxamci_platform_data cm_x300_mci_platform_data = { .detect_delay_ms = 200, .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, - .gpio_card_detect = GPIO82_MMC_IRQ, - .gpio_card_ro = GPIO85_MMC_WP, - .gpio_power = -1, +}; + +static struct gpiod_lookup_table cm_x300_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + /* Card detect on GPIO 82 */ + GPIO_LOOKUP("gpio-pxa", GPIO82_MMC_IRQ, "cd", GPIO_ACTIVE_LOW), + /* Write protect on GPIO 85 */ + GPIO_LOOKUP("gpio-pxa", GPIO85_MMC_WP, "wp", GPIO_ACTIVE_LOW), + { }, + }, }; /* The second MMC slot of CM-X300 is hardwired to Libertas card and has @@ -482,13 +490,11 @@ static struct pxamci_platform_data cm_x300_mci2_platform_data = { .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, .init = cm_x300_mci2_init, .exit = cm_x300_mci2_exit, - .gpio_card_detect = -1, - .gpio_card_ro = -1, - .gpio_power = -1, }; static void __init cm_x300_init_mmc(void) { + gpiod_add_lookup_table(&cm_x300_mci_gpio_table); pxa_set_mci_info(&cm_x300_mci_platform_data); pxa3xx_set_mci2_info(&cm_x300_mci2_platform_data); } diff --git a/arch/arm/mach-pxa/colibri-evalboard.c b/arch/arm/mach-pxa/colibri-evalboard.c @@ -14,7 +14,7 @@ #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/interrupt.h> -#include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <asm/mach-types.h> #include <mach/hardware.h> #include <asm/mach/arch.h> @@ -37,22 +37,44 @@ #if defined(CONFIG_MMC_PXA) || defined(CONFIG_MMC_PXA_MODULE) static struct pxamci_platform_data colibri_mci_platform_data = { .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, - .gpio_power = -1, - .gpio_card_ro = -1, .detect_delay_ms = 200, }; +static struct gpiod_lookup_table colibri_pxa270_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", GPIO0_COLIBRI_PXA270_SD_DETECT, + "cd", GPIO_ACTIVE_LOW), + { }, + }, +}; + +static struct gpiod_lookup_table colibri_pxa300_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", GPIO13_COLIBRI_PXA300_SD_DETECT, + "cd", GPIO_ACTIVE_LOW), + { }, + }, +}; + +static struct gpiod_lookup_table colibri_pxa320_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", GPIO28_COLIBRI_PXA320_SD_DETECT, + "cd", GPIO_ACTIVE_LOW), + { }, + }, +}; + static void __init colibri_mmc_init(void) { if (machine_is_colibri()) /* PXA270 Colibri */ - colibri_mci_platform_data.gpio_card_detect = - GPIO0_COLIBRI_PXA270_SD_DETECT; + gpiod_add_lookup_table(&colibri_pxa270_mci_gpio_table); if (machine_is_colibri300()) /* PXA300 Colibri */ - colibri_mci_platform_data.gpio_card_detect = - GPIO13_COLIBRI_PXA300_SD_DETECT; + gpiod_add_lookup_table(&colibri_pxa300_mci_gpio_table); else /* PXA320 Colibri */ - colibri_mci_platform_data.gpio_card_detect = - GPIO28_COLIBRI_PXA320_SD_DETECT; + gpiod_add_lookup_table(&colibri_pxa320_mci_gpio_table); pxa_set_mci_info(&colibri_mci_platform_data); } diff --git a/arch/arm/mach-pxa/colibri-pxa270-income.c b/arch/arm/mach-pxa/colibri-pxa270-income.c @@ -14,7 +14,7 @@ #include <linux/bitops.h> #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/leds.h> @@ -51,14 +51,25 @@ #if defined(CONFIG_MMC_PXA) || defined(CONFIG_MMC_PXA_MODULE) static struct pxamci_platform_data income_mci_platform_data = { .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, - .gpio_power = -1, - .gpio_card_detect = GPIO0_INCOME_SD_DETECT, - .gpio_card_ro = GPIO0_INCOME_SD_RO, .detect_delay_ms = 200, }; +static struct gpiod_lookup_table income_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + /* Card detect on GPIO 0 */ + GPIO_LOOKUP("gpio-pxa", GPIO0_INCOME_SD_DETECT, + "cd", GPIO_ACTIVE_LOW), + /* Write protect on GPIO 1 */ + GPIO_LOOKUP("gpio-pxa", GPIO0_INCOME_SD_RO, + "wp", GPIO_ACTIVE_LOW), + { }, + }, +}; + static void __init income_mmc_init(void) { + gpiod_add_lookup_table(&income_mci_gpio_table); pxa_set_mci_info(&income_mci_platform_data); } #else diff --git a/arch/arm/mach-pxa/corgi.c b/arch/arm/mach-pxa/corgi.c @@ -24,6 +24,7 @@ #include <linux/mtd/physmap.h> #include <linux/pm.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/backlight.h> #include <linux/i2c.h> #include <linux/platform_data/i2c-pxa.h> @@ -493,11 +494,23 @@ static struct platform_device corgi_audio_device = { static struct pxamci_platform_data corgi_mci_platform_data = { .detect_delay_ms = 250, .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, - .gpio_card_detect = CORGI_GPIO_nSD_DETECT, - .gpio_card_ro = CORGI_GPIO_nSD_WP, - .gpio_power = CORGI_GPIO_SD_PWR, }; +static struct gpiod_lookup_table corgi_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + /* Card detect on GPIO 9 */ + GPIO_LOOKUP("gpio-pxa", CORGI_GPIO_nSD_DETECT, + "cd", GPIO_ACTIVE_LOW), + /* Write protect on GPIO 7 */ + GPIO_LOOKUP("gpio-pxa", CORGI_GPIO_nSD_WP, + "wp", GPIO_ACTIVE_LOW), + /* Power on GPIO 33 */ + GPIO_LOOKUP("gpio-pxa", CORGI_GPIO_SD_PWR, + "power", GPIO_ACTIVE_HIGH), + { }, + }, +}; /* * Irda @@ -731,6 +744,7 @@ static void __init corgi_init(void) corgi_init_spi(); pxa_set_udc_info(&udc_info); + gpiod_add_lookup_table(&corgi_mci_gpio_table); pxa_set_mci_info(&corgi_mci_platform_data); pxa_set_ficp_info(&corgi_ficp_platform_data); pxa_set_i2c_info(NULL); diff --git a/arch/arm/mach-pxa/csb726.c b/arch/arm/mach-pxa/csb726.c @@ -11,7 +11,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/io.h> -#include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/platform_device.h> #include <linux/mtd/physmap.h> #include <linux/mtd/partitions.h> @@ -129,9 +129,19 @@ static struct pxamci_platform_data csb726_mci = { .detect_delay_ms = 500, .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, /* FIXME setpower */ - .gpio_card_detect = CSB726_GPIO_MMC_DETECT, - .gpio_card_ro = CSB726_GPIO_MMC_RO, - .gpio_power = -1, +}; + +static struct gpiod_lookup_table csb726_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + /* Card detect on GPIO 100 */ + GPIO_LOOKUP("gpio-pxa", CSB726_GPIO_MMC_DETECT, + "cd", GPIO_ACTIVE_LOW), + /* Write protect on GPIO 101 */ + GPIO_LOOKUP("gpio-pxa", CSB726_GPIO_MMC_RO, + "wp", GPIO_ACTIVE_LOW), + { }, + }, }; static struct pxaohci_platform_data csb726_ohci_platform_data = { @@ -264,6 +274,7 @@ static void __init csb726_init(void) pxa_set_stuart_info(NULL); pxa_set_i2c_info(NULL); pxa27x_set_i2c_power_info(NULL); + gpiod_add_lookup_table(&csb726_mci_gpio_table); pxa_set_mci_info(&csb726_mci); pxa_set_ohci_info(&csb726_ohci_platform_data); pxa_set_ac97_info(NULL); diff --git a/arch/arm/mach-pxa/em-x270.c b/arch/arm/mach-pxa/em-x270.c @@ -20,6 +20,7 @@ #include <linux/input.h> #include <linux/gpio_keys.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/mfd/da903x.h> #include <linux/regulator/machine.h> #include <linux/regulator/fixed.h> @@ -546,6 +547,15 @@ static inline void em_x270_init_ohci(void) {} #if defined(CONFIG_MMC) || defined(CONFIG_MMC_MODULE) static struct regulator *em_x270_sdio_ldo; +static struct gpiod_lookup_table em_x270_mci_wp_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + /* Write protect on GPIO 95 */ + GPIO_LOOKUP("gpio-pxa", GPIO95_MMC_WP, "wp", GPIO_ACTIVE_LOW), + { }, + }, +}; + static int em_x270_mci_init(struct device *dev, irq_handler_t em_x270_detect_int, void *data) @@ -567,15 +577,7 @@ static int em_x270_mci_init(struct device *dev, goto err_irq; } - if (machine_is_em_x270()) { - err = gpio_request(GPIO95_MMC_WP, "MMC WP"); - if (err) { - dev_err(dev, "can't request MMC write protect: %d\n", - err); - goto err_gpio_wp; - } - gpio_direction_input(GPIO95_MMC_WP); - } else { + if (!machine_is_em_x270()) { err = gpio_request(GPIO38_SD_PWEN, "sdio power"); if (err) { dev_err(dev, "can't request MMC power control : %d\n", @@ -615,17 +617,10 @@ static void em_x270_mci_exit(struct device *dev, void *data) free_irq(gpio_to_irq(mmc_cd), data); regulator_put(em_x270_sdio_ldo); - if (machine_is_em_x270()) - gpio_free(GPIO95_MMC_WP); - else + if (!machine_is_em_x270()) gpio_free(GPIO38_SD_PWEN); } -static int em_x270_mci_get_ro(struct device *dev) -{ - return gpio_get_value(GPIO95_MMC_WP); -} - static struct pxamci_platform_data em_x270_mci_platform_data = { .detect_delay_ms = 250, .ocr_mask = MMC_VDD_20_21|MMC_VDD_21_22|MMC_VDD_22_23| @@ -635,15 +630,12 @@ static struct pxamci_platform_data em_x270_mci_platform_data = { .init = em_x270_mci_init, .setpower = em_x270_mci_setpower, .exit = em_x270_mci_exit, - .gpio_card_detect = -1, - .gpio_card_ro = -1, - .gpio_power = -1, }; static void __init em_x270_init_mmc(void) { if (machine_is_em_x270()) - em_x270_mci_platform_data.get_ro = em_x270_mci_get_ro; + gpiod_add_lookup_table(&em_x270_mci_wp_gpio_table); pxa_set_mci_info(&em_x270_mci_platform_data); } diff --git a/arch/arm/mach-pxa/gumstix.c b/arch/arm/mach-pxa/gumstix.c @@ -90,9 +90,6 @@ static struct platform_device *devices[] __initdata = { #ifdef CONFIG_MMC_PXA static struct pxamci_platform_data gumstix_mci_platform_data = { .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, - .gpio_card_detect = -1, - .gpio_card_ro = -1, - .gpio_power = -1, }; static void __init gumstix_mmc_init(void) diff --git a/arch/arm/mach-pxa/idp.c b/arch/arm/mach-pxa/idp.c @@ -160,9 +160,6 @@ static struct pxafb_mach_info sharp_lm8v31 = { static struct pxamci_platform_data idp_mci_platform_data = { .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, - .gpio_card_detect = -1, - .gpio_card_ro = -1, - .gpio_power = -1, }; static void __init idp_init(void) diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c @@ -20,7 +20,7 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/clk.h> -#include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/spi/spi.h> #include <linux/spi/pxa2xx_spi.h> #include <linux/smc91x.h> @@ -51,8 +51,6 @@ #include "generic.h" -#define GPIO_MMC1_CARD_DETECT mfp_to_gpio(MFP_PIN_GPIO15) - /* Littleton MFP configurations */ static mfp_cfg_t littleton_mfp_cfg[] __initdata = { /* LCD */ @@ -278,13 +276,21 @@ static inline void littleton_init_keypad(void) {} static struct pxamci_platform_data littleton_mci_platform_data = { .detect_delay_ms = 200, .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, - .gpio_card_detect = GPIO_MMC1_CARD_DETECT, - .gpio_card_ro = -1, - .gpio_power = -1, +}; + +static struct gpiod_lookup_table littleton_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + /* Card detect on MFP (gpio-pxa) GPIO 15 */ + GPIO_LOOKUP("gpio-pxa", MFP_PIN_GPIO15, + "cd", GPIO_ACTIVE_LOW), + { }, + }, }; static void __init littleton_init_mmc(void) { + gpiod_add_lookup_table(&littleton_mci_gpio_table); pxa_set_mci_info(&littleton_mci_platform_data); } #else diff --git a/arch/arm/mach-pxa/lubbock.c b/arch/arm/mach-pxa/lubbock.c @@ -440,9 +440,6 @@ static struct pxamci_platform_data lubbock_mci_platform_data = { .init = lubbock_mci_init, .get_ro = lubbock_mci_get_ro, .exit = lubbock_mci_exit, - .gpio_card_detect = -1, - .gpio_card_ro = -1, - .gpio_power = -1, }; static void lubbock_irda_transceiver_mode(struct device *dev, int mode) diff --git a/arch/arm/mach-pxa/magician.c b/arch/arm/mach-pxa/magician.c @@ -775,12 +775,31 @@ static struct pxamci_platform_data magician_mci_info = { .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, .init = magician_mci_init, .exit = magician_mci_exit, - .gpio_card_detect = -1, - .gpio_card_ro = EGPIO_MAGICIAN_nSD_READONLY, .gpio_card_ro_invert = 1, - .gpio_power = EGPIO_MAGICIAN_SD_POWER, }; +/* + * Write protect on EGPIO register 5 index 4, this is on the second HTC + * EGPIO chip which starts at register 4, so we need offset 8+4=12 on that + * particular chip. + */ +#define EGPIO_MAGICIAN_nSD_READONLY_OFFSET 12 +/* + * Power on EGPIO register 2 index 0, so this is on the first HTC EGPIO chip + * starting at register 0 so we need offset 2*8+0 = 16 on that chip. + */ +#define EGPIO_MAGICIAN_nSD_POWER_OFFSET 16 + +static struct gpiod_lookup_table magician_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("htc-egpio-1", EGPIO_MAGICIAN_nSD_READONLY_OFFSET, + "wp", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("htc-egpio-0", EGPIO_MAGICIAN_nSD_POWER_OFFSET, + "power", GPIO_ACTIVE_HIGH), + { }, + }, +}; /* * USB OHCI @@ -979,6 +998,7 @@ static void __init magician_init(void) i2c_register_board_info(1, ARRAY_AND_SIZE(magician_pwr_i2c_board_info)); + gpiod_add_lookup_table(&magician_mci_gpio_table); pxa_set_mci_info(&magician_mci_info); pxa_set_ohci_info(&magician_ohci_info); pxa_set_udc_info(&magician_udc_info); diff --git a/arch/arm/mach-pxa/mainstone.c b/arch/arm/mach-pxa/mainstone.c @@ -361,9 +361,6 @@ static struct pxamci_platform_data mainstone_mci_platform_data = { .init = mainstone_mci_init, .setpower = mainstone_mci_setpower, .exit = mainstone_mci_exit, - .gpio_card_detect = -1, - .gpio_card_ro = -1, - .gpio_power = -1, }; static void mainstone_irda_transceiver_mode(struct device *dev, int mode) diff --git a/arch/arm/mach-pxa/mioa701.c b/arch/arm/mach-pxa/mioa701.c @@ -31,6 +31,7 @@ #include <linux/rtc.h> #include <linux/leds.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/pda_power.h> @@ -397,9 +398,22 @@ struct gpio_vbus_mach_info gpio_vbus_data = { static struct pxamci_platform_data mioa701_mci_info = { .detect_delay_ms = 250, .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, - .gpio_card_detect = GPIO15_SDIO_INSERT, - .gpio_card_ro = GPIO78_SDIO_RO, - .gpio_power = GPIO91_SDIO_EN, +}; + +static struct gpiod_lookup_table mioa701_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + /* Card detect on GPIO 15 */ + GPIO_LOOKUP("gpio-pxa", GPIO15_SDIO_INSERT, + "cd", GPIO_ACTIVE_LOW), + /* Write protect on GPIO 78 */ + GPIO_LOOKUP("gpio-pxa", GPIO78_SDIO_RO, + "wp", GPIO_ACTIVE_LOW), + /* Power on GPIO 91 */ + GPIO_LOOKUP("gpio-pxa", GPIO91_SDIO_EN, + "power", GPIO_ACTIVE_HIGH), + { }, + }, }; /* FlashRAM */ @@ -743,6 +757,7 @@ static void __init mioa701_machine_init(void) pr_err("MioA701: Failed to request GPIOs: %d", rc); bootstrap_init(); pxa_set_fb_info(NULL, &mioa701_pxafb_info); + gpiod_add_lookup_table(&mioa701_mci_gpio_table); pxa_set_mci_info(&mioa701_mci_info); pxa_set_keypad_info(&mioa701_keypad_info); pxa_set_udc_info(&mioa701_udc_info); diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c @@ -21,7 +21,7 @@ #include <linux/serial_8250.h> #include <linux/dm9000.h> -#include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/platform_data/i2c-pxa.h> #include <linux/platform_data/mtd-nand-pxa3xx.h> @@ -326,13 +326,24 @@ static mfp_cfg_t mfp_cfg[] __initdata = { static struct pxamci_platform_data mxm_8x10_mci_platform_data = { .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, .detect_delay_ms = 10, - .gpio_card_detect = MXM_8X10_SD_nCD, - .gpio_card_ro = MXM_8X10_SD_WP, - .gpio_power = -1 +}; + +static struct gpiod_lookup_table mxm_8x10_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + /* Card detect on GPIO 72 */ + GPIO_LOOKUP("gpio-pxa", MXM_8X10_SD_nCD, + "cd", GPIO_ACTIVE_LOW), + /* Write protect on GPIO 84 */ + GPIO_LOOKUP("gpio-pxa", MXM_8X10_SD_WP, + "wp", GPIO_ACTIVE_LOW), + { }, + }, }; void __init mxm_8x10_mmc_init(void) { + gpiod_add_lookup_table(&mxm_8x10_mci_gpio_table); pxa_set_mci_info(&mxm_8x10_mci_platform_data); } #endif diff --git a/arch/arm/mach-pxa/palm27x.c b/arch/arm/mach-pxa/palm27x.c @@ -49,14 +49,10 @@ static struct pxamci_platform_data palm27x_mci_platform_data = { .detect_delay_ms = 200, }; -void __init palm27x_mmc_init(int detect, int ro, int power, - int power_inverted) +void __init palm27x_mmc_init(struct gpiod_lookup_table *gtable) { - palm27x_mci_platform_data.gpio_card_detect = detect; - palm27x_mci_platform_data.gpio_card_ro = ro; - palm27x_mci_platform_data.gpio_power = power; - palm27x_mci_platform_data.gpio_power_invert = power_inverted; - + if (gtable) + gpiod_add_lookup_table(gtable); pxa_set_mci_info(&palm27x_mci_platform_data); } #endif diff --git a/arch/arm/mach-pxa/palm27x.h b/arch/arm/mach-pxa/palm27x.h @@ -15,11 +15,9 @@ #include <linux/gpio/machine.h> #if defined(CONFIG_MMC_PXA) || defined(CONFIG_MMC_PXA_MODULE) -extern void __init palm27x_mmc_init(int detect, int ro, int power, - int power_inverted); +extern void __init palm27x_mmc_init(struct gpiod_lookup_table *gtable); #else -static inline void palm27x_mmc_init(int detect, int ro, int power, - int power_inverted) +static inline void palm27x_mmc_init(struct gpiod_lookup_table *gtable) {} #endif diff --git a/arch/arm/mach-pxa/palmld.c b/arch/arm/mach-pxa/palmld.c @@ -332,6 +332,19 @@ static void __init palmld_map_io(void) iotable_init(palmld_io_desc, ARRAY_SIZE(palmld_io_desc)); } +static struct gpiod_lookup_table palmld_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMLD_SD_DETECT_N, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMLD_SD_READONLY, + "wp", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMLD_SD_POWER, + "power", GPIO_ACTIVE_HIGH), + { }, + }, +}; + static void __init palmld_init(void) { pxa2xx_mfp_config(ARRAY_AND_SIZE(palmld_pin_config)); @@ -339,8 +352,7 @@ static void __init palmld_init(void) pxa_set_btuart_info(NULL); pxa_set_stuart_info(NULL); - palm27x_mmc_init(GPIO_NR_PALMLD_SD_DETECT_N, GPIO_NR_PALMLD_SD_READONLY, - GPIO_NR_PALMLD_SD_POWER, 0); + palm27x_mmc_init(&palmld_mci_gpio_table); palm27x_pm_init(PALMLD_STR_BASE); palm27x_lcd_init(-1, &palm_320x480_lcd_mode); palm27x_irda_init(GPIO_NR_PALMLD_IR_DISABLE); diff --git a/arch/arm/mach-pxa/palmt5.c b/arch/arm/mach-pxa/palmt5.c @@ -182,6 +182,19 @@ static void __init palmt5_reserve(void) memblock_reserve(0xa0200000, 0x1000); } +static struct gpiod_lookup_table palmt5_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMT5_SD_DETECT_N, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMT5_SD_READONLY, + "wp", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMT5_SD_POWER, + "power", GPIO_ACTIVE_HIGH), + { }, + }, +}; + static void __init palmt5_init(void) { pxa2xx_mfp_config(ARRAY_AND_SIZE(palmt5_pin_config)); @@ -189,8 +202,7 @@ static void __init palmt5_init(void) pxa_set_btuart_info(NULL); pxa_set_stuart_info(NULL); - palm27x_mmc_init(GPIO_NR_PALMT5_SD_DETECT_N, GPIO_NR_PALMT5_SD_READONLY, - GPIO_NR_PALMT5_SD_POWER, 0); + palm27x_mmc_init(&palmt5_mci_gpio_table); palm27x_pm_init(PALMT5_STR_BASE); palm27x_lcd_init(-1, &palm_320x480_lcd_mode); palm27x_udc_init(GPIO_NR_PALMT5_USB_DETECT_N, diff --git a/arch/arm/mach-pxa/palmtc.c b/arch/arm/mach-pxa/palmtc.c @@ -20,7 +20,7 @@ #include <linux/input.h> #include <linux/pwm.h> #include <linux/pwm_backlight.h> -#include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/input/matrix_keypad.h> #include <linux/ucb1400.h> #include <linux/power_supply.h> @@ -120,14 +120,25 @@ static unsigned long palmtc_pin_config[] __initdata = { #if defined(CONFIG_MMC_PXA) || defined(CONFIG_MMC_PXA_MODULE) static struct pxamci_platform_data palmtc_mci_platform_data = { .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, - .gpio_power = GPIO_NR_PALMTC_SD_POWER, - .gpio_card_ro = GPIO_NR_PALMTC_SD_READONLY, - .gpio_card_detect = GPIO_NR_PALMTC_SD_DETECT_N, .detect_delay_ms = 200, }; +static struct gpiod_lookup_table palmtc_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMTC_SD_DETECT_N, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMTC_SD_READONLY, + "wp", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMTC_SD_POWER, + "power", GPIO_ACTIVE_HIGH), + { }, + }, +}; + static void __init palmtc_mmc_init(void) { + gpiod_add_lookup_table(&palmtc_mci_gpio_table); pxa_set_mci_info(&palmtc_mci_platform_data); } #else diff --git a/arch/arm/mach-pxa/palmte2.c b/arch/arm/mach-pxa/palmte2.c @@ -19,6 +19,7 @@ #include <linux/delay.h> #include <linux/irq.h> #include <linux/gpio_keys.h> +#include <linux/gpio/machine.h> #include <linux/input.h> #include <linux/pda_power.h> #include <linux/pwm.h> @@ -101,9 +102,19 @@ static unsigned long palmte2_pin_config[] __initdata = { ******************************************************************************/ static struct pxamci_platform_data palmte2_mci_platform_data = { .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, - .gpio_card_detect = GPIO_NR_PALMTE2_SD_DETECT_N, - .gpio_card_ro = GPIO_NR_PALMTE2_SD_READONLY, - .gpio_power = GPIO_NR_PALMTE2_SD_POWER, +}; + +static struct gpiod_lookup_table palmte2_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMTE2_SD_DETECT_N, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMTE2_SD_READONLY, + "wp", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMTE2_SD_POWER, + "power", GPIO_ACTIVE_HIGH), + { }, + }, }; #if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE) @@ -354,6 +365,7 @@ static void __init palmte2_init(void) pxa_set_stuart_info(NULL); pxa_set_fb_info(NULL, &palmte2_lcd_screen); + gpiod_add_lookup_table(&palmte2_mci_gpio_table); pxa_set_mci_info(&palmte2_mci_platform_data); palmte2_udc_init(); pxa_set_ac97_info(&palmte2_ac97_pdata); diff --git a/arch/arm/mach-pxa/palmtreo.c b/arch/arm/mach-pxa/palmtreo.c @@ -480,23 +480,46 @@ void __init treo680_gpio_init(void) gpio_free(GPIO_NR_TREO680_LCD_EN_N); } +static struct gpiod_lookup_table treo680_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", GPIO_NR_TREO_SD_DETECT_N, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_TREO680_SD_READONLY, + "wp", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_TREO680_SD_POWER, + "power", GPIO_ACTIVE_HIGH), + { }, + }, +}; + static void __init treo680_init(void) { pxa2xx_mfp_config(ARRAY_AND_SIZE(treo680_pin_config)); palmphone_common_init(); treo680_gpio_init(); - palm27x_mmc_init(GPIO_NR_TREO_SD_DETECT_N, GPIO_NR_TREO680_SD_READONLY, - GPIO_NR_TREO680_SD_POWER, 0); + palm27x_mmc_init(&treo680_mci_gpio_table); } #endif #ifdef CONFIG_MACH_CENTRO + +static struct gpiod_lookup_table centro685_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", GPIO_NR_TREO_SD_DETECT_N, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_CENTRO_SD_POWER, + "power", GPIO_ACTIVE_LOW), + { }, + }, +}; + static void __init centro_init(void) { pxa2xx_mfp_config(ARRAY_AND_SIZE(centro685_pin_config)); palmphone_common_init(); - palm27x_mmc_init(GPIO_NR_TREO_SD_DETECT_N, -1, - GPIO_NR_CENTRO_SD_POWER, 1); + palm27x_mmc_init(&centro685_mci_gpio_table); } #endif diff --git a/arch/arm/mach-pxa/palmtx.c b/arch/arm/mach-pxa/palmtx.c @@ -337,6 +337,19 @@ static void __init palmtx_map_io(void) iotable_init(palmtx_io_desc, ARRAY_SIZE(palmtx_io_desc)); } +static struct gpiod_lookup_table palmtx_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMTX_SD_DETECT_N, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMTX_SD_READONLY, + "wp", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMTX_SD_POWER, + "power", GPIO_ACTIVE_HIGH), + { }, + }, +}; + static void __init palmtx_init(void) { pxa2xx_mfp_config(ARRAY_AND_SIZE(palmtx_pin_config)); @@ -344,8 +357,7 @@ static void __init palmtx_init(void) pxa_set_btuart_info(NULL); pxa_set_stuart_info(NULL); - palm27x_mmc_init(GPIO_NR_PALMTX_SD_DETECT_N, GPIO_NR_PALMTX_SD_READONLY, - GPIO_NR_PALMTX_SD_POWER, 0); + palm27x_mmc_init(&palmtx_mci_gpio_table); palm27x_pm_init(PALMTX_STR_BASE); palm27x_lcd_init(-1, &palm_320x480_lcd_mode); palm27x_udc_init(GPIO_NR_PALMTX_USB_DETECT_N, diff --git a/arch/arm/mach-pxa/palmz72.c b/arch/arm/mach-pxa/palmz72.c @@ -386,6 +386,19 @@ static void __init palmz72_camera_init(void) static inline void palmz72_camera_init(void) {} #endif +static struct gpiod_lookup_table palmz72_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMZ72_SD_DETECT_N, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMZ72_SD_RO, + "wp", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO_NR_PALMZ72_SD_POWER_N, + "power", GPIO_ACTIVE_LOW), + { }, + }, +}; + /****************************************************************************** * Machine init ******************************************************************************/ @@ -396,8 +409,7 @@ static void __init palmz72_init(void) pxa_set_btuart_info(NULL); pxa_set_stuart_info(NULL); - palm27x_mmc_init(GPIO_NR_PALMZ72_SD_DETECT_N, GPIO_NR_PALMZ72_SD_RO, - GPIO_NR_PALMZ72_SD_POWER_N, 1); + palm27x_mmc_init(&palmz72_mci_gpio_table); palm27x_lcd_init(-1, &palm_320x320_lcd_mode); palm27x_udc_init(GPIO_NR_PALMZ72_USB_DETECT_N, GPIO_NR_PALMZ72_USB_PULLUP, 0); diff --git a/arch/arm/mach-pxa/pcm990-baseboard.c b/arch/arm/mach-pxa/pcm990-baseboard.c @@ -370,9 +370,6 @@ static struct pxamci_platform_data pcm990_mci_platform_data = { .init = pcm990_mci_init, .setpower = pcm990_mci_setpower, .exit = pcm990_mci_exit, - .gpio_card_detect = -1, - .gpio_card_ro = -1, - .gpio_power = -1, }; static struct pxaohci_platform_data pcm990_ohci_platform_data = { diff --git a/arch/arm/mach-pxa/poodle.c b/arch/arm/mach-pxa/poodle.c @@ -23,6 +23,7 @@ #include <linux/delay.h> #include <linux/mtd/physmap.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/i2c.h> #include <linux/platform_data/i2c-pxa.h> #include <linux/regulator/machine.h> @@ -288,11 +289,18 @@ static struct pxamci_platform_data poodle_mci_platform_data = { .init = poodle_mci_init, .setpower = poodle_mci_setpower, .exit = poodle_mci_exit, - .gpio_card_detect = POODLE_GPIO_nSD_DETECT, - .gpio_card_ro = POODLE_GPIO_nSD_WP, - .gpio_power = -1, }; +static struct gpiod_lookup_table poodle_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", POODLE_GPIO_nSD_DETECT, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", POODLE_GPIO_nSD_WP, + "wp", GPIO_ACTIVE_LOW), + { }, + }, +}; /* * Irda @@ -439,6 +447,7 @@ static void __init poodle_init(void) pxa_set_fb_info(&poodle_locomo_device.dev, &poodle_fb_info); pxa_set_udc_info(&udc_info); + gpiod_add_lookup_table(&poodle_mci_gpio_table); pxa_set_mci_info(&poodle_mci_platform_data); pxa_set_ficp_info(&poodle_ficp_platform_data); pxa_set_i2c_info(NULL); diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c @@ -749,9 +749,6 @@ static struct pxamci_platform_data raumfeld_mci_platform_data = { .init = raumfeld_mci_init, .exit = raumfeld_mci_exit, .detect_delay_ms = 200, - .gpio_card_detect = -1, - .gpio_card_ro = -1, - .gpio_power = -1, }; /* diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c @@ -18,6 +18,7 @@ #include <linux/delay.h> #include <linux/gpio_keys.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/leds.h> #include <linux/i2c.h> #include <linux/platform_data/i2c-pxa.h> @@ -615,13 +616,22 @@ static struct pxamci_platform_data spitz_mci_platform_data = { .detect_delay_ms = 250, .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, .setpower = spitz_mci_setpower, - .gpio_card_detect = SPITZ_GPIO_nSD_DETECT, - .gpio_card_ro = SPITZ_GPIO_nSD_WP, - .gpio_power = -1, +}; + +static struct gpiod_lookup_table spitz_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", SPITZ_GPIO_nSD_DETECT, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", SPITZ_GPIO_nSD_WP, + "wp", GPIO_ACTIVE_LOW), + { }, + }, }; static void __init spitz_mmc_init(void) { + gpiod_add_lookup_table(&spitz_mci_gpio_table); pxa_set_mci_info(&spitz_mci_platform_data); } #else diff --git a/arch/arm/mach-pxa/stargate2.c b/arch/arm/mach-pxa/stargate2.c @@ -436,9 +436,6 @@ static int imote2_mci_get_ro(struct device *dev) static struct pxamci_platform_data imote2_mci_platform_data = { .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, /* default anyway */ .get_ro = imote2_mci_get_ro, - .gpio_card_detect = -1, - .gpio_card_ro = -1, - .gpio_power = -1, }; static struct gpio_led imote2_led_pins[] = { diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c @@ -31,6 +31,7 @@ #include <linux/gpio_keys.h> #include <linux/input.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/power/gpio-charger.h> #include <linux/spi/spi.h> #include <linux/spi/pxa2xx_spi.h> @@ -291,9 +292,19 @@ static struct pxamci_platform_data tosa_mci_platform_data = { .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, .init = tosa_mci_init, .exit = tosa_mci_exit, - .gpio_card_detect = TOSA_GPIO_nSD_DETECT, - .gpio_card_ro = TOSA_GPIO_SD_WP, - .gpio_power = TOSA_GPIO_PWR_ON, +}; + +static struct gpiod_lookup_table tosa_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", TOSA_GPIO_nSD_DETECT, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", TOSA_GPIO_SD_WP, + "wp", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", TOSA_GPIO_PWR_ON, + "power", GPIO_ACTIVE_HIGH), + { }, + }, }; /* @@ -908,6 +919,7 @@ static void __init tosa_init(void) /* enable batt_fault */ PMCR = 0x01; + gpiod_add_lookup_table(&tosa_mci_gpio_table); pxa_set_mci_info(&tosa_mci_platform_data); pxa_set_ficp_info(&tosa_ficp_platform_data); pxa_set_i2c_info(NULL); diff --git a/arch/arm/mach-pxa/trizeps4.c b/arch/arm/mach-pxa/trizeps4.c @@ -355,9 +355,6 @@ static struct pxamci_platform_data trizeps4_mci_platform_data = { .exit = trizeps4_mci_exit, .get_ro = NULL, /* write-protection not supported */ .setpower = NULL, /* power-switching not supported */ - .gpio_card_detect = -1, - .gpio_card_ro = -1, - .gpio_power = -1, }; /**************************************************************************** diff --git a/arch/arm/mach-pxa/vpac270.c b/arch/arm/mach-pxa/vpac270.c @@ -17,6 +17,7 @@ #include <linux/input.h> #include <linux/leds.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/usb/gpio_vbus.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> @@ -240,14 +241,23 @@ static void __init vpac270_onenand_init(void) {} #if defined(CONFIG_MMC_PXA) || defined(CONFIG_MMC_PXA_MODULE) static struct pxamci_platform_data vpac270_mci_platform_data = { .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, - .gpio_power = -1, - .gpio_card_detect = GPIO53_VPAC270_SD_DETECT_N, - .gpio_card_ro = GPIO52_VPAC270_SD_READONLY, .detect_delay_ms = 200, }; +static struct gpiod_lookup_table vpac270_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", GPIO53_VPAC270_SD_DETECT_N, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", GPIO52_VPAC270_SD_READONLY, + "wp", GPIO_ACTIVE_LOW), + { }, + }, +}; + static void __init vpac270_mmc_init(void) { + gpiod_add_lookup_table(&vpac270_mci_gpio_table); pxa_set_mci_info(&vpac270_mci_platform_data); } #else diff --git a/arch/arm/mach-pxa/z2.c b/arch/arm/mach-pxa/z2.c @@ -27,6 +27,7 @@ #include <linux/power_supply.h> #include <linux/mtd/physmap.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/gpio_keys.h> #include <linux/delay.h> #include <linux/regulator/machine.h> @@ -290,14 +291,21 @@ static inline void z2_lcd_init(void) {} #if defined(CONFIG_MMC_PXA) || defined(CONFIG_MMC_PXA_MODULE) static struct pxamci_platform_data z2_mci_platform_data = { .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, - .gpio_card_detect = GPIO96_ZIPITZ2_SD_DETECT, - .gpio_power = -1, - .gpio_card_ro = -1, .detect_delay_ms = 200, }; +static struct gpiod_lookup_table z2_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", GPIO96_ZIPITZ2_SD_DETECT, + "cd", GPIO_ACTIVE_LOW), + { }, + }, +}; + static void __init z2_mmc_init(void) { + gpiod_add_lookup_table(&z2_mci_gpio_table); pxa_set_mci_info(&z2_mci_platform_data); } #else diff --git a/arch/arm/mach-pxa/zeus.c b/arch/arm/mach-pxa/zeus.c @@ -663,10 +663,18 @@ static struct pxafb_mach_info zeus_fb_info = { static struct pxamci_platform_data zeus_mci_platform_data = { .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, .detect_delay_ms = 250, - .gpio_card_detect = ZEUS_MMC_CD_GPIO, - .gpio_card_ro = ZEUS_MMC_WP_GPIO, .gpio_card_ro_invert = 1, - .gpio_power = -1 +}; + +static struct gpiod_lookup_table zeus_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("gpio-pxa", ZEUS_MMC_CD_GPIO, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("gpio-pxa", ZEUS_MMC_WP_GPIO, + "wp", GPIO_ACTIVE_HIGH), + { }, + }, }; /* @@ -883,6 +891,7 @@ static void __init zeus_init(void) else pxa_set_fb_info(NULL, &zeus_fb_info); + gpiod_add_lookup_table(&zeus_mci_gpio_table); pxa_set_mci_info(&zeus_mci_platform_data); pxa_set_udc_info(&zeus_udc_info); pxa_set_ac97_info(&zeus_ac97_info); diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c @@ -19,7 +19,7 @@ #include <linux/leds.h> #include <linux/init.h> #include <linux/platform_device.h> -#include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/pwm.h> #include <linux/pwm_backlight.h> #include <linux/smc91x.h> @@ -227,33 +227,68 @@ static inline void zylonite_init_lcd(void) {} static struct pxamci_platform_data zylonite_mci_platform_data = { .detect_delay_ms= 200, .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, - .gpio_card_detect = EXT_GPIO(0), - .gpio_card_ro = EXT_GPIO(2), - .gpio_power = -1, +}; + +#define PCA9539A_MCI_CD 0 +#define PCA9539A_MCI1_CD 1 +#define PCA9539A_MCI_WP 2 +#define PCA9539A_MCI1_WP 3 +#define PCA9539A_MCI3_CD 30 +#define PCA9539A_MCI3_WP 31 + +static struct gpiod_lookup_table zylonite_mci_gpio_table = { + .dev_id = "pxa2xx-mci.0", + .table = { + GPIO_LOOKUP("i2c-pca9539-a", PCA9539A_MCI_CD, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("i2c-pca9539-a", PCA9539A_MCI_WP, + "wp", GPIO_ACTIVE_LOW), + { }, + }, }; static struct pxamci_platform_data zylonite_mci2_platform_data = { .detect_delay_ms= 200, .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, - .gpio_card_detect = EXT_GPIO(1), - .gpio_card_ro = EXT_GPIO(3), - .gpio_power = -1, +}; + +static struct gpiod_lookup_table zylonite_mci2_gpio_table = { + .dev_id = "pxa2xx-mci.1", + .table = { + GPIO_LOOKUP("i2c-pca9539-a", PCA9539A_MCI1_CD, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("i2c-pca9539-a", PCA9539A_MCI1_WP, + "wp", GPIO_ACTIVE_LOW), + { }, + }, }; static struct pxamci_platform_data zylonite_mci3_platform_data = { .detect_delay_ms= 200, .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, - .gpio_card_detect = EXT_GPIO(30), - .gpio_card_ro = EXT_GPIO(31), - .gpio_power = -1, +}; + +static struct gpiod_lookup_table zylonite_mci3_gpio_table = { + .dev_id = "pxa2xx-mci.2", + .table = { + GPIO_LOOKUP("i2c-pca9539-a", PCA9539A_MCI3_CD, + "cd", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("i2c-pca9539-a", PCA9539A_MCI3_WP, + "wp", GPIO_ACTIVE_LOW), + { }, + }, }; static void __init zylonite_init_mmc(void) { + gpiod_add_lookup_table(&zylonite_mci_gpio_table); pxa_set_mci_info(&zylonite_mci_platform_data); + gpiod_add_lookup_table(&zylonite_mci2_gpio_table); pxa3xx_set_mci2_info(&zylonite_mci2_platform_data); - if (cpu_is_pxa310()) + if (cpu_is_pxa310()) { + gpiod_add_lookup_table(&zylonite_mci3_gpio_table); pxa3xx_set_mci3_info(&zylonite_mci3_platform_data); + } } #else static inline void zylonite_init_mmc(void) {} diff --git a/arch/arm/mach-pxa/zylonite_pxa300.c b/arch/arm/mach-pxa/zylonite_pxa300.c @@ -230,11 +230,13 @@ static struct pca953x_platform_data gpio_exp[] = { static struct i2c_board_info zylonite_i2c_board_info[] = { { .type = "pca9539", + .dev_name = "pca9539-a", .addr = 0x74, .platform_data = &gpio_exp[0], .irq = PXA_GPIO_TO_IRQ(18), }, { .type = "pca9539", + .dev_name = "pca9539-b", .addr = 0x75, .platform_data = &gpio_exp[1], .irq = PXA_GPIO_TO_IRQ(19), diff --git a/arch/arm/mach-s3c24xx/mach-at2440evb.c b/arch/arm/mach-s3c24xx/mach-at2440evb.c @@ -9,7 +9,7 @@ #include <linux/kernel.h> #include <linux/types.h> -#include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/interrupt.h> #include <linux/list.h> #include <linux/timer.h> @@ -136,7 +136,16 @@ static struct platform_device at2440evb_device_eth = { }; static struct s3c24xx_mci_pdata at2440evb_mci_pdata __initdata = { - .gpio_detect = S3C2410_GPG(10), + /* Intentionally left blank */ +}; + +static struct gpiod_lookup_table at2440evb_mci_gpio_table = { + .dev_id = "s3c2410-sdi", + .table = { + /* Card detect S3C2410_GPG(10) */ + GPIO_LOOKUP("GPG", 10, "cd", GPIO_ACTIVE_LOW), + { }, + }, }; /* 7" LCD panel */ @@ -200,6 +209,7 @@ static void __init at2440evb_init_time(void) static void __init at2440evb_init(void) { s3c24xx_fb_set_platdata(&at2440evb_fb_info); + gpiod_add_lookup_table(&at2440evb_mci_gpio_table); s3c24xx_mci_set_platdata(&at2440evb_mci_pdata); s3c_nand_set_platdata(&at2440evb_nand_info); s3c_i2c0_set_platdata(NULL); diff --git a/arch/arm/mach-s3c24xx/mach-h1940.c b/arch/arm/mach-s3c24xx/mach-h1940.c @@ -18,6 +18,7 @@ #include <linux/platform_device.h> #include <linux/io.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/input.h> #include <linux/gpio_keys.h> #include <linux/pwm.h> @@ -459,12 +460,21 @@ static void h1940_set_mmc_power(unsigned char power_mode, unsigned short vdd) } static struct s3c24xx_mci_pdata h1940_mmc_cfg __initdata = { - .gpio_detect = S3C2410_GPF(5), - .gpio_wprotect = S3C2410_GPH(8), .set_power = h1940_set_mmc_power, .ocr_avail = MMC_VDD_32_33, }; +static struct gpiod_lookup_table h1940_mmc_gpio_table = { + .dev_id = "s3c2410-sdi", + .table = { + /* Card detect S3C2410_GPF(5) */ + GPIO_LOOKUP("GPF", 5, "cd", GPIO_ACTIVE_LOW), + /* Write protect S3C2410_GPH(8) */ + GPIO_LOOKUP("GPH", 8, "wp", GPIO_ACTIVE_LOW), + { }, + }, +}; + static struct pwm_lookup h1940_pwm_lookup[] = { PWM_LOOKUP("samsung-pwm", 0, "pwm-backlight", NULL, 36296, PWM_POLARITY_NORMAL), @@ -680,6 +690,7 @@ static void __init h1940_init(void) u32 tmp; s3c24xx_fb_set_platdata(&h1940_fb_info); + gpiod_add_lookup_table(&h1940_mmc_gpio_table); s3c24xx_mci_set_platdata(&h1940_mmc_cfg); s3c24xx_udc_set_platdata(&h1940_udc_cfg); s3c24xx_ts_set_platdata(&h1940_ts_cfg); diff --git a/arch/arm/mach-s3c24xx/mach-mini2440.c b/arch/arm/mach-s3c24xx/mach-mini2440.c @@ -15,6 +15,7 @@ #include <linux/timer.h> #include <linux/init.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/input.h> #include <linux/io.h> #include <linux/serial_core.h> @@ -234,13 +235,22 @@ static struct s3c2410fb_mach_info mini2440_fb_info __initdata = { /* MMC/SD */ static struct s3c24xx_mci_pdata mini2440_mmc_cfg __initdata = { - .gpio_detect = S3C2410_GPG(8), - .gpio_wprotect = S3C2410_GPH(8), .wprotect_invert = 1, .set_power = NULL, .ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34, }; +static struct gpiod_lookup_table mini2440_mmc_gpio_table = { + .dev_id = "s3c2410-sdi", + .table = { + /* Card detect S3C2410_GPG(8) */ + GPIO_LOOKUP("GPG", 8, "cd", GPIO_ACTIVE_LOW), + /* Write protect S3C2410_GPH(8) */ + GPIO_LOOKUP("GPH", 8, "wp", GPIO_ACTIVE_HIGH), + { }, + }, +}; + /* NAND Flash on MINI2440 board */ static struct mtd_partition mini2440_default_nand_part[] __initdata = { @@ -696,6 +706,7 @@ static void __init mini2440_init(void) } s3c24xx_udc_set_platdata(&mini2440_udc_cfg); + gpiod_add_lookup_table(&mini2440_mmc_gpio_table); s3c24xx_mci_set_platdata(&mini2440_mmc_cfg); s3c_nand_set_platdata(&mini2440_nand_info); s3c_i2c0_set_platdata(NULL); diff --git a/arch/arm/mach-s3c24xx/mach-n30.c b/arch/arm/mach-s3c24xx/mach-n30.c @@ -17,6 +17,7 @@ #include <linux/gpio_keys.h> #include <linux/init.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/platform_device.h> @@ -350,12 +351,21 @@ static void n30_sdi_set_power(unsigned char power_mode, unsigned short vdd) } static struct s3c24xx_mci_pdata n30_mci_cfg __initdata = { - .gpio_detect = S3C2410_GPF(1), - .gpio_wprotect = S3C2410_GPG(10), .ocr_avail = MMC_VDD_32_33, .set_power = n30_sdi_set_power, }; +static struct gpiod_lookup_table n30_mci_gpio_table = { + .dev_id = "s3c2410-sdi", + .table = { + /* Card detect S3C2410_GPF(1) */ + GPIO_LOOKUP("GPF", 1, "cd", GPIO_ACTIVE_LOW), + /* Write protect S3C2410_GPG(10) */ + GPIO_LOOKUP("GPG", 10, "wp", GPIO_ACTIVE_LOW), + { }, + }, +}; + static struct platform_device *n30_devices[] __initdata = { &s3c_device_lcd, &s3c_device_wdt, @@ -549,6 +559,7 @@ static void __init n30_init(void) s3c24xx_fb_set_platdata(&n30_fb_info); s3c24xx_udc_set_platdata(&n30_udc_cfg); + gpiod_add_lookup_table(&n30_mci_gpio_table); s3c24xx_mci_set_platdata(&n30_mci_cfg); s3c_i2c0_set_platdata(&n30_i2ccfg); diff --git a/arch/arm/mach-s3c24xx/mach-rx1950.c b/arch/arm/mach-s3c24xx/mach-rx1950.c @@ -14,6 +14,7 @@ #include <linux/timer.h> #include <linux/init.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/platform_device.h> #include <linux/serial_core.h> #include <linux/serial_s3c.h> @@ -558,12 +559,21 @@ static void rx1950_set_mmc_power(unsigned char power_mode, unsigned short vdd) } static struct s3c24xx_mci_pdata rx1950_mmc_cfg __initdata = { - .gpio_detect = S3C2410_GPF(5), - .gpio_wprotect = S3C2410_GPH(8), .set_power = rx1950_set_mmc_power, .ocr_avail = MMC_VDD_32_33, }; +static struct gpiod_lookup_table rx1950_mmc_gpio_table = { + .dev_id = "s3c2410-sdi", + .table = { + /* Card detect S3C2410_GPF(5) */ + GPIO_LOOKUP("GPF", 5, "cd", GPIO_ACTIVE_LOW), + /* Write protect S3C2410_GPH(8) */ + GPIO_LOOKUP("GPH", 8, "wp", GPIO_ACTIVE_LOW), + { }, + }, +}; + static struct mtd_partition rx1950_nand_part[] = { [0] = { .name = "Boot0", @@ -762,6 +772,7 @@ static void __init rx1950_init_machine(void) s3c24xx_fb_set_platdata(&rx1950_lcd_cfg); s3c24xx_udc_set_platdata(&rx1950_udc_cfg); s3c24xx_ts_set_platdata(&rx1950_ts_cfg); + gpiod_add_lookup_table(&rx1950_mmc_gpio_table); s3c24xx_mci_set_platdata(&rx1950_mmc_cfg); s3c_i2c0_set_platdata(NULL); s3c_nand_set_platdata(&rx1950_nand_info); diff --git a/arch/mips/include/asm/mach-jz4740/jz4740_mmc.h b/arch/mips/include/asm/mach-jz4740/jz4740_mmc.h @@ -3,12 +3,8 @@ #define __LINUX_MMC_JZ4740_MMC struct jz4740_mmc_platform_data { - int gpio_power; - int gpio_card_detect; - int gpio_read_only; unsigned card_detect_active_low:1; unsigned read_only_active_low:1; - unsigned power_active_low:1; unsigned data_1bit:1; }; diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c @@ -43,9 +43,6 @@ #include "clock.h" /* GPIOs */ -#define QI_LB60_GPIO_SD_CD JZ_GPIO_PORTD(0) -#define QI_LB60_GPIO_SD_VCC_EN_N JZ_GPIO_PORTD(2) - #define QI_LB60_GPIO_KEYOUT(x) (JZ_GPIO_PORTC(10) + (x)) #define QI_LB60_GPIO_KEYIN(x) (JZ_GPIO_PORTD(18) + (x)) #define QI_LB60_GPIO_KEYIN8 JZ_GPIO_PORTD(26) @@ -386,10 +383,16 @@ static struct platform_device qi_lb60_gpio_keys = { }; static struct jz4740_mmc_platform_data qi_lb60_mmc_pdata = { - .gpio_card_detect = QI_LB60_GPIO_SD_CD, - .gpio_read_only = -1, - .gpio_power = QI_LB60_GPIO_SD_VCC_EN_N, - .power_active_low = 1, + /* Intentionally left blank */ +}; + +static struct gpiod_lookup_table qi_lb60_mmc_gpio_table = { + .dev_id = "jz4740-mmc.0", + .table = { + GPIO_LOOKUP("GPIOD", 0, "cd", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("GPIOD", 2, "power", GPIO_ACTIVE_LOW), + { }, + }, }; /* beeper */ @@ -500,6 +503,7 @@ static int __init qi_lb60_init_platform_devices(void) gpiod_add_lookup_table(&qi_lb60_audio_gpio_table); gpiod_add_lookup_table(&qi_lb60_nand_gpio_table); gpiod_add_lookup_table(&qi_lb60_spigpio_gpio_table); + gpiod_add_lookup_table(&qi_lb60_mmc_gpio_table); spi_register_board_info(qi_lb60_spi_board_info, ARRAY_SIZE(qi_lb60_spi_board_info)); diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c @@ -696,13 +696,20 @@ static struct gpiod_lookup_table sdhi0_power_gpiod_table = { }, }; +static struct gpiod_lookup_table sdhi0_gpio_table = { + .dev_id = "sh_mobile_sdhi.0", + .table = { + /* Card detect */ + GPIO_LOOKUP("sh7724_pfc", GPIO_PTY7, "cd", GPIO_ACTIVE_LOW), + { }, + }, +}; + static struct tmio_mmc_data sdhi0_info = { .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, .capabilities = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD | MMC_CAP_NEEDS_POLL, - .flags = TMIO_MMC_USE_GPIO_CD, - .cd_gpio = GPIO_PTY7, }; static struct resource sdhi0_resources[] = { @@ -735,8 +742,15 @@ static struct tmio_mmc_data sdhi1_info = { .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI1_RX, .capabilities = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD | MMC_CAP_NEEDS_POLL, - .flags = TMIO_MMC_USE_GPIO_CD, - .cd_gpio = GPIO_PTW7, +}; + +static struct gpiod_lookup_table sdhi1_gpio_table = { + .dev_id = "sh_mobile_sdhi.1", + .table = { + /* Card detect */ + GPIO_LOOKUP("sh7724_pfc", GPIO_PTW7, "cd", GPIO_ACTIVE_LOW), + { }, + }, }; static struct resource sdhi1_resources[] = { @@ -776,9 +790,19 @@ static struct mmc_spi_platform_data mmc_spi_info = { .caps2 = MMC_CAP2_RO_ACTIVE_HIGH, .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, /* 3.3V only */ .setpower = mmc_spi_setpower, - .flags = MMC_SPI_USE_CD_GPIO | MMC_SPI_USE_RO_GPIO, - .cd_gpio = GPIO_PTY7, - .ro_gpio = GPIO_PTY6, +}; + +static struct gpiod_lookup_table mmc_spi_gpio_table = { + .dev_id = "mmc_spi.0", /* device "mmc_spi" @ CS0 */ + .table = { + /* Card detect */ + GPIO_LOOKUP_IDX("sh7724_pfc", GPIO_PTY7, NULL, 0, + GPIO_ACTIVE_LOW), + /* Write protect */ + GPIO_LOOKUP_IDX("sh7724_pfc", GPIO_PTY6, NULL, 1, + GPIO_ACTIVE_HIGH), + { }, + }, }; static struct spi_board_info spi_bus[] = { @@ -1282,6 +1306,7 @@ static int __init arch_setup(void) gpio_request(GPIO_PTB6, NULL); /* 3.3V power control */ gpio_direction_output(GPIO_PTB6, 0); /* disable power by default */ + gpiod_add_lookup_table(&mmc_spi_gpio_table); spi_register_board_info(spi_bus, ARRAY_SIZE(spi_bus)); #endif @@ -1434,6 +1459,10 @@ static int __init arch_setup(void) gpiod_add_lookup_table(&cn12_power_gpiod_table); #if defined(CONFIG_MMC_SDHI) || defined(CONFIG_MMC_SDHI_MODULE) gpiod_add_lookup_table(&sdhi0_power_gpiod_table); + gpiod_add_lookup_table(&sdhi0_gpio_table); +#if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE) + gpiod_add_lookup_table(&sdhi1_gpio_table); +#endif #endif return platform_add_devices(ecovec_devices, diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c @@ -449,7 +449,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) gc->base = chip->gpio_start; gc->ngpio = gpios; - gc->label = chip->client->name; + gc->label = dev_name(&chip->client->dev); gc->parent = &chip->client->dev; gc->owner = THIS_MODULE; gc->names = chip->names; diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c @@ -18,6 +18,7 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/pm_runtime.h> #define DRIVER_NAME "memstick" @@ -436,6 +437,7 @@ static void memstick_check(struct work_struct *work) struct memstick_dev *card; dev_dbg(&host->dev, "memstick_check started\n"); + pm_runtime_get_noresume(host->dev.parent); mutex_lock(&host->lock); if (!host->card) { if (memstick_power_on(host)) @@ -479,6 +481,7 @@ out_power_off: host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF); mutex_unlock(&host->lock); + pm_runtime_put(host->dev.parent); dev_dbg(&host->dev, "memstick_check finished\n"); } diff --git a/drivers/memstick/host/rtsx_usb_ms.c b/drivers/memstick/host/rtsx_usb_ms.c @@ -40,15 +40,14 @@ struct rtsx_usb_ms { struct mutex host_mutex; struct work_struct handle_req; - - struct task_struct *detect_ms; - struct completion detect_ms_exit; + struct delayed_work poll_card; u8 ssc_depth; unsigned int clock; int power_mode; unsigned char ifmode; bool eject; + bool system_suspending; }; static inline struct device *ms_dev(struct rtsx_usb_ms *host) @@ -545,7 +544,7 @@ static void rtsx_usb_ms_handle_req(struct work_struct *work) host->req->error); } } while (!rc); - pm_runtime_put(ms_dev(host)); + pm_runtime_put_sync(ms_dev(host)); } } @@ -585,14 +584,14 @@ static int rtsx_usb_ms_set_param(struct memstick_host *msh, break; if (value == MEMSTICK_POWER_ON) { - pm_runtime_get_sync(ms_dev(host)); + pm_runtime_get_noresume(ms_dev(host)); err = ms_power_on(host); + if (err) + pm_runtime_put_noidle(ms_dev(host)); } else if (value == MEMSTICK_POWER_OFF) { err = ms_power_off(host); - if (host->msh->card) + if (!err) pm_runtime_put_noidle(ms_dev(host)); - else - pm_runtime_put(ms_dev(host)); } else err = -EINVAL; if (!err) @@ -638,12 +637,16 @@ static int rtsx_usb_ms_set_param(struct memstick_host *msh, } out: mutex_unlock(&ucr->dev_mutex); - pm_runtime_put(ms_dev(host)); + pm_runtime_put_sync(ms_dev(host)); /* power-on delay */ - if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON) + if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON) { usleep_range(10000, 12000); + if (!host->eject) + schedule_delayed_work(&host->poll_card, 100); + } + dev_dbg(ms_dev(host), "%s: return = %d\n", __func__, err); return err; } @@ -654,9 +657,24 @@ static int rtsx_usb_ms_suspend(struct device *dev) struct rtsx_usb_ms *host = dev_get_drvdata(dev); struct memstick_host *msh = host->msh; - dev_dbg(ms_dev(host), "--> %s\n", __func__); + /* Since we use rtsx_usb's resume callback to runtime resume its + * children to implement remote wakeup signaling, this causes + * rtsx_usb_ms' runtime resume callback runs after its suspend + * callback: + * rtsx_usb_ms_suspend() + * rtsx_usb_resume() + * -> rtsx_usb_ms_runtime_resume() + * -> memstick_detect_change() + * + * rtsx_usb_suspend() + * + * To avoid this, skip runtime resume/suspend if system suspend is + * underway. + */ + host->system_suspending = true; memstick_suspend_host(msh); + return 0; } @@ -665,58 +683,85 @@ static int rtsx_usb_ms_resume(struct device *dev) struct rtsx_usb_ms *host = dev_get_drvdata(dev); struct memstick_host *msh = host->msh; - dev_dbg(ms_dev(host), "--> %s\n", __func__); - memstick_resume_host(msh); + host->system_suspending = false; + return 0; } #endif /* CONFIG_PM_SLEEP */ -/* - * Thread function of ms card slot detection. The thread starts right after - * successful host addition. It stops while the driver removal function sets - * host->eject true. - */ -static int rtsx_usb_detect_ms_card(void *__host) +#ifdef CONFIG_PM +static int rtsx_usb_ms_runtime_suspend(struct device *dev) { - struct rtsx_usb_ms *host = (struct rtsx_usb_ms *)__host; + struct rtsx_usb_ms *host = dev_get_drvdata(dev); + + if (host->system_suspending) + return 0; + + if (host->msh->card || host->power_mode != MEMSTICK_POWER_OFF) + return -EAGAIN; + + return 0; +} + +static int rtsx_usb_ms_runtime_resume(struct device *dev) +{ + struct rtsx_usb_ms *host = dev_get_drvdata(dev); + + + if (host->system_suspending) + return 0; + + memstick_detect_change(host->msh); + + return 0; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops rtsx_usb_ms_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rtsx_usb_ms_suspend, rtsx_usb_ms_resume) + SET_RUNTIME_PM_OPS(rtsx_usb_ms_runtime_suspend, rtsx_usb_ms_runtime_resume, NULL) +}; + + +static void rtsx_usb_ms_poll_card(struct work_struct *work) +{ + struct rtsx_usb_ms *host = container_of(work, struct rtsx_usb_ms, + poll_card.work); struct rtsx_ucr *ucr = host->ucr; - u8 val = 0; int err; + u8 val; - for (;;) { - pm_runtime_get_sync(ms_dev(host)); - mutex_lock(&ucr->dev_mutex); - - /* Check pending MS card changes */ - err = rtsx_usb_read_register(ucr, CARD_INT_PEND, &val); - if (err) { - mutex_unlock(&ucr->dev_mutex); - goto poll_again; - } + if (host->eject || host->power_mode != MEMSTICK_POWER_ON) + return; - /* Clear the pending */ - rtsx_usb_write_register(ucr, CARD_INT_PEND, - XD_INT | MS_INT | SD_INT, - XD_INT | MS_INT | SD_INT); + pm_runtime_get_sync(ms_dev(host)); + mutex_lock(&ucr->dev_mutex); + /* Check pending MS card changes */ + err = rtsx_usb_read_register(ucr, CARD_INT_PEND, &val); + if (err) { mutex_unlock(&ucr->dev_mutex); + goto poll_again; + } - if (val & MS_INT) { - dev_dbg(ms_dev(host), "MS slot change detected\n"); - memstick_detect_change(host->msh); - } + /* Clear the pending */ + rtsx_usb_write_register(ucr, CARD_INT_PEND, + XD_INT | MS_INT | SD_INT, + XD_INT | MS_INT | SD_INT); -poll_again: - pm_runtime_put(ms_dev(host)); - if (host->eject) - break; + mutex_unlock(&ucr->dev_mutex); - schedule_timeout_idle(HZ); + if (val & MS_INT) { + dev_dbg(ms_dev(host), "MS slot change detected\n"); + memstick_detect_change(host->msh); } - complete(&host->detect_ms_exit); - return 0; +poll_again: + pm_runtime_put_sync(ms_dev(host)); + + if (!host->eject && host->power_mode == MEMSTICK_POWER_ON) + schedule_delayed_work(&host->poll_card, 100); } static int rtsx_usb_ms_drv_probe(struct platform_device *pdev) @@ -747,45 +792,42 @@ static int rtsx_usb_ms_drv_probe(struct platform_device *pdev) mutex_init(&host->host_mutex); INIT_WORK(&host->handle_req, rtsx_usb_ms_handle_req); - init_completion(&host->detect_ms_exit); - host->detect_ms = kthread_create(rtsx_usb_detect_ms_card, host, - "rtsx_usb_ms_%d", pdev->id); - if (IS_ERR(host->detect_ms)) { - dev_dbg(&(pdev->dev), - "Unable to create polling thread.\n"); - err = PTR_ERR(host->detect_ms); - goto err_out; - } + INIT_DELAYED_WORK(&host->poll_card, rtsx_usb_ms_poll_card); msh->request = rtsx_usb_ms_request; msh->set_param = rtsx_usb_ms_set_param; msh->caps = MEMSTICK_CAP_PAR4; - pm_runtime_enable(&pdev->dev); + pm_runtime_get_noresume(ms_dev(host)); + pm_runtime_set_active(ms_dev(host)); + pm_runtime_enable(ms_dev(host)); + err = memstick_add_host(msh); if (err) goto err_out; - wake_up_process(host->detect_ms); + pm_runtime_put(ms_dev(host)); + return 0; err_out: memstick_free_host(msh); + pm_runtime_disable(ms_dev(host)); + pm_runtime_put_noidle(ms_dev(host)); return err; } static int rtsx_usb_ms_drv_remove(struct platform_device *pdev) { struct rtsx_usb_ms *host = platform_get_drvdata(pdev); - struct memstick_host *msh; + struct memstick_host *msh = host->msh; int err; - msh = host->msh; host->eject = true; cancel_work_sync(&host->handle_req); mutex_lock(&host->host_mutex); if (host->req) { - dev_dbg(&(pdev->dev), + dev_dbg(ms_dev(host), "%s: Controller removed during transfer\n", dev_name(&msh->dev)); host->req->error = -ENOMEDIUM; @@ -797,7 +839,6 @@ static int rtsx_usb_ms_drv_remove(struct platform_device *pdev) } mutex_unlock(&host->host_mutex); - wait_for_completion(&host->detect_ms_exit); memstick_remove_host(msh); memstick_free_host(msh); @@ -807,18 +848,15 @@ static int rtsx_usb_ms_drv_remove(struct platform_device *pdev) if (pm_runtime_active(ms_dev(host))) pm_runtime_put(ms_dev(host)); - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(ms_dev(host)); platform_set_drvdata(pdev, NULL); - dev_dbg(&(pdev->dev), + dev_dbg(ms_dev(host), ": Realtek USB Memstick controller has been removed\n"); return 0; } -static SIMPLE_DEV_PM_OPS(rtsx_usb_ms_pm_ops, - rtsx_usb_ms_suspend, rtsx_usb_ms_resume); - static struct platform_device_id rtsx_usb_ms_ids[] = { { .name = "rtsx_usb_ms", diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile @@ -57,4 +57,4 @@ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_OCXL) += ocxl/ -obj-$(CONFIG_MISC_RTSX) += cardreader/ +obj-y += cardreader/ diff --git a/drivers/misc/cardreader/Kconfig b/drivers/misc/cardreader/Kconfig @@ -1,3 +1,14 @@ +config MISC_ALCOR_PCI + tristate "Alcor Micro/Alcor Link PCI-E card reader" + depends on PCI + select MFD_CORE + help + This supports for Alcor Micro PCI-Express card reader including au6601, + au6621. + Alcor Micro card readers support access to many types of memory cards, + such as Memory Stick, Memory Stick Pro, Secure Digital and + MultiMediaCard. + config MISC_RTSX_PCI tristate "Realtek PCI-E card reader" depends on PCI diff --git a/drivers/misc/cardreader/Makefile b/drivers/misc/cardreader/Makefile @@ -1,4 +1,4 @@ -rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o rts5260.o - +obj-$(CONFIG_MISC_ALCOR_PCI) += alcor_pci.o obj-$(CONFIG_MISC_RTSX_PCI) += rtsx_pci.o +rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o rts5260.o obj-$(CONFIG_MISC_RTSX_USB) += rtsx_usb.o diff --git a/drivers/misc/cardreader/alcor_pci.c b/drivers/misc/cardreader/alcor_pci.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Oleksij Rempel <linux@rempel-privat.de> + * + * Driver for Alcor Micro AU6601 and AU6621 controllers + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/pm.h> + +#include <linux/alcor_pci.h> + +#define DRV_NAME_ALCOR_PCI "alcor_pci" + +static DEFINE_IDA(alcor_pci_idr); + +static struct mfd_cell alcor_pci_cells[] = { + [ALCOR_SD_CARD] = { + .name = DRV_NAME_ALCOR_PCI_SDMMC, + }, + [ALCOR_MS_CARD] = { + .name = DRV_NAME_ALCOR_PCI_MS, + }, +}; + +static const struct alcor_dev_cfg alcor_cfg = { + .dma = 0, +}; + +static const struct alcor_dev_cfg au6621_cfg = { + .dma = 1, +}; + +static const struct pci_device_id pci_ids[] = { + { PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6601), + .driver_data = (kernel_ulong_t)&alcor_cfg }, + { PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6621), + .driver_data = (kernel_ulong_t)&au6621_cfg }, + { }, +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +void alcor_write8(struct alcor_pci_priv *priv, u8 val, unsigned int addr) +{ + writeb(val, priv->iobase + addr); +} +EXPORT_SYMBOL_GPL(alcor_write8); + +void alcor_write16(struct alcor_pci_priv *priv, u16 val, unsigned int addr) +{ + writew(val, priv->iobase + addr); +} +EXPORT_SYMBOL_GPL(alcor_write16); + +void alcor_write32(struct alcor_pci_priv *priv, u32 val, unsigned int addr) +{ + writel(val, priv->iobase + addr); +} +EXPORT_SYMBOL_GPL(alcor_write32); + +void alcor_write32be(struct alcor_pci_priv *priv, u32 val, unsigned int addr) +{ + iowrite32be(val, priv->iobase + addr); +} +EXPORT_SYMBOL_GPL(alcor_write32be); + +u8 alcor_read8(struct alcor_pci_priv *priv, unsigned int addr) +{ + return readb(priv->iobase + addr); +} +EXPORT_SYMBOL_GPL(alcor_read8); + +u32 alcor_read32(struct alcor_pci_priv *priv, unsigned int addr) +{ + return readl(priv->iobase + addr); +} +EXPORT_SYMBOL_GPL(alcor_read32); + +u32 alcor_read32be(struct alcor_pci_priv *priv, unsigned int addr) +{ + return ioread32be(priv->iobase + addr); +} +EXPORT_SYMBOL_GPL(alcor_read32be); + +static int alcor_pci_find_cap_offset(struct alcor_pci_priv *priv, + struct pci_dev *pci) +{ + int where; + u8 val8; + u32 val32; + + where = ALCOR_CAP_START_OFFSET; + pci_read_config_byte(pci, where, &val8); + if (!val8) + return 0; + + where = (int)val8; + while (1) { + pci_read_config_dword(pci, where, &val32); + if (val32 == 0xffffffff) { + dev_dbg(priv->dev, "find_cap_offset invalid value %x.\n", + val32); + return 0; + } + + if ((val32 & 0xff) == 0x10) { + dev_dbg(priv->dev, "pcie cap offset: %x\n", where); + return where; + } + + if ((val32 & 0xff00) == 0x00) { + dev_dbg(priv->dev, "pci_find_cap_offset invalid value %x.\n", + val32); + break; + } + where = (int)((val32 >> 8) & 0xff); + } + + return 0; +} + +static void alcor_pci_init_check_aspm(struct alcor_pci_priv *priv) +{ + struct pci_dev *pci; + int where; + u32 val32; + + priv->pdev_cap_off = alcor_pci_find_cap_offset(priv, priv->pdev); + priv->parent_cap_off = alcor_pci_find_cap_offset(priv, + priv->parent_pdev); + + if ((priv->pdev_cap_off == 0) || (priv->parent_cap_off == 0)) { + dev_dbg(priv->dev, "pci_cap_off: %x, parent_cap_off: %x\n", + priv->pdev_cap_off, priv->parent_cap_off); + return; + } + + /* link capability */ + pci = priv->pdev; + where = priv->pdev_cap_off + ALCOR_PCIE_LINK_CAP_OFFSET; + pci_read_config_dword(pci, where, &val32); + priv->pdev_aspm_cap = (u8)(val32 >> 10) & 0x03; + + pci = priv->parent_pdev; + where = priv->parent_cap_off + ALCOR_PCIE_LINK_CAP_OFFSET; + pci_read_config_dword(pci, where, &val32); + priv->parent_aspm_cap = (u8)(val32 >> 10) & 0x03; + + if (priv->pdev_aspm_cap != priv->parent_aspm_cap) { + u8 aspm_cap; + + dev_dbg(priv->dev, "pdev_aspm_cap: %x, parent_aspm_cap: %x\n", + priv->pdev_aspm_cap, priv->parent_aspm_cap); + aspm_cap = priv->pdev_aspm_cap & priv->parent_aspm_cap; + priv->pdev_aspm_cap = aspm_cap; + priv->parent_aspm_cap = aspm_cap; + } + + dev_dbg(priv->dev, "ext_config_dev_aspm: %x, pdev_aspm_cap: %x\n", + priv->ext_config_dev_aspm, priv->pdev_aspm_cap); + priv->ext_config_dev_aspm &= priv->pdev_aspm_cap; +} + +static void alcor_pci_aspm_ctrl(struct alcor_pci_priv *priv, u8 aspm_enable) +{ + struct pci_dev *pci; + u8 aspm_ctrl, i; + int where; + u32 val32; + + if ((!priv->pdev_cap_off) || (!priv->parent_cap_off)) { + dev_dbg(priv->dev, "pci_cap_off: %x, parent_cap_off: %x\n", + priv->pdev_cap_off, priv->parent_cap_off); + return; + } + + if (!priv->pdev_aspm_cap) + return; + + aspm_ctrl = 0; + if (aspm_enable) { + aspm_ctrl = priv->ext_config_dev_aspm; + + if (!aspm_ctrl) { + dev_dbg(priv->dev, "aspm_ctrl == 0\n"); + return; + } + } + + for (i = 0; i < 2; i++) { + + if (i) { + pci = priv->parent_pdev; + where = priv->parent_cap_off + + ALCOR_PCIE_LINK_CTRL_OFFSET; + } else { + pci = priv->pdev; + where = priv->pdev_cap_off + + ALCOR_PCIE_LINK_CTRL_OFFSET; + } + + pci_read_config_dword(pci, where, &val32); + val32 &= (~0x03); + val32 |= (aspm_ctrl & priv->pdev_aspm_cap); + pci_write_config_byte(pci, where, (u8)val32); + } + +} + +static inline void alcor_mask_sd_irqs(struct alcor_pci_priv *priv) +{ + alcor_write32(priv, 0, AU6601_REG_INT_ENABLE); +} + +static inline void alcor_unmask_sd_irqs(struct alcor_pci_priv *priv) +{ + alcor_write32(priv, AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK | + AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE | + AU6601_INT_OVER_CURRENT_ERR, + AU6601_REG_INT_ENABLE); +} + +static inline void alcor_mask_ms_irqs(struct alcor_pci_priv *priv) +{ + alcor_write32(priv, 0, AU6601_MS_INT_ENABLE); +} + +static inline void alcor_unmask_ms_irqs(struct alcor_pci_priv *priv) +{ + alcor_write32(priv, 0x3d00fa, AU6601_MS_INT_ENABLE); +} + +static int alcor_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct alcor_dev_cfg *cfg; + struct alcor_pci_priv *priv; + int ret, i, bar = 0; + + cfg = (void *)ent->driver_data; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ret = ida_simple_get(&alcor_pci_idr, 0, 0, GFP_KERNEL); + if (ret < 0) + return ret; + priv->id = ret; + + priv->pdev = pdev; + priv->parent_pdev = pdev->bus->self; + priv->dev = &pdev->dev; + priv->cfg = cfg; + priv->irq = pdev->irq; + + ret = pci_request_regions(pdev, DRV_NAME_ALCOR_PCI); + if (ret) { + dev_err(&pdev->dev, "Cannot request region\n"); + return -ENOMEM; + } + + if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { + dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar); + ret = -ENODEV; + goto error_release_regions; + } + + priv->iobase = pcim_iomap(pdev, bar, 0); + if (!priv->iobase) { + ret = -ENOMEM; + goto error_release_regions; + } + + /* make sure irqs are disabled */ + alcor_write32(priv, 0, AU6601_REG_INT_ENABLE); + alcor_write32(priv, 0, AU6601_MS_INT_ENABLE); + + ret = dma_set_mask_and_coherent(priv->dev, AU6601_SDMA_MASK); + if (ret) { + dev_err(priv->dev, "Failed to set DMA mask\n"); + goto error_release_regions; + } + + pci_set_master(pdev); + pci_set_drvdata(pdev, priv); + alcor_pci_init_check_aspm(priv); + + for (i = 0; i < ARRAY_SIZE(alcor_pci_cells); i++) { + alcor_pci_cells[i].platform_data = priv; + alcor_pci_cells[i].pdata_size = sizeof(*priv); + } + ret = mfd_add_devices(&pdev->dev, priv->id, alcor_pci_cells, + ARRAY_SIZE(alcor_pci_cells), NULL, 0, NULL); + if (ret < 0) + goto error_release_regions; + + alcor_pci_aspm_ctrl(priv, 0); + + return 0; + +error_release_regions: + pci_release_regions(pdev); + return ret; +} + +static void alcor_pci_remove(struct pci_dev *pdev) +{ + struct alcor_pci_priv *priv; + + priv = pci_get_drvdata(pdev); + + alcor_pci_aspm_ctrl(priv, 1); + + mfd_remove_devices(&pdev->dev); + + ida_simple_remove(&alcor_pci_idr, priv->id); + + pci_release_regions(pdev); + pci_set_drvdata(pdev, NULL); +} + +#ifdef CONFIG_PM_SLEEP +static int alcor_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct alcor_pci_priv *priv = pci_get_drvdata(pdev); + + alcor_pci_aspm_ctrl(priv, 1); + return 0; +} + +static int alcor_resume(struct device *dev) +{ + + struct pci_dev *pdev = to_pci_dev(dev); + struct alcor_pci_priv *priv = pci_get_drvdata(pdev); + + alcor_pci_aspm_ctrl(priv, 0); + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(alcor_pci_pm_ops, alcor_suspend, alcor_resume); + +static struct pci_driver alcor_driver = { + .name = DRV_NAME_ALCOR_PCI, + .id_table = pci_ids, + .probe = alcor_pci_probe, + .remove = alcor_pci_remove, + .driver = { + .pm = &alcor_pci_pm_ops + }, +}; + +module_pci_driver(alcor_driver); + +MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>"); +MODULE_DESCRIPTION("PCI driver for Alcor Micro AU6601 Secure Digital Host Controller Interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/cardreader/rtsx_usb.c b/drivers/misc/cardreader/rtsx_usb.c @@ -723,8 +723,15 @@ static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) return 0; } +static int rtsx_usb_resume_child(struct device *dev, void *data) +{ + pm_request_resume(dev); + return 0; +} + static int rtsx_usb_resume(struct usb_interface *intf) { + device_for_each_child(&intf->dev, NULL, rtsx_usb_resume_child); return 0; } @@ -734,6 +741,7 @@ static int rtsx_usb_reset_resume(struct usb_interface *intf) (struct rtsx_ucr *)usb_get_intfdata(intf); rtsx_usb_reset_chip(ucr); + device_for_each_child(&intf->dev, NULL, rtsx_usb_resume_child); return 0; } diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c @@ -1960,7 +1960,7 @@ static void mmc_blk_urgent_bkops(struct mmc_queue *mq, struct mmc_queue_req *mqrq) { if (mmc_blk_urgent_bkops_needed(mq, mqrq)) - mmc_start_bkops(mq->card, true); + mmc_run_bkops(mq->card); } void mmc_blk_mq_complete(struct request *req) diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h @@ -23,15 +23,13 @@ #define MMC_STATE_BLOCKADDR (1<<2) /* card uses block-addressing */ #define MMC_CARD_SDXC (1<<3) /* card is SDXC */ #define MMC_CARD_REMOVED (1<<4) /* card has been removed */ -#define MMC_STATE_DOING_BKOPS (1<<5) /* card is doing BKOPS */ -#define MMC_STATE_SUSPENDED (1<<6) /* card is suspended */ +#define MMC_STATE_SUSPENDED (1<<5) /* card is suspended */ #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT) #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) #define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR) #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC) #define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) -#define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS) #define mmc_card_suspended(c) ((c)->state & MMC_STATE_SUSPENDED) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) @@ -39,8 +37,6 @@ #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC) #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED) -#define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS) -#define mmc_card_clr_doing_bkops(c) ((c)->state &= ~MMC_STATE_DOING_BKOPS) #define mmc_card_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED) #define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c @@ -887,7 +887,10 @@ void mmc_release_host(struct mmc_host *host) spin_unlock_irqrestore(&host->lock, flags); wake_up(&host->wq); pm_runtime_mark_last_busy(mmc_dev(host)); - pm_runtime_put_autosuspend(mmc_dev(host)); + if (host->caps & MMC_CAP_SYNC_RUNTIME_PM) + pm_runtime_put_sync_suspend(mmc_dev(host)); + else + pm_runtime_put_autosuspend(mmc_dev(host)); } } EXPORT_SYMBOL(mmc_release_host); @@ -2413,20 +2416,6 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen) } EXPORT_SYMBOL(mmc_set_blocklen); -int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount, - bool is_rel_write) -{ - struct mmc_command cmd = {}; - - cmd.opcode = MMC_SET_BLOCK_COUNT; - cmd.arg = blockcount & 0x0000FFFF; - if (is_rel_write) - cmd.arg |= 1 << 31; - cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; - return mmc_wait_for_cmd(card->host, &cmd, 5); -} -EXPORT_SYMBOL(mmc_set_blockcount); - static void mmc_hw_reset_for_init(struct mmc_host *host) { mmc_pwrseq_reset(host); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h @@ -118,8 +118,6 @@ int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from, unsigned int mmc_calc_max_discard(struct mmc_card *card); int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen); -int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount, - bool is_rel_write); int __mmc_claim_host(struct mmc_host *host, struct mmc_ctx *ctx, atomic_t *abort); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c @@ -1181,6 +1181,9 @@ static int mmc_select_hs400(struct mmc_card *card) if (err) goto out_err; + if (host->ops->hs400_prepare_ddr) + host->ops->hs400_prepare_ddr(host); + /* Switch card to DDR */ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, @@ -2011,12 +2014,6 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) if (mmc_card_suspended(host->card)) goto out; - if (mmc_card_doing_bkops(host->card)) { - err = mmc_stop_bkops(host->card); - if (err) - goto out; - } - err = mmc_flush_cache(host->card); if (err) goto out; diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c @@ -802,12 +802,6 @@ static int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) unsigned int opcode; int err; - if (!card->ext_csd.hpi) { - pr_warn("%s: Card didn't support HPI command\n", - mmc_hostname(card->host)); - return -EINVAL; - } - opcode = card->ext_csd.hpi_cmd; if (opcode == MMC_STOP_TRANSMISSION) cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; @@ -897,34 +891,6 @@ int mmc_can_ext_csd(struct mmc_card *card) return (card && card->csd.mmca_vsn > CSD_SPEC_VER_3); } -/** - * mmc_stop_bkops - stop ongoing BKOPS - * @card: MMC card to check BKOPS - * - * Send HPI command to stop ongoing background operations to - * allow rapid servicing of foreground operations, e.g. read/ - * writes. Wait until the card comes out of the programming state - * to avoid errors in servicing read/write requests. - */ -int mmc_stop_bkops(struct mmc_card *card) -{ - int err = 0; - - err = mmc_interrupt_hpi(card); - - /* - * If err is EINVAL, we can't issue an HPI. - * It should complete the BKOPS. - */ - if (!err || (err == -EINVAL)) { - mmc_card_clr_doing_bkops(card); - mmc_retune_release(card->host); - err = 0; - } - - return err; -} - static int mmc_read_bkops_status(struct mmc_card *card) { int err; @@ -941,22 +907,17 @@ static int mmc_read_bkops_status(struct mmc_card *card) } /** - * mmc_start_bkops - start BKOPS for supported cards - * @card: MMC card to start BKOPS - * @from_exception: A flag to indicate if this function was - * called due to an exception raised by the card + * mmc_run_bkops - Run BKOPS for supported cards + * @card: MMC card to run BKOPS for * - * Start background operations whenever requested. - * When the urgent BKOPS bit is set in a R1 command response - * then background operations should be started immediately. + * Run background operations synchronously for cards having manual BKOPS + * enabled and in case it reports urgent BKOPS level. */ -void mmc_start_bkops(struct mmc_card *card, bool from_exception) +void mmc_run_bkops(struct mmc_card *card) { int err; - int timeout; - bool use_busy_signal; - if (!card->ext_csd.man_bkops_en || mmc_card_doing_bkops(card)) + if (!card->ext_csd.man_bkops_en) return; err = mmc_read_bkops_status(card); @@ -966,44 +927,26 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception) return; } - if (!card->ext_csd.raw_bkops_status) - return; - - if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 && - from_exception) + if (!card->ext_csd.raw_bkops_status || + card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2) return; - if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) { - timeout = MMC_OPS_TIMEOUT_MS; - use_busy_signal = true; - } else { - timeout = 0; - use_busy_signal = false; - } - mmc_retune_hold(card->host); - err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BKOPS_START, 1, timeout, 0, - use_busy_signal, true, false); - if (err) { + /* + * For urgent BKOPS status, LEVEL_2 and higher, let's execute + * synchronously. Future wise, we may consider to start BKOPS, for less + * urgent levels by using an asynchronous background task, when idle. + */ + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BKOPS_START, 1, MMC_OPS_TIMEOUT_MS); + if (err) pr_warn("%s: Error %d starting bkops\n", mmc_hostname(card->host), err); - mmc_retune_release(card->host); - return; - } - /* - * For urgent bkops status (LEVEL_2 and more) - * bkops executed synchronously, otherwise - * the operation is in progress - */ - if (!use_busy_signal) - mmc_card_set_doing_bkops(card); - else - mmc_retune_release(card->host); + mmc_retune_release(card->host); } -EXPORT_SYMBOL(mmc_start_bkops); +EXPORT_SYMBOL(mmc_run_bkops); /* * Flush the cache to the non-volatile storage. diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h @@ -40,8 +40,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, bool use_busy_signal, bool send_status, bool retry_crc_err); int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms); -int mmc_stop_bkops(struct mmc_card *card); -void mmc_start_bkops(struct mmc_card *card, bool from_exception); +void mmc_run_bkops(struct mmc_card *card); int mmc_flush_cache(struct mmc_card *card); int mmc_cmdq_enable(struct mmc_card *card); int mmc_cmdq_disable(struct mmc_card *card); diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c @@ -3145,17 +3145,7 @@ static int mtf_testlist_show(struct seq_file *sf, void *data) return 0; } -static int mtf_testlist_open(struct inode *inode, struct file *file) -{ - return single_open(file, mtf_testlist_show, inode->i_private); -} - -static const struct file_operations mmc_test_fops_testlist = { - .open = mtf_testlist_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(mtf_testlist); static void mmc_test_free_dbgfs_file(struct mmc_card *card) { @@ -3216,7 +3206,7 @@ static int mmc_test_register_dbgfs_file(struct mmc_card *card) goto err; ret = __mmc_test_register_dbgfs_file(card, "testlist", S_IRUGO, - &mmc_test_fops_testlist); + &mtf_testlist_fops); if (ret) goto err; diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c @@ -9,7 +9,6 @@ */ #include <linux/err.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/jiffies.h> @@ -27,8 +26,8 @@ struct mmc_gpio { bool override_cd_active_level; irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id); char *ro_label; + char *cd_label; u32 cd_debounce_delay_ms; - char cd_label[]; }; static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) @@ -45,15 +44,19 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) int mmc_gpio_alloc(struct mmc_host *host) { - size_t len = strlen(dev_name(host->parent)) + 4; struct mmc_gpio *ctx = devm_kzalloc(host->parent, - sizeof(*ctx) + 2 * len, GFP_KERNEL); + sizeof(*ctx), GFP_KERNEL); if (ctx) { - ctx->ro_label = ctx->cd_label + len; ctx->cd_debounce_delay_ms = 200; - snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent)); - snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent)); + ctx->cd_label = devm_kasprintf(host->parent, GFP_KERNEL, + "%s cd", dev_name(host->parent)); + if (!ctx->cd_label) + return -ENOMEM; + ctx->ro_label = devm_kasprintf(host->parent, GFP_KERNEL, + "%s ro", dev_name(host->parent)); + if (!ctx->ro_label) + return -ENOMEM; host->slot.handler_priv = ctx; host->slot.cd_irq = -EINVAL; } @@ -98,36 +101,6 @@ int mmc_gpio_get_cd(struct mmc_host *host) } EXPORT_SYMBOL(mmc_gpio_get_cd); -/** - * mmc_gpio_request_ro - request a gpio for write-protection - * @host: mmc host - * @gpio: gpio number requested - * - * As devm_* managed functions are used in mmc_gpio_request_ro(), client - * drivers do not need to worry about freeing up memory. - * - * Returns zero on success, else an error. - */ -int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) -{ - struct mmc_gpio *ctx = host->slot.handler_priv; - int ret; - - if (!gpio_is_valid(gpio)) - return -EINVAL; - - ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN, - ctx->ro_label); - if (ret < 0) - return ret; - - 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; @@ -197,50 +170,6 @@ void mmc_gpio_set_cd_isr(struct mmc_host *host, EXPORT_SYMBOL(mmc_gpio_set_cd_isr); /** - * mmc_gpio_request_cd - request a gpio for card-detection - * @host: mmc host - * @gpio: gpio number requested - * @debounce: debounce time in microseconds - * - * As devm_* managed functions are used in mmc_gpio_request_cd(), client - * drivers do not need to worry about freeing up memory. - * - * If GPIO debouncing is desired, set the debounce parameter to a non-zero - * value. The caller is responsible for ensuring that the GPIO driver associated - * with the GPIO supports debouncing, otherwise an error will be returned. - * - * Returns zero on success, else an error. - */ -int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, - unsigned int debounce) -{ - struct mmc_gpio *ctx = host->slot.handler_priv; - int ret; - - ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN, - ctx->cd_label); - if (ret < 0) - /* - * don't bother freeing memory. It might still get used by other - * slot functions, in any case it will be freed, when the device - * is destroyed. - */ - return ret; - - if (debounce) { - ret = gpio_set_debounce(gpio, debounce); - if (ret < 0) - return ret; - } - - ctx->override_cd_active_level = true; - ctx->cd_gpio = gpio_to_desc(gpio); - - return 0; -} -EXPORT_SYMBOL(mmc_gpio_request_cd); - -/** * mmc_gpiod_request_cd - request a gpio descriptor for card-detection * @host: mmc host * @con_id: function within the GPIO consumer @@ -250,8 +179,7 @@ EXPORT_SYMBOL(mmc_gpio_request_cd); * @gpio_invert: will return whether the GPIO line is inverted or not, set * to NULL to ignore * - * Use this function in place of mmc_gpio_request_cd() to use the GPIO - * descriptor API. Note that it must be called prior to mmc_add_host() + * Note that this 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. @@ -302,9 +230,6 @@ EXPORT_SYMBOL(mmc_can_gpio_cd); * @gpio_invert: will return whether the GPIO line is inverted or not, * set to NULL to ignore * - * Use this function in place of mmc_gpio_request_ro() to use the GPIO - * descriptor API. - * * Returns zero on success, else an error. */ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig @@ -441,6 +441,13 @@ config MMC_WBSD If unsure, say N. +config MMC_ALCOR + tristate "Alcor Micro/Alcor Link SD/MMC controller" + depends on MISC_ALCOR_PCI + help + Say Y here to include driver code to support SD/MMC card interface + of Alcor Micro PCI-E card reader + config MMC_AU1X tristate "Alchemy AU1XX0 MMC Card Interface support" depends on MIPS_ALCHEMY @@ -646,13 +653,14 @@ config MMC_SDHI_SYS_DMAC config MMC_SDHI_INTERNAL_DMAC tristate "DMA for SDHI SD/SDIO controllers using on-chip bus mastering" - depends on ARM64 || ARCH_R8A77470 || COMPILE_TEST + depends on ARM64 || ARCH_R7S9210 || ARCH_R8A77470 || COMPILE_TEST depends on MMC_SDHI - default MMC_SDHI if (ARM64 || ARCH_R8A77470) + default MMC_SDHI if (ARM64 || ARCH_R7S9210 || ARCH_R8A77470) help This provides DMA support for SDHI SD/SDIO controllers using on-chip bus mastering. This supports the controllers - found in arm64 based SoCs. + found in arm64 based SoCs. This controller is also found in + some RZ family SoCs. config MMC_UNIPHIER tristate "UniPhier SD/eMMC Host Controller support" @@ -969,6 +977,8 @@ config MMC_SDHCI_XENON config MMC_SDHCI_OMAP tristate "TI SDHCI Controller Support" depends on MMC_SDHCI_PLTFM && OF + select THERMAL + select TI_SOC_THERMAL help This selects the Secure Digital Host Controller Interface (SDHCI) support present in TI's DRA7 SOCs. The controller supports @@ -977,3 +987,15 @@ config MMC_SDHCI_OMAP If you have a controller with this interface, say Y or M here. If unsure, say N. + +config MMC_SDHCI_AM654 + tristate "Support for the SDHCI Controller in TI's AM654 SOCs" + depends on MMC_SDHCI_PLTFM && OF + help + This selects the Secure Digital Host Controller Interface (SDHCI) + support present in TI's AM654 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. diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile @@ -22,8 +22,10 @@ obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o +obj-$(CONFIG_MMC_SDHCI_AM654) += sdhci_am654.o obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o +obj-$(CONFIG_MMC_ALCOR) += alcor.o obj-$(CONFIG_MMC_MTK) += mtk-sd.o obj-$(CONFIG_MMC_OMAP) += omap.o obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o diff --git a/drivers/mmc/host/alcor.c b/drivers/mmc/host/alcor.c @@ -0,0 +1,1162 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Oleksij Rempel <linux@rempel-privat.de> + * + * Driver for Alcor Micro AU6601 and AU6621 controllers + */ + +/* Note: this driver was created without any documentation. Based + * on sniffing, testing and in some cases mimic of original driver. + * As soon as some one with documentation or more experience in SD/MMC, or + * reverse engineering then me, please review this driver and question every + * thing what I did. 2018 Oleksij Rempel <linux@rempel-privat.de> + */ + +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/pm.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> + +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> + +#include <linux/alcor_pci.h> + +enum alcor_cookie { + COOKIE_UNMAPPED, + COOKIE_PRE_MAPPED, + COOKIE_MAPPED, +}; + +struct alcor_pll_conf { + unsigned int clk_src_freq; + unsigned int clk_src_reg; + unsigned int min_div; + unsigned int max_div; +}; + +struct alcor_sdmmc_host { + struct device *dev; + struct alcor_pci_priv *alcor_pci; + + struct mmc_host *mmc; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + unsigned int dma_on:1; + unsigned int early_data:1; + + struct mutex cmd_mutex; + + struct delayed_work timeout_work; + + struct sg_mapping_iter sg_miter; /* SG state for PIO */ + struct scatterlist *sg; + unsigned int blocks; /* remaining PIO blocks */ + int sg_count; + + u32 irq_status_sd; + unsigned char cur_power_mode; +}; + +static const struct alcor_pll_conf alcor_pll_cfg[] = { + /* MHZ, CLK src, max div, min div */ + { 31250000, AU6601_CLK_31_25_MHZ, 1, 511}, + { 48000000, AU6601_CLK_48_MHZ, 1, 511}, + {125000000, AU6601_CLK_125_MHZ, 1, 511}, + {384000000, AU6601_CLK_384_MHZ, 1, 511}, +}; + +static inline void alcor_rmw8(struct alcor_sdmmc_host *host, unsigned int addr, + u8 clear, u8 set) +{ + struct alcor_pci_priv *priv = host->alcor_pci; + u32 var; + + var = alcor_read8(priv, addr); + var &= ~clear; + var |= set; + alcor_write8(priv, var, addr); +} + +/* As soon as irqs are masked, some status updates may be missed. + * Use this with care. + */ +static inline void alcor_mask_sd_irqs(struct alcor_sdmmc_host *host) +{ + struct alcor_pci_priv *priv = host->alcor_pci; + + alcor_write32(priv, 0, AU6601_REG_INT_ENABLE); +} + +static inline void alcor_unmask_sd_irqs(struct alcor_sdmmc_host *host) +{ + struct alcor_pci_priv *priv = host->alcor_pci; + + alcor_write32(priv, AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK | + AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE | + AU6601_INT_OVER_CURRENT_ERR, + AU6601_REG_INT_ENABLE); +} + +static void alcor_reset(struct alcor_sdmmc_host *host, u8 val) +{ + struct alcor_pci_priv *priv = host->alcor_pci; + int i; + + alcor_write8(priv, val | AU6601_BUF_CTRL_RESET, + AU6601_REG_SW_RESET); + for (i = 0; i < 100; i++) { + if (!(alcor_read8(priv, AU6601_REG_SW_RESET) & val)) + return; + udelay(50); + } + dev_err(host->dev, "%s: timeout\n", __func__); +} + +static void alcor_data_set_dma(struct alcor_sdmmc_host *host) +{ + struct alcor_pci_priv *priv = host->alcor_pci; + u32 addr; + + if (!host->sg_count) + return; + + if (!host->sg) { + dev_err(host->dev, "have blocks, but no SG\n"); + return; + } + + if (!sg_dma_len(host->sg)) { + dev_err(host->dev, "DMA SG len == 0\n"); + return; + } + + + addr = (u32)sg_dma_address(host->sg); + + alcor_write32(priv, addr, AU6601_REG_SDMA_ADDR); + host->sg = sg_next(host->sg); + host->sg_count--; +} + +static void alcor_trigger_data_transfer(struct alcor_sdmmc_host *host, + bool early) +{ + struct alcor_pci_priv *priv = host->alcor_pci; + struct mmc_data *data = host->data; + u8 ctrl = 0; + + if (data->flags & MMC_DATA_WRITE) + ctrl |= AU6601_DATA_WRITE; + + if (data->host_cookie == COOKIE_MAPPED) { + if (host->early_data) { + host->early_data = false; + return; + } + + host->early_data = early; + + alcor_data_set_dma(host); + ctrl |= AU6601_DATA_DMA_MODE; + host->dma_on = 1; + alcor_write32(priv, data->sg_count * 0x1000, + AU6601_REG_BLOCK_SIZE); + } else { + alcor_write32(priv, data->blksz, AU6601_REG_BLOCK_SIZE); + } + + alcor_write8(priv, ctrl | AU6601_DATA_START_XFER, + AU6601_DATA_XFER_CTRL); +} + +static void alcor_trf_block_pio(struct alcor_sdmmc_host *host, bool read) +{ + struct alcor_pci_priv *priv = host->alcor_pci; + size_t blksize, len; + u8 *buf; + + if (!host->blocks) + return; + + if (host->dma_on) { + dev_err(host->dev, "configured DMA but got PIO request.\n"); + return; + } + + if (!!(host->data->flags & MMC_DATA_READ) != read) { + dev_err(host->dev, "got unexpected direction %i != %i\n", + !!(host->data->flags & MMC_DATA_READ), read); + } + + if (!sg_miter_next(&host->sg_miter)) + return; + + blksize = host->data->blksz; + len = min(host->sg_miter.length, blksize); + + dev_dbg(host->dev, "PIO, %s block size: 0x%zx\n", + read ? "read" : "write", blksize); + + host->sg_miter.consumed = len; + host->blocks--; + + buf = host->sg_miter.addr; + + if (read) + ioread32_rep(priv->iobase + AU6601_REG_BUFFER, buf, len >> 2); + else + iowrite32_rep(priv->iobase + AU6601_REG_BUFFER, buf, len >> 2); + + sg_miter_stop(&host->sg_miter); +} + +static void alcor_prepare_sg_miter(struct alcor_sdmmc_host *host) +{ + unsigned int flags = SG_MITER_ATOMIC; + struct mmc_data *data = host->data; + + if (data->flags & MMC_DATA_READ) + flags |= SG_MITER_TO_SG; + else + flags |= SG_MITER_FROM_SG; + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); +} + +static void alcor_prepare_data(struct alcor_sdmmc_host *host, + struct mmc_command *cmd) +{ + struct mmc_data *data = cmd->data; + + if (!data) + return; + + + host->data = data; + host->data->bytes_xfered = 0; + host->blocks = data->blocks; + host->sg = data->sg; + host->sg_count = data->sg_count; + dev_dbg(host->dev, "prepare DATA: sg %i, blocks: %i\n", + host->sg_count, host->blocks); + + if (data->host_cookie != COOKIE_MAPPED) + alcor_prepare_sg_miter(host); + + alcor_trigger_data_transfer(host, true); +} + +static void alcor_send_cmd(struct alcor_sdmmc_host *host, + struct mmc_command *cmd, bool set_timeout) +{ + struct alcor_pci_priv *priv = host->alcor_pci; + unsigned long timeout = 0; + u8 ctrl = 0; + + host->cmd = cmd; + alcor_prepare_data(host, cmd); + + dev_dbg(host->dev, "send CMD. opcode: 0x%02x, arg; 0x%08x\n", + cmd->opcode, cmd->arg); + alcor_write8(priv, cmd->opcode | 0x40, AU6601_REG_CMD_OPCODE); + alcor_write32be(priv, cmd->arg, AU6601_REG_CMD_ARG); + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + ctrl = AU6601_CMD_NO_RESP; + break; + case MMC_RSP_R1: + ctrl = AU6601_CMD_6_BYTE_CRC; + break; + case MMC_RSP_R1B: + ctrl = AU6601_CMD_6_BYTE_CRC | AU6601_CMD_STOP_WAIT_RDY; + break; + case MMC_RSP_R2: + ctrl = AU6601_CMD_17_BYTE_CRC; + break; + case MMC_RSP_R3: + ctrl = AU6601_CMD_6_BYTE_WO_CRC; + break; + default: + dev_err(host->dev, "%s: cmd->flag (0x%02x) is not valid\n", + mmc_hostname(host->mmc), mmc_resp_type(cmd)); + break; + } + + if (set_timeout) { + if (!cmd->data && cmd->busy_timeout) + timeout = cmd->busy_timeout; + else + timeout = 10000; + + schedule_delayed_work(&host->timeout_work, + msecs_to_jiffies(timeout)); + } + + dev_dbg(host->dev, "xfer ctrl: 0x%02x; timeout: %lu\n", ctrl, timeout); + alcor_write8(priv, ctrl | AU6601_CMD_START_XFER, + AU6601_CMD_XFER_CTRL); +} + +static void alcor_request_complete(struct alcor_sdmmc_host *host, + bool cancel_timeout) +{ + struct mmc_request *mrq; + + /* + * If this work gets rescheduled while running, it will + * be run again afterwards but without any active request. + */ + if (!host->mrq) + return; + + if (cancel_timeout) + cancel_delayed_work(&host->timeout_work); + + mrq = host->mrq; + + host->mrq = NULL; + host->cmd = NULL; + host->data = NULL; + host->dma_on = 0; + + mmc_request_done(host->mmc, mrq); +} + +static void alcor_finish_data(struct alcor_sdmmc_host *host) +{ + struct mmc_data *data; + + data = host->data; + host->data = NULL; + host->dma_on = 0; + + /* + * The specification states that the block count register must + * be updated, but it does not specify at what point in the + * data flow. That makes the register entirely useless to read + * back so we have to assume that nothing made it to the card + * in the event of an error. + */ + if (data->error) + data->bytes_xfered = 0; + else + data->bytes_xfered = data->blksz * data->blocks; + + /* + * Need to send CMD12 if - + * a) open-ended multiblock transfer (no CMD23) + * b) error in multiblock transfer + */ + if (data->stop && + (data->error || + !host->mrq->sbc)) { + + /* + * The controller needs a reset of internal state machines + * upon error conditions. + */ + if (data->error) + alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA); + + alcor_unmask_sd_irqs(host); + alcor_send_cmd(host, data->stop, false); + return; + } + + alcor_request_complete(host, 1); +} + +static void alcor_err_irq(struct alcor_sdmmc_host *host, u32 intmask) +{ + dev_dbg(host->dev, "ERR IRQ %x\n", intmask); + + if (host->cmd) { + if (intmask & AU6601_INT_CMD_TIMEOUT_ERR) + host->cmd->error = -ETIMEDOUT; + else + host->cmd->error = -EILSEQ; + } + + if (host->data) { + if (intmask & AU6601_INT_DATA_TIMEOUT_ERR) + host->data->error = -ETIMEDOUT; + else + host->data->error = -EILSEQ; + + host->data->bytes_xfered = 0; + } + + alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA); + alcor_request_complete(host, 1); +} + +static int alcor_cmd_irq_done(struct alcor_sdmmc_host *host, u32 intmask) +{ + struct alcor_pci_priv *priv = host->alcor_pci; + + intmask &= AU6601_INT_CMD_END; + + if (!intmask) + return true; + + /* got CMD_END but no CMD is in progress, wake thread an process the + * error + */ + if (!host->cmd) + return false; + + if (host->cmd->flags & MMC_RSP_PRESENT) { + struct mmc_command *cmd = host->cmd; + + cmd->resp[0] = alcor_read32be(priv, AU6601_REG_CMD_RSP0); + dev_dbg(host->dev, "RSP0: 0x%04x\n", cmd->resp[0]); + if (host->cmd->flags & MMC_RSP_136) { + cmd->resp[1] = + alcor_read32be(priv, AU6601_REG_CMD_RSP1); + cmd->resp[2] = + alcor_read32be(priv, AU6601_REG_CMD_RSP2); + cmd->resp[3] = + alcor_read32be(priv, AU6601_REG_CMD_RSP3); + dev_dbg(host->dev, "RSP1,2,3: 0x%04x 0x%04x 0x%04x\n", + cmd->resp[1], cmd->resp[2], cmd->resp[3]); + } + + } + + host->cmd->error = 0; + + /* Processed actual command. */ + if (!host->data) + return false; + + alcor_trigger_data_transfer(host, false); + host->cmd = NULL; + return true; +} + +static void alcor_cmd_irq_thread(struct alcor_sdmmc_host *host, u32 intmask) +{ + intmask &= AU6601_INT_CMD_END; + + if (!intmask) + return; + + if (!host->cmd && intmask & AU6601_INT_CMD_END) { + dev_dbg(host->dev, "Got command interrupt 0x%08x even though no command operation was in progress.\n", + intmask); + } + + /* Processed actual command. */ + if (!host->data) + alcor_request_complete(host, 1); + else + alcor_trigger_data_transfer(host, false); + host->cmd = NULL; +} + +static int alcor_data_irq_done(struct alcor_sdmmc_host *host, u32 intmask) +{ + u32 tmp; + + intmask &= AU6601_INT_DATA_MASK; + + /* nothing here to do */ + if (!intmask) + return 1; + + /* we was too fast and got DATA_END after it was processed? + * lets ignore it for now. + */ + if (!host->data && intmask == AU6601_INT_DATA_END) + return 1; + + /* looks like an error, so lets handle it. */ + if (!host->data) + return 0; + + tmp = intmask & (AU6601_INT_READ_BUF_RDY | AU6601_INT_WRITE_BUF_RDY + | AU6601_INT_DMA_END); + switch (tmp) { + case 0: + break; + case AU6601_INT_READ_BUF_RDY: + alcor_trf_block_pio(host, true); + if (!host->blocks) + break; + alcor_trigger_data_transfer(host, false); + return 1; + case AU6601_INT_WRITE_BUF_RDY: + alcor_trf_block_pio(host, false); + if (!host->blocks) + break; + alcor_trigger_data_transfer(host, false); + return 1; + case AU6601_INT_DMA_END: + if (!host->sg_count) + break; + + alcor_data_set_dma(host); + break; + default: + dev_err(host->dev, "Got READ_BUF_RDY and WRITE_BUF_RDY at same time\n"); + break; + } + + if (intmask & AU6601_INT_DATA_END) + return 0; + + return 1; +} + +static void alcor_data_irq_thread(struct alcor_sdmmc_host *host, u32 intmask) +{ + intmask &= AU6601_INT_DATA_MASK; + + if (!intmask) + return; + + if (!host->data) { + dev_dbg(host->dev, "Got data interrupt 0x%08x even though no data operation was in progress.\n", + intmask); + alcor_reset(host, AU6601_RESET_DATA); + return; + } + + if (alcor_data_irq_done(host, intmask)) + return; + + if ((intmask & AU6601_INT_DATA_END) || !host->blocks || + (host->dma_on && !host->sg_count)) + alcor_finish_data(host); +} + +static void alcor_cd_irq(struct alcor_sdmmc_host *host, u32 intmask) +{ + dev_dbg(host->dev, "card %s\n", + intmask & AU6601_INT_CARD_REMOVE ? "removed" : "inserted"); + + if (host->mrq) { + dev_dbg(host->dev, "cancel all pending tasks.\n"); + + if (host->data) + host->data->error = -ENOMEDIUM; + + if (host->cmd) + host->cmd->error = -ENOMEDIUM; + else + host->mrq->cmd->error = -ENOMEDIUM; + + alcor_request_complete(host, 1); + } + + mmc_detect_change(host->mmc, msecs_to_jiffies(1)); +} + +static irqreturn_t alcor_irq_thread(int irq, void *d) +{ + struct alcor_sdmmc_host *host = d; + irqreturn_t ret = IRQ_HANDLED; + u32 intmask, tmp; + + mutex_lock(&host->cmd_mutex); + + intmask = host->irq_status_sd; + + /* some thing bad */ + if (unlikely(!intmask || AU6601_INT_ALL_MASK == intmask)) { + dev_dbg(host->dev, "unexpected IRQ: 0x%04x\n", intmask); + ret = IRQ_NONE; + goto exit; + } + + tmp = intmask & (AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK); + if (tmp) { + if (tmp & AU6601_INT_ERROR_MASK) + alcor_err_irq(host, tmp); + else { + alcor_cmd_irq_thread(host, tmp); + alcor_data_irq_thread(host, tmp); + } + intmask &= ~(AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK); + } + + if (intmask & (AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE)) { + alcor_cd_irq(host, intmask); + intmask &= ~(AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE); + } + + if (intmask & AU6601_INT_OVER_CURRENT_ERR) { + dev_warn(host->dev, + "warning: over current detected!\n"); + intmask &= ~AU6601_INT_OVER_CURRENT_ERR; + } + + if (intmask) + dev_dbg(host->dev, "got not handled IRQ: 0x%04x\n", intmask); + +exit: + mutex_unlock(&host->cmd_mutex); + alcor_unmask_sd_irqs(host); + return ret; +} + + +static irqreturn_t alcor_irq(int irq, void *d) +{ + struct alcor_sdmmc_host *host = d; + struct alcor_pci_priv *priv = host->alcor_pci; + u32 status, tmp; + irqreturn_t ret; + int cmd_done, data_done; + + status = alcor_read32(priv, AU6601_REG_INT_STATUS); + if (!status) + return IRQ_NONE; + + alcor_write32(priv, status, AU6601_REG_INT_STATUS); + + tmp = status & (AU6601_INT_READ_BUF_RDY | AU6601_INT_WRITE_BUF_RDY + | AU6601_INT_DATA_END | AU6601_INT_DMA_END + | AU6601_INT_CMD_END); + if (tmp == status) { + cmd_done = alcor_cmd_irq_done(host, tmp); + data_done = alcor_data_irq_done(host, tmp); + /* use fast path for simple tasks */ + if (cmd_done && data_done) { + ret = IRQ_HANDLED; + goto alcor_irq_done; + } + } + + host->irq_status_sd = status; + ret = IRQ_WAKE_THREAD; + alcor_mask_sd_irqs(host); +alcor_irq_done: + return ret; +} + +static void alcor_set_clock(struct alcor_sdmmc_host *host, unsigned int clock) +{ + struct alcor_pci_priv *priv = host->alcor_pci; + int i, diff = 0x7fffffff, tmp_clock = 0; + u16 clk_src = 0; + u8 clk_div = 0; + + if (clock == 0) { + alcor_write16(priv, 0, AU6601_CLK_SELECT); + return; + } + + for (i = 0; i < ARRAY_SIZE(alcor_pll_cfg); i++) { + unsigned int tmp_div, tmp_diff; + const struct alcor_pll_conf *cfg = &alcor_pll_cfg[i]; + + tmp_div = DIV_ROUND_UP(cfg->clk_src_freq, clock); + if (cfg->min_div > tmp_div || tmp_div > cfg->max_div) + continue; + + tmp_clock = DIV_ROUND_UP(cfg->clk_src_freq, tmp_div); + tmp_diff = abs(clock - tmp_clock); + + if (tmp_diff >= 0 && tmp_diff < diff) { + diff = tmp_diff; + clk_src = cfg->clk_src_reg; + clk_div = tmp_div; + } + } + + clk_src |= ((clk_div - 1) << 8); + clk_src |= AU6601_CLK_ENABLE; + + dev_dbg(host->dev, "set freq %d cal freq %d, use div %d, mod %x\n", + clock, tmp_clock, clk_div, clk_src); + + alcor_write16(priv, clk_src, AU6601_CLK_SELECT); + +} + +static void alcor_set_timing(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct alcor_sdmmc_host *host = mmc_priv(mmc); + + if (ios->timing == MMC_TIMING_LEGACY) { + alcor_rmw8(host, AU6601_CLK_DELAY, + AU6601_CLK_POSITIVE_EDGE_ALL, 0); + } else { + alcor_rmw8(host, AU6601_CLK_DELAY, + 0, AU6601_CLK_POSITIVE_EDGE_ALL); + } +} + +static void alcor_set_bus_width(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct alcor_sdmmc_host *host = mmc_priv(mmc); + struct alcor_pci_priv *priv = host->alcor_pci; + + if (ios->bus_width == MMC_BUS_WIDTH_1) { + alcor_write8(priv, 0, AU6601_REG_BUS_CTRL); + } else if (ios->bus_width == MMC_BUS_WIDTH_4) { + alcor_write8(priv, AU6601_BUS_WIDTH_4BIT, + AU6601_REG_BUS_CTRL); + } else + dev_err(host->dev, "Unknown BUS mode\n"); + +} + +static int alcor_card_busy(struct mmc_host *mmc) +{ + struct alcor_sdmmc_host *host = mmc_priv(mmc); + struct alcor_pci_priv *priv = host->alcor_pci; + u8 status; + + /* Check whether dat[0:3] low */ + status = alcor_read8(priv, AU6601_DATA_PIN_STATE); + + return !(status & AU6601_BUS_STAT_DAT_MASK); +} + +static int alcor_get_cd(struct mmc_host *mmc) +{ + struct alcor_sdmmc_host *host = mmc_priv(mmc); + struct alcor_pci_priv *priv = host->alcor_pci; + u8 detect; + + detect = alcor_read8(priv, AU6601_DETECT_STATUS) + & AU6601_DETECT_STATUS_M; + /* check if card is present then send command and data */ + return (detect == AU6601_SD_DETECTED); +} + +static int alcor_get_ro(struct mmc_host *mmc) +{ + struct alcor_sdmmc_host *host = mmc_priv(mmc); + struct alcor_pci_priv *priv = host->alcor_pci; + u8 status; + + /* get write protect pin status */ + status = alcor_read8(priv, AU6601_INTERFACE_MODE_CTRL); + + return !!(status & AU6601_SD_CARD_WP); +} + +static void alcor_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct alcor_sdmmc_host *host = mmc_priv(mmc); + + mutex_lock(&host->cmd_mutex); + + host->mrq = mrq; + + /* check if card is present then send command and data */ + if (alcor_get_cd(mmc)) + alcor_send_cmd(host, mrq->cmd, true); + else { + mrq->cmd->error = -ENOMEDIUM; + alcor_request_complete(host, 1); + } + + mutex_unlock(&host->cmd_mutex); +} + +static void alcor_pre_req(struct mmc_host *mmc, + struct mmc_request *mrq) +{ + struct alcor_sdmmc_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + struct mmc_command *cmd = mrq->cmd; + struct scatterlist *sg; + unsigned int i, sg_len; + + if (!data || !cmd) + return; + + data->host_cookie = COOKIE_UNMAPPED; + + /* FIXME: looks like the DMA engine works only with CMD18 */ + if (cmd->opcode != 18) + return; + /* + * We don't do DMA on "complex" transfers, i.e. with + * non-word-aligned buffers or lengths. Also, we don't bother + * with all the DMA setup overhead for short transfers. + */ + if (data->blocks * data->blksz < AU6601_MAX_DMA_BLOCK_SIZE) + return; + + if (data->blksz & 3) + return; + + for_each_sg(data->sg, sg, data->sg_len, i) { + if (sg->length != AU6601_MAX_DMA_BLOCK_SIZE) + return; + } + + /* This data might be unmapped at this time */ + + sg_len = dma_map_sg(host->dev, data->sg, data->sg_len, + mmc_get_dma_dir(data)); + if (sg_len) + data->host_cookie = COOKIE_MAPPED; + + data->sg_count = sg_len; +} + +static void alcor_post_req(struct mmc_host *mmc, + struct mmc_request *mrq, + int err) +{ + struct alcor_sdmmc_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (!data) + return; + + if (data->host_cookie == COOKIE_MAPPED) { + dma_unmap_sg(host->dev, + data->sg, + data->sg_len, + mmc_get_dma_dir(data)); + } + + data->host_cookie = COOKIE_UNMAPPED; +} + +static void alcor_set_power_mode(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct alcor_sdmmc_host *host = mmc_priv(mmc); + struct alcor_pci_priv *priv = host->alcor_pci; + + switch (ios->power_mode) { + case MMC_POWER_OFF: + alcor_set_clock(host, ios->clock); + /* set all pins to input */ + alcor_write8(priv, 0, AU6601_OUTPUT_ENABLE); + /* turn of VDD */ + alcor_write8(priv, 0, AU6601_POWER_CONTROL); + break; + case MMC_POWER_UP: + break; + case MMC_POWER_ON: + /* This is most trickiest part. The order and timings of + * instructions seems to play important role. Any changes may + * confuse internal state engine if this HW. + * FIXME: If we will ever get access to documentation, then this + * part should be reviewed again. + */ + + /* enable SD card mode */ + alcor_write8(priv, AU6601_SD_CARD, + AU6601_ACTIVE_CTRL); + /* set signal voltage to 3.3V */ + alcor_write8(priv, 0, AU6601_OPT); + /* no documentation about clk delay, for now just try to mimic + * original driver. + */ + alcor_write8(priv, 0x20, AU6601_CLK_DELAY); + /* set BUS width to 1 bit */ + alcor_write8(priv, 0, AU6601_REG_BUS_CTRL); + /* set CLK first time */ + alcor_set_clock(host, ios->clock); + /* power on VDD */ + alcor_write8(priv, AU6601_SD_CARD, + AU6601_POWER_CONTROL); + /* wait until the CLK will get stable */ + mdelay(20); + /* set CLK again, mimic original driver. */ + alcor_set_clock(host, ios->clock); + + /* enable output */ + alcor_write8(priv, AU6601_SD_CARD, + AU6601_OUTPUT_ENABLE); + /* The clk will not work on au6621. We need to trigger data + * transfer. + */ + alcor_write8(priv, AU6601_DATA_WRITE, + AU6601_DATA_XFER_CTRL); + /* configure timeout. Not clear what exactly it means. */ + alcor_write8(priv, 0x7d, AU6601_TIME_OUT_CTRL); + mdelay(100); + break; + default: + dev_err(host->dev, "Unknown power parameter\n"); + } +} + +static void alcor_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct alcor_sdmmc_host *host = mmc_priv(mmc); + + mutex_lock(&host->cmd_mutex); + + dev_dbg(host->dev, "set ios. bus width: %x, power mode: %x\n", + ios->bus_width, ios->power_mode); + + if (ios->power_mode != host->cur_power_mode) { + alcor_set_power_mode(mmc, ios); + host->cur_power_mode = ios->power_mode; + } else { + alcor_set_timing(mmc, ios); + alcor_set_bus_width(mmc, ios); + alcor_set_clock(host, ios->clock); + } + + mutex_unlock(&host->cmd_mutex); +} + +static int alcor_signal_voltage_switch(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + struct alcor_sdmmc_host *host = mmc_priv(mmc); + + mutex_lock(&host->cmd_mutex); + + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + alcor_rmw8(host, AU6601_OPT, AU6601_OPT_SD_18V, 0); + break; + case MMC_SIGNAL_VOLTAGE_180: + alcor_rmw8(host, AU6601_OPT, 0, AU6601_OPT_SD_18V); + break; + default: + /* No signal voltage switch required */ + break; + } + + mutex_unlock(&host->cmd_mutex); + return 0; +} + +static const struct mmc_host_ops alcor_sdc_ops = { + .card_busy = alcor_card_busy, + .get_cd = alcor_get_cd, + .get_ro = alcor_get_ro, + .post_req = alcor_post_req, + .pre_req = alcor_pre_req, + .request = alcor_request, + .set_ios = alcor_set_ios, + .start_signal_voltage_switch = alcor_signal_voltage_switch, +}; + +static void alcor_timeout_timer(struct work_struct *work) +{ + struct delayed_work *d = to_delayed_work(work); + struct alcor_sdmmc_host *host = container_of(d, struct alcor_sdmmc_host, + timeout_work); + mutex_lock(&host->cmd_mutex); + + dev_dbg(host->dev, "triggered timeout\n"); + if (host->mrq) { + dev_err(host->dev, "Timeout waiting for hardware interrupt.\n"); + + if (host->data) { + host->data->error = -ETIMEDOUT; + } else { + if (host->cmd) + host->cmd->error = -ETIMEDOUT; + else + host->mrq->cmd->error = -ETIMEDOUT; + } + + alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA); + alcor_request_complete(host, 0); + } + + mmiowb(); + mutex_unlock(&host->cmd_mutex); +} + +static void alcor_hw_init(struct alcor_sdmmc_host *host) +{ + struct alcor_pci_priv *priv = host->alcor_pci; + struct alcor_dev_cfg *cfg = priv->cfg; + + /* FIXME: This part is a mimics HW init of original driver. + * If we will ever get access to documentation, then this part + * should be reviewed again. + */ + + /* reset command state engine */ + alcor_reset(host, AU6601_RESET_CMD); + + alcor_write8(priv, 0, AU6601_DMA_BOUNDARY); + /* enable sd card mode */ + alcor_write8(priv, AU6601_SD_CARD, AU6601_ACTIVE_CTRL); + + /* set BUS width to 1 bit */ + alcor_write8(priv, 0, AU6601_REG_BUS_CTRL); + + /* reset data state engine */ + alcor_reset(host, AU6601_RESET_DATA); + /* Not sure if a voodoo with AU6601_DMA_BOUNDARY is really needed */ + alcor_write8(priv, 0, AU6601_DMA_BOUNDARY); + + alcor_write8(priv, 0, AU6601_INTERFACE_MODE_CTRL); + /* not clear what we are doing here. */ + alcor_write8(priv, 0x44, AU6601_PAD_DRIVE0); + alcor_write8(priv, 0x44, AU6601_PAD_DRIVE1); + alcor_write8(priv, 0x00, AU6601_PAD_DRIVE2); + + /* for 6601 - dma_boundary; for 6621 - dma_page_cnt + * exact meaning of this register is not clear. + */ + alcor_write8(priv, cfg->dma, AU6601_DMA_BOUNDARY); + + /* make sure all pins are set to input and VDD is off */ + alcor_write8(priv, 0, AU6601_OUTPUT_ENABLE); + alcor_write8(priv, 0, AU6601_POWER_CONTROL); + + alcor_write8(priv, AU6601_DETECT_EN, AU6601_DETECT_STATUS); + /* now we should be safe to enable IRQs */ + alcor_unmask_sd_irqs(host); +} + +static void alcor_hw_uninit(struct alcor_sdmmc_host *host) +{ + struct alcor_pci_priv *priv = host->alcor_pci; + + alcor_mask_sd_irqs(host); + alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA); + + alcor_write8(priv, 0, AU6601_DETECT_STATUS); + + alcor_write8(priv, 0, AU6601_OUTPUT_ENABLE); + alcor_write8(priv, 0, AU6601_POWER_CONTROL); + + alcor_write8(priv, 0, AU6601_OPT); +} + +static void alcor_init_mmc(struct alcor_sdmmc_host *host) +{ + struct mmc_host *mmc = host->mmc; + + mmc->f_min = AU6601_MIN_CLOCK; + mmc->f_max = AU6601_MAX_CLOCK; + mmc->ocr_avail = MMC_VDD_33_34; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED + | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 + | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50; + mmc->caps2 = MMC_CAP2_NO_SDIO; + mmc->ops = &alcor_sdc_ops; + + /* Hardware cannot do scatter lists */ + mmc->max_segs = AU6601_MAX_DMA_SEGMENTS; + mmc->max_seg_size = AU6601_MAX_DMA_BLOCK_SIZE; + + mmc->max_blk_size = mmc->max_seg_size; + mmc->max_blk_count = mmc->max_segs; + + mmc->max_req_size = mmc->max_seg_size * mmc->max_segs; +} + +static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev) +{ + struct alcor_pci_priv *priv = pdev->dev.platform_data; + struct mmc_host *mmc; + struct alcor_sdmmc_host *host; + int ret; + + mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); + if (!mmc) { + dev_err(&pdev->dev, "Can't allocate MMC\n"); + return -ENOMEM; + } + + host = mmc_priv(mmc); + host->mmc = mmc; + host->dev = &pdev->dev; + host->cur_power_mode = MMC_POWER_UNDEFINED; + host->alcor_pci = priv; + + /* make sure irqs are disabled */ + alcor_write32(priv, 0, AU6601_REG_INT_ENABLE); + alcor_write32(priv, 0, AU6601_MS_INT_ENABLE); + + ret = devm_request_threaded_irq(&pdev->dev, priv->irq, + alcor_irq, alcor_irq_thread, IRQF_SHARED, + DRV_NAME_ALCOR_PCI_SDMMC, host); + + if (ret) { + dev_err(&pdev->dev, "Failed to get irq for data line\n"); + return ret; + } + + mutex_init(&host->cmd_mutex); + INIT_DELAYED_WORK(&host->timeout_work, alcor_timeout_timer); + + alcor_init_mmc(host); + alcor_hw_init(host); + + dev_set_drvdata(&pdev->dev, host); + mmc_add_host(mmc); + return 0; +} + +static int alcor_pci_sdmmc_drv_remove(struct platform_device *pdev) +{ + struct alcor_sdmmc_host *host = dev_get_drvdata(&pdev->dev); + + if (cancel_delayed_work_sync(&host->timeout_work)) + alcor_request_complete(host, 0); + + alcor_hw_uninit(host); + mmc_remove_host(host->mmc); + mmc_free_host(host->mmc); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int alcor_pci_sdmmc_suspend(struct device *dev) +{ + struct alcor_sdmmc_host *host = dev_get_drvdata(dev); + + if (cancel_delayed_work_sync(&host->timeout_work)) + alcor_request_complete(host, 0); + + alcor_hw_uninit(host); + + return 0; +} + +static int alcor_pci_sdmmc_resume(struct device *dev) +{ + struct alcor_sdmmc_host *host = dev_get_drvdata(dev); + + alcor_hw_init(host); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(alcor_mmc_pm_ops, alcor_pci_sdmmc_suspend, + alcor_pci_sdmmc_resume); + +static const struct platform_device_id alcor_pci_sdmmc_ids[] = { + { + .name = DRV_NAME_ALCOR_PCI_SDMMC, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, alcor_pci_sdmmc_ids); + +static struct platform_driver alcor_pci_sdmmc_driver = { + .probe = alcor_pci_sdmmc_drv_probe, + .remove = alcor_pci_sdmmc_drv_remove, + .id_table = alcor_pci_sdmmc_ids, + .driver = { + .name = DRV_NAME_ALCOR_PCI_SDMMC, + .pm = &alcor_mmc_pm_ops + }, +}; +module_platform_driver(alcor_pci_sdmmc_driver); + +MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>"); +MODULE_DESCRIPTION("PCI driver for Alcor Micro AU6601 Secure Digital Host Controller Interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c @@ -446,18 +446,7 @@ static int atmci_req_show(struct seq_file *s, void *v) return 0; } -static int atmci_req_open(struct inode *inode, struct file *file) -{ - return single_open(file, atmci_req_show, inode->i_private); -} - -static const struct file_operations atmci_req_fops = { - .owner = THIS_MODULE, - .open = atmci_req_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(atmci_req); static void atmci_show_status_reg(struct seq_file *s, const char *regname, u32 value) @@ -583,18 +572,7 @@ static int atmci_regs_show(struct seq_file *s, void *v) return ret; } -static int atmci_regs_open(struct inode *inode, struct file *file) -{ - return single_open(file, atmci_regs_show, inode->i_private); -} - -static const struct file_operations atmci_regs_fops = { - .owner = THIS_MODULE, - .open = atmci_regs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(atmci_regs); static void atmci_init_debugfs(struct atmel_mci_slot *slot) { @@ -608,13 +586,14 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot) return; node = debugfs_create_file("regs", S_IRUSR, root, host, - &atmci_regs_fops); + &atmci_regs_fops); if (IS_ERR(node)) return; if (!node) goto err; - node = debugfs_create_file("req", S_IRUSR, root, slot, &atmci_req_fops); + node = debugfs_create_file("req", S_IRUSR, root, slot, + &atmci_req_fops); if (!node) goto err; @@ -1954,13 +1933,14 @@ static void atmci_tasklet_func(unsigned long priv) } atmci_request_end(host, host->mrq); - state = STATE_IDLE; + goto unlock; /* atmci_request_end() sets host->state */ break; } } while (state != prev_state); host->state = state; +unlock: spin_unlock(&host->lock); } diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * bcm2835 sdhost driver. * @@ -25,18 +26,6 @@ * sdhci-bcm2708.c by Broadcom * sdhci-bcm2835.c by Stephen Warren and Oleksandr Tymoshenko * sdhci.c and sdhci-pci.c by Pierre Ossman - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/clk.h> #include <linux/delay.h> @@ -286,6 +275,7 @@ static void bcm2835_reset(struct mmc_host *mmc) if (host->dma_chan) dmaengine_terminate_sync(host->dma_chan); + host->dma_chan = NULL; bcm2835_reset_internal(host); } @@ -463,7 +453,7 @@ static void bcm2835_transfer_pio(struct bcm2835_host *host) static void bcm2835_prepare_dma(struct bcm2835_host *host, struct mmc_data *data) { - int len, dir_data, dir_slave; + int sg_len, dir_data, dir_slave; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *dma_chan; @@ -509,23 +499,24 @@ void bcm2835_prepare_dma(struct bcm2835_host *host, struct mmc_data *data) &host->dma_cfg_rx : &host->dma_cfg_tx); - len = dma_map_sg(dma_chan->device->dev, data->sg, data->sg_len, - dir_data); + sg_len = dma_map_sg(dma_chan->device->dev, data->sg, data->sg_len, + dir_data); + if (!sg_len) + return; - if (len > 0) { - desc = dmaengine_prep_slave_sg(dma_chan, data->sg, - len, dir_slave, - DMA_PREP_INTERRUPT | - DMA_CTRL_ACK); - } + desc = dmaengine_prep_slave_sg(dma_chan, data->sg, sg_len, dir_slave, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (desc) { - desc->callback = bcm2835_dma_complete; - desc->callback_param = host; - host->dma_desc = desc; - host->dma_chan = dma_chan; - host->dma_dir = dir_data; + if (!desc) { + dma_unmap_sg(dma_chan->device->dev, data->sg, sg_len, dir_data); + return; } + + desc->callback = bcm2835_dma_complete; + desc->callback_param = host; + host->dma_desc = desc; + host->dma_chan = dma_chan; + host->dma_dir = dir_data; } static void bcm2835_start_dma(struct bcm2835_host *host) @@ -607,7 +598,7 @@ static void bcm2835_finish_request(struct bcm2835_host *host) struct dma_chan *terminate_chan = NULL; struct mmc_request *mrq; - cancel_delayed_work(&host->timeout_work); + cancel_delayed_work_sync(&host->timeout_work); mrq = host->mrq; @@ -772,6 +763,8 @@ static void bcm2835_finish_command(struct bcm2835_host *host) if (!(sdhsts & SDHSTS_CRC7_ERROR) || (host->cmd->opcode != MMC_SEND_OP_COND)) { + u32 edm, fsm; + if (sdhsts & SDHSTS_CMD_TIME_OUT) { host->cmd->error = -ETIMEDOUT; } else { @@ -780,6 +773,13 @@ static void bcm2835_finish_command(struct bcm2835_host *host) bcm2835_dumpregs(host); host->cmd->error = -EILSEQ; } + edm = readl(host->ioaddr + SDEDM); + fsm = edm & SDEDM_FSM_MASK; + if (fsm == SDEDM_FSM_READWAIT || + fsm == SDEDM_FSM_WRITESTART1) + /* Kick the FSM out of its wait */ + writel(edm | SDEDM_FORCE_DATA_MODE, + host->ioaddr + SDEDM); bcm2835_finish_request(host); return; } @@ -837,6 +837,8 @@ static void bcm2835_timeout(struct work_struct *work) dev_err(dev, "timeout waiting for hardware interrupt.\n"); bcm2835_dumpregs(host); + bcm2835_reset(host->mmc); + if (host->data) { host->data->error = -ETIMEDOUT; bcm2835_finish_data(host); @@ -1052,10 +1054,12 @@ static void bcm2835_dma_complete_work(struct work_struct *work) { struct bcm2835_host *host = container_of(work, struct bcm2835_host, dma_work); - struct mmc_data *data = host->data; + struct mmc_data *data; mutex_lock(&host->mutex); + data = host->data; + if (host->dma_chan) { dma_unmap_sg(host->dma_chan->device->dev, data->sg, data->sg_len, @@ -1180,9 +1184,6 @@ static void bcm2835_request(struct mmc_host *mmc, struct mmc_request *mrq) return; } - if (host->use_dma && mrq->data && (mrq->data->blocks > PIO_THRESHOLD)) - bcm2835_prepare_dma(host, mrq->data); - mutex_lock(&host->mutex); WARN_ON(host->mrq); @@ -1206,6 +1207,9 @@ static void bcm2835_request(struct mmc_host *mmc, struct mmc_request *mrq) return; } + if (host->use_dma && mrq->data && (mrq->data->blocks > PIO_THRESHOLD)) + bcm2835_prepare_dma(host, mrq->data); + host->use_sbc = !!mrq->sbc && host->mrq->data && (host->mrq->data->flags & MMC_DATA_READ); if (host->use_sbc) { @@ -1445,6 +1449,9 @@ static int bcm2835_remove(struct platform_device *pdev) cancel_work_sync(&host->dma_work); cancel_delayed_work_sync(&host->timeout_work); + if (host->dma_chan_rxtx) + dma_release_channel(host->dma_chan_rxtx); + mmc_free_host(host->mmc); platform_set_drvdata(pdev, NULL); diff --git a/drivers/mmc/host/dw_mmc-bluefield.c b/drivers/mmc/host/dw_mmc-bluefield.c @@ -52,16 +52,7 @@ MODULE_DEVICE_TABLE(of, dw_mci_bluefield_match); static int dw_mci_bluefield_probe(struct platform_device *pdev) { - const struct dw_mci_drv_data *drv_data = NULL; - const struct of_device_id *match; - - if (pdev->dev.of_node) { - match = of_match_node(dw_mci_bluefield_match, - pdev->dev.of_node); - drv_data = match->data; - } - - return dw_mci_pltfm_register(pdev, drv_data); + return dw_mci_pltfm_register(pdev, &bluefield_drv_data); } static struct platform_driver dw_mci_bluefield_pltfm_driver = { diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c @@ -21,7 +21,7 @@ #include <linux/dmaengine.h> #include <linux/dma-mapping.h> #include <linux/err.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> @@ -126,9 +126,23 @@ enum jz4740_mmc_state { JZ4740_MMC_STATE_DONE, }; -struct jz4740_mmc_host_next { - int sg_len; - s32 cookie; +/* + * The MMC core allows to prepare a mmc_request while another mmc_request + * is in-flight. This is used via the pre_req/post_req hooks. + * This driver uses the pre_req/post_req hooks to map/unmap the mmc_request. + * Following what other drivers do (sdhci, dw_mmc) we use the following cookie + * flags to keep track of the mmc_request mapping state. + * + * COOKIE_UNMAPPED: the request is not mapped. + * COOKIE_PREMAPPED: the request was mapped in pre_req, + * and should be unmapped in post_req. + * COOKIE_MAPPED: the request was mapped in the irq handler, + * and should be unmapped before mmc_request_done is called.. + */ +enum jz4780_cookie { + COOKIE_UNMAPPED = 0, + COOKIE_PREMAPPED, + COOKIE_MAPPED, }; struct jz4740_mmc_host { @@ -136,6 +150,7 @@ struct jz4740_mmc_host { struct platform_device *pdev; struct jz4740_mmc_platform_data *pdata; struct clk *clk; + struct gpio_desc *power; enum jz4740_mmc_version version; @@ -162,9 +177,7 @@ struct jz4740_mmc_host { /* DMA support */ struct dma_chan *dma_rx; struct dma_chan *dma_tx; - struct jz4740_mmc_host_next next_data; bool use_dma; - int sg_len; /* The DMA trigger level is 8 words, that is to say, the DMA read * trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write @@ -226,9 +239,6 @@ static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host) return PTR_ERR(host->dma_rx); } - /* Initialize DMA pre request cookie */ - host->next_data.cookie = 1; - return 0; } @@ -245,60 +255,44 @@ static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host, enum dma_data_direction dir = mmc_get_dma_dir(data); dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); + data->host_cookie = COOKIE_UNMAPPED; } -/* Prepares DMA data for current/next transfer, returns non-zero on failure */ +/* Prepares DMA data for current or next transfer. + * A request can be in-flight when this is called. + */ static int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host, struct mmc_data *data, - struct jz4740_mmc_host_next *next, - struct dma_chan *chan) + int cookie) { - struct jz4740_mmc_host_next *next_data = &host->next_data; + struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); enum dma_data_direction dir = mmc_get_dma_dir(data); - int sg_len; - - if (!next && data->host_cookie && - data->host_cookie != host->next_data.cookie) { - dev_warn(mmc_dev(host->mmc), - "[%s] invalid cookie: data->host_cookie %d host->next_data.cookie %d\n", - __func__, - data->host_cookie, - host->next_data.cookie); - data->host_cookie = 0; - } + int sg_count; - /* Check if next job is already prepared */ - if (next || data->host_cookie != host->next_data.cookie) { - sg_len = dma_map_sg(chan->device->dev, - data->sg, - data->sg_len, - dir); + if (data->host_cookie == COOKIE_PREMAPPED) + return data->sg_count; - } else { - sg_len = next_data->sg_len; - next_data->sg_len = 0; - } + sg_count = dma_map_sg(chan->device->dev, + data->sg, + data->sg_len, + dir); - if (sg_len <= 0) { + if (sg_count <= 0) { dev_err(mmc_dev(host->mmc), "Failed to map scatterlist for DMA operation\n"); return -EINVAL; } - if (next) { - next->sg_len = sg_len; - data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie; - } else - host->sg_len = sg_len; + data->sg_count = sg_count; + data->host_cookie = cookie; - return 0; + return data->sg_count; } static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host, struct mmc_data *data) { - int ret; - struct dma_chan *chan; + struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); struct dma_async_tx_descriptor *desc; struct dma_slave_config conf = { .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, @@ -306,29 +300,26 @@ static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host, .src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, .dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, }; + int sg_count; if (data->flags & MMC_DATA_WRITE) { conf.direction = DMA_MEM_TO_DEV; conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO; conf.slave_id = JZ4740_DMA_TYPE_MMC_TRANSMIT; - chan = host->dma_tx; } else { conf.direction = DMA_DEV_TO_MEM; conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO; conf.slave_id = JZ4740_DMA_TYPE_MMC_RECEIVE; - chan = host->dma_rx; } - ret = jz4740_mmc_prepare_dma_data(host, data, NULL, chan); - if (ret) - return ret; + sg_count = jz4740_mmc_prepare_dma_data(host, data, COOKIE_MAPPED); + if (sg_count < 0) + return sg_count; dmaengine_slave_config(chan, &conf); - desc = dmaengine_prep_slave_sg(chan, - data->sg, - host->sg_len, - conf.direction, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + desc = dmaengine_prep_slave_sg(chan, data->sg, sg_count, + conf.direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { dev_err(mmc_dev(host->mmc), "Failed to allocate DMA %s descriptor", @@ -342,7 +333,8 @@ static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host, return 0; dma_unmap: - jz4740_mmc_dma_unmap(host, data); + if (data->host_cookie == COOKIE_MAPPED) + jz4740_mmc_dma_unmap(host, data); return -ENOMEM; } @@ -351,16 +343,13 @@ static void jz4740_mmc_pre_request(struct mmc_host *mmc, { struct jz4740_mmc_host *host = mmc_priv(mmc); struct mmc_data *data = mrq->data; - struct jz4740_mmc_host_next *next_data = &host->next_data; - BUG_ON(data->host_cookie); - - if (host->use_dma) { - struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); + if (!host->use_dma) + return; - if (jz4740_mmc_prepare_dma_data(host, data, next_data, chan)) - data->host_cookie = 0; - } + data->host_cookie = COOKIE_UNMAPPED; + if (jz4740_mmc_prepare_dma_data(host, data, COOKIE_PREMAPPED) < 0) + data->host_cookie = COOKIE_UNMAPPED; } static void jz4740_mmc_post_request(struct mmc_host *mmc, @@ -370,10 +359,8 @@ static void jz4740_mmc_post_request(struct mmc_host *mmc, struct jz4740_mmc_host *host = mmc_priv(mmc); struct mmc_data *data = mrq->data; - if (host->use_dma && data->host_cookie) { + if (data && data->host_cookie != COOKIE_UNMAPPED) jz4740_mmc_dma_unmap(host, data); - data->host_cookie = 0; - } if (err) { struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); @@ -436,10 +423,14 @@ static void jz4740_mmc_reset(struct jz4740_mmc_host *host) static void jz4740_mmc_request_done(struct jz4740_mmc_host *host) { struct mmc_request *req; + struct mmc_data *data; req = host->req; + data = req->data; host->req = NULL; + if (data && data->host_cookie == COOKIE_MAPPED) + jz4740_mmc_dma_unmap(host, data); mmc_request_done(host->mmc, req); } @@ -903,18 +894,16 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_UP: jz4740_mmc_reset(host); - if (host->pdata && gpio_is_valid(host->pdata->gpio_power)) - gpio_set_value(host->pdata->gpio_power, - !host->pdata->power_active_low); + if (host->power) + gpiod_set_value(host->power, 1); host->cmdat |= JZ_MMC_CMDAT_INIT; clk_prepare_enable(host->clk); break; case MMC_POWER_ON: break; default: - if (host->pdata && gpio_is_valid(host->pdata->gpio_power)) - gpio_set_value(host->pdata->gpio_power, - host->pdata->power_active_low); + if (host->power) + gpiod_set_value(host->power, 0); clk_disable_unprepare(host->clk); break; } @@ -947,30 +936,9 @@ static const struct mmc_host_ops jz4740_mmc_ops = { .enable_sdio_irq = jz4740_mmc_enable_sdio_irq, }; -static int jz4740_mmc_request_gpio(struct device *dev, int gpio, - const char *name, bool output, int value) -{ - int ret; - - if (!gpio_is_valid(gpio)) - return 0; - - ret = gpio_request(gpio, name); - if (ret) { - dev_err(dev, "Failed to request %s gpio: %d\n", name, ret); - return ret; - } - - if (output) - gpio_direction_output(gpio, value); - else - gpio_direction_input(gpio); - - return 0; -} - -static int jz4740_mmc_request_gpios(struct mmc_host *mmc, - struct platform_device *pdev) +static int jz4740_mmc_request_gpios(struct jz4740_mmc_host *host, + struct mmc_host *mmc, + struct platform_device *pdev) { struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev); int ret = 0; @@ -983,31 +951,21 @@ static int jz4740_mmc_request_gpios(struct mmc_host *mmc, if (!pdata->read_only_active_low) mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; - if (gpio_is_valid(pdata->gpio_card_detect)) { - ret = mmc_gpio_request_cd(mmc, pdata->gpio_card_detect, 0); - if (ret) - return ret; - } - - if (gpio_is_valid(pdata->gpio_read_only)) { - ret = mmc_gpio_request_ro(mmc, pdata->gpio_read_only); - if (ret) - return ret; - } - - return jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_power, - "MMC read only", true, pdata->power_active_low); -} - -static void jz4740_mmc_free_gpios(struct platform_device *pdev) -{ - struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev); + /* + * Get optional card detect and write protect GPIOs, + * only back out on probe deferral. + */ + ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL); + if (ret == -EPROBE_DEFER) + return ret; - if (!pdata) - return; + ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0, NULL); + if (ret == -EPROBE_DEFER) + return ret; - if (gpio_is_valid(pdata->gpio_power)) - gpio_free(pdata->gpio_power); + host->power = devm_gpiod_get_optional(&pdev->dev, "power", + GPIOD_OUT_HIGH); + return PTR_ERR_OR_ZERO(host->power); } static const struct of_device_id jz4740_mmc_of_match[] = { @@ -1053,7 +1011,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev) mmc->caps |= MMC_CAP_SDIO_IRQ; if (!(pdata && pdata->data_1bit)) mmc->caps |= MMC_CAP_4_BIT_DATA; - ret = jz4740_mmc_request_gpios(mmc, pdev); + ret = jz4740_mmc_request_gpios(host, mmc, pdev); if (ret) goto err_free_host; } @@ -1104,7 +1062,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev) dev_name(&pdev->dev), host); if (ret) { dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); - goto err_free_gpios; + goto err_free_host; } jz4740_mmc_clock_disable(host); @@ -1135,8 +1093,6 @@ err_release_dma: jz4740_mmc_release_dma_channels(host); err_free_irq: free_irq(host->irq, host); -err_free_gpios: - jz4740_mmc_free_gpios(pdev); err_free_host: mmc_free_host(mmc); @@ -1155,8 +1111,6 @@ static int jz4740_mmc_remove(struct platform_device *pdev) free_irq(host->irq, host); - jz4740_mmc_free_gpios(pdev); - if (host->use_dma) jz4740_mmc_release_dma_channels(host); diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c @@ -21,11 +21,11 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/ioport.h> -#include <linux/spinlock.h> #include <linux/dma-mapping.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> @@ -66,6 +66,9 @@ #define SD_EMMC_DELAY 0x4 #define SD_EMMC_ADJUST 0x8 +#define ADJUST_ADJ_DELAY_MASK GENMASK(21, 16) +#define ADJUST_DS_EN BIT(15) +#define ADJUST_ADJ_EN BIT(13) #define SD_EMMC_DELAY1 0x4 #define SD_EMMC_DELAY2 0x8 @@ -90,9 +93,11 @@ #define CFG_CLK_ALWAYS_ON BIT(18) #define CFG_CHK_DS BIT(20) #define CFG_AUTO_CLK BIT(23) +#define CFG_ERR_ABORT BIT(27) #define SD_EMMC_STATUS 0x48 #define STATUS_BUSY BIT(31) +#define STATUS_DESC_BUSY BIT(30) #define STATUS_DATI GENMASK(23, 16) #define SD_EMMC_IRQ_EN 0x4c @@ -141,6 +146,7 @@ struct meson_mmc_data { unsigned int tx_delay_mask; unsigned int rx_delay_mask; unsigned int always_on; + unsigned int adjust; }; struct sd_emmc_desc { @@ -156,7 +162,6 @@ struct meson_host { struct mmc_host *mmc; struct mmc_command *cmd; - spinlock_t lock; void __iomem *regs; struct clk *core_clk; struct clk *mmc_clk; @@ -633,14 +638,8 @@ static int meson_mmc_clk_init(struct meson_host *host) if (ret) return ret; - /* - * Set phases : These values are mostly the datasheet recommended ones - * except for the Tx phase. Datasheet recommends 180 but some cards - * fail at initialisation with it. 270 works just fine, it fixes these - * initialisation issues and enable eMMC DDR52 mode. - */ clk_set_phase(host->mmc_clk, 180); - clk_set_phase(host->tx_clk, 270); + clk_set_phase(host->tx_clk, 0); clk_set_phase(host->rx_clk, 0); return clk_prepare_enable(host->mmc_clk); @@ -928,6 +927,7 @@ static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd) cmd_cfg |= FIELD_PREP(CMD_CFG_CMD_INDEX_MASK, cmd->opcode); cmd_cfg |= CMD_CFG_OWNER; /* owned by CPU */ + cmd_cfg |= CMD_CFG_ERROR; /* stop in case of error */ meson_mmc_set_response_bits(cmd, &cmd_cfg); @@ -1022,29 +1022,34 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id) u32 irq_en, status, raw_status; irqreturn_t ret = IRQ_NONE; - if (WARN_ON(!host) || WARN_ON(!host->cmd)) + irq_en = readl(host->regs + SD_EMMC_IRQ_EN); + raw_status = readl(host->regs + SD_EMMC_STATUS); + status = raw_status & irq_en; + + if (!status) { + dev_dbg(host->dev, + "Unexpected IRQ! irq_en 0x%08x - status 0x%08x\n", + irq_en, raw_status); return IRQ_NONE; + } - spin_lock(&host->lock); + if (WARN_ON(!host) || WARN_ON(!host->cmd)) + return IRQ_NONE; cmd = host->cmd; data = cmd->data; - irq_en = readl(host->regs + SD_EMMC_IRQ_EN); - raw_status = readl(host->regs + SD_EMMC_STATUS); - status = raw_status & irq_en; - cmd->error = 0; if (status & IRQ_CRC_ERR) { dev_dbg(host->dev, "CRC Error - status 0x%08x\n", status); cmd->error = -EILSEQ; - ret = IRQ_HANDLED; + ret = IRQ_WAKE_THREAD; goto out; } if (status & IRQ_TIMEOUTS) { dev_dbg(host->dev, "Timeout - status 0x%08x\n", status); cmd->error = -ETIMEDOUT; - ret = IRQ_HANDLED; + ret = IRQ_WAKE_THREAD; goto out; } @@ -1069,17 +1074,48 @@ out: /* ack all enabled interrupts */ writel(irq_en, host->regs + SD_EMMC_STATUS); + if (cmd->error) { + /* Stop desc in case of errors */ + u32 start = readl(host->regs + SD_EMMC_START); + + start &= ~START_DESC_BUSY; + writel(start, host->regs + SD_EMMC_START); + } + if (ret == IRQ_HANDLED) meson_mmc_request_done(host->mmc, cmd->mrq); - else if (ret == IRQ_NONE) - dev_warn(host->dev, - "Unexpected IRQ! status=0x%08x, irq_en=0x%08x\n", - raw_status, irq_en); - spin_unlock(&host->lock); return ret; } +static int meson_mmc_wait_desc_stop(struct meson_host *host) +{ + int loop; + u32 status; + + /* + * It may sometimes take a while for it to actually halt. Here, we + * are giving it 5ms to comply + * + * If we don't confirm the descriptor is stopped, it might raise new + * IRQs after we have called mmc_request_done() which is bad. + */ + for (loop = 50; loop; loop--) { + status = readl(host->regs + SD_EMMC_STATUS); + if (status & (STATUS_BUSY | STATUS_DESC_BUSY)) + udelay(100); + else + break; + } + + if (status & (STATUS_BUSY | STATUS_DESC_BUSY)) { + dev_err(host->dev, "Timed out waiting for host to stop\n"); + return -ETIMEDOUT; + } + + return 0; +} + static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id) { struct meson_host *host = dev_id; @@ -1090,6 +1126,13 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id) if (WARN_ON(!cmd)) return IRQ_NONE; + if (cmd->error) { + meson_mmc_wait_desc_stop(host); + meson_mmc_request_done(host->mmc, cmd->mrq); + + return IRQ_HANDLED; + } + data = cmd->data; if (meson_mmc_bounce_buf_read(data)) { xfer_bytes = data->blksz * data->blocks; @@ -1123,14 +1166,21 @@ static int meson_mmc_get_cd(struct mmc_host *mmc) static void meson_mmc_cfg_init(struct meson_host *host) { - u32 cfg = 0; + u32 cfg = 0, adj = 0; cfg |= FIELD_PREP(CFG_RESP_TIMEOUT_MASK, ilog2(SD_EMMC_CFG_RESP_TIMEOUT)); cfg |= FIELD_PREP(CFG_RC_CC_MASK, ilog2(SD_EMMC_CFG_CMD_GAP)); cfg |= FIELD_PREP(CFG_BLK_LEN_MASK, ilog2(SD_EMMC_CFG_BLK_SIZE)); + /* abort chain on R/W errors */ + cfg |= CFG_ERR_ABORT; + writel(cfg, host->regs + SD_EMMC_CFG); + + /* enable signal resampling w/o delay */ + adj = ADJUST_ADJ_EN; + writel(adj, host->regs + host->data->adjust); } static int meson_mmc_card_busy(struct mmc_host *mmc) @@ -1191,8 +1241,6 @@ static int meson_mmc_probe(struct platform_device *pdev) host->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, host); - spin_lock_init(&host->lock); - /* Get regulators and the supported OCR mask */ host->vqmmc_enabled = false; ret = mmc_regulator_get_supply(mmc); @@ -1356,12 +1404,14 @@ static const struct meson_mmc_data meson_gx_data = { .tx_delay_mask = CLK_V2_TX_DELAY_MASK, .rx_delay_mask = CLK_V2_RX_DELAY_MASK, .always_on = CLK_V2_ALWAYS_ON, + .adjust = SD_EMMC_ADJUST, }; static const struct meson_mmc_data meson_axg_data = { .tx_delay_mask = CLK_V3_TX_DELAY_MASK, .rx_delay_mask = CLK_V3_RX_DELAY_MASK, .always_on = CLK_V3_ALWAYS_ON, + .adjust = SD_EMMC_V3_ADJUST, }; static const struct of_device_id meson_mmc_of_match[] = { diff --git a/drivers/mmc/host/meson-mx-sdio.c b/drivers/mmc/host/meson-mx-sdio.c @@ -596,6 +596,9 @@ static int meson_mx_mmc_register_clks(struct meson_mx_mmc_host *host) init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL, "%s#fixed_factor", dev_name(host->controller_dev)); + if (!init.name) + return -ENOMEM; + init.ops = &clk_fixed_factor_ops; init.flags = 0; init.parent_names = &clk_fixed_factor_parent; @@ -612,6 +615,9 @@ static int meson_mx_mmc_register_clks(struct meson_mx_mmc_host *host) clk_div_parent = __clk_get_name(host->fixed_factor_clk); init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL, "%s#div", dev_name(host->controller_dev)); + if (!init.name) + return -ENOMEM; + init.ops = &clk_divider_ops; init.flags = CLK_SET_RATE_PARENT; init.parent_names = &clk_div_parent; diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c @@ -1434,13 +1434,16 @@ static int mmc_spi_probe(struct spi_device *spi) if (status != 0) goto fail_add_host; - if (host->pdata && host->pdata->flags & MMC_SPI_USE_CD_GPIO) { - status = mmc_gpio_request_cd(mmc, host->pdata->cd_gpio, - host->pdata->cd_debounce); - if (status != 0) - goto fail_add_host; - - /* The platform has a CD GPIO signal that may support + /* + * Index 0 is card detect + * Old boardfiles were specifying 1 ms as debounce + */ + status = mmc_gpiod_request_cd(mmc, NULL, 0, false, 1, NULL); + if (status == -EPROBE_DEFER) + goto fail_add_host; + if (!status) { + /* + * The platform has a CD GPIO signal that may support * interrupts, so let mmc_gpiod_request_cd_irq() decide * if polling is needed or not. */ @@ -1448,12 +1451,12 @@ static int mmc_spi_probe(struct spi_device *spi) mmc_gpiod_request_cd_irq(mmc); } - if (host->pdata && host->pdata->flags & MMC_SPI_USE_RO_GPIO) { + /* Index 1 is write protect/read only */ + status = mmc_gpiod_request_ro(mmc, NULL, 1, false, 0, NULL); + if (status == -EPROBE_DEFER) + goto fail_add_host; + if (!status) has_ro = true; - status = mmc_gpio_request_ro(mmc, host->pdata->ro_gpio); - if (status != 0) - goto fail_add_host; - } dev_info(&spi->dev, "SD/MMC host %s%s%s%s%s\n", dev_name(&mmc->class_dev), diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c @@ -21,6 +21,7 @@ #include <linux/err.h> #include <linux/highmem.h> #include <linux/log2.h> +#include <linux/mmc/mmc.h> #include <linux/mmc/pm.h> #include <linux/mmc/host.h> #include <linux/mmc/card.h> @@ -274,6 +275,7 @@ static struct variant_data variant_stm32_sdmmc = { .cmdreg_lrsp_crc = MCI_CPSM_STM32_LRSP_CRC, .cmdreg_srsp_crc = MCI_CPSM_STM32_SRSP_CRC, .cmdreg_srsp = MCI_CPSM_STM32_SRSP, + .cmdreg_stop = MCI_CPSM_STM32_CMDSTOP, .data_cmd_enable = MCI_CPSM_STM32_CMDTRANS, .irq_pio_mask = MCI_IRQ_PIO_STM32_MASK, .datactrl_first = true, @@ -1100,6 +1102,10 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) mmci_reg_delay(host); } + if (host->variant->cmdreg_stop && + cmd->opcode == MMC_STOP_TRANSMISSION) + c |= host->variant->cmdreg_stop; + c |= cmd->opcode | host->variant->cmdreg_cpsm_enable; if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_136) @@ -1190,11 +1196,10 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, /* The error clause is handled above, success! */ data->bytes_xfered = data->blksz * data->blocks; - if (!data->stop || host->mrq->sbc) { + if (!data->stop || (host->mrq->sbc && !data->error)) mmci_request_end(host, data->mrq); - } else { + else mmci_start_command(host, data->stop, 0); - } } } diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h @@ -264,6 +264,7 @@ struct mmci_host; * @cmdreg_lrsp_crc: enable value for long response with crc * @cmdreg_srsp_crc: enable value for short response with crc * @cmdreg_srsp: enable value for short response without crc + * @cmdreg_stop: enable value for stop and abort transmission * @datalength_bits: number of bits in the MMCIDATALENGTH register * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY * is asserted (likewise for RX) @@ -316,6 +317,7 @@ struct variant_data { unsigned int cmdreg_lrsp_crc; unsigned int cmdreg_srsp_crc; unsigned int cmdreg_srsp; + unsigned int cmdreg_stop; unsigned int datalength_bits; unsigned int fifosize; unsigned int fifohalfsize; diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c @@ -1114,6 +1114,7 @@ static void msdc_start_command(struct msdc_host *host, struct mmc_request *mrq, struct mmc_command *cmd) { u32 rawcmd; + unsigned long flags; WARN_ON(host->cmd); host->cmd = cmd; @@ -1131,7 +1132,10 @@ static void msdc_start_command(struct msdc_host *host, cmd->error = 0; rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd); + spin_lock_irqsave(&host->lock, flags); sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask); + spin_unlock_irqrestore(&host->lock, flags); + writel(cmd->arg, host->base + SDC_ARG); writel(rawcmd, host->base + SDC_CMD); } @@ -1351,6 +1355,31 @@ static void msdc_request_timeout(struct work_struct *work) } } +static void __msdc_enable_sdio_irq(struct mmc_host *mmc, int enb) +{ + unsigned long flags; + struct msdc_host *host = mmc_priv(mmc); + + spin_lock_irqsave(&host->lock, flags); + if (enb) + sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ); + else + sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ); + spin_unlock_irqrestore(&host->lock, flags); +} + +static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enb) +{ + struct msdc_host *host = mmc_priv(mmc); + + __msdc_enable_sdio_irq(mmc, enb); + + if (enb) + pm_runtime_get_noresume(host->dev); + else + pm_runtime_put_noidle(host->dev); +} + static irqreturn_t msdc_irq(int irq, void *dev_id) { struct msdc_host *host = (struct msdc_host *) dev_id; @@ -1373,7 +1402,12 @@ static irqreturn_t msdc_irq(int irq, void *dev_id) data = host->data; spin_unlock_irqrestore(&host->lock, flags); - if (!(events & event_mask)) + if ((events & event_mask) & MSDC_INT_SDIOIRQ) { + __msdc_enable_sdio_irq(host->mmc, 0); + sdio_signal_irq(host->mmc); + } + + if (!(events & (event_mask & ~MSDC_INT_SDIOIRQ))) break; if (!mrq) { @@ -1493,8 +1527,11 @@ static void msdc_init_hw(struct msdc_host *host) */ sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIO); - /* disable detect SDIO device interrupt function */ - sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); + /* Config SDIO device detect interrupt function */ + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) + sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); + else + sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); /* Configure to default data timeout */ sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, 3); @@ -2013,6 +2050,11 @@ static void msdc_hw_reset(struct mmc_host *mmc) sdr_clr_bits(host->base + EMMC_IOCON, 1); } +static void msdc_ack_sdio_irq(struct mmc_host *mmc) +{ + __msdc_enable_sdio_irq(mmc, 1); +} + static const struct mmc_host_ops mt_msdc_ops = { .post_req = msdc_post_req, .pre_req = msdc_pre_req, @@ -2020,6 +2062,8 @@ static const struct mmc_host_ops mt_msdc_ops = { .set_ios = msdc_ops_set_ios, .get_ro = mmc_gpio_get_ro, .get_cd = mmc_gpio_get_cd, + .enable_sdio_irq = msdc_enable_sdio_irq, + .ack_sdio_irq = msdc_ack_sdio_irq, .start_signal_voltage_switch = msdc_ops_switch_volt, .card_busy = msdc_card_busy, .execute_tuning = msdc_execute_tuning, @@ -2147,6 +2191,9 @@ static int msdc_drv_probe(struct platform_device *pdev) else mmc->f_min = DIV_ROUND_UP(host->src_clk_freq, 4 * 4095); + if (mmc->caps & MMC_CAP_SDIO_IRQ) + mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; + mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23; /* MMC core transfer sizes tunable parameters */ mmc->max_segs = MAX_BD_NUM; diff --git a/drivers/mmc/host/of_mmc_spi.c b/drivers/mmc/host/of_mmc_spi.c @@ -16,9 +16,7 @@ #include <linux/device.h> #include <linux/slab.h> #include <linux/irq.h> -#include <linux/gpio.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/of_irq.h> #include <linux/spi/spi.h> #include <linux/spi/mmc_spi.h> @@ -32,15 +30,7 @@ MODULE_LICENSE("GPL"); -enum { - CD_GPIO = 0, - WP_GPIO, - NUM_GPIOS, -}; - struct of_mmc_spi { - int gpios[NUM_GPIOS]; - bool alow_gpios[NUM_GPIOS]; int detect_irq; struct mmc_spi_platform_data pdata; }; @@ -102,30 +92,6 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi) oms->pdata.ocr_mask |= mask; } - for (i = 0; i < ARRAY_SIZE(oms->gpios); i++) { - enum of_gpio_flags gpio_flags; - - oms->gpios[i] = of_get_gpio_flags(np, i, &gpio_flags); - if (!gpio_is_valid(oms->gpios[i])) - continue; - - if (gpio_flags & OF_GPIO_ACTIVE_LOW) - oms->alow_gpios[i] = true; - } - - if (gpio_is_valid(oms->gpios[CD_GPIO])) { - oms->pdata.cd_gpio = oms->gpios[CD_GPIO]; - oms->pdata.flags |= MMC_SPI_USE_CD_GPIO; - if (!oms->alow_gpios[CD_GPIO]) - oms->pdata.caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; - } - if (gpio_is_valid(oms->gpios[WP_GPIO])) { - oms->pdata.ro_gpio = oms->gpios[WP_GPIO]; - oms->pdata.flags |= MMC_SPI_USE_RO_GPIO; - if (!oms->alow_gpios[WP_GPIO]) - oms->pdata.caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; - } - oms->detect_irq = irq_of_parse_and_map(np, 0); if (oms->detect_irq != 0) { oms->pdata.init = of_mmc_spi_init; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c @@ -1652,7 +1652,7 @@ static struct mmc_host_ops omap_hsmmc_ops = { #ifdef CONFIG_DEBUG_FS -static int omap_hsmmc_regs_show(struct seq_file *s, void *data) +static int mmc_regs_show(struct seq_file *s, void *data) { struct mmc_host *mmc = s->private; struct omap_hsmmc_host *host = mmc_priv(mmc); @@ -1691,17 +1691,7 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data) return 0; } -static int omap_hsmmc_regs_open(struct inode *inode, struct file *file) -{ - return single_open(file, omap_hsmmc_regs_show, inode->i_private); -} - -static const struct file_operations mmc_regs_fops = { - .open = omap_hsmmc_regs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(mmc_regs); static void omap_hsmmc_debugfs(struct mmc_host *mmc) { diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c @@ -30,10 +30,9 @@ #include <linux/mmc/slot-gpio.h> #include <linux/io.h> #include <linux/regulator/consumer.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/gfp.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/of_device.h> #include <asm/sizes.h> @@ -63,6 +62,8 @@ struct pxamci_host { unsigned int imask; unsigned int power_mode; unsigned long detect_delay_ms; + bool use_ro_gpio; + struct gpio_desc *power; struct pxamci_platform_data *pdata; struct mmc_request *mrq; @@ -101,16 +102,13 @@ static inline int pxamci_set_power(struct pxamci_host *host, { struct mmc_host *mmc = host->mmc; struct regulator *supply = mmc->supply.vmmc; - int on; if (!IS_ERR(supply)) return mmc_regulator_set_ocr(mmc, supply, vdd); - if (host->pdata && - gpio_is_valid(host->pdata->gpio_power)) { - on = ((1 << vdd) & host->pdata->ocr_mask); - gpio_set_value(host->pdata->gpio_power, - !!on ^ host->pdata->gpio_power_invert); + if (host->power) { + bool on = !!((1 << vdd) & host->pdata->ocr_mask); + gpiod_set_value(host->power, on); } if (host->pdata && host->pdata->setpower) @@ -432,7 +430,7 @@ static int pxamci_get_ro(struct mmc_host *mmc) { struct pxamci_host *host = mmc_priv(mmc); - if (host->pdata && gpio_is_valid(host->pdata->gpio_card_ro)) + if (host->use_ro_gpio) return mmc_gpio_get_ro(mmc); if (host->pdata && host->pdata->get_ro) return !!host->pdata->get_ro(mmc_dev(mmc)); @@ -730,52 +728,38 @@ static int pxamci_probe(struct platform_device *pdev) } if (host->pdata) { - int gpio_cd = host->pdata->gpio_card_detect; - int gpio_ro = host->pdata->gpio_card_ro; - int gpio_power = host->pdata->gpio_power; - host->detect_delay_ms = host->pdata->detect_delay_ms; - if (gpio_is_valid(gpio_power)) { - ret = devm_gpio_request(dev, gpio_power, - "mmc card power"); - if (ret) { - dev_err(dev, - "Failed requesting gpio_power %d\n", - gpio_power); - goto out; - } - gpio_direction_output(gpio_power, - host->pdata->gpio_power_invert); + host->power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(host->power)) { + dev_err(dev, "Failed requesting gpio_power\n"); + goto out; } - if (gpio_is_valid(gpio_ro)) { - ret = mmc_gpio_request_ro(mmc, gpio_ro); - if (ret) { - dev_err(dev, - "Failed requesting gpio_ro %d\n", - gpio_ro); - goto out; - } else { - mmc->caps2 |= host->pdata->gpio_card_ro_invert ? - 0 : MMC_CAP2_RO_ACTIVE_HIGH; - } + /* FIXME: should we pass detection delay to debounce? */ + ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL); + if (ret && ret != -ENOENT) { + dev_err(dev, "Failed requesting gpio_cd\n"); + goto out; } - if (gpio_is_valid(gpio_cd)) - ret = mmc_gpio_request_cd(mmc, gpio_cd, 0); - if (ret) { - dev_err(dev, "Failed requesting gpio_cd %d\n", - gpio_cd); + ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0, NULL); + if (ret && ret != -ENOENT) { + dev_err(dev, "Failed requesting gpio_ro\n"); goto out; } + if (!ret) { + host->use_ro_gpio = true; + mmc->caps2 |= host->pdata->gpio_card_ro_invert ? + 0 : MMC_CAP2_RO_ACTIVE_HIGH; + } if (host->pdata->init) host->pdata->init(dev, pxamci_detect_irq, mmc); - if (gpio_is_valid(gpio_power) && host->pdata->setpower) + if (host->power && host->pdata->setpower) dev_warn(dev, "gpio_power and setpower() both defined\n"); - if (gpio_is_valid(gpio_ro) && host->pdata->get_ro) + if (host->use_ro_gpio && host->pdata->get_ro) dev_warn(dev, "gpio_ro and get_ro() both defined\n"); } diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c @@ -32,6 +32,7 @@ #include <linux/pinctrl/consumer.h> #include <linux/pinctrl/pinctrl-state.h> #include <linux/regulator/consumer.h> +#include <linux/sys_soc.h> #include "renesas_sdhi.h" #include "tmio_mmc.h" @@ -45,6 +46,11 @@ #define SDHI_VER_GEN3_SD 0xcc10 #define SDHI_VER_GEN3_SDMMC 0xcd10 +struct renesas_sdhi_quirks { + bool hs400_disabled; + bool hs400_4taps; +}; + static void renesas_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width) { u32 val; @@ -163,15 +169,6 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host, if (new_clock == 0) goto out; - /* - * Both HS400 and HS200/SD104 set 200MHz, but some devices need to - * set 400MHz to distinguish the CPG settings in HS400. - */ - if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 && - host->pdata->flags & TMIO_MMC_HAVE_4TAP_HS400 && - new_clock == 200000000) - new_clock = 400000000; - clock = renesas_sdhi_clk_update(host, new_clock) / 512; for (clk = 0x80000080; new_clock >= (clock << 1); clk >>= 1) @@ -532,6 +529,10 @@ static void renesas_sdhi_hw_reset(struct tmio_mmc_host *host) sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN & sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); + + if (host->pdata->flags & TMIO_MMC_MIN_RCAR2) + sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK, + TMIO_MASK_INIT_RCAR2); } static int renesas_sdhi_wait_idle(struct tmio_mmc_host *host, u32 bit) @@ -602,11 +603,31 @@ static void renesas_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable) renesas_sdhi_sdbuf_width(host, enable ? width : 16); } +static const struct renesas_sdhi_quirks sdhi_quirks_h3_m3w_es1 = { + .hs400_disabled = true, + .hs400_4taps = true, +}; + +static const struct renesas_sdhi_quirks sdhi_quirks_h3_es2 = { + .hs400_disabled = false, + .hs400_4taps = true, +}; + +static const struct soc_device_attribute sdhi_quirks_match[] = { + { .soc_id = "r8a7795", .revision = "ES1.*", .data = &sdhi_quirks_h3_m3w_es1 }, + { .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_h3_es2 }, + { .soc_id = "r8a7796", .revision = "ES1.0", .data = &sdhi_quirks_h3_m3w_es1 }, + { .soc_id = "r8a7796", .revision = "ES1.1", .data = &sdhi_quirks_h3_m3w_es1 }, + { /* Sentinel. */ }, +}; + int renesas_sdhi_probe(struct platform_device *pdev, const struct tmio_mmc_dma_ops *dma_ops) { struct tmio_mmc_data *mmd = pdev->dev.platform_data; + const struct renesas_sdhi_quirks *quirks = NULL; const struct renesas_sdhi_of_data *of_data; + const struct soc_device_attribute *attr; struct tmio_mmc_data *mmc_data; struct tmio_mmc_dma *dma_priv; struct tmio_mmc_host *host; @@ -616,6 +637,10 @@ int renesas_sdhi_probe(struct platform_device *pdev, of_data = of_device_get_match_data(&pdev->dev); + attr = soc_device_match(sdhi_quirks_match); + if (attr) + quirks = attr->data; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -EINVAL; @@ -681,6 +706,12 @@ int renesas_sdhi_probe(struct platform_device *pdev, host->multi_io_quirk = renesas_sdhi_multi_io_quirk; host->dma_ops = dma_ops; + if (quirks && quirks->hs400_disabled) + host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES); + + if (quirks && quirks->hs400_4taps) + mmc_data->flags |= TMIO_MMC_HAVE_4TAP_HS400; + /* For some SoC, we disable internal WP. GPIO may override this */ if (mmc_can_gpio_ro(host->mmc)) mmc_data->capabilities2 &= ~MMC_CAP2_NO_WRITE_PROTECT; @@ -691,6 +722,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, host->ops.card_busy = renesas_sdhi_card_busy; host->ops.start_signal_voltage_switch = renesas_sdhi_start_signal_voltage_switch; + host->sdcard_irq_setbit_mask = TMIO_STAT_ALWAYS_SET_27; } /* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */ diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -34,7 +34,7 @@ #define DTRAN_MODE_CH_NUM_CH0 0 /* "downstream" = for write commands */ #define DTRAN_MODE_CH_NUM_CH1 BIT(16) /* "upstream" = for read commands */ #define DTRAN_MODE_BUS_WIDTH (BIT(5) | BIT(4)) -#define DTRAN_MODE_ADDR_MODE BIT(0) /* 1 = Increment address */ +#define DTRAN_MODE_ADDR_MODE BIT(0) /* 1 = Increment address, 0 = Fixed */ /* DM_CM_DTRAN_CTRL */ #define DTRAN_CTRL_DM_START BIT(0) @@ -73,6 +73,9 @@ static unsigned long global_flags; #define SDHI_INTERNAL_DMAC_ONE_RX_ONLY 0 #define SDHI_INTERNAL_DMAC_RX_IN_USE 1 +/* RZ/A2 does not have the ADRR_MODE bit */ +#define SDHI_INTERNAL_DMAC_ADDR_MODE_FIXED_ONLY 2 + /* Definitions for sampling clocks */ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { { @@ -81,15 +84,14 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { }, }; -static const struct renesas_sdhi_of_data of_rcar_r8a7795_compatible = { +static const struct renesas_sdhi_of_data of_rza2_compatible = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | - TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 | - TMIO_MMC_HAVE_4TAP_HS400, + TMIO_MMC_HAVE_CBSY, + .tmio_ocr_mask = MMC_VDD_32_33, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23, - .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, .bus_shift = 2, - .scc_offset = 0x1000, + .scc_offset = 0 - 0x1000, .taps = rcar_gen3_scc_taps, .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), /* DMAC can handle 0xffffffff blk count but only 1 segment */ @@ -113,9 +115,10 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { }; static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = { + { .compatible = "renesas,sdhi-r7s9210", .data = &of_rza2_compatible, }, { .compatible = "renesas,sdhi-mmc-r8a77470", .data = &of_rcar_gen3_compatible, }, - { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_r8a7795_compatible, }, - { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_r8a7795_compatible, }, + { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, }, + { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, }, { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, {}, }; @@ -172,7 +175,10 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, struct mmc_data *data) { struct scatterlist *sg = host->sg_ptr; - u32 dtran_mode = DTRAN_MODE_BUS_WIDTH | DTRAN_MODE_ADDR_MODE; + u32 dtran_mode = DTRAN_MODE_BUS_WIDTH; + + if (!test_bit(SDHI_INTERNAL_DMAC_ADDR_MODE_FIXED_ONLY, &global_flags)) + dtran_mode |= DTRAN_MODE_ADDR_MODE; if (!dma_map_sg(&host->pdev->dev, sg, host->sg_len, mmc_get_dma_dir(data))) @@ -292,18 +298,22 @@ static const struct tmio_mmc_dma_ops renesas_sdhi_internal_dmac_dma_ops = { */ static const struct soc_device_attribute soc_whitelist[] = { /* specific ones */ + { .soc_id = "r7s9210", + .data = (void *)BIT(SDHI_INTERNAL_DMAC_ADDR_MODE_FIXED_ONLY) }, { .soc_id = "r8a7795", .revision = "ES1.*", .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) }, { .soc_id = "r8a7796", .revision = "ES1.0", .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) }, /* generic ones */ { .soc_id = "r8a774a1" }, + { .soc_id = "r8a774c0" }, { .soc_id = "r8a77470" }, { .soc_id = "r8a7795" }, { .soc_id = "r8a7796" }, { .soc_id = "r8a77965" }, { .soc_id = "r8a77970" }, { .soc_id = "r8a77980" }, + { .soc_id = "r8a77990" }, { .soc_id = "r8a77995" }, { /* sentinel */ } }; diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c @@ -75,19 +75,6 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { }, }; -static const struct renesas_sdhi_of_data of_rcar_r8a7795_compatible = { - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | - TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 | - TMIO_MMC_HAVE_4TAP_HS400, - .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | - MMC_CAP_CMD23, - .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, - .bus_shift = 2, - .scc_offset = 0x1000, - .taps = rcar_gen3_scc_taps, - .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), -}; - static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, @@ -114,8 +101,8 @@ static const struct of_device_id renesas_sdhi_sys_dmac_of_match[] = { { .compatible = "renesas,sdhi-r8a7792", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, }, - { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_r8a7795_compatible, }, - { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_r8a7795_compatible, }, + { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, }, + { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, }, { .compatible = "renesas,rcar-gen1-sdhi", .data = &of_rcar_gen1_compatible, }, { .compatible = "renesas,rcar-gen2-sdhi", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, @@ -493,8 +480,7 @@ static const struct soc_device_attribute gen3_soc_whitelist[] = { static int renesas_sdhi_sys_dmac_probe(struct platform_device *pdev) { - if ((of_device_get_match_data(&pdev->dev) == &of_rcar_gen3_compatible || - of_device_get_match_data(&pdev->dev) == &of_rcar_r8a7795_compatible) && + if (of_device_get_match_data(&pdev->dev) == &of_rcar_gen3_compatible && !soc_device_match(gen3_soc_whitelist)) return -ENODEV; diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -28,6 +28,7 @@ #include <linux/mmc/sd.h> #include <linux/mmc/card.h> #include <linux/scatterlist.h> +#include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/rtsx_usb.h> @@ -1042,9 +1043,9 @@ static int sd_set_power_mode(struct rtsx_usb_sdmmc *host, if (power_mode == MMC_POWER_OFF) { err = sd_power_off(host); - pm_runtime_put(sdmmc_dev(host)); + pm_runtime_put_noidle(sdmmc_dev(host)); } else { - pm_runtime_get_sync(sdmmc_dev(host)); + pm_runtime_get_noresume(sdmmc_dev(host)); err = sd_power_on(host); } @@ -1297,16 +1298,20 @@ static void rtsx_usb_update_led(struct work_struct *work) container_of(work, struct rtsx_usb_sdmmc, led_work); struct rtsx_ucr *ucr = host->ucr; - pm_runtime_get_sync(sdmmc_dev(host)); + pm_runtime_get_noresume(sdmmc_dev(host)); mutex_lock(&ucr->dev_mutex); + if (host->power_mode == MMC_POWER_OFF) + goto out; + if (host->led.brightness == LED_OFF) rtsx_usb_turn_off_led(ucr); else rtsx_usb_turn_on_led(ucr); +out: mutex_unlock(&ucr->dev_mutex); - pm_runtime_put(sdmmc_dev(host)); + pm_runtime_put_sync_suspend(sdmmc_dev(host)); } #endif @@ -1320,7 +1325,7 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host) mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | - MMC_CAP_NEEDS_POLL | MMC_CAP_ERASE; + MMC_CAP_ERASE | MMC_CAP_SYNC_RUNTIME_PM; mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE | MMC_CAP2_NO_SDIO; @@ -1363,8 +1368,6 @@ static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev) mutex_init(&host->host_mutex); rtsx_usb_init_host(host); - pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_set_autosuspend_delay(&pdev->dev, 50); pm_runtime_enable(&pdev->dev); #ifdef RTSX_USB_USE_LEDS_CLASS @@ -1419,7 +1422,6 @@ static int rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev) mmc_free_host(mmc); pm_runtime_disable(&pdev->dev); - pm_runtime_dont_use_autosuspend(&pdev->dev); platform_set_drvdata(pdev, NULL); dev_dbg(&(pdev->dev), @@ -1428,6 +1430,31 @@ static int rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int rtsx_usb_sdmmc_runtime_suspend(struct device *dev) +{ + struct rtsx_usb_sdmmc *host = dev_get_drvdata(dev); + + host->mmc->caps &= ~MMC_CAP_NEEDS_POLL; + return 0; +} + +static int rtsx_usb_sdmmc_runtime_resume(struct device *dev) +{ + struct rtsx_usb_sdmmc *host = dev_get_drvdata(dev); + + host->mmc->caps |= MMC_CAP_NEEDS_POLL; + if (sdmmc_get_cd(host->mmc) == 1) + mmc_detect_change(host->mmc, 0); + return 0; +} +#endif + +static const struct dev_pm_ops rtsx_usb_sdmmc_dev_pm_ops = { + SET_RUNTIME_PM_OPS(rtsx_usb_sdmmc_runtime_suspend, + rtsx_usb_sdmmc_runtime_resume, NULL) +}; + static const struct platform_device_id rtsx_usb_sdmmc_ids[] = { { .name = "rtsx_usb_sdmmc", @@ -1443,6 +1470,7 @@ static struct platform_driver rtsx_usb_sdmmc_driver = { .id_table = rtsx_usb_sdmmc_ids, .driver = { .name = "rtsx_usb_sdmmc", + .pm = &rtsx_usb_sdmmc_dev_pm_ops, }, }; module_platform_driver(rtsx_usb_sdmmc_driver); diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c @@ -26,7 +26,6 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> #include <linux/mmc/slot-gpio.h> #include <plat/gpio-cfg.h> @@ -1406,18 +1405,7 @@ static int s3cmci_state_show(struct seq_file *seq, void *v) return 0; } -static int s3cmci_state_open(struct inode *inode, struct file *file) -{ - return single_open(file, s3cmci_state_show, inode->i_private); -} - -static const struct file_operations s3cmci_fops_state = { - .owner = THIS_MODULE, - .open = s3cmci_state_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(s3cmci_state); #define DBG_REG(_r) { .addr = S3C2410_SDI##_r, .name = #_r } @@ -1459,18 +1447,7 @@ static int s3cmci_regs_show(struct seq_file *seq, void *v) return 0; } -static int s3cmci_regs_open(struct inode *inode, struct file *file) -{ - return single_open(file, s3cmci_regs_show, inode->i_private); -} - -static const struct file_operations s3cmci_fops_regs = { - .owner = THIS_MODULE, - .open = s3cmci_regs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(s3cmci_regs); static void s3cmci_debugfs_attach(struct s3cmci_host *host) { @@ -1484,14 +1461,14 @@ static void s3cmci_debugfs_attach(struct s3cmci_host *host) host->debug_state = debugfs_create_file("state", 0444, host->debug_root, host, - &s3cmci_fops_state); + &s3cmci_state_fops); if (IS_ERR(host->debug_state)) dev_err(dev, "failed to create debug state file\n"); host->debug_regs = debugfs_create_file("regs", 0444, host->debug_root, host, - &s3cmci_fops_regs); + &s3cmci_regs_fops); if (IS_ERR(host->debug_regs)) dev_err(dev, "failed to create debug regs file\n"); @@ -1545,25 +1522,19 @@ static int s3cmci_probe_pdata(struct s3cmci_host *host) if (pdata->wprotect_invert) mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; - if (pdata->detect_invert) - mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; - - if (gpio_is_valid(pdata->gpio_detect)) { - ret = mmc_gpio_request_cd(mmc, pdata->gpio_detect, 0); - if (ret) { - dev_err(&pdev->dev, "error requesting GPIO for CD %d\n", - ret); - return ret; - } + /* If we get -ENOENT we have no card detect GPIO line */ + ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL); + if (ret != -ENOENT) { + dev_err(&pdev->dev, "error requesting GPIO for CD %d\n", + ret); + return ret; } - if (gpio_is_valid(pdata->gpio_wprotect)) { - ret = mmc_gpio_request_ro(mmc, pdata->gpio_wprotect); - if (ret) { - dev_err(&pdev->dev, "error requesting GPIO for WP %d\n", - ret); - return ret; - } + ret = mmc_gpiod_request_ro(host->mmc, "wp", 0, false, 0, NULL); + if (ret != -ENOENT) { + dev_err(&pdev->dev, "error requesting GPIO for WP %d\n", + ret); + return ret; } return 0; diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c @@ -437,7 +437,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = { MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR | MMC_CAP_CMD_DURING_TFR | MMC_CAP_WAIT_WHILE_BUSY, .flags = SDHCI_ACPI_RUNTIME_PM, - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_NO_LED, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC | SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400, @@ -448,6 +449,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 | + SDHCI_QUIRK_NO_LED | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON, .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD | @@ -462,7 +464,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_SD_CD_OVERRIDE_LEVEL | SDHCI_ACPI_RUNTIME_PM, - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_NO_LED, .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | SDHCI_QUIRK2_STOP_WITH_TC, .caps = MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_AGGRESSIVE_PM, diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c @@ -14,7 +14,7 @@ */ #include <linux/bitfield.h> -#include <linux/bitops.h> +#include <linux/bits.h> #include <linux/iopoll.h> #include <linux/module.h> #include <linux/mmc/host.h> diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -12,7 +12,6 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/clk.h> -#include <linux/gpio.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/mmc/host.h> @@ -21,7 +20,6 @@ #include <linux/mmc/slot-gpio.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_data/mmc-esdhc-imx.h> #include <linux/pm_runtime.h> @@ -429,7 +427,7 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg) val = readl(host->ioaddr + ESDHC_MIX_CTRL); else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) /* the std tuning bits is in ACMD12_ERR for imx6sl */ - val = readl(host->ioaddr + SDHCI_ACMD12_ERR); + val = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS); } if (val & ESDHC_MIX_CTRL_EXE_TUNE) @@ -494,7 +492,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) } writel(new_val , host->ioaddr + ESDHC_MIX_CTRL); } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { - u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR); + u32 v = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS); u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); if (val & SDHCI_CTRL_TUNED_CLK) { v |= ESDHC_MIX_CTRL_SMPCLK_SEL; @@ -512,7 +510,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) v &= ~ESDHC_MIX_CTRL_EXE_TUNE; } - writel(v, host->ioaddr + SDHCI_ACMD12_ERR); + writel(v, host->ioaddr + SDHCI_AUTO_CMD_STATUS); writel(m, host->ioaddr + ESDHC_MIX_CTRL); } return; @@ -957,9 +955,9 @@ static void esdhc_reset_tuning(struct sdhci_host *host) writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL); writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS); } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { - ctrl = readl(host->ioaddr + SDHCI_ACMD12_ERR); + ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS); ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; - writel(ctrl, host->ioaddr + SDHCI_ACMD12_ERR); + writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS); } } } @@ -1139,8 +1137,12 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, if (of_get_property(np, "fsl,wp-controller", NULL)) boarddata->wp_type = ESDHC_WP_CONTROLLER; - boarddata->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0); - if (gpio_is_valid(boarddata->wp_gpio)) + /* + * If we have this property, then activate WP check. + * Retrieveing and requesting the actual WP GPIO will happen + * in the call to mmc_of_parse(). + */ + if (of_property_read_bool(np, "wp-gpios")) boarddata->wp_type = ESDHC_WP_GPIO; of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step); @@ -1198,7 +1200,7 @@ static int sdhci_esdhc_imx_probe_nondt(struct platform_device *pdev, host->mmc->parent->platform_data); /* write_protect */ if (boarddata->wp_type == ESDHC_WP_GPIO) { - err = mmc_gpio_request_ro(host->mmc, boarddata->wp_gpio); + err = mmc_gpiod_request_ro(host->mmc, "wp", 0, false, 0, NULL); if (err) { dev_err(mmc_dev(host->mmc), "failed to request write-protect gpio!\n"); @@ -1210,7 +1212,7 @@ static int sdhci_esdhc_imx_probe_nondt(struct platform_device *pdev, /* card_detect */ switch (boarddata->cd_type) { case ESDHC_CD_GPIO: - err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio, 0); + err = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0, NULL); if (err) { dev_err(mmc_dev(host->mmc), "failed to request card-detect gpio!\n"); @@ -1317,7 +1319,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) /* clear tuning bits in case ROM has set it already */ writel(0x0, host->ioaddr + ESDHC_MIX_CTRL); - writel(0x0, host->ioaddr + SDHCI_ACMD12_ERR); + writel(0x0, host->ioaddr + SDHCI_AUTO_CMD_STATUS); writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS); } diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h @@ -59,9 +59,33 @@ /* Tuning Block Control Register */ #define ESDHC_TBCTL 0x120 +#define ESDHC_HS400_WNDW_ADJUST 0x00000040 +#define ESDHC_HS400_MODE 0x00000010 #define ESDHC_TB_EN 0x00000004 #define ESDHC_TBPTR 0x128 +/* SD Clock Control Register */ +#define ESDHC_SDCLKCTL 0x144 +#define ESDHC_LPBK_CLK_SEL 0x80000000 +#define ESDHC_CMD_CLK_CTL 0x00008000 + +/* SD Timing Control Register */ +#define ESDHC_SDTIMNGCTL 0x148 +#define ESDHC_FLW_CTL_BG 0x00008000 + +/* DLL Config 0 Register */ +#define ESDHC_DLLCFG0 0x160 +#define ESDHC_DLL_ENABLE 0x80000000 +#define ESDHC_DLL_FREQ_SEL 0x08000000 + +/* DLL Config 1 Register */ +#define ESDHC_DLLCFG1 0x164 +#define ESDHC_DLL_PD_PULSE_STRETCH_SEL 0x80000000 + +/* DLL Status 0 Register */ +#define ESDHC_DLLSTAT0 0x170 +#define ESDHC_DLL_STS_SLV_LOCK 0x08000000 + /* Control Register for DMA transfer */ #define ESDHC_DMA_SYSCTL 0x40c #define ESDHC_PERIPHERAL_CLK_SEL 0x00080000 diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c @@ -232,6 +232,7 @@ struct sdhci_msm_variant_ops { */ struct sdhci_msm_variant_info { bool mci_removed; + bool restore_dll_config; const struct sdhci_msm_variant_ops *var_ops; const struct sdhci_msm_offset *offset; }; @@ -256,8 +257,11 @@ struct sdhci_msm_host { bool pwr_irq_flag; u32 caps_0; bool mci_removed; + bool restore_dll_config; const struct sdhci_msm_variant_ops *var_ops; const struct sdhci_msm_offset *offset; + bool use_cdr; + u32 transfer_mode; }; static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) @@ -1025,6 +1029,69 @@ out: return ret; } +static bool sdhci_msm_is_tuning_needed(struct sdhci_host *host) +{ + 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 <= CORE_FREQ_100MHZ || + !(ios->timing == MMC_TIMING_MMC_HS400 || + ios->timing == MMC_TIMING_MMC_HS200 || + ios->timing == MMC_TIMING_UHS_SDR104) || + ios->enhanced_strobe) + return false; + + return true; +} + +static int sdhci_msm_restore_sdr_dll_config(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + int ret; + + /* + * SDR DLL comes into picture only for timing modes which needs + * tuning. + */ + if (!sdhci_msm_is_tuning_needed(host)) + return 0; + + /* Reset the tuning block */ + ret = msm_init_cm_dll(host); + if (ret) + return ret; + + /* Restore the tuning block */ + ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase); + + return ret; +} + +static void sdhci_msm_set_cdr(struct sdhci_host *host, bool enable) +{ + const struct sdhci_msm_offset *msm_offset = sdhci_priv_msm_offset(host); + u32 config, oldconfig = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); + + config = oldconfig; + if (enable) { + config |= CORE_CDR_EN; + config &= ~CORE_CDR_EXT_EN; + } else { + config &= ~CORE_CDR_EN; + config |= CORE_CDR_EXT_EN; + } + + if (config != oldconfig) { + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); + } +} + static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct sdhci_host *host = mmc_priv(mmc); @@ -1035,15 +1102,14 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); - /* - * Tuning is required for SDR104, HS200 and HS400 cards and - * if clock frequency is greater than 100MHz in these modes. - */ - if (host->clock <= CORE_FREQ_100MHZ || - !(ios.timing == MMC_TIMING_MMC_HS400 || - ios.timing == MMC_TIMING_MMC_HS200 || - ios.timing == MMC_TIMING_UHS_SDR104)) + if (!sdhci_msm_is_tuning_needed(host)) { + msm_host->use_cdr = false; + sdhci_msm_set_cdr(host, false); return 0; + } + + /* Clock-Data-Recovery used to dynamically adjust RX sampling point */ + msm_host->use_cdr = true; /* * For HS400 tuning in HS200 timing requires: @@ -1069,7 +1135,6 @@ retry: if (rc) return rc; - msm_host->saved_tuning_phase = phase; rc = mmc_send_tuning(mmc, opcode, NULL); if (!rc) { /* Tuning is successful at this tuning point */ @@ -1094,6 +1159,7 @@ retry: rc = msm_config_cm_dll_phase(host, phase); if (rc) return rc; + msm_host->saved_tuning_phase = phase; dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n", mmc_hostname(mmc), phase); } else { @@ -1525,6 +1591,19 @@ static int __sdhci_msm_check_write(struct sdhci_host *host, u16 val, int reg) case SDHCI_POWER_CONTROL: req_type = !val ? REQ_BUS_OFF : REQ_BUS_ON; break; + case SDHCI_TRANSFER_MODE: + msm_host->transfer_mode = val; + break; + case SDHCI_COMMAND: + if (!msm_host->use_cdr) + break; + if ((msm_host->transfer_mode & SDHCI_TRNS_READ) && + SDHCI_GET_CMD(val) != MMC_SEND_TUNING_BLOCK_HS200 && + SDHCI_GET_CMD(val) != MMC_SEND_TUNING_BLOCK) + sdhci_msm_set_cdr(host, true); + else + sdhci_msm_set_cdr(host, false); + break; } if (req_type) { @@ -1616,7 +1695,6 @@ static const struct sdhci_msm_variant_ops v5_var_ops = { }; static const struct sdhci_msm_variant_info sdhci_msm_mci_var = { - .mci_removed = false, .var_ops = &mci_var_ops, .offset = &sdhci_msm_mci_offset, }; @@ -1627,9 +1705,17 @@ static const struct sdhci_msm_variant_info sdhci_msm_v5_var = { .offset = &sdhci_msm_v5_offset, }; +static const struct sdhci_msm_variant_info sdm845_sdhci_var = { + .mci_removed = true, + .restore_dll_config = true, + .var_ops = &v5_var_ops, + .offset = &sdhci_msm_v5_offset, +}; + static const struct of_device_id sdhci_msm_dt_match[] = { {.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var}, {.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var}, + {.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var}, {}, }; @@ -1689,6 +1775,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) var_info = of_device_get_match_data(&pdev->dev); msm_host->mci_removed = var_info->mci_removed; + msm_host->restore_dll_config = var_info->restore_dll_config; msm_host->var_ops = var_info->var_ops; msm_host->offset = var_info->offset; @@ -1910,8 +1997,7 @@ static int sdhci_msm_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int sdhci_msm_runtime_suspend(struct device *dev) +static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -1923,16 +2009,26 @@ static int sdhci_msm_runtime_suspend(struct device *dev) return 0; } -static int sdhci_msm_runtime_resume(struct device *dev) +static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + int ret; - return clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks), + ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks), msm_host->bulk_clks); + if (ret) + return ret; + /* + * Whenever core-clock is gated dynamically, it's needed to + * restore the SDR DLL settings when the clock is ungated. + */ + if (msm_host->restore_dll_config && msm_host->clk_rate) + return sdhci_msm_restore_sdr_dll_config(host); + + return 0; } -#endif static const struct dev_pm_ops sdhci_msm_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c @@ -231,25 +231,6 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock) } } -static void sdhci_arasan_am654_set_clock(struct sdhci_host *host, - unsigned int clock) -{ - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); - - if (sdhci_arasan->is_phy_on) { - phy_power_off(sdhci_arasan->phy); - sdhci_arasan->is_phy_on = false; - } - - sdhci_set_clock(host, clock); - - if (clock > PHY_CLK_TOO_SLOW_HZ) { - phy_power_on(sdhci_arasan->phy); - sdhci_arasan->is_phy_on = true; - } -} - static void sdhci_arasan_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios) { @@ -335,29 +316,6 @@ static struct sdhci_arasan_of_data sdhci_arasan_data = { .pdata = &sdhci_arasan_pdata, }; -static const struct sdhci_ops sdhci_arasan_am654_ops = { - .set_clock = sdhci_arasan_am654_set_clock, - .get_max_clock = sdhci_pltfm_clk_get_max_clock, - .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, - .set_bus_width = sdhci_set_bus_width, - .reset = sdhci_arasan_reset, - .set_uhs_signaling = sdhci_set_uhs_signaling, -}; - -static const struct sdhci_pltfm_data sdhci_arasan_am654_pdata = { - .ops = &sdhci_arasan_am654_ops, - .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | - SDHCI_QUIRK_INVERTED_WRITE_PROTECT | - SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | - SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400, -}; - -static const struct sdhci_arasan_of_data sdhci_arasan_am654_data = { - .pdata = &sdhci_arasan_am654_pdata, -}; - static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask) { int cmd_error = 0; @@ -520,10 +478,6 @@ static const struct of_device_id sdhci_arasan_of_match[] = { .compatible = "rockchip,rk3399-sdhci-5.1", .data = &sdhci_arasan_rk3399_data, }, - { - .compatible = "ti,am654-sdhci-5.1", - .data = &sdhci_arasan_am654_data, - }, /* Generic compatible below here */ { .compatible = "arasan,sdhci-8.9a", diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c @@ -78,6 +78,8 @@ struct sdhci_esdhc { u8 vendor_ver; u8 spec_ver; bool quirk_incorrect_hostver; + bool quirk_limited_clk_division; + bool quirk_unreliable_pulse_detection; bool quirk_fixup_tuning; unsigned int peripheral_clock; const struct esdhc_clk_fixup *clk_fixup; @@ -528,8 +530,12 @@ static void esdhc_clock_enable(struct sdhci_host *host, bool enable) /* Wait max 20 ms */ timeout = ktime_add_ms(ktime_get(), 20); val = ESDHC_CLOCK_STABLE; - while (!(sdhci_readl(host, ESDHC_PRSSTAT) & val)) { - if (ktime_after(ktime_get(), timeout)) { + while (1) { + bool timedout = ktime_after(ktime_get(), timeout); + + if (sdhci_readl(host, ESDHC_PRSSTAT) & val) + break; + if (timedout) { pr_err("%s: Internal clock never stabilised.\n", mmc_hostname(host->mmc)); break; @@ -544,6 +550,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); int pre_div = 1; int div = 1; + int division; ktime_t timeout; long fixup = 0; u32 temp; @@ -579,6 +586,26 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) while (host->max_clk / pre_div / div > clock && div < 16) div++; + if (esdhc->quirk_limited_clk_division && + clock == MMC_HS200_MAX_DTR && + (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 || + host->flags & SDHCI_HS400_TUNING)) { + division = pre_div * div; + if (division <= 4) { + pre_div = 4; + div = 1; + } else if (division <= 8) { + pre_div = 4; + div = 2; + } else if (division <= 12) { + pre_div = 4; + div = 3; + } else { + pr_warn("%s: using unsupported clock division.\n", + mmc_hostname(host->mmc)); + } + } + dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", clock, host->max_clk / pre_div / div); host->mmc->actual_clock = host->max_clk / pre_div / div; @@ -592,10 +619,36 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) | (pre_div << ESDHC_PREDIV_SHIFT)); sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 && + clock == MMC_HS200_MAX_DTR) { + temp = sdhci_readl(host, ESDHC_TBCTL); + sdhci_writel(host, temp | ESDHC_HS400_MODE, ESDHC_TBCTL); + temp = sdhci_readl(host, ESDHC_SDCLKCTL); + sdhci_writel(host, temp | ESDHC_CMD_CLK_CTL, ESDHC_SDCLKCTL); + esdhc_clock_enable(host, true); + + temp = sdhci_readl(host, ESDHC_DLLCFG0); + temp |= ESDHC_DLL_ENABLE; + if (host->mmc->actual_clock == MMC_HS200_MAX_DTR) + temp |= ESDHC_DLL_FREQ_SEL; + sdhci_writel(host, temp, ESDHC_DLLCFG0); + temp = sdhci_readl(host, ESDHC_TBCTL); + sdhci_writel(host, temp | ESDHC_HS400_WNDW_ADJUST, ESDHC_TBCTL); + + esdhc_clock_enable(host, false); + temp = sdhci_readl(host, ESDHC_DMA_SYSCTL); + temp |= ESDHC_FLUSH_ASYNC_FIFO; + sdhci_writel(host, temp, ESDHC_DMA_SYSCTL); + } + /* Wait max 20 ms */ timeout = ktime_add_ms(ktime_get(), 20); - while (!(sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)) { - if (ktime_after(ktime_get(), timeout)) { + while (1) { + bool timedout = ktime_after(ktime_get(), timeout); + + if (sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE) + break; + if (timedout) { pr_err("%s: Internal clock never stabilised.\n", mmc_hostname(host->mmc)); return; @@ -603,6 +656,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) udelay(10); } + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); temp |= ESDHC_CLOCK_SDCLKEN; sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); } @@ -631,6 +685,8 @@ static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) static void esdhc_reset(struct sdhci_host *host, u8 mask) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); u32 val; sdhci_reset(host, mask); @@ -642,6 +698,12 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask) val = sdhci_readl(host, ESDHC_TBCTL); val &= ~ESDHC_TB_EN; sdhci_writel(host, val, ESDHC_TBCTL); + + if (esdhc->quirk_unreliable_pulse_detection) { + val = sdhci_readl(host, ESDHC_DLLCFG1); + val &= ~ESDHC_DLL_PD_PULSE_STRETCH_SEL; + sdhci_writel(host, val, ESDHC_DLLCFG1); + } } } @@ -728,25 +790,50 @@ static struct soc_device_attribute soc_fixup_tuning[] = { { }, }; -static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) +static void esdhc_tuning_block_enable(struct sdhci_host *host, bool enable) { - struct sdhci_host *host = mmc_priv(mmc); - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); u32 val; - /* Use tuning block for tuning procedure */ esdhc_clock_enable(host, false); + val = sdhci_readl(host, ESDHC_DMA_SYSCTL); val |= ESDHC_FLUSH_ASYNC_FIFO; sdhci_writel(host, val, ESDHC_DMA_SYSCTL); val = sdhci_readl(host, ESDHC_TBCTL); - val |= ESDHC_TB_EN; + if (enable) + val |= ESDHC_TB_EN; + else + val &= ~ESDHC_TB_EN; sdhci_writel(host, val, ESDHC_TBCTL); + esdhc_clock_enable(host, true); +} + +static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); + bool hs400_tuning; + u32 val; + int ret; + + if (esdhc->quirk_limited_clk_division && + host->flags & SDHCI_HS400_TUNING) + esdhc_of_set_clock(host, host->clock); + + esdhc_tuning_block_enable(host, true); + + hs400_tuning = host->flags & SDHCI_HS400_TUNING; + ret = sdhci_execute_tuning(mmc, opcode); + + if (hs400_tuning) { + val = sdhci_readl(host, ESDHC_SDTIMNGCTL); + val |= ESDHC_FLW_CTL_BG; + sdhci_writel(host, val, ESDHC_SDTIMNGCTL); + } - sdhci_execute_tuning(mmc, opcode); if (host->tuning_err == -EAGAIN && esdhc->quirk_fixup_tuning) { /* program TBPTR[TB_WNDW_END_PTR] = 3*DIV_RATIO and @@ -765,7 +852,16 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) sdhci_writel(host, val, ESDHC_TBCTL); sdhci_execute_tuning(mmc, opcode); } - return 0; + return ret; +} + +static void esdhc_set_uhs_signaling(struct sdhci_host *host, + unsigned int timing) +{ + if (timing == MMC_TIMING_MMC_HS400) + esdhc_tuning_block_enable(host, true); + else + sdhci_set_uhs_signaling(host, timing); } #ifdef CONFIG_PM_SLEEP @@ -814,7 +910,7 @@ static const struct sdhci_ops sdhci_esdhc_be_ops = { .adma_workaround = esdhc_of_adma_workaround, .set_bus_width = esdhc_pltfm_set_bus_width, .reset = esdhc_reset, - .set_uhs_signaling = sdhci_set_uhs_signaling, + .set_uhs_signaling = esdhc_set_uhs_signaling, }; static const struct sdhci_ops sdhci_esdhc_le_ops = { @@ -831,7 +927,7 @@ static const struct sdhci_ops sdhci_esdhc_le_ops = { .adma_workaround = esdhc_of_adma_workaround, .set_bus_width = esdhc_pltfm_set_bus_width, .reset = esdhc_reset, - .set_uhs_signaling = sdhci_set_uhs_signaling, + .set_uhs_signaling = esdhc_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = { @@ -857,6 +953,16 @@ static struct soc_device_attribute soc_incorrect_hostver[] = { { }, }; +static struct soc_device_attribute soc_fixup_sdhc_clkdivs[] = { + { .family = "QorIQ LX2160A", .revision = "1.0", }, + { }, +}; + +static struct soc_device_attribute soc_unreliable_pulse_detection[] = { + { .family = "QorIQ LX2160A", .revision = "1.0", }, + { }, +}; + static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) { const struct of_device_id *match; @@ -879,6 +985,16 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) else esdhc->quirk_incorrect_hostver = false; + if (soc_device_match(soc_fixup_sdhc_clkdivs)) + esdhc->quirk_limited_clk_division = true; + else + esdhc->quirk_limited_clk_division = false; + + if (soc_device_match(soc_unreliable_pulse_detection)) + esdhc->quirk_unreliable_pulse_detection = true; + else + esdhc->quirk_unreliable_pulse_detection = false; + match = of_match_node(sdhci_esdhc_of_match, pdev->dev.of_node); if (match) esdhc->clk_fixup = match->data; @@ -909,6 +1025,12 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) } } +static int esdhc_hs400_prepare_ddr(struct mmc_host *mmc) +{ + esdhc_tuning_block_enable(mmc_priv(mmc), false); + return 0; +} + static int sdhci_esdhc_probe(struct platform_device *pdev) { struct sdhci_host *host; @@ -932,6 +1054,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) host->mmc_host_ops.start_signal_voltage_switch = esdhc_signal_voltage_switch; host->mmc_host_ops.execute_tuning = esdhc_execute_tuning; + host->mmc_host_ops.hs400_prepare_ddr = esdhc_hs400_prepare_ddr; host->tuning_delay = 1; esdhc_init(pdev, host); diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c @@ -27,6 +27,7 @@ #include <linux/regulator/consumer.h> #include <linux/pinctrl/consumer.h> #include <linux/sys_soc.h> +#include <linux/thermal.h> #include "sdhci-pltfm.h" @@ -115,6 +116,7 @@ struct sdhci_omap_host { struct pinctrl *pinctrl; struct pinctrl_state **pinctrl_state; + bool is_tuning; }; static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host); @@ -220,8 +222,12 @@ static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host, /* wait 1ms */ timeout = ktime_add_ms(ktime_get(), SDHCI_OMAP_TIMEOUT); - while (!(sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL) & HCTL_SDBP)) { - if (WARN_ON(ktime_after(ktime_get(), timeout))) + while (1) { + bool timedout = ktime_after(ktime_get(), timeout); + + if (sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL) & HCTL_SDBP) + break; + if (WARN_ON(timedout)) return; usleep_range(5, 10); } @@ -285,19 +291,19 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode) struct sdhci_host *host = mmc_priv(mmc); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); + struct thermal_zone_device *thermal_dev; struct device *dev = omap_host->dev; struct mmc_ios *ios = &mmc->ios; u32 start_window = 0, max_window = 0; + bool single_point_failure = false; bool dcrc_was_enabled = false; u8 cur_match, prev_match = 0; u32 length = 0, max_len = 0; u32 phase_delay = 0; + int temperature; int ret = 0; u32 reg; - - pltfm_host = sdhci_priv(host); - omap_host = sdhci_pltfm_priv(pltfm_host); - dev = omap_host->dev; + int i; /* clock tuning is not needed for upto 52MHz */ if (ios->clock <= 52000000) @@ -307,6 +313,16 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode) if (ios->timing == MMC_TIMING_UHS_SDR50 && !(reg & CAPA2_TSDR50)) return 0; + thermal_dev = thermal_zone_get_zone_by_name("cpu_thermal"); + if (IS_ERR(thermal_dev)) { + dev_err(dev, "Unable to get thermal zone for tuning\n"); + return PTR_ERR(thermal_dev); + } + + ret = thermal_zone_get_temp(thermal_dev, &temperature); + if (ret) + return ret; + reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL); reg |= DLL_SWT; sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg); @@ -322,6 +338,13 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode) dcrc_was_enabled = true; } + omap_host->is_tuning = true; + + /* + * Stage 1: Search for a maximum pass window ignoring any + * any single point failures. If the tuning value ends up + * near it, move away from it in stage 2 below + */ while (phase_delay <= MAX_PHASE_DELAY) { sdhci_omap_set_dll(omap_host, phase_delay); @@ -329,10 +352,15 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode) if (cur_match) { if (prev_match) { length++; + } else if (single_point_failure) { + /* ignore single point failure */ + length++; } else { start_window = phase_delay; length = 1; } + } else { + single_point_failure = prev_match; } if (length > max_len) { @@ -350,18 +378,84 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode) goto tuning_error; } + /* + * Assign tuning value as a ratio of maximum pass window based + * on temperature + */ + if (temperature < -20000) + phase_delay = min(max_window + 4 * max_len - 24, + max_window + + DIV_ROUND_UP(13 * max_len, 16) * 4); + else if (temperature < 20000) + phase_delay = max_window + DIV_ROUND_UP(9 * max_len, 16) * 4; + else if (temperature < 40000) + phase_delay = max_window + DIV_ROUND_UP(8 * max_len, 16) * 4; + else if (temperature < 70000) + phase_delay = max_window + DIV_ROUND_UP(7 * max_len, 16) * 4; + else if (temperature < 90000) + phase_delay = max_window + DIV_ROUND_UP(5 * max_len, 16) * 4; + else if (temperature < 120000) + phase_delay = max_window + DIV_ROUND_UP(4 * max_len, 16) * 4; + else + phase_delay = max_window + DIV_ROUND_UP(3 * max_len, 16) * 4; + + /* + * Stage 2: Search for a single point failure near the chosen tuning + * value in two steps. First in the +3 to +10 range and then in the + * +2 to -10 range. If found, move away from it in the appropriate + * direction by the appropriate amount depending on the temperature. + */ + for (i = 3; i <= 10; i++) { + sdhci_omap_set_dll(omap_host, phase_delay + i); + + if (mmc_send_tuning(mmc, opcode, NULL)) { + if (temperature < 10000) + phase_delay += i + 6; + else if (temperature < 20000) + phase_delay += i - 12; + else if (temperature < 70000) + phase_delay += i - 8; + else + phase_delay += i - 6; + + goto single_failure_found; + } + } + + for (i = 2; i >= -10; i--) { + sdhci_omap_set_dll(omap_host, phase_delay + i); + + if (mmc_send_tuning(mmc, opcode, NULL)) { + if (temperature < 10000) + phase_delay += i + 12; + else if (temperature < 20000) + phase_delay += i + 8; + else if (temperature < 70000) + phase_delay += i + 8; + else if (temperature < 90000) + phase_delay += i + 10; + else + phase_delay += i + 12; + + goto single_failure_found; + } + } + +single_failure_found: reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12); if (!(reg & AC12_SCLK_SEL)) { ret = -EIO; goto tuning_error; } - phase_delay = max_window + 4 * (max_len >> 1); sdhci_omap_set_dll(omap_host, phase_delay); + omap_host->is_tuning = false; + goto ret; tuning_error: + omap_host->is_tuning = false; dev_err(dev, "Tuning failed\n"); sdhci_omap_disable_tuning(omap_host); @@ -653,8 +747,12 @@ static void sdhci_omap_init_74_clocks(struct sdhci_host *host, u8 power_mode) /* wait 1ms */ timeout = ktime_add_ms(ktime_get(), SDHCI_OMAP_TIMEOUT); - while (!(sdhci_omap_readl(omap_host, SDHCI_OMAP_STAT) & INT_CC_EN)) { - if (WARN_ON(ktime_after(ktime_get(), timeout))) + while (1) { + bool timedout = ktime_after(ktime_get(), timeout); + + if (sdhci_omap_readl(omap_host, SDHCI_OMAP_STAT) & INT_CC_EN) + break; + if (WARN_ON(timedout)) return; usleep_range(5, 10); } @@ -687,6 +785,18 @@ static void sdhci_omap_set_uhs_signaling(struct sdhci_host *host, sdhci_omap_start_clock(omap_host); } +void sdhci_omap_reset(struct sdhci_host *host, u8 mask) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); + + /* Don't reset data lines during tuning operation */ + if (omap_host->is_tuning) + mask &= ~SDHCI_RESET_DATA; + + sdhci_reset(host, mask); +} + static struct sdhci_ops sdhci_omap_ops = { .set_clock = sdhci_omap_set_clock, .set_power = sdhci_omap_set_power, @@ -695,7 +805,7 @@ static struct sdhci_ops sdhci_omap_ops = { .get_min_clock = sdhci_omap_get_min_clock, .set_bus_width = sdhci_omap_set_bus_width, .platform_send_init_74_clocks = sdhci_omap_init_74_clocks, - .reset = sdhci_reset, + .reset = sdhci_omap_reset, .set_uhs_signaling = sdhci_omap_set_uhs_signaling, }; diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c @@ -710,11 +710,15 @@ static int intel_execute_tuning(struct mmc_host *mmc, u32 opcode) static void byt_probe_slot(struct sdhci_pci_slot *slot) { struct mmc_host_ops *ops = &slot->host->mmc_host_ops; + struct device *dev = &slot->chip->pdev->dev; + struct mmc_host *mmc = slot->host->mmc; byt_read_dsm(slot); ops->execute_tuning = intel_execute_tuning; ops->start_signal_voltage_switch = intel_start_signal_voltage_switch; + + device_property_read_u32(dev, "max-frequency", &mmc->f_max); } static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) @@ -937,7 +941,8 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = { .allow_runtime_pm = true, .probe_slot = byt_emmc_probe_slot, - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_NO_LED, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 | SDHCI_QUIRK2_STOP_WITH_TC, @@ -957,7 +962,8 @@ static const struct sdhci_pci_fixes sdhci_intel_glk_emmc = { .runtime_suspend = glk_runtime_suspend, .runtime_resume = glk_runtime_resume, #endif - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_NO_LED, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 | SDHCI_QUIRK2_STOP_WITH_TC, @@ -966,7 +972,8 @@ static const struct sdhci_pci_fixes sdhci_intel_glk_emmc = { }; static const struct sdhci_pci_fixes sdhci_ni_byt_sdio = { - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_NO_LED, .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON | SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .allow_runtime_pm = true, @@ -976,7 +983,8 @@ static const struct sdhci_pci_fixes sdhci_ni_byt_sdio = { }; static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_NO_LED, .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON | SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .allow_runtime_pm = true, @@ -986,7 +994,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { }; static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_NO_LED, .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC, diff --git a/drivers/mmc/host/sdhci-xenon-phy.c b/drivers/mmc/host/sdhci-xenon-phy.c @@ -357,9 +357,13 @@ static int xenon_emmc_phy_enable_dll(struct sdhci_host *host) /* Wait max 32 ms */ timeout = ktime_add_ms(ktime_get(), 32); - while (!(sdhci_readw(host, XENON_SLOT_EXT_PRESENT_STATE) & - XENON_DLL_LOCK_STATE)) { - if (ktime_after(ktime_get(), timeout)) { + while (1) { + bool timedout = ktime_after(ktime_get(), timeout); + + if (sdhci_readw(host, XENON_SLOT_EXT_PRESENT_STATE) & + XENON_DLL_LOCK_STATE) + break; + if (timedout) { dev_err(mmc_dev(host->mmc), "Wait for DLL Lock time-out\n"); return -ETIMEDOUT; } diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c @@ -34,9 +34,13 @@ static int xenon_enable_internal_clk(struct sdhci_host *host) sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL); /* Wait max 20 ms */ timeout = ktime_add_ms(ktime_get(), 20); - while (!((reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) - & SDHCI_CLOCK_INT_STABLE)) { - if (ktime_after(ktime_get(), timeout)) { + while (1) { + bool timedout = ktime_after(ktime_get(), timeout); + + reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + if (reg & SDHCI_CLOCK_INT_STABLE) + break; + if (timedout) { dev_err(mmc_dev(host->mmc), "Internal clock never stabilised.\n"); return -ETIMEDOUT; } diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c @@ -82,8 +82,8 @@ void sdhci_dumpregs(struct sdhci_host *host) SDHCI_DUMP("Int enab: 0x%08x | Sig enab: 0x%08x\n", sdhci_readl(host, SDHCI_INT_ENABLE), sdhci_readl(host, SDHCI_SIGNAL_ENABLE)); - SDHCI_DUMP("AC12 err: 0x%08x | Slot int: 0x%08x\n", - sdhci_readw(host, SDHCI_ACMD12_ERR), + SDHCI_DUMP("ACmd stat: 0x%08x | Slot int: 0x%08x\n", + sdhci_readw(host, SDHCI_AUTO_CMD_STATUS), sdhci_readw(host, SDHCI_SLOT_INT_STATUS)); SDHCI_DUMP("Caps: 0x%08x | Caps_1: 0x%08x\n", sdhci_readl(host, SDHCI_CAPABILITIES), @@ -349,6 +349,9 @@ static void __sdhci_led_activate(struct sdhci_host *host) { u8 ctrl; + if (host->quirks & SDHCI_QUIRK_NO_LED) + return; + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl |= SDHCI_CTRL_LED; sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); @@ -358,6 +361,9 @@ static void __sdhci_led_deactivate(struct sdhci_host *host) { u8 ctrl; + if (host->quirks & SDHCI_QUIRK_NO_LED) + return; + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl &= ~SDHCI_CTRL_LED; sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); @@ -387,6 +393,9 @@ static int sdhci_led_register(struct sdhci_host *host) { struct mmc_host *mmc = host->mmc; + if (host->quirks & SDHCI_QUIRK_NO_LED) + return 0; + snprintf(host->led_name, sizeof(host->led_name), "%s::", mmc_hostname(mmc)); @@ -400,6 +409,9 @@ static int sdhci_led_register(struct sdhci_host *host) static void sdhci_led_unregister(struct sdhci_host *host) { + if (host->quirks & SDHCI_QUIRK_NO_LED) + return; + led_classdev_unregister(&host->led); } @@ -933,6 +945,11 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host) else host->ier = (host->ier & ~dma_irqs) | pio_irqs; + if (host->flags & (SDHCI_AUTO_CMD23 | SDHCI_AUTO_CMD12)) + host->ier |= SDHCI_INT_AUTO_CMD_ERR; + else + host->ier &= ~SDHCI_INT_AUTO_CMD_ERR; + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } @@ -1191,8 +1208,7 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq) return (!(host->flags & SDHCI_DEVICE_DEAD) && ((mrq->cmd && mrq->cmd->error) || (mrq->sbc && mrq->sbc->error) || - (mrq->data && ((mrq->data->error && !mrq->data->stop) || - (mrq->data->stop && mrq->data->stop->error))) || + (mrq->data && mrq->data->stop && mrq->data->stop->error) || (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))); } @@ -1244,6 +1260,16 @@ static void sdhci_finish_data(struct sdhci_host *host) host->data = NULL; host->data_cmd = NULL; + /* + * The controller needs a reset of internal state machines upon error + * conditions. + */ + if (data->error) { + if (!host->cmd || host->cmd == data_cmd) + sdhci_do_reset(host, SDHCI_RESET_CMD); + sdhci_do_reset(host, SDHCI_RESET_DATA); + } + if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) == (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) sdhci_adma_table_post(host, data); @@ -1268,17 +1294,6 @@ static void sdhci_finish_data(struct sdhci_host *host) if (data->stop && (data->error || !data->mrq->sbc)) { - - /* - * The controller needs a reset of internal state machines - * upon error conditions. - */ - if (data->error) { - if (!host->cmd || host->cmd == data_cmd) - sdhci_do_reset(host, SDHCI_RESET_CMD); - sdhci_do_reset(host, SDHCI_RESET_DATA); - } - /* * 'cap_cmd_during_tfr' request must not use the command line * after mmc_command_done() has been called. It is upper layer's @@ -2757,8 +2772,23 @@ static void sdhci_timeout_data_timer(struct timer_list *t) * * \*****************************************************************************/ -static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) +static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p) { + /* Handle auto-CMD12 error */ + if (intmask & SDHCI_INT_AUTO_CMD_ERR && host->data_cmd) { + struct mmc_request *mrq = host->data_cmd->mrq; + u16 auto_cmd_status = sdhci_readw(host, SDHCI_AUTO_CMD_STATUS); + int data_err_bit = (auto_cmd_status & SDHCI_AUTO_CMD_TIMEOUT) ? + SDHCI_INT_DATA_TIMEOUT : + SDHCI_INT_DATA_CRC; + + /* Treat auto-CMD12 error the same as data error */ + if (!mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) { + *intmask_p |= data_err_bit; + return; + } + } + if (!host->cmd) { /* * SDHCI recovers from errors by resetting the cmd and data @@ -2780,20 +2810,12 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) else host->cmd->error = -EILSEQ; - /* - * If this command initiates a data phase and a response - * CRC error is signalled, the card can start transferring - * data - the card may have received the command without - * error. We must not terminate the mmc_request early. - * - * If the card did not receive the command or returned an - * error which prevented it sending data, the data phase - * will time out. - */ + /* Treat data command CRC error the same as data CRC error */ if (host->cmd->data && (intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) == SDHCI_INT_CRC) { host->cmd = NULL; + *intmask_p |= SDHCI_INT_DATA_CRC; return; } @@ -2801,6 +2823,21 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) return; } + /* Handle auto-CMD23 error */ + if (intmask & SDHCI_INT_AUTO_CMD_ERR) { + struct mmc_request *mrq = host->cmd->mrq; + u16 auto_cmd_status = sdhci_readw(host, SDHCI_AUTO_CMD_STATUS); + int err = (auto_cmd_status & SDHCI_AUTO_CMD_TIMEOUT) ? + -ETIMEDOUT : + -EILSEQ; + + if (mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) { + mrq->sbc->error = err; + sdhci_finish_mrq(host, mrq); + return; + } + } + if (intmask & SDHCI_INT_RESPONSE) sdhci_finish_command(host); } @@ -3021,7 +3058,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) } if (intmask & SDHCI_INT_CMD_MASK) - sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); + sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK, &intmask); if (intmask & SDHCI_INT_DATA_MASK) sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); @@ -3541,7 +3578,7 @@ void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1) } EXPORT_SYMBOL_GPL(__sdhci_read_caps); -static int sdhci_allocate_bounce_buffer(struct sdhci_host *host) +static void sdhci_allocate_bounce_buffer(struct sdhci_host *host) { struct mmc_host *mmc = host->mmc; unsigned int max_blocks; @@ -3579,7 +3616,7 @@ static int sdhci_allocate_bounce_buffer(struct sdhci_host *host) * Exiting with zero here makes sure we proceed with * mmc->max_segs == 1. */ - return 0; + return; } host->bounce_addr = dma_map_single(mmc->parent, @@ -3589,7 +3626,7 @@ static int sdhci_allocate_bounce_buffer(struct sdhci_host *host) ret = dma_mapping_error(mmc->parent, host->bounce_addr); if (ret) /* Again fall back to max_segs == 1 */ - return 0; + return; host->bounce_buffer_size = bounce_size; /* Lie about this since we're bouncing */ @@ -3599,8 +3636,6 @@ static int sdhci_allocate_bounce_buffer(struct sdhci_host *host) pr_info("%s bounce up to %u segments into one, max segment size %u bytes\n", mmc_hostname(mmc), max_blocks, bounce_size); - - return 0; } static inline bool sdhci_can_64bit_dma(struct sdhci_host *host) @@ -4134,12 +4169,9 @@ int sdhci_setup_host(struct sdhci_host *host) */ mmc->max_blk_count = (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535; - if (mmc->max_segs == 1) { + if (mmc->max_segs == 1) /* This may alter mmc->*_blk_* parameters */ - ret = sdhci_allocate_bounce_buffer(host); - if (ret) - return ret; - } + sdhci_allocate_bounce_buffer(host); return 0; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h @@ -146,14 +146,15 @@ #define SDHCI_INT_DATA_CRC 0x00200000 #define SDHCI_INT_DATA_END_BIT 0x00400000 #define SDHCI_INT_BUS_POWER 0x00800000 -#define SDHCI_INT_ACMD12ERR 0x01000000 +#define SDHCI_INT_AUTO_CMD_ERR 0x01000000 #define SDHCI_INT_ADMA_ERROR 0x02000000 #define SDHCI_INT_NORMAL_MASK 0x00007FFF #define SDHCI_INT_ERROR_MASK 0xFFFF8000 #define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \ - SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX) + SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX | \ + SDHCI_INT_AUTO_CMD_ERR) #define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \ SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \ SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ @@ -168,7 +169,11 @@ #define SDHCI_CQE_INT_MASK (SDHCI_CQE_INT_ERR_MASK | SDHCI_INT_CQE) -#define SDHCI_ACMD12_ERR 0x3C +#define SDHCI_AUTO_CMD_STATUS 0x3C +#define SDHCI_AUTO_CMD_TIMEOUT 0x00000002 +#define SDHCI_AUTO_CMD_CRC 0x00000004 +#define SDHCI_AUTO_CMD_END_BIT 0x00000008 +#define SDHCI_AUTO_CMD_INDEX 0x00000010 #define SDHCI_HOST_CONTROL2 0x3E #define SDHCI_CTRL_UHS_MASK 0x0007 @@ -403,6 +408,8 @@ struct sdhci_host { #define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16) /* Controller does not like fast PIO transfers */ #define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) +/* Controller does not have a LED */ +#define SDHCI_QUIRK_NO_LED (1<<19) /* Controller has to be forced to use block size of 2048 bytes */ #define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20) /* Controller cannot do multi-block transfers */ diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sdhci_am654.c - SDHCI driver for TI's AM654 SOCs + * + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com + * + */ +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/regmap.h> + +#include "sdhci-pltfm.h" + +/* CTL_CFG Registers */ +#define CTL_CFG_2 0x14 + +#define SLOTTYPE_MASK GENMASK(31, 30) +#define SLOTTYPE_EMBEDDED BIT(30) + +/* PHY Registers */ +#define PHY_CTRL1 0x100 +#define PHY_CTRL2 0x104 +#define PHY_CTRL3 0x108 +#define PHY_CTRL4 0x10C +#define PHY_CTRL5 0x110 +#define PHY_CTRL6 0x114 +#define PHY_STAT1 0x130 +#define PHY_STAT2 0x134 + +#define IOMUX_ENABLE_SHIFT 31 +#define IOMUX_ENABLE_MASK BIT(IOMUX_ENABLE_SHIFT) +#define OTAPDLYENA_SHIFT 20 +#define OTAPDLYENA_MASK BIT(OTAPDLYENA_SHIFT) +#define OTAPDLYSEL_SHIFT 12 +#define OTAPDLYSEL_MASK GENMASK(15, 12) +#define STRBSEL_SHIFT 24 +#define STRBSEL_MASK GENMASK(27, 24) +#define SEL50_SHIFT 8 +#define SEL50_MASK BIT(SEL50_SHIFT) +#define SEL100_SHIFT 9 +#define SEL100_MASK BIT(SEL100_SHIFT) +#define DLL_TRIM_ICP_SHIFT 4 +#define DLL_TRIM_ICP_MASK GENMASK(7, 4) +#define DR_TY_SHIFT 20 +#define DR_TY_MASK GENMASK(22, 20) +#define ENDLL_SHIFT 1 +#define ENDLL_MASK BIT(ENDLL_SHIFT) +#define DLLRDY_SHIFT 0 +#define DLLRDY_MASK BIT(DLLRDY_SHIFT) +#define PDB_SHIFT 0 +#define PDB_MASK BIT(PDB_SHIFT) +#define CALDONE_SHIFT 1 +#define CALDONE_MASK BIT(CALDONE_SHIFT) +#define RETRIM_SHIFT 17 +#define RETRIM_MASK BIT(RETRIM_SHIFT) + +#define DRIVER_STRENGTH_50_OHM 0x0 +#define DRIVER_STRENGTH_33_OHM 0x1 +#define DRIVER_STRENGTH_66_OHM 0x2 +#define DRIVER_STRENGTH_100_OHM 0x3 +#define DRIVER_STRENGTH_40_OHM 0x4 + +#define CLOCK_TOO_SLOW_HZ 400000 + +static struct regmap_config sdhci_am654_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .fast_io = true, +}; + +struct sdhci_am654_data { + struct regmap *base; + int otap_del_sel; + int trm_icp; + int drv_strength; + bool dll_on; +}; + +static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); + int sel50, sel100; + u32 mask, val; + int ret; + + if (sdhci_am654->dll_on) { + regmap_update_bits(sdhci_am654->base, PHY_CTRL1, + ENDLL_MASK, 0); + + sdhci_am654->dll_on = false; + } + + sdhci_set_clock(host, clock); + + if (clock > CLOCK_TOO_SLOW_HZ) { + /* Setup DLL Output TAP delay */ + mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; + val = (1 << OTAPDLYENA_SHIFT) | + (sdhci_am654->otap_del_sel << OTAPDLYSEL_SHIFT); + regmap_update_bits(sdhci_am654->base, PHY_CTRL4, + mask, val); + switch (clock) { + case 200000000: + sel50 = 0; + sel100 = 0; + break; + case 100000000: + sel50 = 0; + sel100 = 1; + break; + default: + sel50 = 1; + sel100 = 0; + } + + /* Configure PHY DLL frequency */ + mask = SEL50_MASK | SEL100_MASK; + val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT); + regmap_update_bits(sdhci_am654->base, PHY_CTRL5, + mask, val); + /* Configure DLL TRIM */ + mask = DLL_TRIM_ICP_MASK; + val = sdhci_am654->trm_icp << DLL_TRIM_ICP_SHIFT; + + /* Configure DLL driver strength */ + mask |= DR_TY_MASK; + val |= sdhci_am654->drv_strength << DR_TY_SHIFT; + regmap_update_bits(sdhci_am654->base, PHY_CTRL1, + mask, val); + /* Enable DLL */ + regmap_update_bits(sdhci_am654->base, PHY_CTRL1, + ENDLL_MASK, 0x1 << ENDLL_SHIFT); + /* + * Poll for DLL ready. Use a one second timeout. + * Works in all experiments done so far + */ + ret = regmap_read_poll_timeout(sdhci_am654->base, + PHY_STAT1, val, + val & DLLRDY_MASK, + 1000, 1000000); + + sdhci_am654->dll_on = true; + } +} + +static void sdhci_am654_set_power(struct sdhci_host *host, unsigned char mode, + unsigned short vdd) +{ + if (!IS_ERR(host->mmc->supply.vmmc)) { + struct mmc_host *mmc = host->mmc; + + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); + } + sdhci_set_power_noreg(host, mode, vdd); +} + +struct sdhci_ops sdhci_am654_ops = { + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .set_bus_width = sdhci_set_bus_width, + .set_power = sdhci_am654_set_power, + .set_clock = sdhci_am654_set_clock, + .reset = sdhci_reset, +}; + +static const struct sdhci_pltfm_data sdhci_am654_pdata = { + .ops = &sdhci_am654_ops, + .quirks = SDHCI_QUIRK_INVERTED_WRITE_PROTECT | + SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +}; + +static int sdhci_am654_init(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); + u32 ctl_cfg_2 = 0; + u32 mask; + u32 val; + int ret; + + /* Reset OTAP to default value */ + mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; + regmap_update_bits(sdhci_am654->base, PHY_CTRL4, + mask, 0x0); + + regmap_read(sdhci_am654->base, PHY_STAT1, &val); + if (~val & CALDONE_MASK) { + /* Calibrate IO lines */ + regmap_update_bits(sdhci_am654->base, PHY_CTRL1, + PDB_MASK, PDB_MASK); + ret = regmap_read_poll_timeout(sdhci_am654->base, PHY_STAT1, + val, val & CALDONE_MASK, 1, 20); + if (ret) + return ret; + } + + /* Enable pins by setting IO mux to 0 */ + regmap_update_bits(sdhci_am654->base, PHY_CTRL1, + IOMUX_ENABLE_MASK, 0); + + /* Set slot type based on SD or eMMC */ + if (host->mmc->caps & MMC_CAP_NONREMOVABLE) + ctl_cfg_2 = SLOTTYPE_EMBEDDED; + + regmap_update_bits(sdhci_am654->base, CTL_CFG_2, + ctl_cfg_2, SLOTTYPE_MASK); + + return sdhci_add_host(host); +} + +static int sdhci_am654_get_of_property(struct platform_device *pdev, + struct sdhci_am654_data *sdhci_am654) +{ + struct device *dev = &pdev->dev; + int drv_strength; + int ret; + + ret = device_property_read_u32(dev, "ti,trm-icp", + &sdhci_am654->trm_icp); + if (ret) + return ret; + + ret = device_property_read_u32(dev, "ti,otap-del-sel", + &sdhci_am654->otap_del_sel); + if (ret) + return ret; + + ret = device_property_read_u32(dev, "ti,driver-strength-ohm", + &drv_strength); + if (ret) + return ret; + + switch (drv_strength) { + case 50: + sdhci_am654->drv_strength = DRIVER_STRENGTH_50_OHM; + break; + case 33: + sdhci_am654->drv_strength = DRIVER_STRENGTH_33_OHM; + break; + case 66: + sdhci_am654->drv_strength = DRIVER_STRENGTH_66_OHM; + break; + case 100: + sdhci_am654->drv_strength = DRIVER_STRENGTH_100_OHM; + break; + case 40: + sdhci_am654->drv_strength = DRIVER_STRENGTH_40_OHM; + break; + default: + dev_err(dev, "Invalid driver strength\n"); + return -EINVAL; + } + + sdhci_get_of_property(pdev); + + return 0; +} + +static int sdhci_am654_probe(struct platform_device *pdev) +{ + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_am654_data *sdhci_am654; + struct sdhci_host *host; + struct resource *res; + struct clk *clk_xin; + struct device *dev = &pdev->dev; + void __iomem *base; + int ret; + + host = sdhci_pltfm_init(pdev, &sdhci_am654_pdata, sizeof(*sdhci_am654)); + if (IS_ERR(host)) + return PTR_ERR(host); + + pltfm_host = sdhci_priv(host); + sdhci_am654 = sdhci_pltfm_priv(pltfm_host); + + clk_xin = devm_clk_get(dev, "clk_xin"); + if (IS_ERR(clk_xin)) { + dev_err(dev, "clk_xin clock not found.\n"); + ret = PTR_ERR(clk_xin); + goto err_pltfm_free; + } + + pltfm_host->clk = clk_xin; + + /* Clocks are enabled using pm_runtime */ + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + goto pm_runtime_disable; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + goto pm_runtime_put; + } + + sdhci_am654->base = devm_regmap_init_mmio(dev, base, + &sdhci_am654_regmap_config); + if (IS_ERR(sdhci_am654->base)) { + dev_err(dev, "Failed to initialize regmap\n"); + ret = PTR_ERR(sdhci_am654->base); + goto pm_runtime_put; + } + + ret = sdhci_am654_get_of_property(pdev, sdhci_am654); + if (ret) + goto pm_runtime_put; + + ret = mmc_of_parse(host->mmc); + if (ret) { + dev_err(dev, "parsing dt failed (%d)\n", ret); + goto pm_runtime_put; + } + + ret = sdhci_am654_init(host); + if (ret) + goto pm_runtime_put; + + return 0; + +pm_runtime_put: + pm_runtime_put_sync(dev); +pm_runtime_disable: + pm_runtime_disable(dev); +err_pltfm_free: + sdhci_pltfm_free(pdev); + return ret; +} + +static int sdhci_am654_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + int ret; + + sdhci_remove_host(host, true); + ret = pm_runtime_put_sync(&pdev->dev); + if (ret < 0) + return ret; + + pm_runtime_disable(&pdev->dev); + sdhci_pltfm_free(pdev); + + return 0; +} + +static const struct of_device_id sdhci_am654_of_match[] = { + { .compatible = "ti,am654-sdhci-5.1" }, + { /* sentinel */ } +}; + +static struct platform_driver sdhci_am654_driver = { + .driver = { + .name = "sdhci-am654", + .of_match_table = sdhci_am654_of_match, + }, + .probe = sdhci_am654_probe, + .remove = sdhci_am654_remove, +}; + +module_platform_driver(sdhci_am654_driver); + +MODULE_DESCRIPTION("Driver for SDHCI Controller on TI's AM654 devices"); +MODULE_AUTHOR("Faiz Abbas <faiz_abbas@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h @@ -70,6 +70,7 @@ #define TMIO_STAT_DAT0 BIT(23) /* only known on R-Car so far */ #define TMIO_STAT_RXRDY BIT(24) #define TMIO_STAT_TXRQ BIT(25) +#define TMIO_STAT_ALWAYS_SET_27 BIT(27) /* only known on R-Car 2+ so far */ #define TMIO_STAT_ILL_FUNC BIT(29) /* only when !TMIO_MMC_HAS_IDLE_WAIT */ #define TMIO_STAT_SCLKDIVEN BIT(29) /* only when TMIO_MMC_HAS_IDLE_WAIT */ #define TMIO_STAT_CMD_BUSY BIT(30) @@ -96,6 +97,7 @@ /* Define some IRQ masks */ /* This is the mask used at reset by the chip */ +#define TMIO_MASK_INIT_RCAR2 0x8b7f031d /* Initial value for R-Car Gen2+ */ #define TMIO_MASK_ALL 0x837f031d #define TMIO_MASK_READOP (TMIO_STAT_RXRDY | TMIO_STAT_DATAEND) #define TMIO_MASK_WRITEOP (TMIO_STAT_TXRQ | TMIO_STAT_DATAEND) @@ -153,6 +155,7 @@ struct tmio_mmc_host { u32 sdcard_irq_mask; u32 sdio_irq_mask; unsigned int clk_cache; + u32 sdcard_irq_setbit_mask; spinlock_t lock; /* protect host private data */ unsigned long last_req_ts; @@ -267,6 +270,9 @@ static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr, static inline void sd_ctrl_write32_as_16_and_16(struct tmio_mmc_host *host, int addr, u32 val) { + if (addr == CTL_IRQ_MASK || addr == CTL_STATUS) + val |= host->sdcard_irq_setbit_mask; + iowrite16(val & 0xffff, host->ctl + (addr << host->bus_shift)); iowrite16(val >> 16, host->ctl + ((addr + 2) << host->bus_shift)); } diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c @@ -171,6 +171,18 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host) } } +static void tmio_mmc_hw_reset(struct mmc_host *mmc) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + + host->reset(host); + + tmio_mmc_abort_dma(host); + + if (host->hw_reset) + host->hw_reset(host); +} + static void tmio_mmc_reset_work(struct work_struct *work) { struct tmio_mmc_host *host = container_of(work, struct tmio_mmc_host, @@ -209,12 +221,11 @@ static void tmio_mmc_reset_work(struct work_struct *work) spin_unlock_irqrestore(&host->lock, flags); - host->reset(host); + tmio_mmc_hw_reset(host->mmc); /* Ready for new calls */ host->mrq = NULL; - tmio_mmc_abort_dma(host); mmc_request_done(host->mmc, mrq); } @@ -696,14 +707,6 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host, return 0; } -static void tmio_mmc_hw_reset(struct mmc_host *mmc) -{ - struct tmio_mmc_host *host = mmc_priv(mmc); - - if (host->hw_reset) - host->hw_reset(host); -} - static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct tmio_mmc_host *host = mmc_priv(mmc); @@ -734,8 +737,6 @@ static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode) ret = mmc_send_tuning(mmc, opcode, NULL); if (ret == 0) set_bit(i, host->taps); - - usleep_range(1000, 1200); } ret = host->select_tuning(host); @@ -1167,11 +1168,13 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host) if (ret < 0) return ret; - if (pdata->flags & TMIO_MMC_USE_GPIO_CD) { - ret = mmc_gpio_request_cd(mmc, pdata->cd_gpio, 0); - if (ret) - return ret; - } + /* + * Look for a card detect GPIO, if it fails with anything + * else than a probe deferral, just live without it. + */ + ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL); + if (ret == -EPROBE_DEFER) + return ret; mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities; mmc->caps2 |= pdata->capabilities2; @@ -1228,7 +1231,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host) _host->sdio_irq_mask = TMIO_SDIO_MASK_ALL; _host->set_clock(_host, 0); - _host->reset(_host); + tmio_mmc_hw_reset(mmc); _host->sdcard_irq_mask = sd_ctrl_read16_and_16_as_32(_host, CTL_IRQ_MASK); tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL); @@ -1328,8 +1331,8 @@ int tmio_mmc_host_runtime_resume(struct device *dev) { struct tmio_mmc_host *host = dev_get_drvdata(dev); - host->reset(host); tmio_mmc_clk_enable(host); + tmio_mmc_hw_reset(host->mmc); if (host->clk_cache) host->set_clock(host, host->clk_cache); diff --git a/include/linux/alcor_pci.h b/include/linux/alcor_pci.h @@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 Oleksij Rempel <linux@rempel-privat.de> + * + * Driver for Alcor Micro AU6601 and AU6621 controllers + */ + +#ifndef __ALCOR_PCI_H +#define __ALCOR_PCI_H + +#define ALCOR_SD_CARD 0 +#define ALCOR_MS_CARD 1 + +#define DRV_NAME_ALCOR_PCI_SDMMC "alcor_sdmmc" +#define DRV_NAME_ALCOR_PCI_MS "alcor_ms" + +#define PCI_ID_ALCOR_MICRO 0x1AEA +#define PCI_ID_AU6601 0x6601 +#define PCI_ID_AU6621 0x6621 + +#define MHZ_TO_HZ(freq) ((freq) * 1000 * 1000) + +#define AU6601_BASE_CLOCK 31000000 +#define AU6601_MIN_CLOCK 150000 +#define AU6601_MAX_CLOCK 208000000 +#define AU6601_MAX_DMA_SEGMENTS 1 +#define AU6601_MAX_PIO_SEGMENTS 1 +#define AU6601_MAX_DMA_BLOCK_SIZE 0x1000 +#define AU6601_MAX_PIO_BLOCK_SIZE 0x200 +#define AU6601_MAX_DMA_BLOCKS 1 +#define AU6601_DMA_LOCAL_SEGMENTS 1 + +/* registers spotter by reverse engineering but still + * with unknown functionality: + * 0x10 - ADMA phy address. AU6621 only? + * 0x51 - LED ctrl? + * 0x52 - unknown + * 0x61 - LED related? Always toggled BIT0 + * 0x63 - Same as 0x61? + * 0x77 - unknown + */ + +/* SDMA phy address. Higher then 0x0800.0000? + * The au6601 and au6621 have different DMA engines with different issues. One + * For example au6621 engine is triggered by addr change. No other interaction + * is needed. This means, if we get two buffers with same address, then engine + * will stall. + */ +#define AU6601_REG_SDMA_ADDR 0x00 +#define AU6601_SDMA_MASK 0xffffffff + +#define AU6601_DMA_BOUNDARY 0x05 +#define AU6621_DMA_PAGE_CNT 0x05 +/* PIO */ +#define AU6601_REG_BUFFER 0x08 +/* ADMA ctrl? AU6621 only. */ +#define AU6621_DMA_CTRL 0x0c +#define AU6621_DMA_ENABLE BIT(0) +/* CMD index */ +#define AU6601_REG_CMD_OPCODE 0x23 +/* CMD parametr */ +#define AU6601_REG_CMD_ARG 0x24 +/* CMD response 4x4 Bytes */ +#define AU6601_REG_CMD_RSP0 0x30 +#define AU6601_REG_CMD_RSP1 0x34 +#define AU6601_REG_CMD_RSP2 0x38 +#define AU6601_REG_CMD_RSP3 0x3C +/* default timeout set to 125: 125 * 40ms = 5 sec + * how exactly it is calculated? + */ +#define AU6601_TIME_OUT_CTRL 0x69 +/* Block size for SDMA or PIO */ +#define AU6601_REG_BLOCK_SIZE 0x6c +/* Some power related reg, used together with AU6601_OUTPUT_ENABLE */ +#define AU6601_POWER_CONTROL 0x70 + +/* PLL ctrl */ +#define AU6601_CLK_SELECT 0x72 +#define AU6601_CLK_OVER_CLK 0x80 +#define AU6601_CLK_384_MHZ 0x30 +#define AU6601_CLK_125_MHZ 0x20 +#define AU6601_CLK_48_MHZ 0x10 +#define AU6601_CLK_EXT_PLL 0x04 +#define AU6601_CLK_X2_MODE 0x02 +#define AU6601_CLK_ENABLE 0x01 +#define AU6601_CLK_31_25_MHZ 0x00 + +#define AU6601_CLK_DIVIDER 0x73 + +#define AU6601_INTERFACE_MODE_CTRL 0x74 +#define AU6601_DLINK_MODE 0x80 +#define AU6601_INTERRUPT_DELAY_TIME 0x40 +#define AU6601_SIGNAL_REQ_CTRL 0x30 +#define AU6601_MS_CARD_WP BIT(3) +#define AU6601_SD_CARD_WP BIT(0) + +/* same register values are used for: + * - AU6601_OUTPUT_ENABLE + * - AU6601_POWER_CONTROL + */ +#define AU6601_ACTIVE_CTRL 0x75 +#define AU6601_XD_CARD BIT(4) +/* AU6601_MS_CARD_ACTIVE - will cativate MS card section? */ +#define AU6601_MS_CARD BIT(3) +#define AU6601_SD_CARD BIT(0) + +/* card slot state. It should automatically detect type of + * the card + */ +#define AU6601_DETECT_STATUS 0x76 +#define AU6601_DETECT_EN BIT(7) +#define AU6601_MS_DETECTED BIT(3) +#define AU6601_SD_DETECTED BIT(0) +#define AU6601_DETECT_STATUS_M 0xf + +#define AU6601_REG_SW_RESET 0x79 +#define AU6601_BUF_CTRL_RESET BIT(7) +#define AU6601_RESET_DATA BIT(3) +#define AU6601_RESET_CMD BIT(0) + +#define AU6601_OUTPUT_ENABLE 0x7a + +#define AU6601_PAD_DRIVE0 0x7b +#define AU6601_PAD_DRIVE1 0x7c +#define AU6601_PAD_DRIVE2 0x7d +/* read EEPROM? */ +#define AU6601_FUNCTION 0x7f + +#define AU6601_CMD_XFER_CTRL 0x81 +#define AU6601_CMD_17_BYTE_CRC 0xc0 +#define AU6601_CMD_6_BYTE_WO_CRC 0x80 +#define AU6601_CMD_6_BYTE_CRC 0x40 +#define AU6601_CMD_START_XFER 0x20 +#define AU6601_CMD_STOP_WAIT_RDY 0x10 +#define AU6601_CMD_NO_RESP 0x00 + +#define AU6601_REG_BUS_CTRL 0x82 +#define AU6601_BUS_WIDTH_4BIT 0x20 +#define AU6601_BUS_WIDTH_8BIT 0x10 +#define AU6601_BUS_WIDTH_1BIT 0x00 + +#define AU6601_DATA_XFER_CTRL 0x83 +#define AU6601_DATA_WRITE BIT(7) +#define AU6601_DATA_DMA_MODE BIT(6) +#define AU6601_DATA_START_XFER BIT(0) + +#define AU6601_DATA_PIN_STATE 0x84 +#define AU6601_BUS_STAT_CMD BIT(15) +/* BIT(4) - BIT(7) are permanently 1. + * May be reserved or not attached DAT4-DAT7 + */ +#define AU6601_BUS_STAT_DAT3 BIT(3) +#define AU6601_BUS_STAT_DAT2 BIT(2) +#define AU6601_BUS_STAT_DAT1 BIT(1) +#define AU6601_BUS_STAT_DAT0 BIT(0) +#define AU6601_BUS_STAT_DAT_MASK 0xf + +#define AU6601_OPT 0x85 +#define AU6601_OPT_CMD_LINE_LEVEL 0x80 +#define AU6601_OPT_NCRC_16_CLK BIT(4) +#define AU6601_OPT_CMD_NWT BIT(3) +#define AU6601_OPT_STOP_CLK BIT(2) +#define AU6601_OPT_DDR_MODE BIT(1) +#define AU6601_OPT_SD_18V BIT(0) + +#define AU6601_CLK_DELAY 0x86 +#define AU6601_CLK_DATA_POSITIVE_EDGE 0x80 +#define AU6601_CLK_CMD_POSITIVE_EDGE 0x40 +#define AU6601_CLK_POSITIVE_EDGE_ALL (AU6601_CLK_CMD_POSITIVE_EDGE \ + | AU6601_CLK_DATA_POSITIVE_EDGE) + + +#define AU6601_REG_INT_STATUS 0x90 +#define AU6601_REG_INT_ENABLE 0x94 +#define AU6601_INT_DATA_END_BIT_ERR BIT(22) +#define AU6601_INT_DATA_CRC_ERR BIT(21) +#define AU6601_INT_DATA_TIMEOUT_ERR BIT(20) +#define AU6601_INT_CMD_INDEX_ERR BIT(19) +#define AU6601_INT_CMD_END_BIT_ERR BIT(18) +#define AU6601_INT_CMD_CRC_ERR BIT(17) +#define AU6601_INT_CMD_TIMEOUT_ERR BIT(16) +#define AU6601_INT_ERROR BIT(15) +#define AU6601_INT_OVER_CURRENT_ERR BIT(8) +#define AU6601_INT_CARD_INSERT BIT(7) +#define AU6601_INT_CARD_REMOVE BIT(6) +#define AU6601_INT_READ_BUF_RDY BIT(5) +#define AU6601_INT_WRITE_BUF_RDY BIT(4) +#define AU6601_INT_DMA_END BIT(3) +#define AU6601_INT_DATA_END BIT(1) +#define AU6601_INT_CMD_END BIT(0) + +#define AU6601_INT_NORMAL_MASK 0x00007FFF +#define AU6601_INT_ERROR_MASK 0xFFFF8000 + +#define AU6601_INT_CMD_MASK (AU6601_INT_CMD_END | \ + AU6601_INT_CMD_TIMEOUT_ERR | AU6601_INT_CMD_CRC_ERR | \ + AU6601_INT_CMD_END_BIT_ERR | AU6601_INT_CMD_INDEX_ERR) +#define AU6601_INT_DATA_MASK (AU6601_INT_DATA_END | AU6601_INT_DMA_END | \ + AU6601_INT_READ_BUF_RDY | AU6601_INT_WRITE_BUF_RDY | \ + AU6601_INT_DATA_TIMEOUT_ERR | AU6601_INT_DATA_CRC_ERR | \ + AU6601_INT_DATA_END_BIT_ERR) +#define AU6601_INT_ALL_MASK ((u32)-1) + +/* MS_CARD mode registers */ + +#define AU6601_MS_STATUS 0xa0 + +#define AU6601_MS_BUS_MODE_CTRL 0xa1 +#define AU6601_MS_BUS_8BIT_MODE 0x03 +#define AU6601_MS_BUS_4BIT_MODE 0x01 +#define AU6601_MS_BUS_1BIT_MODE 0x00 + +#define AU6601_MS_TPC_CMD 0xa2 +#define AU6601_MS_TPC_READ_PAGE_DATA 0x02 +#define AU6601_MS_TPC_READ_REG 0x04 +#define AU6601_MS_TPC_GET_INT 0x07 +#define AU6601_MS_TPC_WRITE_PAGE_DATA 0x0D +#define AU6601_MS_TPC_WRITE_REG 0x0B +#define AU6601_MS_TPC_SET_RW_REG_ADRS 0x08 +#define AU6601_MS_TPC_SET_CMD 0x0E +#define AU6601_MS_TPC_EX_SET_CMD 0x09 +#define AU6601_MS_TPC_READ_SHORT_DATA 0x03 +#define AU6601_MS_TPC_WRITE_SHORT_DATA 0x0C + +#define AU6601_MS_TRANSFER_MODE 0xa3 +#define AU6601_MS_XFER_INT_TIMEOUT_CHK BIT(2) +#define AU6601_MS_XFER_DMA_ENABLE BIT(1) +#define AU6601_MS_XFER_START BIT(0) + +#define AU6601_MS_DATA_PIN_STATE 0xa4 + +#define AU6601_MS_INT_STATUS 0xb0 +#define AU6601_MS_INT_ENABLE 0xb4 +#define AU6601_MS_INT_OVER_CURRENT_ERROR BIT(23) +#define AU6601_MS_INT_DATA_CRC_ERROR BIT(21) +#define AU6601_MS_INT_INT_TIMEOUT BIT(20) +#define AU6601_MS_INT_INT_RESP_ERROR BIT(19) +#define AU6601_MS_INT_CED_ERROR BIT(18) +#define AU6601_MS_INT_TPC_TIMEOUT BIT(16) +#define AU6601_MS_INT_ERROR BIT(15) +#define AU6601_MS_INT_CARD_INSERT BIT(7) +#define AU6601_MS_INT_CARD_REMOVE BIT(6) +#define AU6601_MS_INT_BUF_READ_RDY BIT(5) +#define AU6601_MS_INT_BUF_WRITE_RDY BIT(4) +#define AU6601_MS_INT_DMA_END BIT(3) +#define AU6601_MS_INT_TPC_END BIT(1) + +#define AU6601_MS_INT_DATA_MASK 0x00000038 +#define AU6601_MS_INT_TPC_MASK 0x003d8002 +#define AU6601_MS_INT_TPC_ERROR 0x003d0000 + +#define ALCOR_PCIE_LINK_CTRL_OFFSET 0x10 +#define ALCOR_PCIE_LINK_CAP_OFFSET 0x0c +#define ALCOR_CAP_START_OFFSET 0x34 + +struct alcor_dev_cfg { + u8 dma; +}; + +struct alcor_pci_priv { + struct pci_dev *pdev; + struct pci_dev *parent_pdev; + struct device *dev; + void __iomem *iobase; + unsigned int irq; + + unsigned long id; /* idr id */ + + struct alcor_dev_cfg *cfg; + + /* PCI ASPM related vars */ + int pdev_cap_off; + u8 pdev_aspm_cap; + int parent_cap_off; + u8 parent_aspm_cap; + u8 ext_config_dev_aspm; +}; + +void alcor_write8(struct alcor_pci_priv *priv, u8 val, unsigned int addr); +void alcor_write16(struct alcor_pci_priv *priv, u16 val, unsigned int addr); +void alcor_write32(struct alcor_pci_priv *priv, u32 val, unsigned int addr); +void alcor_write32be(struct alcor_pci_priv *priv, u32 val, unsigned int addr); +u8 alcor_read8(struct alcor_pci_priv *priv, unsigned int addr); +u32 alcor_read32(struct alcor_pci_priv *priv, unsigned int addr); +u32 alcor_read32be(struct alcor_pci_priv *priv, unsigned int addr); +#endif diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h @@ -54,12 +54,8 @@ * idle before writing to some registers. */ #define TMIO_MMC_HAS_IDLE_WAIT BIT(4) -/* - * A GPIO is used for card hotplug detection. We need an extra flag for this, - * because 0 is a valid GPIO number too, and requiring users to specify - * cd_gpio < 0 to disable GPIO hotplug would break backwards compatibility. - */ -#define TMIO_MMC_USE_GPIO_CD BIT(5) + +/* BIT(5) is unused */ /* * Some controllers have CMD12 automatically @@ -104,7 +100,6 @@ struct tmio_mmc_data { unsigned long capabilities2; unsigned long flags; u32 ocr_mask; /* available voltages */ - unsigned int cd_gpio; int alignment_shift; dma_addr_t dma_rx_offset; unsigned int max_blk_count; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h @@ -147,6 +147,9 @@ struct mmc_host_ops { /* Prepare HS400 target operating frequency depending host driver */ int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios); + /* Prepare switch to DDR during the HS400 init sequence */ + int (*hs400_prepare_ddr)(struct mmc_host *host); + /* Prepare for switching from HS400 to HS200 */ void (*hs400_downgrade)(struct mmc_host *host); @@ -331,7 +334,7 @@ struct mmc_host { #define MMC_CAP_UHS (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | \ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | \ MMC_CAP_UHS_DDR50) -/* (1 << 21) is free for reuse */ +#define MMC_CAP_SYNC_RUNTIME_PM (1 << 21) /* Synced runtime PM suspends. */ #define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */ #define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */ #define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */ diff --git a/include/linux/mmc/slot-gpio.h b/include/linux/mmc/slot-gpio.h @@ -17,12 +17,7 @@ struct mmc_host; int mmc_gpio_get_ro(struct mmc_host *host); -int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio); - int mmc_gpio_get_cd(struct mmc_host *host); -int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, - unsigned int debounce); - int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, unsigned int idx, bool override_active_level, unsigned int debounce, bool *gpio_invert); diff --git a/include/linux/platform_data/mmc-esdhc-imx.h b/include/linux/platform_data/mmc-esdhc-imx.h @@ -30,15 +30,11 @@ enum cd_types { * * ESDHC_WP(CD)_CONTROLLER type is not available on i.MX25/35. * - * @wp_gpio: gpio for write_protect - * @cd_gpio: gpio for card_detect interrupt * @wp_type: type of write_protect method (see wp_types enum above) * @cd_type: type of card_detect method (see cd_types enum above) */ struct esdhc_platform_data { - unsigned int wp_gpio; - unsigned int cd_gpio; enum wp_types wp_type; enum cd_types cd_type; int max_bus_width; diff --git a/include/linux/platform_data/mmc-pxamci.h b/include/linux/platform_data/mmc-pxamci.h @@ -15,11 +15,7 @@ struct pxamci_platform_data { int (*get_ro)(struct device *); int (*setpower)(struct device *, unsigned int); void (*exit)(struct device *, void *); - int gpio_card_detect; /* gpio detecting card insertion */ - int gpio_card_ro; /* gpio detecting read only toggle */ bool gpio_card_ro_invert; /* gpio ro is inverted */ - int gpio_power; /* gpio powering up MMC bus */ - bool gpio_power_invert; /* gpio power is inverted */ }; extern void pxa_set_mci_info(struct pxamci_platform_data *info); diff --git a/include/linux/platform_data/mmc-s3cmci.h b/include/linux/platform_data/mmc-s3cmci.h @@ -7,7 +7,6 @@ * @no_wprotect: Set this to indicate there is no write-protect switch. * @no_detect: Set this if there is no detect switch. * @wprotect_invert: Invert the default sense of the write protect switch. - * @detect_invert: Invert the default sense of the write protect switch. * @use_dma: Set to allow the use of DMA. * @gpio_detect: GPIO number for the card detect line. * @gpio_wprotect: GPIO number for the write protect line. @@ -31,11 +30,8 @@ struct s3c24xx_mci_pdata { unsigned int no_wprotect:1; unsigned int no_detect:1; unsigned int wprotect_invert:1; - unsigned int detect_invert:1; /* set => detect active high */ unsigned int use_dma:1; - unsigned int gpio_detect; - unsigned int gpio_wprotect; unsigned long ocr_avail; void (*set_power)(unsigned char power_mode, unsigned short vdd); diff --git a/include/linux/spi/mmc_spi.h b/include/linux/spi/mmc_spi.h @@ -8,11 +8,6 @@ struct device; struct mmc_host; -#define MMC_SPI_USE_CD_GPIO (1 << 0) -#define MMC_SPI_USE_RO_GPIO (1 << 1) -#define MMC_SPI_CD_GPIO_ACTIVE_LOW (1 << 2) -#define MMC_SPI_RO_GPIO_ACTIVE_LOW (1 << 3) - /* Put this in platform_data of a device being used to manage an MMC/SD * card slot. (Modeled after PXA mmc glue; see that for usage examples.) * @@ -27,16 +22,6 @@ struct mmc_spi_platform_data { void *); void (*exit)(struct device *, void *); - /* - * Card Detect and Read Only GPIOs. To enable debouncing on the card - * detect GPIO, set the cd_debounce to the debounce time in - * microseconds. - */ - unsigned int flags; - unsigned int cd_gpio; - unsigned int cd_debounce; - unsigned int ro_gpio; - /* Capabilities to pass into mmc core (e.g. MMC_CAP_NEEDS_POLL). */ unsigned long caps; unsigned long caps2; diff --git a/include/uapi/linux/mmc/ioctl.h b/include/uapi/linux/mmc/ioctl.h @@ -5,7 +5,10 @@ #include <linux/types.h> struct mmc_ioc_cmd { - /* Implies direction of data. true = write, false = read */ + /* + * Direction of data: nonzero = write, zero = read. + * Bit 31 selects 'Reliable Write' for RPMB. + */ int write_flag; /* Application-specific command. true = precede with CMD55 */