MMC core:
- Add support for marking HPI as broken through devicetree - Enable runtime PM management of host devices - Remove the ->enable|disable() callbacks - Restructure code and cleanups - Refreshed some of the MMC sections in MAINTAINERS MMC host: - dw_mmc: HS400 mode support - dw_mmc: Add the cmd11 timer to detect a timeout - dw_mmc: Endian agnostic IO accessors - dw_mmc: Bugfixes - sh_mmcif: Add exclusion between cmd and interrupt - omap_hsmmc: Hibernation support - omap_hsmmc: Rework and simplify cover/card detect - omap_hsmmc: Stop using ->enable|disable() callbacks - atmel-mci: Endian agnostic IO - sunxi: Enable MMC_CAP_SDIO_IRQ - sdhci-st: Add support for the stih407 family silicon - sdhci-st: UHS card support in SDR104 mode - sdhci-st: HS200 mode support - sdhci-esdhc-imx: Use common mmc DT parser - sdhci-of-arasan: Use common mmc DT parser - sdhci-iproc: Add new driver for Broadcom IPROC SDHCI controller - sdhci-tegra: Convert to GPIO descriptors - sdhci-tegra: Optmize write_w path for tegra114 and later - sdhci-sirf: Update tuning procedure - sdhci: Fix card presence logic - sdhci: Cleanups and consolidation -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVK362AAoJEP4mhCVzWIwpcRUP/RrapvmsBJ9eJy4XJXpC/D+C ZfLcyA/aVOr/U7z8cGfBDVxZLi8j7Nqx1wSA7dahSXTPqsRcQEbxAdYdjJ0jI9Ew XrNh2bYBV0AcGP3YdCcHDqZxVPetcB3HbgsE5WgUcnmwmuD4TXP62TDPk1jUCaya KfLW9EvGLICu+W7T/Q+Gv/Q4+zo7rfg0xZZEXkcSKN/zEssWG0wngkv7Him3eKn+ 4SveBzog3dOjS6p/S9KjT8qd89e13TsNR0DPuWCHyT/rj/KY+BqEm2qCueqaVOQ/ rPM1vsjapibtPif+jSorAVoW5+hUe/LnhnLXSH0DGXF6WFOAJqYtu9IqVva6UF4x fj8p7XspR4pjEDgJf8jjylhvdglX6/AzXv6XHL6aG5TKxBiAJsSeVe2waQ8+Mk7M bUp321exr27L5gBXzE09jFDR9IoJ+ungEKeoRNsRVHC6Eg1NkAhVP7d7TpW6W9tu V/Ncn3t/OeplKpD5JAya1DOGgLKD+K5x3e4HIp4ZDP+72arue8tnxZFSJr9vkkek u6x+lgZ7r1P1s66uKbFFZluZhVVSbWYKPMTQmFp5XeP36OgOIbAkaChKCxiwo2Ar lgOlB3ngNfl20swEAdrP4uGZjUHerrp8KIYIxVwDrf7Ec5wFe56hyFT7O9H8t1l1 Z28m9KkRWGjJ1k61pzw9 =mKlf -----END PGP SIGNATURE----- Merge tag 'mmc-v4.1' of git://git.linaro.org/people/ulf.hansson/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Add support for marking HPI as broken through devicetree - Enable runtime PM management of host devices - Remove the ->enable|disable() callbacks - Restructure code and cleanups - Refreshed some of the MMC sections in MAINTAINERS MMC host: - dw_mmc: HS400 mode support - dw_mmc: Add the cmd11 timer to detect a timeout - dw_mmc: Endian agnostic IO accessors - dw_mmc: Bugfixes - sh_mmcif: Add exclusion between cmd and interrupt - omap_hsmmc: Hibernation support - omap_hsmmc: Rework and simplify cover/card detect - omap_hsmmc: Stop using ->enable|disable() callbacks - atmel-mci: Endian agnostic IO - sunxi: Enable MMC_CAP_SDIO_IRQ - sdhci-st: Add support for the stih407 family silicon - sdhci-st: UHS card support in SDR104 mode - sdhci-st: HS200 mode support - sdhci-esdhc-imx: Use common mmc DT parser - sdhci-of-arasan: Use common mmc DT parser - sdhci-iproc: Add new driver for Broadcom IPROC SDHCI controller - sdhci-tegra: Convert to GPIO descriptors - sdhci-tegra: Optmize write_w path for tegra114 and later - sdhci-sirf: Update tuning procedure - sdhci: Fix card presence logic - sdhci: Cleanups and consolidation" * tag 'mmc-v4.1' of git://git.linaro.org/people/ulf.hansson/mmc: (79 commits) mmc: sdhci-st: Update ST SDHCI binding documentation. mmc: sdhci-st: Update the quirks for this controller. mmc: sdhci-st: Add sdhci_st_set_uhs_signaling function. mmc: sdhci-st: Add st_mmcss_cconfig function to configure mmcss glue registers. mmc: sdhci-st: Add delay management functions for top registers (eMMC). mmc: sdhci-st: Add support for de-asserting reset signal and top regs resource mmc: sdhci-st: Add macros for register offsets and bitfields for mmcss glue regs mmc: sdhci-esdhc-imx: Call mmc_of_parse() mmc: dw_mmc: Add locking around cmd11 timer mmc: dw_mmc: Add a return in an unexpected cmd11 timeout mmc: dw_mmc: Increase cmd11 timeout to 500ms mmc: dw_mmc: fix fifo ordering in big endian mmc: dw_mmc: change idmac descriptor files to __le32 mmc: dw_mmc: make IO accessors endian agnostic mmc: core: Convert the error field in struct mmc_command|data into an int mmc: sdhci-of-arasan: Call OF parsing for MMC mmc: sdhci-pci: fix 64 BIT DMA quirks for rtsx mmc: Add support for marking hpi as broken through devicetree mmc: sdhci-tegra: convert to use GPIO descriptors mmc: omap_hsmmc: use generic slot-gpio isr to manage card detect pin ...
This commit is contained in:
commit
8d9095c667
4
CREDITS
4
CREDITS
|
@ -187,6 +187,10 @@ N: Krishna Balasubramanian
|
|||
E: balasub@cis.ohio-state.edu
|
||||
D: Wrote SYS V IPC (part of standard kernel since 0.99.10)
|
||||
|
||||
N: Chris Ball
|
||||
E: chris@printf.net
|
||||
D: Former maintainer of the MMC/SD/SDIO subsystem.
|
||||
|
||||
N: Dario Ballabio
|
||||
E: ballabio_dario@emc.com
|
||||
E: dario.ballabio@tiscalinet.it
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
Broadcom IPROC SDHCI controller
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties that represent the IPROC SDHCI controller.
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "brcm,sdhci-iproc-cygnus".
|
||||
- clocks : The clock feeding the SDHCI controller.
|
||||
|
||||
Optional properties:
|
||||
- sdhci,auto-cmd12: specifies that controller should use auto CMD12.
|
||||
|
||||
Example:
|
||||
|
||||
sdhci0: sdhci@0x18041000 {
|
||||
compatible = "brcm,sdhci-iproc-cygnus";
|
||||
reg = <0x18041000 0x100>;
|
||||
interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&lcpll0_clks BCM_CYGNUS_LCPLL0_SDIO_CLK>;
|
||||
bus-width = <4>;
|
||||
sdhci,auto-cmd12;
|
||||
no-1-8-v;
|
||||
};
|
|
@ -36,6 +36,8 @@ Required Properties:
|
|||
in transmit mode and CIU clock phase shift value in receive mode for double
|
||||
data rate mode operation. Refer notes below for the order of the cells and the
|
||||
valid values.
|
||||
* samsung,dw-mshc-hs400-timing: Specifies the value of CIU TX and RX clock phase
|
||||
shift value for hs400 mode operation.
|
||||
|
||||
Notes for the sdr-timing and ddr-timing values:
|
||||
|
||||
|
@ -50,6 +52,9 @@ Required Properties:
|
|||
- if CIU clock divider value is 0 (that is divide by 1), both tx and rx
|
||||
phase shift clocks should be 0.
|
||||
|
||||
* samsung,read-strobe-delay: RCLK (Data strobe) delay to control HS400 mode
|
||||
(Latency value for delay line in Read path)
|
||||
|
||||
Required properties for a slot (Deprecated - Recommend to use one slot per host):
|
||||
|
||||
* gpios: specifies a list of gpios used for command, clock and data bus. The
|
||||
|
@ -82,5 +87,7 @@ Example:
|
|||
samsung,dw-mshc-ciu-div = <3>;
|
||||
samsung,dw-mshc-sdr-timing = <2 3>;
|
||||
samsung,dw-mshc-ddr-timing = <1 2>;
|
||||
samsung,dw-mshc-hs400-timing = <0 2>;
|
||||
samsung,read-strobe-delay = <90>;
|
||||
bus-width = <8>;
|
||||
};
|
||||
|
|
|
@ -17,6 +17,10 @@ Optional properties:
|
|||
to select a proper data sampling window in case the clock quality is not good
|
||||
due to signal path is too long on the board. Please refer to eSDHC/uSDHC
|
||||
chapter, DLL (Delay Line) section in RM for details.
|
||||
- voltage-ranges : Specify the voltage range in case there are software
|
||||
transparent level shifters on the outputs of the controller. Two cells are
|
||||
required, first cell specifies minimum slot voltage (mV), second cell
|
||||
specifies maximum slot voltage (mV). Several ranges could be specified.
|
||||
|
||||
Examples:
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
mmc-card / eMMC bindings
|
||||
------------------------
|
||||
|
||||
This documents describes the devicetree bindings for a mmc-host controller
|
||||
child node describing a mmc-card / an eMMC, see "Use of Function subnodes"
|
||||
in mmc.txt
|
||||
|
||||
Required properties:
|
||||
-compatible : Must be "mmc-card"
|
||||
-reg : Must be <0>
|
||||
|
||||
Optional properties:
|
||||
-broken-hpi : Use this to indicate that the mmc-card has a broken hpi
|
||||
implementation, and that hpi should not be used
|
||||
|
||||
Example:
|
||||
|
||||
&mmc2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&mmc2_pins_a>;
|
||||
vmmc-supply = <®_vcc3v3>;
|
||||
bus-width = <8>;
|
||||
non-removable;
|
||||
status = "okay";
|
||||
|
||||
mmccard: mmccard@0 {
|
||||
reg = <0>;
|
||||
compatible = "mmc-card";
|
||||
broken-hpi;
|
||||
};
|
||||
};
|
|
@ -5,20 +5,62 @@ Documentation/devicetree/bindings/mmc/mmc.txt and the properties
|
|||
used by the sdhci-st driver.
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "st,sdhci"
|
||||
- clock-names : Should be "mmc"
|
||||
See: Documentation/devicetree/bindings/resource-names.txt
|
||||
- clocks : Phandle of the clock used by the sdhci controler
|
||||
See: Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
- compatible: Must be "st,sdhci" and it can be compatible to "st,sdhci-stih407"
|
||||
to set the internal glue logic used for configuring the MMC
|
||||
subsystem (mmcss) inside the FlashSS (available in STiH407 SoC
|
||||
family).
|
||||
|
||||
- clock-names: Should be "mmc".
|
||||
See: Documentation/devicetree/bindings/resource-names.txt
|
||||
- clocks: Phandle to the clock.
|
||||
See: Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
- interrupts: One mmc interrupt should be described here.
|
||||
- interrupt-names: Should be "mmcirq".
|
||||
|
||||
- pinctrl-names: A pinctrl state names "default" must be defined.
|
||||
- pinctrl-0: Phandle referencing pin configuration of the sd/emmc controller.
|
||||
See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
|
||||
|
||||
- reg: This must provide the host controller base address and it can also
|
||||
contain the FlashSS Top register for TX/RX delay used by the driver
|
||||
to configure DLL inside the flashSS, if so reg-names must also be
|
||||
specified.
|
||||
|
||||
Optional properties:
|
||||
- non-removable: non-removable slot
|
||||
See: Documentation/devicetree/bindings/mmc/mmc.txt
|
||||
- bus-width: Number of data lines
|
||||
See: Documentation/devicetree/bindings/mmc/mmc.txt
|
||||
- reg-names: Should be "mmc" and "top-mmc-delay". "top-mmc-delay" is optional
|
||||
for eMMC on stih407 family silicon to configure DLL inside FlashSS.
|
||||
|
||||
- non-removable: Non-removable slot. Also used for configuring mmcss in STiH407 SoC
|
||||
family.
|
||||
See: Documentation/devicetree/bindings/mmc/mmc.txt.
|
||||
|
||||
- bus-width: Number of data lines.
|
||||
See: Documentation/devicetree/bindings/mmc/mmc.txt.
|
||||
|
||||
- max-frequency: Can be 200MHz, 100Mz or 50MHz (default) and used for
|
||||
configuring the CCONFIG3 in the mmcss.
|
||||
See: Documentation/devicetree/bindings/mmc/mmc.txt.
|
||||
|
||||
- resets: Phandle and reset specifier pair to softreset line of HC IP.
|
||||
See: Documentation/devicetree/bindings/reset/reset.txt
|
||||
|
||||
- vqmmc-supply: Phandle to the regulator dt node, mentioned as the vcc/vdd
|
||||
supply in eMMC/SD specs.
|
||||
|
||||
- sd-uhs--sdr50: To enable the SDR50 in the mmcss.
|
||||
See: Documentation/devicetree/bindings/mmc/mmc.txt.
|
||||
|
||||
- sd-uhs-sdr104: To enable the SDR104 in the mmcss.
|
||||
See: Documentation/devicetree/bindings/mmc/mmc.txt.
|
||||
|
||||
- sd-uhs-ddr50: To enable the DDR50 in the mmcss.
|
||||
See: Documentation/devicetree/bindings/mmc/mmc.txt.
|
||||
|
||||
Example:
|
||||
|
||||
/* Example stih416e eMMC configuration */
|
||||
|
||||
mmc0: sdhci@fe81e000 {
|
||||
compatible = "st,sdhci";
|
||||
status = "disabled";
|
||||
|
@ -29,5 +71,43 @@ mmc0: sdhci@fe81e000 {
|
|||
pinctrl-0 = <&pinctrl_mmc0>;
|
||||
clock-names = "mmc";
|
||||
clocks = <&clk_s_a1_ls 1>;
|
||||
bus-width = <8>
|
||||
bus-width = <8>
|
||||
|
||||
/* Example SD stih407 family configuration */
|
||||
|
||||
mmc1: sdhci@09080000 {
|
||||
compatible = "st,sdhci-stih407", "st,sdhci";
|
||||
status = "disabled";
|
||||
reg = <0x09080000 0x7ff>;
|
||||
reg-names = "mmc";
|
||||
interrupts = <GIC_SPI 90 IRQ_TYPE_NONE>;
|
||||
interrupt-names = "mmcirq";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_sd1>;
|
||||
clock-names = "mmc";
|
||||
clocks = <&clk_s_c0_flexgen CLK_MMC_1>;
|
||||
resets = <&softreset STIH407_MMC1_SOFTRESET>;
|
||||
bus-width = <4>;
|
||||
};
|
||||
|
||||
/* Example eMMC stih407 family configuration */
|
||||
|
||||
mmc0: sdhci@09060000 {
|
||||
compatible = "st,sdhci-stih407", "st,sdhci";
|
||||
status = "disabled";
|
||||
reg = <0x09060000 0x7ff>, <0x9061008 0x20>;
|
||||
reg-names = "mmc", "top-mmc-delay";
|
||||
interrupts = <GIC_SPI 92 IRQ_TYPE_NONE>;
|
||||
interrupt-names = "mmcirq";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_mmc0>;
|
||||
clock-names = "mmc";
|
||||
clocks = <&clk_s_c0_flexgen CLK_MMC_0>;
|
||||
vqmmc-supply = <&vmmc_reg>;
|
||||
max-frequency = <200000000>;
|
||||
bus-width = <8>;
|
||||
non-removable;
|
||||
sd-uhs-sdr50;
|
||||
sd-uhs-sdr104;
|
||||
sd-uhs-ddr50;
|
||||
};
|
||||
|
|
16
MAINTAINERS
16
MAINTAINERS
|
@ -6566,10 +6566,8 @@ F: drivers/mfd/
|
|||
F: include/linux/mfd/
|
||||
|
||||
MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
|
||||
M: Chris Ball <chris@printf.net>
|
||||
M: Ulf Hansson <ulf.hansson@linaro.org>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
|
||||
T: git git://git.linaro.org/people/ulf.hansson/mmc.git
|
||||
S: Maintained
|
||||
F: drivers/mmc/
|
||||
|
@ -8670,10 +8668,8 @@ S: Maintained
|
|||
F: drivers/mmc/host/sdricoh_cs.c
|
||||
|
||||
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
|
||||
M: Chris Ball <chris@printf.net>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
|
||||
S: Maintained
|
||||
S: Orphan
|
||||
F: drivers/mmc/host/sdhci.*
|
||||
F: drivers/mmc/host/sdhci-pltfm.[ch]
|
||||
|
||||
|
@ -8689,18 +8685,12 @@ F: include/linux/seccomp.h
|
|||
K: \bsecure_computing
|
||||
K: \bTIF_SECCOMP\b
|
||||
|
||||
SECURE DIGITAL HOST CONTROLLER INTERFACE, OPEN FIRMWARE BINDINGS (SDHCI-OF)
|
||||
M: Anton Vorontsov <anton@enomsg.org>
|
||||
L: linuxppc-dev@lists.ozlabs.org
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/mmc/host/sdhci-pltfm.[ch]
|
||||
|
||||
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) SAMSUNG DRIVER
|
||||
M: Ben Dooks <ben-linux@fluff.org>
|
||||
M: Jaehoon Chung <jh80.chung@samsung.com>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/mmc/host/sdhci-s3c.c
|
||||
F: drivers/mmc/host/sdhci-s3c*
|
||||
|
||||
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) ST SPEAR DRIVER
|
||||
M: Viresh Kumar <viresh.linux@gmail.com>
|
||||
|
|
|
@ -150,9 +150,13 @@ static int nop_mmc_set_power(struct device *dev, int power_on, int vdd)
|
|||
static inline void omap_hsmmc_mux(struct omap_hsmmc_platform_data
|
||||
*mmc_controller, int controller_nr)
|
||||
{
|
||||
if (gpio_is_valid(mmc_controller->switch_pin) &&
|
||||
(mmc_controller->switch_pin < OMAP_MAX_GPIO_LINES))
|
||||
omap_mux_init_gpio(mmc_controller->switch_pin,
|
||||
if (gpio_is_valid(mmc_controller->gpio_cd) &&
|
||||
(mmc_controller->gpio_cd < OMAP_MAX_GPIO_LINES))
|
||||
omap_mux_init_gpio(mmc_controller->gpio_cd,
|
||||
OMAP_PIN_INPUT_PULLUP);
|
||||
if (gpio_is_valid(mmc_controller->gpio_cod) &&
|
||||
(mmc_controller->gpio_cod < OMAP_MAX_GPIO_LINES))
|
||||
omap_mux_init_gpio(mmc_controller->gpio_cod,
|
||||
OMAP_PIN_INPUT_PULLUP);
|
||||
if (gpio_is_valid(mmc_controller->gpio_wp) &&
|
||||
(mmc_controller->gpio_wp < OMAP_MAX_GPIO_LINES))
|
||||
|
@ -250,15 +254,20 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
|
|||
mmc->internal_clock = !c->ext_clock;
|
||||
mmc->reg_offset = 0;
|
||||
|
||||
mmc->switch_pin = c->gpio_cd;
|
||||
if (c->cover_only) {
|
||||
/* detect if mobile phone cover removed */
|
||||
mmc->gpio_cd = -EINVAL;
|
||||
mmc->gpio_cod = c->gpio_cd;
|
||||
} else {
|
||||
/* card detect pin on the mmc socket itself */
|
||||
mmc->gpio_cd = c->gpio_cd;
|
||||
mmc->gpio_cod = -EINVAL;
|
||||
}
|
||||
mmc->gpio_wp = c->gpio_wp;
|
||||
|
||||
mmc->remux = c->remux;
|
||||
mmc->init_card = c->init_card;
|
||||
|
||||
if (c->cover_only)
|
||||
mmc->cover = 1;
|
||||
|
||||
if (c->nonremovable)
|
||||
mmc->nonremovable = 1;
|
||||
|
||||
|
@ -358,7 +367,15 @@ void omap_hsmmc_late_init(struct omap2_hsmmc_info *c)
|
|||
if (!mmc_pdata)
|
||||
continue;
|
||||
|
||||
mmc_pdata->switch_pin = c->gpio_cd;
|
||||
if (c->cover_only) {
|
||||
/* detect if mobile phone cover removed */
|
||||
mmc_pdata->gpio_cd = -EINVAL;
|
||||
mmc_pdata->gpio_cod = c->gpio_cd;
|
||||
} else {
|
||||
/* card detect pin on the mmc socket itself */
|
||||
mmc_pdata->gpio_cd = c->gpio_cd;
|
||||
mmc_pdata->gpio_cod = -EINVAL;
|
||||
}
|
||||
mmc_pdata->gpio_wp = c->gpio_wp;
|
||||
|
||||
res = omap_device_register(pdev);
|
||||
|
|
|
@ -897,6 +897,7 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
|
|||
DECLARE_WAITQUEUE(wait, current);
|
||||
unsigned long flags;
|
||||
int stop;
|
||||
bool pm = false;
|
||||
|
||||
might_sleep();
|
||||
|
||||
|
@ -916,15 +917,18 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
|
|||
host->claimed = 1;
|
||||
host->claimer = current;
|
||||
host->claim_cnt += 1;
|
||||
if (host->claim_cnt == 1)
|
||||
pm = true;
|
||||
} else
|
||||
wake_up(&host->wq);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
remove_wait_queue(&host->wq, &wait);
|
||||
if (host->ops->enable && !stop && host->claim_cnt == 1)
|
||||
host->ops->enable(host);
|
||||
|
||||
if (pm)
|
||||
pm_runtime_get_sync(mmc_dev(host));
|
||||
|
||||
return stop;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(__mmc_claim_host);
|
||||
|
||||
/**
|
||||
|
@ -940,9 +944,6 @@ void mmc_release_host(struct mmc_host *host)
|
|||
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
if (host->ops->disable && host->claim_cnt == 1)
|
||||
host->ops->disable(host);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
if (--host->claim_cnt) {
|
||||
/* Release for nested claim */
|
||||
|
@ -952,6 +953,8 @@ void mmc_release_host(struct mmc_host *host)
|
|||
host->claimer = NULL;
|
||||
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));
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_release_host);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
@ -336,6 +337,8 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||
{
|
||||
int err = 0, idx;
|
||||
unsigned int part_size;
|
||||
struct device_node *np;
|
||||
bool broken_hpi = false;
|
||||
|
||||
/* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
|
||||
card->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE];
|
||||
|
@ -349,6 +352,11 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||
}
|
||||
}
|
||||
|
||||
np = mmc_of_find_child_device(card->host, 0);
|
||||
if (np && of_device_is_compatible(np, "mmc-card"))
|
||||
broken_hpi = of_property_read_bool(np, "broken-hpi");
|
||||
of_node_put(np);
|
||||
|
||||
/*
|
||||
* The EXT_CSD format is meant to be forward compatible. As long
|
||||
* as CSD_STRUCTURE does not change, all values for EXT_CSD_REV
|
||||
|
@ -494,7 +502,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||
}
|
||||
|
||||
/* check whether the eMMC card supports HPI */
|
||||
if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
|
||||
if (!broken_hpi && (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1)) {
|
||||
card->ext_csd.hpi = 1;
|
||||
if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2)
|
||||
card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
struct mmc_pwrseq_match {
|
||||
const char *compatible;
|
||||
int (*alloc)(struct mmc_host *host, struct device *dev);
|
||||
struct mmc_pwrseq *(*alloc)(struct mmc_host *host, struct device *dev);
|
||||
};
|
||||
|
||||
static struct mmc_pwrseq_match pwrseq_match[] = {
|
||||
|
@ -52,6 +52,7 @@ int mmc_pwrseq_alloc(struct mmc_host *host)
|
|||
struct platform_device *pdev;
|
||||
struct device_node *np;
|
||||
struct mmc_pwrseq_match *match;
|
||||
struct mmc_pwrseq *pwrseq;
|
||||
int ret = 0;
|
||||
|
||||
np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0);
|
||||
|
@ -70,9 +71,14 @@ int mmc_pwrseq_alloc(struct mmc_host *host)
|
|||
goto err;
|
||||
}
|
||||
|
||||
ret = match->alloc(host, &pdev->dev);
|
||||
if (!ret)
|
||||
dev_info(host->parent, "allocated mmc-pwrseq\n");
|
||||
pwrseq = match->alloc(host, &pdev->dev);
|
||||
if (IS_ERR(pwrseq)) {
|
||||
ret = PTR_ERR(host->pwrseq);
|
||||
goto err;
|
||||
}
|
||||
|
||||
host->pwrseq = pwrseq;
|
||||
dev_info(host->parent, "allocated mmc-pwrseq\n");
|
||||
|
||||
err:
|
||||
of_node_put(np);
|
||||
|
@ -109,4 +115,6 @@ void mmc_pwrseq_free(struct mmc_host *host)
|
|||
|
||||
if (pwrseq && pwrseq->ops && pwrseq->ops->free)
|
||||
pwrseq->ops->free(host);
|
||||
|
||||
host->pwrseq = NULL;
|
||||
}
|
||||
|
|
|
@ -27,8 +27,10 @@ void mmc_pwrseq_post_power_on(struct mmc_host *host);
|
|||
void mmc_pwrseq_power_off(struct mmc_host *host);
|
||||
void mmc_pwrseq_free(struct mmc_host *host);
|
||||
|
||||
int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev);
|
||||
int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev);
|
||||
struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host,
|
||||
struct device *dev);
|
||||
struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host,
|
||||
struct device *dev);
|
||||
|
||||
#else
|
||||
|
||||
|
|
|
@ -49,7 +49,6 @@ static void mmc_pwrseq_emmc_free(struct mmc_host *host)
|
|||
unregister_restart_handler(&pwrseq->reset_nb);
|
||||
gpiod_put(pwrseq->reset_gpio);
|
||||
kfree(pwrseq);
|
||||
host->pwrseq = NULL;
|
||||
}
|
||||
|
||||
static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
|
||||
|
@ -67,14 +66,15 @@ static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this,
|
|||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev)
|
||||
struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host,
|
||||
struct device *dev)
|
||||
{
|
||||
struct mmc_pwrseq_emmc *pwrseq;
|
||||
int ret = 0;
|
||||
|
||||
pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL);
|
||||
if (!pwrseq)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW);
|
||||
if (IS_ERR(pwrseq->reset_gpio)) {
|
||||
|
@ -92,10 +92,9 @@ int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev)
|
|||
register_restart_handler(&pwrseq->reset_nb);
|
||||
|
||||
pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops;
|
||||
host->pwrseq = &pwrseq->pwrseq;
|
||||
|
||||
return 0;
|
||||
return &pwrseq->pwrseq;
|
||||
free:
|
||||
kfree(pwrseq);
|
||||
return ret;
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
|
|
@ -85,7 +85,6 @@ static void mmc_pwrseq_simple_free(struct mmc_host *host)
|
|||
clk_put(pwrseq->ext_clk);
|
||||
|
||||
kfree(pwrseq);
|
||||
host->pwrseq = NULL;
|
||||
}
|
||||
|
||||
static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
|
||||
|
@ -95,7 +94,8 @@ static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
|
|||
.free = mmc_pwrseq_simple_free,
|
||||
};
|
||||
|
||||
int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev)
|
||||
struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host,
|
||||
struct device *dev)
|
||||
{
|
||||
struct mmc_pwrseq_simple *pwrseq;
|
||||
int i, nr_gpios, ret = 0;
|
||||
|
@ -107,7 +107,7 @@ int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev)
|
|||
pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple) + nr_gpios *
|
||||
sizeof(struct gpio_desc *), GFP_KERNEL);
|
||||
if (!pwrseq)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pwrseq->ext_clk = clk_get(dev, "ext_clock");
|
||||
if (IS_ERR(pwrseq->ext_clk) &&
|
||||
|
@ -133,13 +133,12 @@ int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev)
|
|||
|
||||
pwrseq->nr_gpios = nr_gpios;
|
||||
pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops;
|
||||
host->pwrseq = &pwrseq->pwrseq;
|
||||
|
||||
return 0;
|
||||
return &pwrseq->pwrseq;
|
||||
clk_put:
|
||||
if (!IS_ERR(pwrseq->ext_clk))
|
||||
clk_put(pwrseq->ext_clk);
|
||||
free:
|
||||
kfree(pwrseq);
|
||||
return ret;
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
|
|
@ -293,19 +293,22 @@ static int sdio_enable_4bit_bus(struct mmc_card *card)
|
|||
int err;
|
||||
|
||||
if (card->type == MMC_TYPE_SDIO)
|
||||
return sdio_enable_wide(card);
|
||||
|
||||
if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
|
||||
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
|
||||
err = sdio_enable_wide(card);
|
||||
else if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
|
||||
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
|
||||
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
|
||||
if (err)
|
||||
return err;
|
||||
err = sdio_enable_wide(card);
|
||||
if (err <= 0)
|
||||
mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1);
|
||||
} else
|
||||
return 0;
|
||||
|
||||
err = sdio_enable_wide(card);
|
||||
if (err <= 0)
|
||||
mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1);
|
||||
if (err > 0) {
|
||||
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
|
||||
err = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -547,13 +550,8 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card)
|
|||
/*
|
||||
* Switch to wider bus (if supported).
|
||||
*/
|
||||
if (card->host->caps & MMC_CAP_4_BIT_DATA) {
|
||||
if (card->host->caps & MMC_CAP_4_BIT_DATA)
|
||||
err = sdio_enable_4bit_bus(card);
|
||||
if (err > 0) {
|
||||
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the driver strength for the card */
|
||||
sdio_select_driver_type(card);
|
||||
|
@ -803,9 +801,7 @@ try_again:
|
|||
* Switch to wider bus (if supported).
|
||||
*/
|
||||
err = sdio_enable_4bit_bus(card);
|
||||
if (err > 0)
|
||||
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
|
||||
else if (err)
|
||||
if (err)
|
||||
goto remove;
|
||||
}
|
||||
finish:
|
||||
|
@ -983,10 +979,6 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
|||
} else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
|
||||
/* We may have switched to 1-bit mode during suspend */
|
||||
err = sdio_enable_4bit_bus(host->card);
|
||||
if (err > 0) {
|
||||
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!err && host->sdio_irqs) {
|
||||
|
|
|
@ -132,7 +132,7 @@ config MMC_SDHCI_OF_ARASAN
|
|||
config MMC_SDHCI_OF_ESDHC
|
||||
tristate "SDHCI OF support for the Freescale eSDHC controller"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on PPC_OF
|
||||
depends on PPC
|
||||
select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
|
||||
help
|
||||
This selects the Freescale eSDHC controller support.
|
||||
|
@ -144,7 +144,7 @@ config MMC_SDHCI_OF_ESDHC
|
|||
config MMC_SDHCI_OF_HLWD
|
||||
tristate "SDHCI OF support for the Nintendo Wii SDHCI controllers"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on PPC_OF
|
||||
depends on PPC
|
||||
select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
|
||||
help
|
||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||
|
@ -230,7 +230,7 @@ config MMC_SDHCI_PXAV3
|
|||
tristate "Marvell MMP2 SD Host Controller support (PXAV3)"
|
||||
depends on CLKDEV_LOOKUP
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on ARCH_MMP || COMPILE_TEST
|
||||
depends on ARCH_BERLIN || ARCH_MMP || ARCH_MVEBU || COMPILE_TEST
|
||||
default CPU_MMP2
|
||||
help
|
||||
This selects the Marvell(R) PXAV3 SD Host Controller.
|
||||
|
@ -255,6 +255,7 @@ config MMC_SDHCI_PXAV2
|
|||
config MMC_SDHCI_SPEAR
|
||||
tristate "SDHCI support on ST SPEAr platform"
|
||||
depends on MMC_SDHCI && PLAT_SPEAR
|
||||
depends on OF
|
||||
help
|
||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||
often referrered to as the HSMMC block in some of the ST SPEAR range
|
||||
|
@ -307,6 +308,20 @@ config MMC_SDHCI_F_SDH30
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_IPROC
|
||||
tristate "SDHCI platform support for the iProc SD/MMC Controller"
|
||||
depends on ARCH_BCM_IPROC || COMPILE_TEST
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
default ARCH_BCM_IPROC
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
help
|
||||
This selects the iProc SD/MMC controller.
|
||||
|
||||
If you have an IPROC platform with SD or MMC devices,
|
||||
say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_MOXART
|
||||
tristate "MOXART SD/MMC Host Controller support"
|
||||
depends on ARCH_MOXART && MMC
|
||||
|
|
|
@ -71,6 +71,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
|
|||
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
|
||||
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
|
||||
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
|
||||
obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
|
||||
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
|
||||
obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
|
||||
|
||||
|
|
|
@ -135,10 +135,17 @@
|
|||
#define ATMCI_REGS_SIZE 0x100
|
||||
|
||||
/* Register access macros */
|
||||
#define atmci_readl(port,reg) \
|
||||
#ifdef CONFIG_AVR32
|
||||
#define atmci_readl(port, reg) \
|
||||
__raw_readl((port)->regs + reg)
|
||||
#define atmci_writel(port,reg,value) \
|
||||
#define atmci_writel(port, reg, value) \
|
||||
__raw_writel((value), (port)->regs + reg)
|
||||
#else
|
||||
#define atmci_readl(port, reg) \
|
||||
readl_relaxed((port)->regs + reg)
|
||||
#define atmci_writel(port, reg, value) \
|
||||
writel_relaxed((value), (port)->regs + reg)
|
||||
#endif
|
||||
|
||||
/* On AVR chips the Peripheral DMA Controller is not connected to MCI. */
|
||||
#ifdef CONFIG_AVR32
|
||||
|
|
|
@ -40,7 +40,12 @@ struct dw_mci_exynos_priv_data {
|
|||
u8 ciu_div;
|
||||
u32 sdr_timing;
|
||||
u32 ddr_timing;
|
||||
u32 hs400_timing;
|
||||
u32 tuned_sample;
|
||||
u32 cur_speed;
|
||||
u32 dqs_delay;
|
||||
u32 saved_dqs_en;
|
||||
u32 saved_strobe_ctrl;
|
||||
};
|
||||
|
||||
static struct dw_mci_exynos_compatible {
|
||||
|
@ -71,6 +76,21 @@ static struct dw_mci_exynos_compatible {
|
|||
},
|
||||
};
|
||||
|
||||
static inline u8 dw_mci_exynos_get_ciu_div(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
|
||||
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
|
||||
return EXYNOS4412_FIXED_CIU_CLK_DIV;
|
||||
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
|
||||
return EXYNOS4210_FIXED_CIU_CLK_DIV;
|
||||
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||
return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL64)) + 1;
|
||||
else
|
||||
return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL)) + 1;
|
||||
}
|
||||
|
||||
static int dw_mci_exynos_priv_init(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
|
@ -85,6 +105,16 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host)
|
|||
SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT);
|
||||
}
|
||||
|
||||
if (priv->ctrl_type >= DW_MCI_TYPE_EXYNOS5420) {
|
||||
priv->saved_strobe_ctrl = mci_readl(host, HS400_DLINE_CTRL);
|
||||
priv->saved_dqs_en = mci_readl(host, HS400_DQS_EN);
|
||||
priv->saved_dqs_en |= AXI_NON_BLOCKING_WR;
|
||||
mci_writel(host, HS400_DQS_EN, priv->saved_dqs_en);
|
||||
if (!priv->dqs_delay)
|
||||
priv->dqs_delay =
|
||||
DQS_CTRL_GET_RD_DELAY(priv->saved_strobe_ctrl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -97,6 +127,26 @@ static int dw_mci_exynos_setup_clock(struct dw_mci *host)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
|
||||
{
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
u32 clksel;
|
||||
|
||||
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||
clksel = mci_readl(host, CLKSEL64);
|
||||
else
|
||||
clksel = mci_readl(host, CLKSEL);
|
||||
|
||||
clksel = (clksel & ~SDMMC_CLKSEL_TIMING_MASK) | timing;
|
||||
|
||||
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||
mci_writel(host, CLKSEL64, clksel);
|
||||
else
|
||||
mci_writel(host, CLKSEL, clksel);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dw_mci_exynos_suspend(struct device *dev)
|
||||
{
|
||||
|
@ -172,30 +222,38 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
|
|||
}
|
||||
}
|
||||
|
||||
static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||
static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing)
|
||||
{
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
unsigned int wanted = ios->clock;
|
||||
unsigned long actual;
|
||||
u8 div = priv->ciu_div + 1;
|
||||
u32 dqs, strobe;
|
||||
|
||||
if (ios->timing == MMC_TIMING_MMC_DDR52) {
|
||||
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||
mci_writel(host, CLKSEL64, priv->ddr_timing);
|
||||
else
|
||||
mci_writel(host, CLKSEL, priv->ddr_timing);
|
||||
/* Should be double rate for DDR mode */
|
||||
if (ios->bus_width == MMC_BUS_WIDTH_8)
|
||||
wanted <<= 1;
|
||||
/*
|
||||
* Not supported to configure register
|
||||
* related to HS400
|
||||
*/
|
||||
if (priv->ctrl_type < DW_MCI_TYPE_EXYNOS5420)
|
||||
return;
|
||||
|
||||
dqs = priv->saved_dqs_en;
|
||||
strobe = priv->saved_strobe_ctrl;
|
||||
|
||||
if (timing == MMC_TIMING_MMC_HS400) {
|
||||
dqs |= DATA_STROBE_EN;
|
||||
strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay);
|
||||
} else {
|
||||
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||
mci_writel(host, CLKSEL64, priv->sdr_timing);
|
||||
else
|
||||
mci_writel(host, CLKSEL, priv->sdr_timing);
|
||||
dqs &= ~DATA_STROBE_EN;
|
||||
}
|
||||
|
||||
mci_writel(host, HS400_DQS_EN, dqs);
|
||||
mci_writel(host, HS400_DLINE_CTRL, strobe);
|
||||
}
|
||||
|
||||
static void dw_mci_exynos_adjust_clock(struct dw_mci *host, unsigned int wanted)
|
||||
{
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
unsigned long actual;
|
||||
u8 div;
|
||||
int ret;
|
||||
/*
|
||||
* Don't care if wanted clock is zero or
|
||||
* ciu clock is unavailable
|
||||
|
@ -207,17 +265,52 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
|||
if (wanted < EXYNOS_CCLKIN_MIN)
|
||||
wanted = EXYNOS_CCLKIN_MIN;
|
||||
|
||||
if (wanted != priv->cur_speed) {
|
||||
int ret = clk_set_rate(host->ciu_clk, wanted * div);
|
||||
if (ret)
|
||||
dev_warn(host->dev,
|
||||
"failed to set clk-rate %u error: %d\n",
|
||||
wanted * div, ret);
|
||||
actual = clk_get_rate(host->ciu_clk);
|
||||
host->bus_hz = actual / div;
|
||||
priv->cur_speed = wanted;
|
||||
host->current_speed = 0;
|
||||
if (wanted == priv->cur_speed)
|
||||
return;
|
||||
|
||||
div = dw_mci_exynos_get_ciu_div(host);
|
||||
ret = clk_set_rate(host->ciu_clk, wanted * div);
|
||||
if (ret)
|
||||
dev_warn(host->dev,
|
||||
"failed to set clk-rate %u error: %d\n",
|
||||
wanted * div, ret);
|
||||
actual = clk_get_rate(host->ciu_clk);
|
||||
host->bus_hz = actual / div;
|
||||
priv->cur_speed = wanted;
|
||||
host->current_speed = 0;
|
||||
}
|
||||
|
||||
static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||
{
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
unsigned int wanted = ios->clock;
|
||||
u32 timing = ios->timing, clksel;
|
||||
|
||||
switch (timing) {
|
||||
case MMC_TIMING_MMC_HS400:
|
||||
/* Update tuned sample timing */
|
||||
clksel = SDMMC_CLKSEL_UP_SAMPLE(
|
||||
priv->hs400_timing, priv->tuned_sample);
|
||||
wanted <<= 1;
|
||||
break;
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
clksel = priv->ddr_timing;
|
||||
/* Should be double rate for DDR mode */
|
||||
if (ios->bus_width == MMC_BUS_WIDTH_8)
|
||||
wanted <<= 1;
|
||||
break;
|
||||
default:
|
||||
clksel = priv->sdr_timing;
|
||||
}
|
||||
|
||||
/* Set clock timing for the requested speed mode*/
|
||||
dw_mci_exynos_set_clksel_timing(host, clksel);
|
||||
|
||||
/* Configure setting for HS400 */
|
||||
dw_mci_exynos_config_hs400(host, timing);
|
||||
|
||||
/* Configure clock rate */
|
||||
dw_mci_exynos_adjust_clock(host, wanted);
|
||||
}
|
||||
|
||||
static int dw_mci_exynos_parse_dt(struct dw_mci *host)
|
||||
|
@ -260,6 +353,16 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
|
|||
return ret;
|
||||
|
||||
priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
|
||||
|
||||
ret = of_property_read_u32_array(np,
|
||||
"samsung,dw-mshc-hs400-timing", timing, 2);
|
||||
if (!ret && of_property_read_u32(np,
|
||||
"samsung,read-strobe-delay", &priv->dqs_delay))
|
||||
dev_dbg(host->dev,
|
||||
"read-strobe-delay is not found, assuming usage of default value\n");
|
||||
|
||||
priv->hs400_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1],
|
||||
HS400_FIXED_CIU_CLK_DIV);
|
||||
host->priv = priv;
|
||||
return 0;
|
||||
}
|
||||
|
@ -285,7 +388,7 @@ static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
|
|||
clksel = mci_readl(host, CLKSEL64);
|
||||
else
|
||||
clksel = mci_readl(host, CLKSEL);
|
||||
clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample);
|
||||
clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
|
||||
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||
mci_writel(host, CLKSEL64, clksel);
|
||||
|
@ -304,13 +407,16 @@ static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
|
|||
clksel = mci_readl(host, CLKSEL64);
|
||||
else
|
||||
clksel = mci_readl(host, CLKSEL);
|
||||
|
||||
sample = (clksel + 1) & 0x7;
|
||||
clksel = (clksel & ~0x7) | sample;
|
||||
clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
|
||||
|
||||
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||
mci_writel(host, CLKSEL64, clksel);
|
||||
else
|
||||
mci_writel(host, CLKSEL, clksel);
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
|
@ -343,6 +449,7 @@ out:
|
|||
static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot)
|
||||
{
|
||||
struct dw_mci *host = slot->host;
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
struct mmc_host *mmc = slot->mmc;
|
||||
u8 start_smpl, smpl, candiates = 0;
|
||||
s8 found = -1;
|
||||
|
@ -360,14 +467,27 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot)
|
|||
} while (start_smpl != smpl);
|
||||
|
||||
found = dw_mci_exynos_get_best_clksmpl(candiates);
|
||||
if (found >= 0)
|
||||
if (found >= 0) {
|
||||
dw_mci_exynos_set_clksmpl(host, found);
|
||||
else
|
||||
priv->tuned_sample = found;
|
||||
} else {
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dw_mci_exynos_prepare_hs400_tuning(struct dw_mci *host,
|
||||
struct mmc_ios *ios)
|
||||
{
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
|
||||
dw_mci_exynos_set_clksel_timing(host, priv->hs400_timing);
|
||||
dw_mci_exynos_adjust_clock(host, (ios->clock) << 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Common capabilities of Exynos4/Exynos5 SoC */
|
||||
static unsigned long exynos_dwmmc_caps[4] = {
|
||||
MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
|
||||
|
@ -384,6 +504,7 @@ static const struct dw_mci_drv_data exynos_drv_data = {
|
|||
.set_ios = dw_mci_exynos_set_ios,
|
||||
.parse_dt = dw_mci_exynos_parse_dt,
|
||||
.execute_tuning = dw_mci_exynos_execute_tuning,
|
||||
.prepare_hs400_tuning = dw_mci_exynos_prepare_hs400_tuning,
|
||||
};
|
||||
|
||||
static const struct of_device_id dw_mci_exynos_match[] = {
|
||||
|
|
|
@ -12,20 +12,36 @@
|
|||
#ifndef _DW_MMC_EXYNOS_H_
|
||||
#define _DW_MMC_EXYNOS_H_
|
||||
|
||||
/* Extended Register's Offset */
|
||||
#define SDMMC_CLKSEL 0x09C
|
||||
#define SDMMC_CLKSEL64 0x0A8
|
||||
|
||||
/* Extended Register's Offset */
|
||||
#define SDMMC_HS400_DQS_EN 0x180
|
||||
#define SDMMC_HS400_ASYNC_FIFO_CTRL 0x184
|
||||
#define SDMMC_HS400_DLINE_CTRL 0x188
|
||||
|
||||
/* CLKSEL register defines */
|
||||
#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0)
|
||||
#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16)
|
||||
#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
|
||||
#define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7)
|
||||
#define SDMMC_CLKSEL_GET_DIV(x) (((x) >> 24) & 0x7)
|
||||
#define SDMMC_CLKSEL_UP_SAMPLE(x, y) (((x) & ~SDMMC_CLKSEL_CCLK_SAMPLE(7)) |\
|
||||
SDMMC_CLKSEL_CCLK_SAMPLE(y))
|
||||
#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \
|
||||
SDMMC_CLKSEL_CCLK_DRIVE(y) | \
|
||||
SDMMC_CLKSEL_CCLK_DIVIDER(z))
|
||||
#define SDMMC_CLKSEL_TIMING_MASK SDMMC_CLKSEL_TIMING(0x7, 0x7, 0x7)
|
||||
#define SDMMC_CLKSEL_WAKEUP_INT BIT(11)
|
||||
|
||||
/* RCLK_EN register defines */
|
||||
#define DATA_STROBE_EN BIT(0)
|
||||
#define AXI_NON_BLOCKING_WR BIT(7)
|
||||
|
||||
/* DLINE_CTRL register defines */
|
||||
#define DQS_CTRL_RD_DELAY(x, y) (((x) & ~0x3FF) | ((y) & 0x3FF))
|
||||
#define DQS_CTRL_GET_RD_DELAY(x) ((x) & 0x3FF)
|
||||
|
||||
/* Protector Register */
|
||||
#define SDMMC_EMMCP_BASE 0x1000
|
||||
#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010)
|
||||
|
@ -49,6 +65,7 @@
|
|||
/* Fixed clock divider */
|
||||
#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
|
||||
#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
|
||||
#define HS400_FIXED_CIU_CLK_DIV 1
|
||||
|
||||
/* Minimal required clock frequency for cclkin, unit: HZ */
|
||||
#define EXYNOS_CCLKIN_MIN 50000000
|
||||
|
|
|
@ -76,12 +76,20 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Common capabilities of RK3288 SoC */
|
||||
static unsigned long dw_mci_rk3288_dwmmc_caps[4] = {
|
||||
MMC_CAP_RUNTIME_RESUME, /* emmc */
|
||||
MMC_CAP_RUNTIME_RESUME, /* sdmmc */
|
||||
MMC_CAP_RUNTIME_RESUME, /* sdio0 */
|
||||
MMC_CAP_RUNTIME_RESUME, /* sdio1 */
|
||||
};
|
||||
static const struct dw_mci_drv_data rk2928_drv_data = {
|
||||
.prepare_command = dw_mci_rockchip_prepare_command,
|
||||
.init = dw_mci_rockchip_init,
|
||||
};
|
||||
|
||||
static const struct dw_mci_drv_data rk3288_drv_data = {
|
||||
.caps = dw_mci_rk3288_dwmmc_caps,
|
||||
.prepare_command = dw_mci_rockchip_prepare_command,
|
||||
.set_ios = dw_mci_rk3288_set_ios,
|
||||
.setup_clock = dw_mci_rk3288_setup_clock,
|
||||
|
|
|
@ -69,7 +69,8 @@ struct idmac_desc_64addr {
|
|||
|
||||
u32 des2; /*Buffer sizes */
|
||||
#define IDMAC_64ADDR_SET_BUFFER1_SIZE(d, s) \
|
||||
((d)->des2 = ((d)->des2 & 0x03ffe000) | ((s) & 0x1fff))
|
||||
((d)->des2 = ((d)->des2 & cpu_to_le32(0x03ffe000)) | \
|
||||
((cpu_to_le32(s)) & cpu_to_le32(0x1fff)))
|
||||
|
||||
u32 des3; /* Reserved */
|
||||
|
||||
|
@ -81,7 +82,7 @@ struct idmac_desc_64addr {
|
|||
};
|
||||
|
||||
struct idmac_desc {
|
||||
u32 des0; /* Control Descriptor */
|
||||
__le32 des0; /* Control Descriptor */
|
||||
#define IDMAC_DES0_DIC BIT(1)
|
||||
#define IDMAC_DES0_LD BIT(2)
|
||||
#define IDMAC_DES0_FD BIT(3)
|
||||
|
@ -90,18 +91,19 @@ struct idmac_desc {
|
|||
#define IDMAC_DES0_CES BIT(30)
|
||||
#define IDMAC_DES0_OWN BIT(31)
|
||||
|
||||
u32 des1; /* Buffer sizes */
|
||||
__le32 des1; /* Buffer sizes */
|
||||
#define IDMAC_SET_BUFFER1_SIZE(d, s) \
|
||||
((d)->des1 = ((d)->des1 & 0x03ffe000) | ((s) & 0x1fff))
|
||||
|
||||
u32 des2; /* buffer 1 physical address */
|
||||
__le32 des2; /* buffer 1 physical address */
|
||||
|
||||
u32 des3; /* buffer 2 physical address */
|
||||
__le32 des3; /* buffer 2 physical address */
|
||||
};
|
||||
#endif /* CONFIG_MMC_DW_IDMAC */
|
||||
|
||||
static bool dw_mci_reset(struct dw_mci *host);
|
||||
static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset);
|
||||
static int dw_mci_card_busy(struct mmc_host *mmc);
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
static int dw_mci_req_show(struct seq_file *s, void *v)
|
||||
|
@ -335,6 +337,31 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
|
|||
return cmdr;
|
||||
}
|
||||
|
||||
static void dw_mci_wait_while_busy(struct dw_mci *host, u32 cmd_flags)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(500);
|
||||
|
||||
/*
|
||||
* Databook says that before issuing a new data transfer command
|
||||
* we need to check to see if the card is busy. Data transfer commands
|
||||
* all have SDMMC_CMD_PRV_DAT_WAIT set, so we'll key off that.
|
||||
*
|
||||
* ...also allow sending for SDMMC_CMD_VOLT_SWITCH where busy is
|
||||
* expected.
|
||||
*/
|
||||
if ((cmd_flags & SDMMC_CMD_PRV_DAT_WAIT) &&
|
||||
!(cmd_flags & SDMMC_CMD_VOLT_SWITCH)) {
|
||||
while (mci_readl(host, STATUS) & SDMMC_STATUS_BUSY) {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
/* Command will fail; we'll pass error then */
|
||||
dev_err(host->dev, "Busy; trying anyway\n");
|
||||
break;
|
||||
}
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dw_mci_start_command(struct dw_mci *host,
|
||||
struct mmc_command *cmd, u32 cmd_flags)
|
||||
{
|
||||
|
@ -345,6 +372,7 @@ static void dw_mci_start_command(struct dw_mci *host,
|
|||
|
||||
mci_writel(host, CMDARG, cmd->arg);
|
||||
wmb();
|
||||
dw_mci_wait_while_busy(host, cmd_flags);
|
||||
|
||||
mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
|
||||
}
|
||||
|
@ -477,23 +505,23 @@ static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data,
|
|||
* Set the OWN bit and disable interrupts for this
|
||||
* descriptor
|
||||
*/
|
||||
desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC |
|
||||
IDMAC_DES0_CH;
|
||||
desc->des0 = cpu_to_le32(IDMAC_DES0_OWN |
|
||||
IDMAC_DES0_DIC | IDMAC_DES0_CH);
|
||||
/* Buffer length */
|
||||
IDMAC_SET_BUFFER1_SIZE(desc, length);
|
||||
|
||||
/* Physical address to DMA to/from */
|
||||
desc->des2 = mem_addr;
|
||||
desc->des2 = cpu_to_le32(mem_addr);
|
||||
}
|
||||
|
||||
/* Set first descriptor */
|
||||
desc = host->sg_cpu;
|
||||
desc->des0 |= IDMAC_DES0_FD;
|
||||
desc->des0 |= cpu_to_le32(IDMAC_DES0_FD);
|
||||
|
||||
/* Set last descriptor */
|
||||
desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
|
||||
desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
|
||||
desc->des0 |= IDMAC_DES0_LD;
|
||||
desc->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | IDMAC_DES0_DIC));
|
||||
desc->des0 |= cpu_to_le32(IDMAC_DES0_LD);
|
||||
}
|
||||
|
||||
wmb();
|
||||
|
@ -562,12 +590,12 @@ static int dw_mci_idmac_init(struct dw_mci *host)
|
|||
|
||||
/* Forward link the descriptor list */
|
||||
for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
|
||||
p->des3 = host->sg_dma + (sizeof(struct idmac_desc) *
|
||||
(i + 1));
|
||||
p->des3 = cpu_to_le32(host->sg_dma +
|
||||
(sizeof(struct idmac_desc) * (i + 1)));
|
||||
|
||||
/* Set the last descriptor as the end-of-ring descriptor */
|
||||
p->des3 = host->sg_dma;
|
||||
p->des0 = IDMAC_DES0_ER;
|
||||
p->des3 = cpu_to_le32(host->sg_dma);
|
||||
p->des0 = cpu_to_le32(IDMAC_DES0_ER);
|
||||
}
|
||||
|
||||
dw_mci_idmac_reset(host);
|
||||
|
@ -737,6 +765,7 @@ static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data)
|
|||
return;
|
||||
|
||||
if (host->timing != MMC_TIMING_MMC_HS200 &&
|
||||
host->timing != MMC_TIMING_MMC_HS400 &&
|
||||
host->timing != MMC_TIMING_UHS_SDR104)
|
||||
goto disable;
|
||||
|
||||
|
@ -876,6 +905,7 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
|
|||
|
||||
mci_writel(host, CMDARG, arg);
|
||||
wmb();
|
||||
dw_mci_wait_while_busy(host, cmd);
|
||||
mci_writel(host, CMD, SDMMC_CMD_START | cmd);
|
||||
|
||||
while (time_before(jiffies, timeout)) {
|
||||
|
@ -992,6 +1022,26 @@ static void __dw_mci_start_request(struct dw_mci *host,
|
|||
|
||||
dw_mci_start_command(host, cmd, cmdflags);
|
||||
|
||||
if (cmd->opcode == SD_SWITCH_VOLTAGE) {
|
||||
unsigned long irqflags;
|
||||
|
||||
/*
|
||||
* Databook says to fail after 2ms w/ no response, but evidence
|
||||
* shows that sometimes the cmd11 interrupt takes over 130ms.
|
||||
* We'll set to 500ms, plus an extra jiffy just in case jiffies
|
||||
* is just about to roll over.
|
||||
*
|
||||
* We do this whole thing under spinlock and only if the
|
||||
* command hasn't already completed (indicating the the irq
|
||||
* already ran so we don't want the timeout).
|
||||
*/
|
||||
spin_lock_irqsave(&host->irq_lock, irqflags);
|
||||
if (!test_bit(EVENT_CMD_COMPLETE, &host->pending_events))
|
||||
mod_timer(&host->cmd11_timer,
|
||||
jiffies + msecs_to_jiffies(500) + 1);
|
||||
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
||||
}
|
||||
|
||||
if (mrq->stop)
|
||||
host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
|
||||
else
|
||||
|
@ -1084,7 +1134,8 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
regs = mci_readl(slot->host, UHS_REG);
|
||||
|
||||
/* DDR mode set */
|
||||
if (ios->timing == MMC_TIMING_MMC_DDR52)
|
||||
if (ios->timing == MMC_TIMING_MMC_DDR52 ||
|
||||
ios->timing == MMC_TIMING_MMC_HS400)
|
||||
regs |= ((0x1 << slot->id) << 16);
|
||||
else
|
||||
regs &= ~((0x1 << slot->id) << 16);
|
||||
|
@ -1101,12 +1152,6 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
if (drv_data && drv_data->set_ios)
|
||||
drv_data->set_ios(slot->host, ios);
|
||||
|
||||
/* Slot specific timing and width adjustment */
|
||||
dw_mci_setup_bus(slot, false);
|
||||
|
||||
if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0)
|
||||
slot->host->state = STATE_IDLE;
|
||||
|
||||
switch (ios->power_mode) {
|
||||
case MMC_POWER_UP:
|
||||
if (!IS_ERR(mmc->supply.vmmc)) {
|
||||
|
@ -1125,23 +1170,39 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
mci_writel(slot->host, PWREN, regs);
|
||||
break;
|
||||
case MMC_POWER_ON:
|
||||
if (!IS_ERR(mmc->supply.vqmmc) && !slot->host->vqmmc_enabled) {
|
||||
ret = regulator_enable(mmc->supply.vqmmc);
|
||||
if (ret < 0)
|
||||
dev_err(slot->host->dev,
|
||||
"failed to enable vqmmc regulator\n");
|
||||
else
|
||||
if (!slot->host->vqmmc_enabled) {
|
||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||
ret = regulator_enable(mmc->supply.vqmmc);
|
||||
if (ret < 0)
|
||||
dev_err(slot->host->dev,
|
||||
"failed to enable vqmmc\n");
|
||||
else
|
||||
slot->host->vqmmc_enabled = true;
|
||||
|
||||
} else {
|
||||
/* Keep track so we don't reset again */
|
||||
slot->host->vqmmc_enabled = true;
|
||||
}
|
||||
|
||||
/* Reset our state machine after powering on */
|
||||
dw_mci_ctrl_reset(slot->host,
|
||||
SDMMC_CTRL_ALL_RESET_FLAGS);
|
||||
}
|
||||
|
||||
/* Adjust clock / bus width after power is up */
|
||||
dw_mci_setup_bus(slot, false);
|
||||
|
||||
break;
|
||||
case MMC_POWER_OFF:
|
||||
/* Turn clock off before power goes down */
|
||||
dw_mci_setup_bus(slot, false);
|
||||
|
||||
if (!IS_ERR(mmc->supply.vmmc))
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
|
||||
|
||||
if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled) {
|
||||
if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled)
|
||||
regulator_disable(mmc->supply.vqmmc);
|
||||
slot->host->vqmmc_enabled = false;
|
||||
}
|
||||
slot->host->vqmmc_enabled = false;
|
||||
|
||||
regs = mci_readl(slot->host, PWREN);
|
||||
regs &= ~(1 << slot->id);
|
||||
|
@ -1150,6 +1211,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0)
|
||||
slot->host->state = STATE_IDLE;
|
||||
}
|
||||
|
||||
static int dw_mci_card_busy(struct mmc_host *mmc)
|
||||
|
@ -1323,6 +1387,18 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int dw_mci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct dw_mci *host = slot->host;
|
||||
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
|
||||
if (drv_data && drv_data->prepare_hs400_tuning)
|
||||
return drv_data->prepare_hs400_tuning(host, ios);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mmc_host_ops dw_mci_ops = {
|
||||
.request = dw_mci_request,
|
||||
.pre_req = dw_mci_pre_req,
|
||||
|
@ -1335,6 +1411,7 @@ static const struct mmc_host_ops dw_mci_ops = {
|
|||
.card_busy = dw_mci_card_busy,
|
||||
.start_signal_voltage_switch = dw_mci_switch_voltage,
|
||||
.init_card = dw_mci_init_card,
|
||||
.prepare_hs400_tuning = dw_mci_prepare_hs400_tuning,
|
||||
};
|
||||
|
||||
static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
|
||||
|
@ -1520,7 +1597,10 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|||
if (test_and_clear_bit(EVENT_DATA_ERROR,
|
||||
&host->pending_events)) {
|
||||
dw_mci_stop_dma(host);
|
||||
send_stop_abort(host, data);
|
||||
if (data->stop ||
|
||||
!(host->data_status & (SDMMC_INT_DRTO |
|
||||
SDMMC_INT_EBE)))
|
||||
send_stop_abort(host, data);
|
||||
state = STATE_DATA_ERROR;
|
||||
break;
|
||||
}
|
||||
|
@ -1547,7 +1627,10 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|||
if (test_and_clear_bit(EVENT_DATA_ERROR,
|
||||
&host->pending_events)) {
|
||||
dw_mci_stop_dma(host);
|
||||
send_stop_abort(host, data);
|
||||
if (data->stop ||
|
||||
!(host->data_status & (SDMMC_INT_DRTO |
|
||||
SDMMC_INT_EBE)))
|
||||
send_stop_abort(host, data);
|
||||
state = STATE_DATA_ERROR;
|
||||
break;
|
||||
}
|
||||
|
@ -1685,8 +1768,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
|
|||
buf += len;
|
||||
cnt -= len;
|
||||
if (host->part_buf_count == 2) {
|
||||
mci_writew(host, DATA(host->data_offset),
|
||||
host->part_buf16);
|
||||
mci_fifo_writew(host->fifo_reg, host->part_buf16);
|
||||
host->part_buf_count = 0;
|
||||
}
|
||||
}
|
||||
|
@ -1703,15 +1785,14 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
|
|||
cnt -= len;
|
||||
/* push data from aligned buffer into fifo */
|
||||
for (i = 0; i < items; ++i)
|
||||
mci_writew(host, DATA(host->data_offset),
|
||||
aligned_buf[i]);
|
||||
mci_fifo_writew(host->fifo_reg, aligned_buf[i]);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
u16 *pdata = buf;
|
||||
for (; cnt >= 2; cnt -= 2)
|
||||
mci_writew(host, DATA(host->data_offset), *pdata++);
|
||||
mci_fifo_writew(host->fifo_reg, *pdata++);
|
||||
buf = pdata;
|
||||
}
|
||||
/* put anything remaining in the part_buf */
|
||||
|
@ -1720,8 +1801,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
|
|||
/* Push data if we have reached the expected data length */
|
||||
if ((data->bytes_xfered + init_cnt) ==
|
||||
(data->blksz * data->blocks))
|
||||
mci_writew(host, DATA(host->data_offset),
|
||||
host->part_buf16);
|
||||
mci_fifo_writew(host->fifo_reg, host->part_buf16);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1736,8 +1816,7 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
|
|||
int items = len >> 1;
|
||||
int i;
|
||||
for (i = 0; i < items; ++i)
|
||||
aligned_buf[i] = mci_readw(host,
|
||||
DATA(host->data_offset));
|
||||
aligned_buf[i] = mci_fifo_readw(host->fifo_reg);
|
||||
/* memcpy from aligned buffer into output buffer */
|
||||
memcpy(buf, aligned_buf, len);
|
||||
buf += len;
|
||||
|
@ -1748,11 +1827,11 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
|
|||
{
|
||||
u16 *pdata = buf;
|
||||
for (; cnt >= 2; cnt -= 2)
|
||||
*pdata++ = mci_readw(host, DATA(host->data_offset));
|
||||
*pdata++ = mci_fifo_readw(host->fifo_reg);
|
||||
buf = pdata;
|
||||
}
|
||||
if (cnt) {
|
||||
host->part_buf16 = mci_readw(host, DATA(host->data_offset));
|
||||
host->part_buf16 = mci_fifo_readw(host->fifo_reg);
|
||||
dw_mci_pull_final_bytes(host, buf, cnt);
|
||||
}
|
||||
}
|
||||
|
@ -1768,8 +1847,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
|
|||
buf += len;
|
||||
cnt -= len;
|
||||
if (host->part_buf_count == 4) {
|
||||
mci_writel(host, DATA(host->data_offset),
|
||||
host->part_buf32);
|
||||
mci_fifo_writel(host->fifo_reg, host->part_buf32);
|
||||
host->part_buf_count = 0;
|
||||
}
|
||||
}
|
||||
|
@ -1786,15 +1864,14 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
|
|||
cnt -= len;
|
||||
/* push data from aligned buffer into fifo */
|
||||
for (i = 0; i < items; ++i)
|
||||
mci_writel(host, DATA(host->data_offset),
|
||||
aligned_buf[i]);
|
||||
mci_fifo_writel(host->fifo_reg, aligned_buf[i]);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
u32 *pdata = buf;
|
||||
for (; cnt >= 4; cnt -= 4)
|
||||
mci_writel(host, DATA(host->data_offset), *pdata++);
|
||||
mci_fifo_writel(host->fifo_reg, *pdata++);
|
||||
buf = pdata;
|
||||
}
|
||||
/* put anything remaining in the part_buf */
|
||||
|
@ -1803,8 +1880,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
|
|||
/* Push data if we have reached the expected data length */
|
||||
if ((data->bytes_xfered + init_cnt) ==
|
||||
(data->blksz * data->blocks))
|
||||
mci_writel(host, DATA(host->data_offset),
|
||||
host->part_buf32);
|
||||
mci_fifo_writel(host->fifo_reg, host->part_buf32);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1819,8 +1895,7 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
|
|||
int items = len >> 2;
|
||||
int i;
|
||||
for (i = 0; i < items; ++i)
|
||||
aligned_buf[i] = mci_readl(host,
|
||||
DATA(host->data_offset));
|
||||
aligned_buf[i] = mci_fifo_readl(host->fifo_reg);
|
||||
/* memcpy from aligned buffer into output buffer */
|
||||
memcpy(buf, aligned_buf, len);
|
||||
buf += len;
|
||||
|
@ -1831,11 +1906,11 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
|
|||
{
|
||||
u32 *pdata = buf;
|
||||
for (; cnt >= 4; cnt -= 4)
|
||||
*pdata++ = mci_readl(host, DATA(host->data_offset));
|
||||
*pdata++ = mci_fifo_readl(host->fifo_reg);
|
||||
buf = pdata;
|
||||
}
|
||||
if (cnt) {
|
||||
host->part_buf32 = mci_readl(host, DATA(host->data_offset));
|
||||
host->part_buf32 = mci_fifo_readl(host->fifo_reg);
|
||||
dw_mci_pull_final_bytes(host, buf, cnt);
|
||||
}
|
||||
}
|
||||
|
@ -1852,8 +1927,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
|
|||
cnt -= len;
|
||||
|
||||
if (host->part_buf_count == 8) {
|
||||
mci_writeq(host, DATA(host->data_offset),
|
||||
host->part_buf);
|
||||
mci_fifo_writeq(host->fifo_reg, host->part_buf);
|
||||
host->part_buf_count = 0;
|
||||
}
|
||||
}
|
||||
|
@ -1870,15 +1944,14 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
|
|||
cnt -= len;
|
||||
/* push data from aligned buffer into fifo */
|
||||
for (i = 0; i < items; ++i)
|
||||
mci_writeq(host, DATA(host->data_offset),
|
||||
aligned_buf[i]);
|
||||
mci_fifo_writeq(host->fifo_reg, aligned_buf[i]);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
u64 *pdata = buf;
|
||||
for (; cnt >= 8; cnt -= 8)
|
||||
mci_writeq(host, DATA(host->data_offset), *pdata++);
|
||||
mci_fifo_writeq(host->fifo_reg, *pdata++);
|
||||
buf = pdata;
|
||||
}
|
||||
/* put anything remaining in the part_buf */
|
||||
|
@ -1887,8 +1960,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
|
|||
/* Push data if we have reached the expected data length */
|
||||
if ((data->bytes_xfered + init_cnt) ==
|
||||
(data->blksz * data->blocks))
|
||||
mci_writeq(host, DATA(host->data_offset),
|
||||
host->part_buf);
|
||||
mci_fifo_writeq(host->fifo_reg, host->part_buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1903,8 +1975,8 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
|
|||
int items = len >> 3;
|
||||
int i;
|
||||
for (i = 0; i < items; ++i)
|
||||
aligned_buf[i] = mci_readq(host,
|
||||
DATA(host->data_offset));
|
||||
aligned_buf[i] = mci_fifo_readq(host->fifo_reg);
|
||||
|
||||
/* memcpy from aligned buffer into output buffer */
|
||||
memcpy(buf, aligned_buf, len);
|
||||
buf += len;
|
||||
|
@ -1915,11 +1987,11 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
|
|||
{
|
||||
u64 *pdata = buf;
|
||||
for (; cnt >= 8; cnt -= 8)
|
||||
*pdata++ = mci_readq(host, DATA(host->data_offset));
|
||||
*pdata++ = mci_fifo_readq(host->fifo_reg);
|
||||
buf = pdata;
|
||||
}
|
||||
if (cnt) {
|
||||
host->part_buf = mci_readq(host, DATA(host->data_offset));
|
||||
host->part_buf = mci_fifo_readq(host->fifo_reg);
|
||||
dw_mci_pull_final_bytes(host, buf, cnt);
|
||||
}
|
||||
}
|
||||
|
@ -2097,9 +2169,20 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|||
/* Check volt switch first, since it can look like an error */
|
||||
if ((host->state == STATE_SENDING_CMD11) &&
|
||||
(pending & SDMMC_INT_VOLT_SWITCH)) {
|
||||
unsigned long irqflags;
|
||||
|
||||
mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH);
|
||||
pending &= ~SDMMC_INT_VOLT_SWITCH;
|
||||
|
||||
/*
|
||||
* Hold the lock; we know cmd11_timer can't be kicked
|
||||
* off after the lock is released, so safe to delete.
|
||||
*/
|
||||
spin_lock_irqsave(&host->irq_lock, irqflags);
|
||||
dw_mci_cmd_interrupt(host, pending);
|
||||
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
||||
|
||||
del_timer(&host->cmd11_timer);
|
||||
}
|
||||
|
||||
if (pending & DW_MCI_CMD_ERROR_FLAGS) {
|
||||
|
@ -2156,6 +2239,10 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|||
/* Handle SDIO Interrupts */
|
||||
for (i = 0; i < host->num_slots; i++) {
|
||||
struct dw_mci_slot *slot = host->slot[i];
|
||||
|
||||
if (!slot)
|
||||
continue;
|
||||
|
||||
if (pending & SDMMC_INT_SDIO(slot->sdio_id)) {
|
||||
mci_writel(host, RINTSTS,
|
||||
SDMMC_INT_SDIO(slot->sdio_id));
|
||||
|
@ -2506,6 +2593,20 @@ ciu_out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void dw_mci_cmd11_timer(unsigned long arg)
|
||||
{
|
||||
struct dw_mci *host = (struct dw_mci *)arg;
|
||||
|
||||
if (host->state != STATE_SENDING_CMD11) {
|
||||
dev_warn(host->dev, "Unexpected CMD11 timeout\n");
|
||||
return;
|
||||
}
|
||||
|
||||
host->cmd_status = SDMMC_INT_RTO;
|
||||
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct dw_mci_of_quirks {
|
||||
char *quirk;
|
||||
|
@ -2574,6 +2675,34 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
|||
}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static void dw_mci_enable_cd(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_board *brd = host->pdata;
|
||||
unsigned long irqflags;
|
||||
u32 temp;
|
||||
int i;
|
||||
|
||||
/* No need for CD if broken card detection */
|
||||
if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
|
||||
return;
|
||||
|
||||
/* No need for CD if all slots have a non-error GPIO */
|
||||
for (i = 0; i < host->num_slots; i++) {
|
||||
struct dw_mci_slot *slot = host->slot[i];
|
||||
|
||||
if (IS_ERR_VALUE(mmc_gpio_get_cd(slot->mmc)))
|
||||
break;
|
||||
}
|
||||
if (i == host->num_slots)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&host->irq_lock, irqflags);
|
||||
temp = mci_readl(host, INTMASK);
|
||||
temp |= SDMMC_INT_CD;
|
||||
mci_writel(host, INTMASK, temp);
|
||||
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
||||
}
|
||||
|
||||
int dw_mci_probe(struct dw_mci *host)
|
||||
{
|
||||
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
|
@ -2652,6 +2781,9 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
}
|
||||
}
|
||||
|
||||
setup_timer(&host->cmd11_timer,
|
||||
dw_mci_cmd11_timer, (unsigned long)host);
|
||||
|
||||
host->quirks = host->pdata->quirks;
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
|
@ -2731,9 +2863,9 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
dev_info(host->dev, "Version ID is %04x\n", host->verid);
|
||||
|
||||
if (host->verid < DW_MMC_240A)
|
||||
host->data_offset = DATA_OFFSET;
|
||||
host->fifo_reg = host->regs + DATA_OFFSET;
|
||||
else
|
||||
host->data_offset = DATA_240A_OFFSET;
|
||||
host->fifo_reg = host->regs + DATA_240A_OFFSET;
|
||||
|
||||
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
|
||||
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
|
||||
|
@ -2747,13 +2879,13 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
|
||||
|
||||
/*
|
||||
* Enable interrupts for command done, data over, data empty, card det,
|
||||
* Enable interrupts for command done, data over, data empty,
|
||||
* receive ready and error such as transmit, receive timeout, crc error
|
||||
*/
|
||||
mci_writel(host, RINTSTS, 0xFFFFFFFF);
|
||||
mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
|
||||
SDMMC_INT_TXDR | SDMMC_INT_RXDR |
|
||||
DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
|
||||
DW_MCI_ERROR_FLAGS);
|
||||
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
|
||||
|
||||
dev_info(host->dev, "DW MMC controller at irq %d, "
|
||||
|
@ -2778,6 +2910,9 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
goto err_dmaunmap;
|
||||
}
|
||||
|
||||
/* Now that slots are all setup, we can enable card detect */
|
||||
dw_mci_enable_cd(host);
|
||||
|
||||
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
|
||||
dev_info(host->dev, "Internal DMAC interrupt fix enabled.\n");
|
||||
|
||||
|
@ -2864,7 +2999,7 @@ int dw_mci_resume(struct dw_mci *host)
|
|||
mci_writel(host, RINTSTS, 0xFFFFFFFF);
|
||||
mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
|
||||
SDMMC_INT_TXDR | SDMMC_INT_RXDR |
|
||||
DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
|
||||
DW_MCI_ERROR_FLAGS);
|
||||
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE);
|
||||
|
||||
for (i = 0; i < host->num_slots; i++) {
|
||||
|
@ -2876,6 +3011,10 @@ int dw_mci_resume(struct dw_mci *host)
|
|||
dw_mci_setup_bus(slot, true);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now that slots are all setup, we can enable card detect */
|
||||
dw_mci_enable_cd(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(dw_mci_resume);
|
||||
|
|
|
@ -169,24 +169,34 @@
|
|||
#define SDMMC_CTRL_ALL_RESET_FLAGS \
|
||||
(SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)
|
||||
|
||||
/* FIFO register access macros. These should not change the data endian-ness
|
||||
* as they are written to memory to be dealt with by the upper layers */
|
||||
#define mci_fifo_readw(__reg) __raw_readw(__reg)
|
||||
#define mci_fifo_readl(__reg) __raw_readl(__reg)
|
||||
#define mci_fifo_readq(__reg) __raw_readq(__reg)
|
||||
|
||||
#define mci_fifo_writew(__value, __reg) __raw_writew(__reg, __value)
|
||||
#define mci_fifo_writel(__value, __reg) __raw_writel(__reg, __value)
|
||||
#define mci_fifo_writeq(__value, __reg) __raw_writeq(__reg, __value)
|
||||
|
||||
/* Register access macros */
|
||||
#define mci_readl(dev, reg) \
|
||||
__raw_readl((dev)->regs + SDMMC_##reg)
|
||||
readl_relaxed((dev)->regs + SDMMC_##reg)
|
||||
#define mci_writel(dev, reg, value) \
|
||||
__raw_writel((value), (dev)->regs + SDMMC_##reg)
|
||||
writel_relaxed((value), (dev)->regs + SDMMC_##reg)
|
||||
|
||||
/* 16-bit FIFO access macros */
|
||||
#define mci_readw(dev, reg) \
|
||||
__raw_readw((dev)->regs + SDMMC_##reg)
|
||||
readw_relaxed((dev)->regs + SDMMC_##reg)
|
||||
#define mci_writew(dev, reg, value) \
|
||||
__raw_writew((value), (dev)->regs + SDMMC_##reg)
|
||||
writew_relaxed((value), (dev)->regs + SDMMC_##reg)
|
||||
|
||||
/* 64-bit FIFO access macros */
|
||||
#ifdef readq
|
||||
#define mci_readq(dev, reg) \
|
||||
__raw_readq((dev)->regs + SDMMC_##reg)
|
||||
readq_relaxed((dev)->regs + SDMMC_##reg)
|
||||
#define mci_writeq(dev, reg, value) \
|
||||
__raw_writeq((value), (dev)->regs + SDMMC_##reg)
|
||||
writeq_relaxed((value), (dev)->regs + SDMMC_##reg)
|
||||
#else
|
||||
/*
|
||||
* Dummy readq implementation for architectures that don't define it.
|
||||
|
@ -200,6 +210,10 @@
|
|||
(*(volatile u64 __force *)((dev)->regs + SDMMC_##reg))
|
||||
#define mci_writeq(dev, reg, value) \
|
||||
(*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value))
|
||||
|
||||
#define __raw_writeq(__value, __reg) \
|
||||
(*(volatile u64 __force *)(__reg) = (__value))
|
||||
#define __raw_readq(__reg) (*(volatile u64 __force *)(__reg))
|
||||
#endif
|
||||
|
||||
extern int dw_mci_probe(struct dw_mci *host);
|
||||
|
@ -271,5 +285,7 @@ struct dw_mci_drv_data {
|
|||
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
|
||||
int (*parse_dt)(struct dw_mci *host);
|
||||
int (*execute_tuning)(struct dw_mci_slot *slot);
|
||||
int (*prepare_hs400_tuning)(struct dw_mci *host,
|
||||
struct mmc_ios *ios);
|
||||
};
|
||||
#endif /* _DW_MMC_H_ */
|
||||
|
|
|
@ -1507,7 +1507,7 @@ static int mmc_spi_remove(struct spi_device *spi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id mmc_spi_of_match_table[] = {
|
||||
static const struct of_device_id mmc_spi_of_match_table[] = {
|
||||
{ .compatible = "mmc-spi-slot", },
|
||||
{},
|
||||
};
|
||||
|
|
|
@ -1613,7 +1613,10 @@ static int mmci_probe(struct amba_device *dev,
|
|||
dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max);
|
||||
|
||||
/* Get regulators and the supported OCR mask */
|
||||
mmc_regulator_get_supply(mmc);
|
||||
ret = mmc_regulator_get_supply(mmc);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto clk_disable;
|
||||
|
||||
if (!mmc->ocr_avail)
|
||||
mmc->ocr_avail = plat->ocr_mask;
|
||||
else if (plat->ocr_mask)
|
||||
|
|
|
@ -222,10 +222,6 @@ struct omap_hsmmc_host {
|
|||
struct omap_hsmmc_next next_data;
|
||||
struct omap_hsmmc_platform_data *pdata;
|
||||
|
||||
/* To handle board related suspend/resume functionality for MMC */
|
||||
int (*suspend)(struct device *dev);
|
||||
int (*resume)(struct device *dev);
|
||||
|
||||
/* return MMC cover switch state, can be NULL if not supported.
|
||||
*
|
||||
* possible return values:
|
||||
|
@ -234,12 +230,7 @@ struct omap_hsmmc_host {
|
|||
*/
|
||||
int (*get_cover_state)(struct device *dev);
|
||||
|
||||
/* Card detection IRQs */
|
||||
int card_detect_irq;
|
||||
|
||||
int (*card_detect)(struct device *dev);
|
||||
int (*get_ro)(struct device *dev);
|
||||
|
||||
};
|
||||
|
||||
struct omap_mmc_of_data {
|
||||
|
@ -256,13 +247,6 @@ static int omap_hsmmc_card_detect(struct device *dev)
|
|||
return mmc_gpio_get_cd(host->mmc);
|
||||
}
|
||||
|
||||
static int omap_hsmmc_get_wp(struct device *dev)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||
|
||||
return mmc_gpio_get_ro(host->mmc);
|
||||
}
|
||||
|
||||
static int omap_hsmmc_get_cover_state(struct device *dev)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||
|
@ -434,7 +418,7 @@ static inline int omap_hsmmc_have_reg(void)
|
|||
|
||||
#endif
|
||||
|
||||
static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id);
|
||||
static irqreturn_t omap_hsmmc_cover_irq(int irq, void *dev_id);
|
||||
|
||||
static int omap_hsmmc_gpio_init(struct mmc_host *mmc,
|
||||
struct omap_hsmmc_host *host,
|
||||
|
@ -442,29 +426,25 @@ static int omap_hsmmc_gpio_init(struct mmc_host *mmc,
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (gpio_is_valid(pdata->switch_pin)) {
|
||||
if (pdata->cover)
|
||||
host->get_cover_state =
|
||||
omap_hsmmc_get_cover_state;
|
||||
else
|
||||
host->card_detect = omap_hsmmc_card_detect;
|
||||
host->card_detect_irq =
|
||||
gpio_to_irq(pdata->switch_pin);
|
||||
mmc_gpio_set_cd_isr(mmc, omap_hsmmc_detect);
|
||||
ret = mmc_gpio_request_cd(mmc, pdata->switch_pin, 0);
|
||||
if (gpio_is_valid(pdata->gpio_cod)) {
|
||||
ret = mmc_gpio_request_cd(mmc, pdata->gpio_cod, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
pdata->switch_pin = -EINVAL;
|
||||
|
||||
host->get_cover_state = omap_hsmmc_get_cover_state;
|
||||
mmc_gpio_set_cd_isr(mmc, omap_hsmmc_cover_irq);
|
||||
} else if (gpio_is_valid(pdata->gpio_cd)) {
|
||||
ret = mmc_gpio_request_cd(mmc, pdata->gpio_cd, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
host->card_detect = omap_hsmmc_card_detect;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(pdata->gpio_wp)) {
|
||||
host->get_ro = omap_hsmmc_get_wp;
|
||||
ret = mmc_gpio_request_ro(mmc, pdata->gpio_wp);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
pdata->gpio_wp = -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -882,6 +862,8 @@ static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_req
|
|||
return;
|
||||
host->mrq = NULL;
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
pm_runtime_mark_last_busy(host->dev);
|
||||
pm_runtime_put_autosuspend(host->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1252,26 +1234,16 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
|
|||
}
|
||||
|
||||
/*
|
||||
* irq handler to notify the core about card insertion/removal
|
||||
* irq handler when (cell-phone) cover is mounted/removed
|
||||
*/
|
||||
static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
|
||||
static irqreturn_t omap_hsmmc_cover_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_id;
|
||||
int carddetect;
|
||||
|
||||
sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
|
||||
|
||||
if (host->card_detect)
|
||||
carddetect = host->card_detect(host->dev);
|
||||
else {
|
||||
omap_hsmmc_protect_card(host);
|
||||
carddetect = -ENOSYS;
|
||||
}
|
||||
|
||||
if (carddetect)
|
||||
mmc_detect_change(host->mmc, (HZ * 200) / 1000);
|
||||
else
|
||||
mmc_detect_change(host->mmc, (HZ * 50) / 1000);
|
||||
omap_hsmmc_protect_card(host);
|
||||
mmc_detect_change(host->mmc, (HZ * 200) / 1000);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -1305,6 +1277,8 @@ static void omap_hsmmc_dma_callback(void *param)
|
|||
|
||||
host->mrq = NULL;
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
pm_runtime_mark_last_busy(host->dev);
|
||||
pm_runtime_put_autosuspend(host->dev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1537,6 +1511,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
|
|||
|
||||
BUG_ON(host->req_in_progress);
|
||||
BUG_ON(host->dma_ch != -1);
|
||||
pm_runtime_get_sync(host->dev);
|
||||
if (host->protect_card) {
|
||||
if (host->reqs_blocked < 3) {
|
||||
/*
|
||||
|
@ -1553,6 +1528,8 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
|
|||
req->data->error = -EBADF;
|
||||
req->cmd->retries = 0;
|
||||
mmc_request_done(mmc, req);
|
||||
pm_runtime_mark_last_busy(host->dev);
|
||||
pm_runtime_put_autosuspend(host->dev);
|
||||
return;
|
||||
} else if (host->reqs_blocked)
|
||||
host->reqs_blocked = 0;
|
||||
|
@ -1566,6 +1543,8 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
|
|||
req->data->error = err;
|
||||
host->mrq = NULL;
|
||||
mmc_request_done(mmc, req);
|
||||
pm_runtime_mark_last_busy(host->dev);
|
||||
pm_runtime_put_autosuspend(host->dev);
|
||||
return;
|
||||
}
|
||||
if (req->sbc && !(host->flags & AUTO_CMD23)) {
|
||||
|
@ -1641,15 +1620,6 @@ static int omap_hsmmc_get_cd(struct mmc_host *mmc)
|
|||
return host->card_detect(host->dev);
|
||||
}
|
||||
|
||||
static int omap_hsmmc_get_ro(struct mmc_host *mmc)
|
||||
{
|
||||
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
||||
|
||||
if (!host->get_ro)
|
||||
return -ENOSYS;
|
||||
return host->get_ro(host->dev);
|
||||
}
|
||||
|
||||
static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
|
||||
{
|
||||
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
||||
|
@ -1778,25 +1748,6 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
|
|||
set_sd_bus_power(host);
|
||||
}
|
||||
|
||||
static int omap_hsmmc_enable_fclk(struct mmc_host *mmc)
|
||||
{
|
||||
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
||||
|
||||
pm_runtime_get_sync(host->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_hsmmc_disable_fclk(struct mmc_host *mmc)
|
||||
{
|
||||
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
||||
|
||||
pm_runtime_mark_last_busy(host->dev);
|
||||
pm_runtime_put_autosuspend(host->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_hsmmc_multi_io_quirk(struct mmc_card *card,
|
||||
unsigned int direction, int blk_size)
|
||||
{
|
||||
|
@ -1808,14 +1759,12 @@ static int omap_hsmmc_multi_io_quirk(struct mmc_card *card,
|
|||
}
|
||||
|
||||
static struct mmc_host_ops omap_hsmmc_ops = {
|
||||
.enable = omap_hsmmc_enable_fclk,
|
||||
.disable = omap_hsmmc_disable_fclk,
|
||||
.post_req = omap_hsmmc_post_req,
|
||||
.pre_req = omap_hsmmc_pre_req,
|
||||
.request = omap_hsmmc_request,
|
||||
.set_ios = omap_hsmmc_set_ios,
|
||||
.get_cd = omap_hsmmc_get_cd,
|
||||
.get_ro = omap_hsmmc_get_ro,
|
||||
.get_ro = mmc_gpio_get_ro,
|
||||
.init_card = omap_hsmmc_init_card,
|
||||
.enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
|
||||
};
|
||||
|
@ -1937,7 +1886,8 @@ static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
|
|||
if (of_find_property(np, "ti,dual-volt", NULL))
|
||||
pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
|
||||
|
||||
pdata->switch_pin = -EINVAL;
|
||||
pdata->gpio_cd = -EINVAL;
|
||||
pdata->gpio_cod = -EINVAL;
|
||||
pdata->gpio_wp = -EINVAL;
|
||||
|
||||
if (of_find_property(np, "ti,non-removable", NULL)) {
|
||||
|
@ -2179,9 +2129,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
goto err_slot_name;
|
||||
}
|
||||
if (host->card_detect_irq && host->get_cover_state) {
|
||||
if (host->get_cover_state) {
|
||||
ret = device_create_file(&mmc->class_dev,
|
||||
&dev_attr_cover_switch);
|
||||
&dev_attr_cover_switch);
|
||||
if (ret < 0)
|
||||
goto err_slot_name;
|
||||
}
|
||||
|
@ -2236,7 +2186,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int omap_hsmmc_suspend(struct device *dev)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||
|
@ -2292,10 +2242,6 @@ static int omap_hsmmc_resume(struct device *dev)
|
|||
pm_runtime_put_autosuspend(host->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define omap_hsmmc_suspend NULL
|
||||
#define omap_hsmmc_resume NULL
|
||||
#endif
|
||||
|
||||
static int omap_hsmmc_runtime_suspend(struct device *dev)
|
||||
|
@ -2376,8 +2322,7 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
|
|||
}
|
||||
|
||||
static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
|
||||
.suspend = omap_hsmmc_suspend,
|
||||
.resume = omap_hsmmc_resume,
|
||||
SET_SYSTEM_SLEEP_PM_OPS(omap_hsmmc_suspend, omap_hsmmc_resume)
|
||||
.runtime_suspend = omap_hsmmc_runtime_suspend,
|
||||
.runtime_resume = omap_hsmmc_runtime_resume,
|
||||
};
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/pm.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/mmc/sdhci.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
|
||||
|
|
|
@ -54,7 +54,6 @@
|
|||
|
||||
struct sdhci_bcm_kona_dev {
|
||||
struct mutex write_lock; /* protect back to back writes */
|
||||
struct clk *external_clk;
|
||||
};
|
||||
|
||||
|
||||
|
@ -175,24 +174,6 @@ static void sdhci_bcm_kona_card_event(struct sdhci_host *host)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the base clock. Use central clock source for now. Not sure if different
|
||||
* clock speed to each dev is allowed
|
||||
*/
|
||||
static unsigned int sdhci_bcm_kona_get_max_clk(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_bcm_kona_dev *kona_dev;
|
||||
struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
|
||||
kona_dev = sdhci_pltfm_priv(pltfm_priv);
|
||||
|
||||
return host->mmc->f_max;
|
||||
}
|
||||
|
||||
static unsigned int sdhci_bcm_kona_get_timeout_clock(struct sdhci_host *host)
|
||||
{
|
||||
return sdhci_bcm_kona_get_max_clk(host);
|
||||
}
|
||||
|
||||
static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host,
|
||||
u8 power_mode)
|
||||
{
|
||||
|
@ -207,8 +188,8 @@ static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host,
|
|||
|
||||
static struct sdhci_ops sdhci_bcm_kona_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.get_max_clock = sdhci_bcm_kona_get_max_clk,
|
||||
.get_timeout_clock = sdhci_bcm_kona_get_timeout_clock,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
|
@ -264,21 +245,21 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
|
|||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
/* Get and enable the external clock */
|
||||
kona_dev->external_clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(kona_dev->external_clk)) {
|
||||
dev_err(dev, "Failed to get external clock\n");
|
||||
ret = PTR_ERR(kona_dev->external_clk);
|
||||
/* Get and enable the core clock */
|
||||
pltfm_priv->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(pltfm_priv->clk)) {
|
||||
dev_err(dev, "Failed to get core clock\n");
|
||||
ret = PTR_ERR(pltfm_priv->clk);
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
if (clk_set_rate(kona_dev->external_clk, host->mmc->f_max) != 0) {
|
||||
dev_err(dev, "Failed to set rate external clock\n");
|
||||
if (clk_set_rate(pltfm_priv->clk, host->mmc->f_max) != 0) {
|
||||
dev_err(dev, "Failed to set rate core clock\n");
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
if (clk_prepare_enable(kona_dev->external_clk) != 0) {
|
||||
dev_err(dev, "Failed to enable external clock\n");
|
||||
if (clk_prepare_enable(pltfm_priv->clk) != 0) {
|
||||
dev_err(dev, "Failed to enable core clock\n");
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
|
@ -333,7 +314,7 @@ err_reset:
|
|||
sdhci_bcm_kona_sd_reset(host);
|
||||
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(kona_dev->external_clk);
|
||||
clk_disable_unprepare(pltfm_priv->clk);
|
||||
|
||||
err_pltfm_free:
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
@ -342,22 +323,6 @@ err_pltfm_free:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_bcm_kona_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
|
||||
struct sdhci_bcm_kona_dev *kona_dev = sdhci_pltfm_priv(pltfm_priv);
|
||||
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
|
||||
|
||||
sdhci_remove_host(host, dead);
|
||||
|
||||
clk_disable_unprepare(kona_dev->external_clk);
|
||||
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sdhci_bcm_kona_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-kona",
|
||||
|
@ -365,7 +330,7 @@ static struct platform_driver sdhci_bcm_kona_driver = {
|
|||
.of_match_table = sdhci_bcm_kona_of_match,
|
||||
},
|
||||
.probe = sdhci_bcm_kona_probe,
|
||||
.remove = sdhci_bcm_kona_remove,
|
||||
.remove = sdhci_pltfm_unregister,
|
||||
};
|
||||
module_platform_driver(sdhci_bcm_kona_driver);
|
||||
|
||||
|
|
|
@ -180,11 +180,6 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int bcm2835_sdhci_remove(struct platform_device *pdev)
|
||||
{
|
||||
return sdhci_pltfm_unregister(pdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id bcm2835_sdhci_of_match[] = {
|
||||
{ .compatible = "brcm,bcm2835-sdhci" },
|
||||
{ }
|
||||
|
@ -198,7 +193,7 @@ static struct platform_driver bcm2835_sdhci_driver = {
|
|||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
},
|
||||
.probe = bcm2835_sdhci_probe,
|
||||
.remove = bcm2835_sdhci_remove,
|
||||
.remove = sdhci_pltfm_unregister,
|
||||
};
|
||||
module_platform_driver(bcm2835_sdhci_driver);
|
||||
|
||||
|
|
|
@ -98,18 +98,13 @@ static int sdhci_cns3xxx_probe(struct platform_device *pdev)
|
|||
return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata, 0);
|
||||
}
|
||||
|
||||
static int sdhci_cns3xxx_remove(struct platform_device *pdev)
|
||||
{
|
||||
return sdhci_pltfm_unregister(pdev);
|
||||
}
|
||||
|
||||
static struct platform_driver sdhci_cns3xxx_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-cns3xxx",
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
},
|
||||
.probe = sdhci_cns3xxx_probe,
|
||||
.remove = sdhci_cns3xxx_remove,
|
||||
.remove = sdhci_pltfm_unregister,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_cns3xxx_driver);
|
||||
|
|
|
@ -28,10 +28,6 @@
|
|||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
struct sdhci_dove_priv {
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)
|
||||
{
|
||||
u16 ret;
|
||||
|
@ -84,27 +80,17 @@ static int sdhci_dove_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_dove_priv *priv;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_dove_priv),
|
||||
GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev, "unable to allocate private data");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata, 0);
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = priv;
|
||||
pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_prepare_enable(priv->clk);
|
||||
if (!IS_ERR(pltfm_host->clk))
|
||||
clk_prepare_enable(pltfm_host->clk);
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret)
|
||||
|
@ -117,26 +103,11 @@ static int sdhci_dove_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
err_sdhci_add:
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_disable_unprepare(priv->clk);
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
sdhci_pltfm_free(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_dove_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_dove_priv *priv = pltfm_host->priv;
|
||||
|
||||
sdhci_pltfm_unregister(pdev);
|
||||
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sdhci_dove_of_match_table[] = {
|
||||
{ .compatible = "marvell,dove-sdhci", },
|
||||
{}
|
||||
|
@ -150,7 +121,7 @@ static struct platform_driver sdhci_dove_driver = {
|
|||
.of_match_table = sdhci_dove_of_match_table,
|
||||
},
|
||||
.probe = sdhci_dove_probe,
|
||||
.remove = sdhci_dove_remove,
|
||||
.remove = sdhci_pltfm_unregister,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_dove_driver);
|
||||
|
|
|
@ -416,7 +416,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
|||
new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
|
||||
else
|
||||
new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
|
||||
writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
|
||||
writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
|
||||
return;
|
||||
case SDHCI_HOST_CONTROL2:
|
||||
new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
|
||||
|
@ -864,6 +864,7 @@ static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
|
|||
#ifdef CONFIG_OF
|
||||
static int
|
||||
sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
||||
struct sdhci_host *host,
|
||||
struct esdhc_platform_data *boarddata)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
|
@ -900,11 +901,14 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
|||
if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
|
||||
boarddata->delay_line = 0;
|
||||
|
||||
mmc_of_parse_voltage(np, &host->ocr_mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int
|
||||
sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
||||
struct sdhci_host *host,
|
||||
struct esdhc_platform_data *boarddata)
|
||||
{
|
||||
return -ENODEV;
|
||||
|
@ -999,7 +1003,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
|
||||
boarddata = &imx_data->boarddata;
|
||||
if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
|
||||
if (sdhci_esdhc_imx_probe_dt(pdev, host, boarddata) < 0) {
|
||||
if (!host->mmc->parent->platform_data) {
|
||||
dev_err(mmc_dev(host->mmc), "no board data!\n");
|
||||
err = -EINVAL;
|
||||
|
@ -1009,40 +1013,9 @@ static int sdhci_esdhc_imx_probe(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);
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"failed to request write-protect gpio!\n");
|
||||
goto disable_clk;
|
||||
}
|
||||
host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
}
|
||||
|
||||
/* card_detect */
|
||||
switch (boarddata->cd_type) {
|
||||
case ESDHC_CD_GPIO:
|
||||
err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio, 0);
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"failed to request card-detect gpio!\n");
|
||||
goto disable_clk;
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
case ESDHC_CD_CONTROLLER:
|
||||
/* we have a working card_detect back */
|
||||
if (boarddata->cd_type == ESDHC_CD_CONTROLLER)
|
||||
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||
break;
|
||||
|
||||
case ESDHC_CD_PERMANENT:
|
||||
host->mmc->caps |= MMC_CAP_NONREMOVABLE;
|
||||
break;
|
||||
|
||||
case ESDHC_CD_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (boarddata->max_bus_width) {
|
||||
case 8:
|
||||
|
@ -1075,6 +1048,11 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
|
||||
}
|
||||
|
||||
/* call to generic mmc_of_parse to support additional capabilities */
|
||||
err = mmc_of_parse(host->mmc);
|
||||
if (err)
|
||||
goto disable_clk;
|
||||
|
||||
err = sdhci_add_host(host);
|
||||
if (err)
|
||||
goto disable_clk;
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* iProc SDHCI platform driver
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
struct sdhci_iproc_data {
|
||||
const struct sdhci_pltfm_data *pdata;
|
||||
u32 caps;
|
||||
u32 caps1;
|
||||
};
|
||||
|
||||
struct sdhci_iproc_host {
|
||||
const struct sdhci_iproc_data *data;
|
||||
u32 shadow_cmd;
|
||||
u32 shadow_blk;
|
||||
};
|
||||
|
||||
#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
|
||||
|
||||
static inline u32 sdhci_iproc_readl(struct sdhci_host *host, int reg)
|
||||
{
|
||||
u32 val = readl(host->ioaddr + reg);
|
||||
|
||||
pr_debug("%s: readl [0x%02x] 0x%08x\n",
|
||||
mmc_hostname(host->mmc), reg, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static u16 sdhci_iproc_readw(struct sdhci_host *host, int reg)
|
||||
{
|
||||
u32 val = sdhci_iproc_readl(host, (reg & ~3));
|
||||
u16 word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
|
||||
return word;
|
||||
}
|
||||
|
||||
static u8 sdhci_iproc_readb(struct sdhci_host *host, int reg)
|
||||
{
|
||||
u32 val = sdhci_iproc_readl(host, (reg & ~3));
|
||||
u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff;
|
||||
return byte;
|
||||
}
|
||||
|
||||
static inline void sdhci_iproc_writel(struct sdhci_host *host, u32 val, int reg)
|
||||
{
|
||||
pr_debug("%s: writel [0x%02x] 0x%08x\n",
|
||||
mmc_hostname(host->mmc), reg, val);
|
||||
|
||||
writel(val, host->ioaddr + reg);
|
||||
|
||||
if (host->clock <= 400000) {
|
||||
/* Round up to micro-second four SD clock delay */
|
||||
if (host->clock)
|
||||
udelay((4 * 1000000 + host->clock - 1) / host->clock);
|
||||
else
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The Arasan has a bugette whereby it may lose the content of successive
|
||||
* writes to the same register that are within two SD-card clock cycles of
|
||||
* each other (a clock domain crossing problem). The data
|
||||
* register does not have this problem, which is just as well - otherwise we'd
|
||||
* have to nobble the DMA engine too.
|
||||
*
|
||||
* This wouldn't be a problem with the code except that we can only write the
|
||||
* controller with 32-bit writes. So two different 16-bit registers are
|
||||
* written back to back creates the problem.
|
||||
*
|
||||
* In reality, this only happens when SDHCI_BLOCK_SIZE and SDHCI_BLOCK_COUNT
|
||||
* are written followed by SDHCI_TRANSFER_MODE and SDHCI_COMMAND.
|
||||
* The BLOCK_SIZE and BLOCK_COUNT are meaningless until a command issued so
|
||||
* the work around can be further optimized. We can keep shadow values of
|
||||
* BLOCK_SIZE, BLOCK_COUNT, and TRANSFER_MODE until a COMMAND is issued.
|
||||
* Then, write the BLOCK_SIZE+BLOCK_COUNT in a single 32-bit write followed
|
||||
* by the TRANSFER+COMMAND in another 32-bit write.
|
||||
*/
|
||||
static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_iproc_host *iproc_host = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 word_shift = REG_OFFSET_IN_BITS(reg);
|
||||
u32 mask = 0xffff << word_shift;
|
||||
u32 oldval, newval;
|
||||
|
||||
if (reg == SDHCI_COMMAND) {
|
||||
/* Write the block now as we are issuing a command */
|
||||
if (iproc_host->shadow_blk != 0) {
|
||||
sdhci_iproc_writel(host, iproc_host->shadow_blk,
|
||||
SDHCI_BLOCK_SIZE);
|
||||
iproc_host->shadow_blk = 0;
|
||||
}
|
||||
oldval = iproc_host->shadow_cmd;
|
||||
} else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
|
||||
/* Block size and count are stored in shadow reg */
|
||||
oldval = iproc_host->shadow_blk;
|
||||
} else {
|
||||
/* Read reg, all other registers are not shadowed */
|
||||
oldval = sdhci_iproc_readl(host, (reg & ~3));
|
||||
}
|
||||
newval = (oldval & ~mask) | (val << word_shift);
|
||||
|
||||
if (reg == SDHCI_TRANSFER_MODE) {
|
||||
/* Save the transfer mode until the command is issued */
|
||||
iproc_host->shadow_cmd = newval;
|
||||
} else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
|
||||
/* Save the block info until the command is issued */
|
||||
iproc_host->shadow_blk = newval;
|
||||
} else {
|
||||
/* Command or other regular 32-bit write */
|
||||
sdhci_iproc_writel(host, newval, reg & ~3);
|
||||
}
|
||||
}
|
||||
|
||||
static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg)
|
||||
{
|
||||
u32 oldval = sdhci_iproc_readl(host, (reg & ~3));
|
||||
u32 byte_shift = REG_OFFSET_IN_BITS(reg);
|
||||
u32 mask = 0xff << byte_shift;
|
||||
u32 newval = (oldval & ~mask) | (val << byte_shift);
|
||||
|
||||
sdhci_iproc_writel(host, newval, reg & ~3);
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_iproc_ops = {
|
||||
.read_l = sdhci_iproc_readl,
|
||||
.read_w = sdhci_iproc_readw,
|
||||
.read_b = sdhci_iproc_readb,
|
||||
.write_l = sdhci_iproc_writel,
|
||||
.write_w = sdhci_iproc_writew,
|
||||
.write_b = sdhci_iproc_writeb,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_iproc_pltfm_data = {
|
||||
.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
|
||||
.quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN,
|
||||
.ops = &sdhci_iproc_ops,
|
||||
};
|
||||
|
||||
static const struct sdhci_iproc_data iproc_data = {
|
||||
.pdata = &sdhci_iproc_pltfm_data,
|
||||
.caps = 0x05E90000,
|
||||
.caps1 = 0x00000064,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_iproc_of_match[] = {
|
||||
{ .compatible = "brcm,sdhci-iproc-cygnus", .data = &iproc_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_iproc_of_match);
|
||||
|
||||
static int sdhci_iproc_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
const struct sdhci_iproc_data *iproc_data;
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_iproc_host *iproc_host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
int ret;
|
||||
|
||||
match = of_match_device(sdhci_iproc_of_match, &pdev->dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
iproc_data = match->data;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, iproc_data->pdata, sizeof(*iproc_host));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
iproc_host = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
iproc_host->data = iproc_data;
|
||||
|
||||
mmc_of_parse(host->mmc);
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
/* Enable EMMC 1/8V DDR capable */
|
||||
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||
|
||||
pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(pltfm_host->clk)) {
|
||||
ret = PTR_ERR(pltfm_host->clk);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (iproc_host->data->pdata->quirks & SDHCI_QUIRK_MISSING_CAPS) {
|
||||
host->caps = iproc_host->data->caps;
|
||||
host->caps1 = iproc_host->data->caps1;
|
||||
}
|
||||
|
||||
return sdhci_add_host(host);
|
||||
|
||||
err:
|
||||
sdhci_pltfm_free(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_iproc_remove(struct platform_device *pdev)
|
||||
{
|
||||
return sdhci_pltfm_unregister(pdev);
|
||||
}
|
||||
|
||||
static struct platform_driver sdhci_iproc_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-iproc",
|
||||
.of_match_table = sdhci_iproc_of_match,
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
},
|
||||
.probe = sdhci_iproc_probe,
|
||||
.remove = sdhci_iproc_remove,
|
||||
};
|
||||
module_platform_driver(sdhci_iproc_driver);
|
||||
|
||||
MODULE_AUTHOR("Broadcom");
|
||||
MODULE_DESCRIPTION("IPROC SDHCI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -22,6 +22,11 @@
|
|||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
#define CORE_MCI_VERSION 0x50
|
||||
#define CORE_VERSION_MAJOR_SHIFT 28
|
||||
#define CORE_VERSION_MAJOR_MASK (0xf << CORE_VERSION_MAJOR_SHIFT)
|
||||
#define CORE_VERSION_MINOR_MASK 0xff
|
||||
|
||||
#define CORE_HC_MODE 0x78
|
||||
#define HC_MODE_EN 0x1
|
||||
#define CORE_POWER 0x0
|
||||
|
@ -41,6 +46,8 @@
|
|||
#define CORE_VENDOR_SPEC 0x10c
|
||||
#define CORE_CLK_PWRSAVE BIT(1)
|
||||
|
||||
#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c
|
||||
|
||||
#define CDR_SELEXT_SHIFT 20
|
||||
#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)
|
||||
#define CMUX_SHIFT_PHASE_SHIFT 24
|
||||
|
@ -426,7 +433,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
struct sdhci_msm_host *msm_host;
|
||||
struct resource *core_memres;
|
||||
int ret;
|
||||
u16 host_version;
|
||||
u16 host_version, core_minor;
|
||||
u32 core_version, caps;
|
||||
u8 core_major;
|
||||
|
||||
msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL);
|
||||
if (!msm_host)
|
||||
|
@ -516,6 +525,24 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
|
||||
SDHCI_VENDOR_VER_SHIFT));
|
||||
|
||||
core_version = readl_relaxed(msm_host->core_mem + CORE_MCI_VERSION);
|
||||
core_major = (core_version & CORE_VERSION_MAJOR_MASK) >>
|
||||
CORE_VERSION_MAJOR_SHIFT;
|
||||
core_minor = core_version & CORE_VERSION_MINOR_MASK;
|
||||
dev_dbg(&pdev->dev, "MCI Version: 0x%08x, major: 0x%04x, minor: 0x%02x\n",
|
||||
core_version, core_major, core_minor);
|
||||
|
||||
/*
|
||||
* Support for some capabilities is not advertised by newer
|
||||
* controller versions and must be explicitly enabled.
|
||||
*/
|
||||
if (core_major >= 1 && core_minor != 0x11 && core_minor != 0x12) {
|
||||
caps = readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES);
|
||||
caps |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT;
|
||||
writel_relaxed(caps, host->ioaddr +
|
||||
CORE_VENDOR_SPEC_CAPABILITIES0);
|
||||
}
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto clk_disable;
|
||||
|
|
|
@ -173,6 +173,12 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
|||
pltfm_host->priv = sdhci_arasan;
|
||||
pltfm_host->clk = clk_xin;
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "parsing dt failed (%u)\n", ret);
|
||||
goto clk_disable_all;
|
||||
}
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto err_pltfm_free;
|
||||
|
@ -195,7 +201,6 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
|
|||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
|
||||
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
clk_disable_unprepare(sdhci_arasan->clk_ahb);
|
||||
|
||||
return sdhci_pltfm_unregister(pdev);
|
||||
|
|
|
@ -386,11 +386,6 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_esdhc_remove(struct platform_device *pdev)
|
||||
{
|
||||
return sdhci_pltfm_unregister(pdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id sdhci_esdhc_of_match[] = {
|
||||
{ .compatible = "fsl,mpc8379-esdhc" },
|
||||
{ .compatible = "fsl,mpc8536-esdhc" },
|
||||
|
@ -406,7 +401,7 @@ static struct platform_driver sdhci_esdhc_driver = {
|
|||
.pm = ESDHC_PMOPS,
|
||||
},
|
||||
.probe = sdhci_esdhc_probe,
|
||||
.remove = sdhci_esdhc_remove,
|
||||
.remove = sdhci_pltfm_unregister,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_esdhc_driver);
|
||||
|
|
|
@ -75,11 +75,6 @@ static int sdhci_hlwd_probe(struct platform_device *pdev)
|
|||
return sdhci_pltfm_register(pdev, &sdhci_hlwd_pdata, 0);
|
||||
}
|
||||
|
||||
static int sdhci_hlwd_remove(struct platform_device *pdev)
|
||||
{
|
||||
return sdhci_pltfm_unregister(pdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id sdhci_hlwd_of_match[] = {
|
||||
{ .compatible = "nintendo,hollywood-sdhci" },
|
||||
{ }
|
||||
|
@ -93,7 +88,7 @@ static struct platform_driver sdhci_hlwd_driver = {
|
|||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
},
|
||||
.probe = sdhci_hlwd_probe,
|
||||
.remove = sdhci_hlwd_remove,
|
||||
.remove = sdhci_pltfm_unregister,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_hlwd_driver);
|
||||
|
|
|
@ -650,6 +650,7 @@ static int rtsx_probe_slot(struct sdhci_pci_slot *slot)
|
|||
|
||||
static const struct sdhci_pci_fixes sdhci_rtsx = {
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_BROKEN_64_BIT_DMA |
|
||||
SDHCI_QUIRK2_BROKEN_DDR50,
|
||||
.probe_slot = rtsx_probe_slot,
|
||||
};
|
||||
|
|
|
@ -75,43 +75,41 @@ void sdhci_get_of_property(struct platform_device *pdev)
|
|||
u32 bus_width;
|
||||
int size;
|
||||
|
||||
if (of_device_is_available(np)) {
|
||||
if (of_get_property(np, "sdhci,auto-cmd12", NULL))
|
||||
host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
|
||||
if (of_get_property(np, "sdhci,auto-cmd12", NULL))
|
||||
host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
|
||||
|
||||
if (of_get_property(np, "sdhci,1-bit-only", NULL) ||
|
||||
(of_property_read_u32(np, "bus-width", &bus_width) == 0 &&
|
||||
bus_width == 1))
|
||||
host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
|
||||
if (of_get_property(np, "sdhci,1-bit-only", NULL) ||
|
||||
(of_property_read_u32(np, "bus-width", &bus_width) == 0 &&
|
||||
bus_width == 1))
|
||||
host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
|
||||
|
||||
if (sdhci_of_wp_inverted(np))
|
||||
host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
|
||||
if (sdhci_of_wp_inverted(np))
|
||||
host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
|
||||
|
||||
if (of_get_property(np, "broken-cd", NULL))
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||
if (of_get_property(np, "broken-cd", NULL))
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||
|
||||
if (of_get_property(np, "no-1-8-v", NULL))
|
||||
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
|
||||
if (of_get_property(np, "no-1-8-v", NULL))
|
||||
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc"))
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_DMA;
|
||||
if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc"))
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_DMA;
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,p2020-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,p1010-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,t4240-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,mpc8536-esdhc"))
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||
if (of_device_is_compatible(np, "fsl,p2020-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,p1010-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,t4240-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,mpc8536-esdhc"))
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||
|
||||
clk = of_get_property(np, "clock-frequency", &size);
|
||||
if (clk && size == sizeof(*clk) && *clk)
|
||||
pltfm_host->clock = be32_to_cpup(clk);
|
||||
clk = of_get_property(np, "clock-frequency", &size);
|
||||
if (clk && size == sizeof(*clk) && *clk)
|
||||
pltfm_host->clock = be32_to_cpup(clk);
|
||||
|
||||
if (of_find_property(np, "keep-power-in-suspend", NULL))
|
||||
host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
|
||||
if (of_find_property(np, "keep-power-in-suspend", NULL))
|
||||
host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
|
||||
|
||||
if (of_find_property(np, "enable-sdio-wakeup", NULL))
|
||||
host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
||||
}
|
||||
if (of_find_property(np, "enable-sdio-wakeup", NULL))
|
||||
host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
||||
}
|
||||
#else
|
||||
void sdhci_get_of_property(struct platform_device *pdev) {}
|
||||
|
@ -225,9 +223,11 @@ EXPORT_SYMBOL_GPL(sdhci_pltfm_register);
|
|||
int sdhci_pltfm_unregister(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
|
||||
|
||||
sdhci_remove_host(host, dead);
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -20,17 +20,9 @@
|
|||
#define SIRF_TUNING_COUNT 128
|
||||
|
||||
struct sdhci_sirf_priv {
|
||||
struct clk *clk;
|
||||
int gpio_cd;
|
||||
};
|
||||
|
||||
static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
return clk_get_rate(priv->clk);
|
||||
}
|
||||
|
||||
static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width)
|
||||
{
|
||||
u8 ctrl;
|
||||
|
@ -56,7 +48,7 @@ static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
|
|||
int tuning_seq_cnt = 3;
|
||||
u8 phase, tuned_phases[SIRF_TUNING_COUNT];
|
||||
u8 tuned_phase_cnt = 0;
|
||||
int rc, longest_range = 0;
|
||||
int rc = 0, longest_range = 0;
|
||||
int start = -1, end = 0, tuning_value = -1, range = 0;
|
||||
u16 clock_setting;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
@ -68,7 +60,7 @@ retry:
|
|||
phase = 0;
|
||||
do {
|
||||
sdhci_writel(host,
|
||||
clock_setting | phase | (phase << 7) | (phase << 16),
|
||||
clock_setting | phase,
|
||||
SDHCI_CLK_DELAY_SETTING);
|
||||
|
||||
if (!mmc_send_tuning(mmc)) {
|
||||
|
@ -102,7 +94,7 @@ retry:
|
|||
*/
|
||||
phase = tuning_value;
|
||||
sdhci_writel(host,
|
||||
clock_setting | phase | (phase << 7) | (phase << 16),
|
||||
clock_setting | phase,
|
||||
SDHCI_CLK_DELAY_SETTING);
|
||||
|
||||
dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
|
||||
|
@ -122,7 +114,7 @@ retry:
|
|||
static struct sdhci_ops sdhci_sirf_ops = {
|
||||
.platform_execute_tuning = sdhci_sirf_execute_tuning,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.get_max_clock = sdhci_sirf_get_max_clk,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.set_bus_width = sdhci_sirf_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
|
@ -162,13 +154,13 @@ static int sdhci_sirf_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->clk = clk;
|
||||
priv = sdhci_pltfm_priv(pltfm_host);
|
||||
priv->clk = clk;
|
||||
priv->gpio_cd = gpio_cd;
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
ret = clk_prepare_enable(pltfm_host->clk);
|
||||
if (ret)
|
||||
goto err_clk_prepare;
|
||||
|
||||
|
@ -195,37 +187,24 @@ static int sdhci_sirf_probe(struct platform_device *pdev)
|
|||
err_request_cd:
|
||||
sdhci_remove_host(host, 0);
|
||||
err_sdhci_add:
|
||||
clk_disable_unprepare(priv->clk);
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
err_clk_prepare:
|
||||
sdhci_pltfm_free(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_sirf_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
sdhci_pltfm_unregister(pdev);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int sdhci_sirf_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = sdhci_suspend_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable(priv->clk);
|
||||
clk_disable(pltfm_host->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -234,10 +213,9 @@ static int sdhci_sirf_resume(struct device *dev)
|
|||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(priv->clk);
|
||||
ret = clk_enable(pltfm_host->clk);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "Resume: Error enabling clock\n");
|
||||
return ret;
|
||||
|
@ -264,7 +242,7 @@ static struct platform_driver sdhci_sirf_driver = {
|
|||
#endif
|
||||
},
|
||||
.probe = sdhci_sirf_probe,
|
||||
.remove = sdhci_sirf_remove,
|
||||
.remove = sdhci_pltfm_unregister,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_sirf_driver);
|
||||
|
|
|
@ -26,14 +26,13 @@
|
|||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/sdhci-spear.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include "sdhci.h"
|
||||
|
||||
struct spear_sdhci {
|
||||
struct clk *clk;
|
||||
struct sdhci_plat_data *data;
|
||||
int card_int_gpio;
|
||||
};
|
||||
|
||||
/* sdhci ops */
|
||||
|
@ -44,38 +43,20 @@ static const struct sdhci_ops sdhci_pltfm_ops = {
|
|||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev)
|
||||
static void sdhci_probe_config_dt(struct device_node *np,
|
||||
struct spear_sdhci *host)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct sdhci_plat_data *pdata = NULL;
|
||||
int cd_gpio;
|
||||
|
||||
cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
|
||||
if (!gpio_is_valid(cd_gpio))
|
||||
cd_gpio = -1;
|
||||
|
||||
/* If pdata is required */
|
||||
if (cd_gpio != -1) {
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
dev_err(&pdev->dev, "DT: kzalloc failed\n");
|
||||
else
|
||||
pdata->card_int_gpio = cd_gpio;
|
||||
}
|
||||
|
||||
return pdata;
|
||||
host->card_int_gpio = cd_gpio;
|
||||
}
|
||||
#else
|
||||
static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev)
|
||||
{
|
||||
return ERR_PTR(-ENOSYS);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int sdhci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct sdhci_host *host;
|
||||
struct resource *iomem;
|
||||
struct spear_sdhci *sdhci;
|
||||
|
@ -124,28 +105,18 @@ static int sdhci_probe(struct platform_device *pdev)
|
|||
dev_dbg(&pdev->dev, "Error setting desired clk, clk=%lu\n",
|
||||
clk_get_rate(sdhci->clk));
|
||||
|
||||
if (np) {
|
||||
sdhci->data = sdhci_probe_config_dt(pdev);
|
||||
if (IS_ERR(sdhci->data)) {
|
||||
dev_err(&pdev->dev, "DT: Failed to get pdata\n");
|
||||
goto disable_clk;
|
||||
}
|
||||
} else {
|
||||
sdhci->data = dev_get_platdata(&pdev->dev);
|
||||
}
|
||||
|
||||
sdhci_probe_config_dt(pdev->dev.of_node, sdhci);
|
||||
/*
|
||||
* It is optional to use GPIOs for sdhci card detection. If
|
||||
* sdhci->data is NULL, then use original sdhci lines otherwise
|
||||
* sdhci->card_int_gpio < 0, then use original sdhci lines otherwise
|
||||
* GPIO lines. We use the built-in GPIO support for this.
|
||||
*/
|
||||
if (sdhci->data && sdhci->data->card_int_gpio >= 0) {
|
||||
ret = mmc_gpio_request_cd(host->mmc,
|
||||
sdhci->data->card_int_gpio, 0);
|
||||
if (sdhci->card_int_gpio >= 0) {
|
||||
ret = mmc_gpio_request_cd(host->mmc, sdhci->card_int_gpio, 0);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"failed to request card-detect gpio%d\n",
|
||||
sdhci->data->card_int_gpio);
|
||||
sdhci->card_int_gpio);
|
||||
goto disable_clk;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,295 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
#include <linux/reset.h>
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
struct st_mmc_platform_data {
|
||||
struct reset_control *rstc;
|
||||
void __iomem *top_ioaddr;
|
||||
};
|
||||
|
||||
/* MMCSS glue logic to setup the HC on some ST SoCs (e.g. STiH407 family) */
|
||||
|
||||
#define ST_MMC_CCONFIG_REG_1 0x400
|
||||
#define ST_MMC_CCONFIG_TIMEOUT_CLK_UNIT BIT(24)
|
||||
#define ST_MMC_CCONFIG_TIMEOUT_CLK_FREQ BIT(12)
|
||||
#define ST_MMC_CCONFIG_TUNING_COUNT_DEFAULT BIT(8)
|
||||
#define ST_MMC_CCONFIG_ASYNC_WAKEUP BIT(0)
|
||||
#define ST_MMC_CCONFIG_1_DEFAULT \
|
||||
((ST_MMC_CCONFIG_TIMEOUT_CLK_UNIT) | \
|
||||
(ST_MMC_CCONFIG_TIMEOUT_CLK_FREQ) | \
|
||||
(ST_MMC_CCONFIG_TUNING_COUNT_DEFAULT))
|
||||
|
||||
#define ST_MMC_CCONFIG_REG_2 0x404
|
||||
#define ST_MMC_CCONFIG_HIGH_SPEED BIT(28)
|
||||
#define ST_MMC_CCONFIG_ADMA2 BIT(24)
|
||||
#define ST_MMC_CCONFIG_8BIT BIT(20)
|
||||
#define ST_MMC_CCONFIG_MAX_BLK_LEN 16
|
||||
#define MAX_BLK_LEN_1024 1
|
||||
#define MAX_BLK_LEN_2048 2
|
||||
#define BASE_CLK_FREQ_200 0xc8
|
||||
#define BASE_CLK_FREQ_100 0x64
|
||||
#define BASE_CLK_FREQ_50 0x32
|
||||
#define ST_MMC_CCONFIG_2_DEFAULT \
|
||||
(ST_MMC_CCONFIG_HIGH_SPEED | ST_MMC_CCONFIG_ADMA2 | \
|
||||
ST_MMC_CCONFIG_8BIT | \
|
||||
(MAX_BLK_LEN_1024 << ST_MMC_CCONFIG_MAX_BLK_LEN))
|
||||
|
||||
#define ST_MMC_CCONFIG_REG_3 0x408
|
||||
#define ST_MMC_CCONFIG_EMMC_SLOT_TYPE BIT(28)
|
||||
#define ST_MMC_CCONFIG_64BIT BIT(24)
|
||||
#define ST_MMC_CCONFIG_ASYNCH_INTR_SUPPORT BIT(20)
|
||||
#define ST_MMC_CCONFIG_1P8_VOLT BIT(16)
|
||||
#define ST_MMC_CCONFIG_3P0_VOLT BIT(12)
|
||||
#define ST_MMC_CCONFIG_3P3_VOLT BIT(8)
|
||||
#define ST_MMC_CCONFIG_SUSP_RES_SUPPORT BIT(4)
|
||||
#define ST_MMC_CCONFIG_SDMA BIT(0)
|
||||
#define ST_MMC_CCONFIG_3_DEFAULT \
|
||||
(ST_MMC_CCONFIG_ASYNCH_INTR_SUPPORT | \
|
||||
ST_MMC_CCONFIG_3P3_VOLT | \
|
||||
ST_MMC_CCONFIG_SUSP_RES_SUPPORT | \
|
||||
ST_MMC_CCONFIG_SDMA)
|
||||
|
||||
#define ST_MMC_CCONFIG_REG_4 0x40c
|
||||
#define ST_MMC_CCONFIG_D_DRIVER BIT(20)
|
||||
#define ST_MMC_CCONFIG_C_DRIVER BIT(16)
|
||||
#define ST_MMC_CCONFIG_A_DRIVER BIT(12)
|
||||
#define ST_MMC_CCONFIG_DDR50 BIT(8)
|
||||
#define ST_MMC_CCONFIG_SDR104 BIT(4)
|
||||
#define ST_MMC_CCONFIG_SDR50 BIT(0)
|
||||
#define ST_MMC_CCONFIG_4_DEFAULT 0
|
||||
|
||||
#define ST_MMC_CCONFIG_REG_5 0x410
|
||||
#define ST_MMC_CCONFIG_TUNING_FOR_SDR50 BIT(8)
|
||||
#define RETUNING_TIMER_CNT_MAX 0xf
|
||||
#define ST_MMC_CCONFIG_5_DEFAULT 0
|
||||
|
||||
/* I/O configuration for Arasan IP */
|
||||
#define ST_MMC_GP_OUTPUT 0x450
|
||||
#define ST_MMC_GP_OUTPUT_CD BIT(12)
|
||||
|
||||
#define ST_MMC_STATUS_R 0x460
|
||||
|
||||
#define ST_TOP_MMC_DLY_FIX_OFF(x) (x - 0x8)
|
||||
|
||||
/* TOP config registers to manage static and dynamic delay */
|
||||
#define ST_TOP_MMC_TX_CLK_DLY ST_TOP_MMC_DLY_FIX_OFF(0x8)
|
||||
#define ST_TOP_MMC_RX_CLK_DLY ST_TOP_MMC_DLY_FIX_OFF(0xc)
|
||||
/* MMC delay control register */
|
||||
#define ST_TOP_MMC_DLY_CTRL ST_TOP_MMC_DLY_FIX_OFF(0x18)
|
||||
#define ST_TOP_MMC_DLY_CTRL_DLL_BYPASS_CMD BIT(0)
|
||||
#define ST_TOP_MMC_DLY_CTRL_DLL_BYPASS_PH_SEL BIT(1)
|
||||
#define ST_TOP_MMC_DLY_CTRL_TX_DLL_ENABLE BIT(8)
|
||||
#define ST_TOP_MMC_DLY_CTRL_RX_DLL_ENABLE BIT(9)
|
||||
#define ST_TOP_MMC_DLY_CTRL_ATUNE_NOT_CFG_DLY BIT(10)
|
||||
#define ST_TOP_MMC_START_DLL_LOCK BIT(11)
|
||||
|
||||
/* register to provide the phase-shift value for DLL */
|
||||
#define ST_TOP_MMC_TX_DLL_STEP_DLY ST_TOP_MMC_DLY_FIX_OFF(0x1c)
|
||||
#define ST_TOP_MMC_RX_DLL_STEP_DLY ST_TOP_MMC_DLY_FIX_OFF(0x20)
|
||||
#define ST_TOP_MMC_RX_CMD_STEP_DLY ST_TOP_MMC_DLY_FIX_OFF(0x24)
|
||||
|
||||
/* phase shift delay on the tx clk 2.188ns */
|
||||
#define ST_TOP_MMC_TX_DLL_STEP_DLY_VALID 0x6
|
||||
|
||||
#define ST_TOP_MMC_DLY_MAX 0xf
|
||||
|
||||
#define ST_TOP_MMC_DYN_DLY_CONF \
|
||||
(ST_TOP_MMC_DLY_CTRL_TX_DLL_ENABLE | \
|
||||
ST_TOP_MMC_DLY_CTRL_ATUNE_NOT_CFG_DLY | \
|
||||
ST_TOP_MMC_START_DLL_LOCK)
|
||||
|
||||
/*
|
||||
* For clock speeds greater than 90MHz, we need to check that the
|
||||
* DLL procedure has finished before switching to ultra-speed modes.
|
||||
*/
|
||||
#define CLK_TO_CHECK_DLL_LOCK 90000000
|
||||
|
||||
static inline void st_mmcss_set_static_delay(void __iomem *ioaddr)
|
||||
{
|
||||
if (!ioaddr)
|
||||
return;
|
||||
|
||||
writel_relaxed(0x0, ioaddr + ST_TOP_MMC_DLY_CTRL);
|
||||
writel_relaxed(ST_TOP_MMC_DLY_MAX,
|
||||
ioaddr + ST_TOP_MMC_TX_CLK_DLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_mmcss_cconfig: configure the Arasan HC inside the flashSS.
|
||||
* @np: dt device node.
|
||||
* @host: sdhci host
|
||||
* Description: this function is to configure the Arasan host controller.
|
||||
* On some ST SoCs, i.e. STiH407 family, the MMC devices inside a dedicated
|
||||
* flashSS sub-system which needs to be configured to be compliant to eMMC 4.5
|
||||
* or eMMC4.3. This has to be done before registering the sdhci host.
|
||||
*/
|
||||
static void st_mmcss_cconfig(struct device_node *np, struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct mmc_host *mhost = host->mmc;
|
||||
u32 cconf2, cconf3, cconf4, cconf5;
|
||||
|
||||
if (!of_device_is_compatible(np, "st,sdhci-stih407"))
|
||||
return;
|
||||
|
||||
cconf2 = ST_MMC_CCONFIG_2_DEFAULT;
|
||||
cconf3 = ST_MMC_CCONFIG_3_DEFAULT;
|
||||
cconf4 = ST_MMC_CCONFIG_4_DEFAULT;
|
||||
cconf5 = ST_MMC_CCONFIG_5_DEFAULT;
|
||||
|
||||
writel_relaxed(ST_MMC_CCONFIG_1_DEFAULT,
|
||||
host->ioaddr + ST_MMC_CCONFIG_REG_1);
|
||||
|
||||
/* Set clock frequency, default to 50MHz if max-frequency is not
|
||||
* provided */
|
||||
|
||||
switch (mhost->f_max) {
|
||||
case 200000000:
|
||||
clk_set_rate(pltfm_host->clk, mhost->f_max);
|
||||
cconf2 |= BASE_CLK_FREQ_200;
|
||||
break;
|
||||
case 100000000:
|
||||
clk_set_rate(pltfm_host->clk, mhost->f_max);
|
||||
cconf2 |= BASE_CLK_FREQ_100;
|
||||
break;
|
||||
default:
|
||||
clk_set_rate(pltfm_host->clk, 50000000);
|
||||
cconf2 |= BASE_CLK_FREQ_50;
|
||||
}
|
||||
|
||||
writel_relaxed(cconf2, host->ioaddr + ST_MMC_CCONFIG_REG_2);
|
||||
|
||||
if (mhost->caps & MMC_CAP_NONREMOVABLE)
|
||||
cconf3 |= ST_MMC_CCONFIG_EMMC_SLOT_TYPE;
|
||||
else
|
||||
/* CARD _D ET_CTRL */
|
||||
writel_relaxed(ST_MMC_GP_OUTPUT_CD,
|
||||
host->ioaddr + ST_MMC_GP_OUTPUT);
|
||||
|
||||
if (mhost->caps & MMC_CAP_UHS_SDR50) {
|
||||
/* use 1.8V */
|
||||
cconf3 |= ST_MMC_CCONFIG_1P8_VOLT;
|
||||
cconf4 |= ST_MMC_CCONFIG_SDR50;
|
||||
/* Use tuning */
|
||||
cconf5 |= ST_MMC_CCONFIG_TUNING_FOR_SDR50;
|
||||
/* Max timeout for retuning */
|
||||
cconf5 |= RETUNING_TIMER_CNT_MAX;
|
||||
}
|
||||
|
||||
if (mhost->caps & MMC_CAP_UHS_SDR104) {
|
||||
/*
|
||||
* SDR104 implies the HC can support HS200 mode, so
|
||||
* it's mandatory to use 1.8V
|
||||
*/
|
||||
cconf3 |= ST_MMC_CCONFIG_1P8_VOLT;
|
||||
cconf4 |= ST_MMC_CCONFIG_SDR104;
|
||||
/* Max timeout for retuning */
|
||||
cconf5 |= RETUNING_TIMER_CNT_MAX;
|
||||
}
|
||||
|
||||
if (mhost->caps & MMC_CAP_UHS_DDR50)
|
||||
cconf4 |= ST_MMC_CCONFIG_DDR50;
|
||||
|
||||
writel_relaxed(cconf3, host->ioaddr + ST_MMC_CCONFIG_REG_3);
|
||||
writel_relaxed(cconf4, host->ioaddr + ST_MMC_CCONFIG_REG_4);
|
||||
writel_relaxed(cconf5, host->ioaddr + ST_MMC_CCONFIG_REG_5);
|
||||
}
|
||||
|
||||
static inline void st_mmcss_set_dll(void __iomem *ioaddr)
|
||||
{
|
||||
if (!ioaddr)
|
||||
return;
|
||||
|
||||
writel_relaxed(ST_TOP_MMC_DYN_DLY_CONF, ioaddr + ST_TOP_MMC_DLY_CTRL);
|
||||
writel_relaxed(ST_TOP_MMC_TX_DLL_STEP_DLY_VALID,
|
||||
ioaddr + ST_TOP_MMC_TX_DLL_STEP_DLY);
|
||||
}
|
||||
|
||||
static int st_mmcss_lock_dll(void __iomem *ioaddr)
|
||||
{
|
||||
unsigned long curr, value;
|
||||
unsigned long finish = jiffies + HZ;
|
||||
|
||||
/* Checks if the DLL procedure is finished */
|
||||
do {
|
||||
curr = jiffies;
|
||||
value = readl(ioaddr + ST_MMC_STATUS_R);
|
||||
if (value & 0x1)
|
||||
return 0;
|
||||
|
||||
cpu_relax();
|
||||
} while (!time_after_eq(curr, finish));
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int sdhci_st_set_dll_for_clock(struct sdhci_host *host)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct st_mmc_platform_data *pdata = pltfm_host->priv;
|
||||
|
||||
if (host->clock > CLK_TO_CHECK_DLL_LOCK) {
|
||||
st_mmcss_set_dll(pdata->top_ioaddr);
|
||||
ret = st_mmcss_lock_dll(host->ioaddr);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sdhci_st_set_uhs_signaling(struct sdhci_host *host,
|
||||
unsigned int uhs)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct st_mmc_platform_data *pdata = pltfm_host->priv;
|
||||
u16 ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
int ret = 0;
|
||||
|
||||
/* Select Bus Speed Mode for host */
|
||||
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
|
||||
switch (uhs) {
|
||||
/*
|
||||
* Set V18_EN -- UHS modes do not work without this.
|
||||
* does not change signaling voltage
|
||||
*/
|
||||
|
||||
case MMC_TIMING_UHS_SDR12:
|
||||
st_mmcss_set_static_delay(pdata->top_ioaddr);
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR12 | SDHCI_CTRL_VDD_180;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR25:
|
||||
st_mmcss_set_static_delay(pdata->top_ioaddr);
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR25 | SDHCI_CTRL_VDD_180;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR50:
|
||||
st_mmcss_set_static_delay(pdata->top_ioaddr);
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR50 | SDHCI_CTRL_VDD_180;
|
||||
ret = sdhci_st_set_dll_for_clock(host);
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
st_mmcss_set_static_delay(pdata->top_ioaddr);
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180;
|
||||
ret = sdhci_st_set_dll_for_clock(host);
|
||||
break;
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
st_mmcss_set_static_delay(pdata->top_ioaddr);
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
dev_warn(mmc_dev(host->mmc), "Error setting dll for clock "
|
||||
"(uhs %d)\n", uhs);
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "uhs %d, ctrl_2 %04X\n", uhs, ctrl_2);
|
||||
|
||||
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||
}
|
||||
|
||||
static u32 sdhci_st_readl(struct sdhci_host *host, int reg)
|
||||
{
|
||||
u32 ret;
|
||||
|
@ -48,22 +334,33 @@ static const struct sdhci_ops sdhci_st_ops = {
|
|||
.set_bus_width = sdhci_set_bus_width,
|
||||
.read_l = sdhci_st_readl,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_st_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_st_pdata = {
|
||||
.ops = &sdhci_st_ops,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_NO_HISPD_BIT,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||
};
|
||||
|
||||
|
||||
static int sdhci_st_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct sdhci_host *host;
|
||||
struct st_mmc_platform_data *pdata;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct clk *clk;
|
||||
int ret = 0;
|
||||
u16 host_version;
|
||||
struct resource *res;
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, "mmc");
|
||||
if (IS_ERR(clk)) {
|
||||
|
@ -71,10 +368,17 @@ static int sdhci_st_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
pdata->rstc = devm_reset_control_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(pdata->rstc))
|
||||
pdata->rstc = NULL;
|
||||
else
|
||||
reset_control_deassert(pdata->rstc);
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_st_pdata, 0);
|
||||
if (IS_ERR(host)) {
|
||||
dev_err(&pdev->dev, "Failed sdhci_pltfm_init\n");
|
||||
return PTR_ERR(host);
|
||||
ret = PTR_ERR(host);
|
||||
goto err_pltfm_init;
|
||||
}
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
|
@ -85,9 +389,22 @@ static int sdhci_st_probe(struct platform_device *pdev)
|
|||
|
||||
clk_prepare_enable(clk);
|
||||
|
||||
/* Configure the FlashSS Top registers for setting eMMC TX/RX delay */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"top-mmc-delay");
|
||||
pdata->top_ioaddr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(pdata->top_ioaddr)) {
|
||||
dev_warn(&pdev->dev, "FlashSS Top Dly registers not available");
|
||||
pdata->top_ioaddr = NULL;
|
||||
}
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = pdata;
|
||||
pltfm_host->clk = clk;
|
||||
|
||||
/* Configure the Arasan HC inside the flashSS */
|
||||
st_mmcss_cconfig(np, host);
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed sdhci_add_host\n");
|
||||
|
@ -109,6 +426,9 @@ err_out:
|
|||
clk_disable_unprepare(clk);
|
||||
err_of:
|
||||
sdhci_pltfm_free(pdev);
|
||||
err_pltfm_init:
|
||||
if (pdata->rstc)
|
||||
reset_control_assert(pdata->rstc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -117,10 +437,15 @@ static int sdhci_st_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct st_mmc_platform_data *pdata = pltfm_host->priv;
|
||||
int ret;
|
||||
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
ret = sdhci_pltfm_unregister(pdev);
|
||||
|
||||
return sdhci_pltfm_unregister(pdev);
|
||||
if (pdata->rstc)
|
||||
reset_control_assert(pdata->rstc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
@ -128,11 +453,15 @@ static int sdhci_st_suspend(struct device *dev)
|
|||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct st_mmc_platform_data *pdata = pltfm_host->priv;
|
||||
int ret = sdhci_suspend_host(host);
|
||||
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (pdata->rstc)
|
||||
reset_control_assert(pdata->rstc);
|
||||
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
out:
|
||||
return ret;
|
||||
|
@ -142,9 +471,16 @@ static int sdhci_st_resume(struct device *dev)
|
|||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct st_mmc_platform_data *pdata = pltfm_host->priv;
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
clk_prepare_enable(pltfm_host->clk);
|
||||
|
||||
if (pdata->rstc)
|
||||
reset_control_deassert(pdata->rstc);
|
||||
|
||||
st_mmcss_cconfig(np, host);
|
||||
|
||||
return sdhci_resume_host(host);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -20,11 +20,10 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
|
@ -41,7 +40,6 @@
|
|||
#define NVQUIRK_DISABLE_SDR50 BIT(3)
|
||||
#define NVQUIRK_DISABLE_SDR104 BIT(4)
|
||||
#define NVQUIRK_DISABLE_DDR50 BIT(5)
|
||||
#define NVQUIRK_SHADOW_XFER_MODE_REG BIT(6)
|
||||
|
||||
struct sdhci_tegra_soc_data {
|
||||
const struct sdhci_pltfm_data *pdata;
|
||||
|
@ -50,7 +48,7 @@ struct sdhci_tegra_soc_data {
|
|||
|
||||
struct sdhci_tegra {
|
||||
const struct sdhci_tegra_soc_data *soc_data;
|
||||
int power_gpio;
|
||||
struct gpio_desc *power_gpio;
|
||||
};
|
||||
|
||||
static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
|
||||
|
@ -71,23 +69,19 @@ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
|
|||
static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = pltfm_host->priv;
|
||||
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
|
||||
|
||||
if (soc_data->nvquirks & NVQUIRK_SHADOW_XFER_MODE_REG) {
|
||||
switch (reg) {
|
||||
case SDHCI_TRANSFER_MODE:
|
||||
/*
|
||||
* Postpone this write, we must do it together with a
|
||||
* command write that is down below.
|
||||
*/
|
||||
pltfm_host->xfer_mode_shadow = val;
|
||||
return;
|
||||
case SDHCI_COMMAND:
|
||||
writel((val << 16) | pltfm_host->xfer_mode_shadow,
|
||||
host->ioaddr + SDHCI_TRANSFER_MODE);
|
||||
return;
|
||||
}
|
||||
switch (reg) {
|
||||
case SDHCI_TRANSFER_MODE:
|
||||
/*
|
||||
* Postpone this write, we must do it together with a
|
||||
* command write that is down below.
|
||||
*/
|
||||
pltfm_host->xfer_mode_shadow = val;
|
||||
return;
|
||||
case SDHCI_COMMAND:
|
||||
writel((val << 16) | pltfm_host->xfer_mode_shadow,
|
||||
host->ioaddr + SDHCI_TRANSFER_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
writew(val, host->ioaddr + reg);
|
||||
|
@ -173,7 +167,6 @@ static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
|
|||
static const struct sdhci_ops tegra_sdhci_ops = {
|
||||
.get_ro = tegra_sdhci_get_ro,
|
||||
.read_w = tegra_sdhci_readw,
|
||||
.write_w = tegra_sdhci_writew,
|
||||
.write_l = tegra_sdhci_writel,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = tegra_sdhci_set_bus_width,
|
||||
|
@ -214,6 +207,18 @@ static struct sdhci_tegra_soc_data soc_data_tegra30 = {
|
|||
NVQUIRK_DISABLE_SDR104,
|
||||
};
|
||||
|
||||
static const struct sdhci_ops tegra114_sdhci_ops = {
|
||||
.get_ro = tegra_sdhci_get_ro,
|
||||
.read_w = tegra_sdhci_readw,
|
||||
.write_w = tegra_sdhci_writew,
|
||||
.write_l = tegra_sdhci_writel,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = tegra_sdhci_set_bus_width,
|
||||
.reset = tegra_sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
|
@ -221,15 +226,14 @@ static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
|
|||
SDHCI_QUIRK_NO_HISPD_BIT |
|
||||
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||
.ops = &tegra_sdhci_ops,
|
||||
.ops = &tegra114_sdhci_ops,
|
||||
};
|
||||
|
||||
static struct sdhci_tegra_soc_data soc_data_tegra114 = {
|
||||
.pdata = &sdhci_tegra114_pdata,
|
||||
.nvquirks = NVQUIRK_DISABLE_SDR50 |
|
||||
NVQUIRK_DISABLE_DDR50 |
|
||||
NVQUIRK_DISABLE_SDR104 |
|
||||
NVQUIRK_SHADOW_XFER_MODE_REG,
|
||||
NVQUIRK_DISABLE_SDR104,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_tegra_dt_match[] = {
|
||||
|
@ -241,17 +245,6 @@ static const struct of_device_id sdhci_tegra_dt_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
|
||||
|
||||
static int sdhci_tegra_parse_dt(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = pltfm_host->priv;
|
||||
|
||||
tegra_host->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
|
||||
return mmc_of_parse(host->mmc);
|
||||
}
|
||||
|
||||
static int sdhci_tegra_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
|
@ -281,21 +274,18 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
|
|||
tegra_host->soc_data = soc_data;
|
||||
pltfm_host->priv = tegra_host;
|
||||
|
||||
rc = sdhci_tegra_parse_dt(&pdev->dev);
|
||||
rc = mmc_of_parse(host->mmc);
|
||||
if (rc)
|
||||
goto err_parse_dt;
|
||||
|
||||
if (gpio_is_valid(tegra_host->power_gpio)) {
|
||||
rc = gpio_request(tegra_host->power_gpio, "sdhci_power");
|
||||
if (rc) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"failed to allocate power gpio\n");
|
||||
goto err_power_req;
|
||||
}
|
||||
gpio_direction_output(tegra_host->power_gpio, 1);
|
||||
tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(tegra_host->power_gpio)) {
|
||||
rc = PTR_ERR(tegra_host->power_gpio);
|
||||
goto err_power_req;
|
||||
}
|
||||
|
||||
clk = clk_get(mmc_dev(host->mmc), NULL);
|
||||
clk = devm_clk_get(mmc_dev(host->mmc), NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(mmc_dev(host->mmc), "clk err\n");
|
||||
rc = PTR_ERR(clk);
|
||||
|
@ -312,10 +302,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
|
|||
|
||||
err_add_host:
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
clk_put(pltfm_host->clk);
|
||||
err_clk_get:
|
||||
if (gpio_is_valid(tegra_host->power_gpio))
|
||||
gpio_free(tegra_host->power_gpio);
|
||||
err_power_req:
|
||||
err_parse_dt:
|
||||
err_alloc_tegra_host:
|
||||
|
@ -323,26 +310,6 @@ err_alloc_tegra_host:
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int sdhci_tegra_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = pltfm_host->priv;
|
||||
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
|
||||
|
||||
sdhci_remove_host(host, dead);
|
||||
|
||||
if (gpio_is_valid(tegra_host->power_gpio))
|
||||
gpio_free(tegra_host->power_gpio);
|
||||
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
clk_put(pltfm_host->clk);
|
||||
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sdhci_tegra_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-tegra",
|
||||
|
@ -350,7 +317,7 @@ static struct platform_driver sdhci_tegra_driver = {
|
|||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
},
|
||||
.probe = sdhci_tegra_probe,
|
||||
.remove = sdhci_tegra_remove,
|
||||
.remove = sdhci_pltfm_unregister,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_tegra_driver);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
|
@ -56,6 +57,7 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
|
|||
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
||||
struct mmc_data *data,
|
||||
struct sdhci_host_next *next);
|
||||
static int sdhci_do_get_cd(struct sdhci_host *host);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int sdhci_runtime_pm_get(struct sdhci_host *host);
|
||||
|
@ -931,7 +933,8 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
|
|||
* If we are sending CMD23, CMD12 never gets sent
|
||||
* on successful completion (so no Auto-CMD12).
|
||||
*/
|
||||
if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12))
|
||||
if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12) &&
|
||||
(cmd->opcode != SD_IO_RW_EXTENDED))
|
||||
mode |= SDHCI_TRNS_AUTO_CMD12;
|
||||
else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
|
||||
mode |= SDHCI_TRNS_AUTO_CMD23;
|
||||
|
@ -1356,7 +1359,8 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
|
||||
sdhci_runtime_pm_get(host);
|
||||
|
||||
present = mmc_gpio_get_cd(host->mmc);
|
||||
/* Firstly check card presence */
|
||||
present = sdhci_do_get_cd(host);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
|
@ -1379,22 +1383,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
|
||||
host->mrq = mrq;
|
||||
|
||||
/*
|
||||
* Firstly check card presence from cd-gpio. The return could
|
||||
* be one of the following possibilities:
|
||||
* negative: cd-gpio is not available
|
||||
* zero: cd-gpio is used, and card is removed
|
||||
* one: cd-gpio is used, and card is present
|
||||
*/
|
||||
if (present < 0) {
|
||||
/* If polling, assume that the card is always present. */
|
||||
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
|
||||
present = 1;
|
||||
else
|
||||
present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
|
||||
SDHCI_CARD_PRESENT;
|
||||
}
|
||||
|
||||
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
|
||||
host->mrq->cmd->error = -ENOMEDIUM;
|
||||
tasklet_schedule(&host->finish_tasklet);
|
||||
|
@ -3164,7 +3152,8 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
/* Auto-CMD23 stuff only works in ADMA or PIO. */
|
||||
if ((host->version >= SDHCI_SPEC_300) &&
|
||||
((host->flags & SDHCI_USE_ADMA) ||
|
||||
!(host->flags & SDHCI_USE_SDMA))) {
|
||||
!(host->flags & SDHCI_USE_SDMA)) &&
|
||||
!(host->quirks2 & SDHCI_QUIRK2_ACMD23_BROKEN)) {
|
||||
host->flags |= SDHCI_AUTO_CMD23;
|
||||
DBG("%s: Auto-CMD23 available\n", mmc_hostname(mmc));
|
||||
} else {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <linux/mmc/sdhci.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
/*
|
||||
* Controller registers
|
||||
|
@ -309,6 +309,207 @@ struct sdhci_adma2_64_desc {
|
|||
*/
|
||||
#define SDHCI_MAX_SEGS 128
|
||||
|
||||
struct sdhci_host_next {
|
||||
unsigned int sg_count;
|
||||
s32 cookie;
|
||||
};
|
||||
|
||||
struct sdhci_host {
|
||||
/* Data set by hardware interface driver */
|
||||
const char *hw_name; /* Hardware bus name */
|
||||
|
||||
unsigned int quirks; /* Deviations from spec. */
|
||||
|
||||
/* Controller doesn't honor resets unless we touch the clock register */
|
||||
#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
|
||||
/* Controller has bad caps bits, but really supports DMA */
|
||||
#define SDHCI_QUIRK_FORCE_DMA (1<<1)
|
||||
/* Controller doesn't like to be reset when there is no card inserted. */
|
||||
#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2)
|
||||
/* Controller doesn't like clearing the power reg before a change */
|
||||
#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3)
|
||||
/* Controller has flaky internal state so reset it on each ios change */
|
||||
#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4)
|
||||
/* Controller has an unusable DMA engine */
|
||||
#define SDHCI_QUIRK_BROKEN_DMA (1<<5)
|
||||
/* Controller has an unusable ADMA engine */
|
||||
#define SDHCI_QUIRK_BROKEN_ADMA (1<<6)
|
||||
/* Controller can only DMA from 32-bit aligned addresses */
|
||||
#define SDHCI_QUIRK_32BIT_DMA_ADDR (1<<7)
|
||||
/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
|
||||
#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<8)
|
||||
/* Controller can only ADMA chunks that are a multiple of 32 bits */
|
||||
#define SDHCI_QUIRK_32BIT_ADMA_SIZE (1<<9)
|
||||
/* Controller needs to be reset after each request to stay stable */
|
||||
#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<10)
|
||||
/* Controller needs voltage and power writes to happen separately */
|
||||
#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<11)
|
||||
/* Controller provides an incorrect timeout value for transfers */
|
||||
#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12)
|
||||
/* Controller has an issue with buffer bits for small transfers */
|
||||
#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13)
|
||||
/* Controller does not provide transfer-complete interrupt when not busy */
|
||||
#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14)
|
||||
/* Controller has unreliable card detection */
|
||||
#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
|
||||
/* Controller reports inverted write-protect state */
|
||||
#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
|
||||
/* Controller does not like fast PIO transfers */
|
||||
#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
|
||||
/* 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 */
|
||||
#define SDHCI_QUIRK_NO_MULTIBLOCK (1<<21)
|
||||
/* Controller can only handle 1-bit data transfers */
|
||||
#define SDHCI_QUIRK_FORCE_1_BIT_DATA (1<<22)
|
||||
/* Controller needs 10ms delay between applying power and clock */
|
||||
#define SDHCI_QUIRK_DELAY_AFTER_POWER (1<<23)
|
||||
/* Controller uses SDCLK instead of TMCLK for data timeouts */
|
||||
#define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24)
|
||||
/* Controller reports wrong base clock capability */
|
||||
#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25)
|
||||
/* Controller cannot support End Attribute in NOP ADMA descriptor */
|
||||
#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26)
|
||||
/* Controller is missing device caps. Use caps provided by host */
|
||||
#define SDHCI_QUIRK_MISSING_CAPS (1<<27)
|
||||
/* Controller uses Auto CMD12 command to stop the transfer */
|
||||
#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28)
|
||||
/* Controller doesn't have HISPD bit field in HI-SPEED SD card */
|
||||
#define SDHCI_QUIRK_NO_HISPD_BIT (1<<29)
|
||||
/* Controller treats ADMA descriptors with length 0000h incorrectly */
|
||||
#define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC (1<<30)
|
||||
/* The read-only detection via SDHCI_PRESENT_STATE register is unstable */
|
||||
#define SDHCI_QUIRK_UNSTABLE_RO_DETECT (1<<31)
|
||||
|
||||
unsigned int quirks2; /* More deviations from spec. */
|
||||
|
||||
#define SDHCI_QUIRK2_HOST_OFF_CARD_ON (1<<0)
|
||||
#define SDHCI_QUIRK2_HOST_NO_CMD23 (1<<1)
|
||||
/* The system physically doesn't support 1.8v, even if the host does */
|
||||
#define SDHCI_QUIRK2_NO_1_8_V (1<<2)
|
||||
#define SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1<<3)
|
||||
#define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1<<4)
|
||||
/* Controller has a non-standard host control register */
|
||||
#define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5)
|
||||
/* Controller does not support HS200 */
|
||||
#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6)
|
||||
/* Controller does not support DDR50 */
|
||||
#define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7)
|
||||
/* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */
|
||||
#define SDHCI_QUIRK2_STOP_WITH_TC (1<<8)
|
||||
/* Controller does not support 64-bit DMA */
|
||||
#define SDHCI_QUIRK2_BROKEN_64_BIT_DMA (1<<9)
|
||||
/* need clear transfer mode register before send cmd */
|
||||
#define SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1<<10)
|
||||
/* Capability register bit-63 indicates HS400 support */
|
||||
#define SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1<<11)
|
||||
/* forced tuned clock */
|
||||
#define SDHCI_QUIRK2_TUNING_WORK_AROUND (1<<12)
|
||||
/* disable the block count for single block transactions */
|
||||
#define SDHCI_QUIRK2_SUPPORT_SINGLE (1<<13)
|
||||
/* Controller broken with using ACMD23 */
|
||||
#define SDHCI_QUIRK2_ACMD23_BROKEN (1<<14)
|
||||
|
||||
int irq; /* Device IRQ */
|
||||
void __iomem *ioaddr; /* Mapped address */
|
||||
|
||||
const struct sdhci_ops *ops; /* Low level hw interface */
|
||||
|
||||
/* Internal data */
|
||||
struct mmc_host *mmc; /* MMC structure */
|
||||
u64 dma_mask; /* custom DMA mask */
|
||||
|
||||
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
|
||||
struct led_classdev led; /* LED control */
|
||||
char led_name[32];
|
||||
#endif
|
||||
|
||||
spinlock_t lock; /* Mutex */
|
||||
|
||||
int flags; /* Host attributes */
|
||||
#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */
|
||||
#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
|
||||
#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
|
||||
#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
|
||||
#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
|
||||
#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */
|
||||
#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
|
||||
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
|
||||
#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
|
||||
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
|
||||
#define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */
|
||||
#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
|
||||
#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
|
||||
#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
|
||||
|
||||
unsigned int version; /* SDHCI spec. version */
|
||||
|
||||
unsigned int max_clk; /* Max possible freq (MHz) */
|
||||
unsigned int timeout_clk; /* Timeout freq (KHz) */
|
||||
unsigned int clk_mul; /* Clock Muliplier value */
|
||||
|
||||
unsigned int clock; /* Current clock (MHz) */
|
||||
u8 pwr; /* Current voltage */
|
||||
|
||||
bool runtime_suspended; /* Host is runtime suspended */
|
||||
bool bus_on; /* Bus power prevents runtime suspend */
|
||||
bool preset_enabled; /* Preset is enabled */
|
||||
|
||||
struct mmc_request *mrq; /* Current request */
|
||||
struct mmc_command *cmd; /* Current command */
|
||||
struct mmc_data *data; /* Current data request */
|
||||
unsigned int data_early:1; /* Data finished before cmd */
|
||||
unsigned int busy_handle:1; /* Handling the order of Busy-end */
|
||||
|
||||
struct sg_mapping_iter sg_miter; /* SG state for PIO */
|
||||
unsigned int blocks; /* remaining PIO blocks */
|
||||
|
||||
int sg_count; /* Mapped sg entries */
|
||||
|
||||
void *adma_table; /* ADMA descriptor table */
|
||||
void *align_buffer; /* Bounce buffer */
|
||||
|
||||
size_t adma_table_sz; /* ADMA descriptor table size */
|
||||
size_t align_buffer_sz; /* Bounce buffer size */
|
||||
|
||||
dma_addr_t adma_addr; /* Mapped ADMA descr. table */
|
||||
dma_addr_t align_addr; /* Mapped bounce buffer */
|
||||
|
||||
unsigned int desc_sz; /* ADMA descriptor size */
|
||||
unsigned int align_sz; /* ADMA alignment */
|
||||
unsigned int align_mask; /* ADMA alignment mask */
|
||||
|
||||
struct tasklet_struct finish_tasklet; /* Tasklet structures */
|
||||
|
||||
struct timer_list timer; /* Timer for timeouts */
|
||||
|
||||
u32 caps; /* Alternative CAPABILITY_0 */
|
||||
u32 caps1; /* Alternative CAPABILITY_1 */
|
||||
|
||||
unsigned int ocr_avail_sdio; /* OCR bit masks */
|
||||
unsigned int ocr_avail_sd;
|
||||
unsigned int ocr_avail_mmc;
|
||||
u32 ocr_mask; /* available voltages */
|
||||
|
||||
unsigned timing; /* Current timing */
|
||||
|
||||
u32 thread_isr;
|
||||
|
||||
/* cached registers */
|
||||
u32 ier;
|
||||
|
||||
wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
|
||||
unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
|
||||
|
||||
unsigned int tuning_count; /* Timer count for re-tuning */
|
||||
unsigned int tuning_mode; /* Re-tuning mode supported by host */
|
||||
#define SDHCI_TUNING_MODE_1 0
|
||||
struct timer_list tuning_timer; /* Timer for tuning */
|
||||
|
||||
struct sdhci_host_next next_data;
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
struct sdhci_ops {
|
||||
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
||||
u32 (*read_l)(struct sdhci_host *host, int reg);
|
||||
|
|
|
@ -875,6 +875,7 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
|
|||
struct mmc_command *cmd = mrq->cmd;
|
||||
u32 opc = cmd->opcode;
|
||||
u32 mask;
|
||||
unsigned long flags;
|
||||
|
||||
switch (opc) {
|
||||
/* response busy check */
|
||||
|
@ -909,10 +910,12 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
|
|||
/* set arg */
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_ARG, cmd->arg);
|
||||
/* set cmd */
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_CMD_SET, opc);
|
||||
|
||||
host->wait_for = MMCIF_WAIT_FOR_CMD;
|
||||
schedule_delayed_work(&host->timeout_work, host->timeout);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
|
||||
|
@ -1171,6 +1174,12 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
|
|||
struct sh_mmcif_host *host = dev_id;
|
||||
struct mmc_request *mrq;
|
||||
bool wait = false;
|
||||
unsigned long flags;
|
||||
int wait_work;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
wait_work = host->wait_for;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
cancel_delayed_work_sync(&host->timeout_work);
|
||||
|
||||
|
@ -1188,7 +1197,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
|
|||
* All handlers return true, if processing continues, and false, if the
|
||||
* request has to be completed - successfully or not
|
||||
*/
|
||||
switch (host->wait_for) {
|
||||
switch (wait_work) {
|
||||
case MMCIF_WAIT_FOR_REQUEST:
|
||||
/* We're too late, the timeout has already kicked in */
|
||||
mutex_unlock(&host->thread_lock);
|
||||
|
@ -1312,15 +1321,15 @@ static void mmcif_timeout_work(struct work_struct *work)
|
|||
/* Don't run after mmc_remove_host() */
|
||||
return;
|
||||
|
||||
dev_err(&host->pd->dev, "Timeout waiting for %u on CMD%u\n",
|
||||
host->wait_for, mrq->cmd->opcode);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
if (host->state == STATE_IDLE) {
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_err(&host->pd->dev, "Timeout waiting for %u on CMD%u\n",
|
||||
host->wait_for, mrq->cmd->opcode);
|
||||
|
||||
host->state = STATE_TIMEOUT;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
|
|
|
@ -293,7 +293,7 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host,
|
|||
struct mmc_data *data)
|
||||
{
|
||||
struct sunxi_idma_des *pdes = (struct sunxi_idma_des *)host->sg_cpu;
|
||||
struct sunxi_idma_des *pdes_pa = (struct sunxi_idma_des *)host->sg_dma;
|
||||
dma_addr_t next_desc = host->sg_dma;
|
||||
int i, max_len = (1 << host->idma_des_size_bits);
|
||||
|
||||
for (i = 0; i < data->sg_len; i++) {
|
||||
|
@ -305,8 +305,9 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host,
|
|||
else
|
||||
pdes[i].buf_size = data->sg[i].length;
|
||||
|
||||
next_desc += sizeof(struct sunxi_idma_des);
|
||||
pdes[i].buf_addr_ptr1 = sg_dma_address(&data->sg[i]);
|
||||
pdes[i].buf_addr_ptr2 = (u32)&pdes_pa[i + 1];
|
||||
pdes[i].buf_addr_ptr2 = (u32)next_desc;
|
||||
}
|
||||
|
||||
pdes[0].config |= SDXC_IDMAC_DES0_FD;
|
||||
|
@ -930,7 +931,9 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
|
|||
return PTR_ERR(host->clk_sample);
|
||||
}
|
||||
|
||||
host->reset = devm_reset_control_get(&pdev->dev, "ahb");
|
||||
host->reset = devm_reset_control_get_optional(&pdev->dev, "ahb");
|
||||
if (PTR_ERR(host->reset) == -EPROBE_DEFER)
|
||||
return PTR_ERR(host->reset);
|
||||
|
||||
ret = clk_prepare_enable(host->clk_ahb);
|
||||
if (ret) {
|
||||
|
@ -1028,7 +1031,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
|
|||
mmc->f_min = 400000;
|
||||
mmc->f_max = 50000000;
|
||||
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_ERASE;
|
||||
MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ;
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret)
|
||||
|
|
|
@ -1073,8 +1073,6 @@ EXPORT_SYMBOL(tmio_mmc_host_alloc);
|
|||
void tmio_mmc_host_free(struct tmio_mmc_host *host)
|
||||
{
|
||||
mmc_free_host(host->mmc);
|
||||
|
||||
host->mmc = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_free);
|
||||
|
||||
|
|
|
@ -744,7 +744,7 @@ static struct wmt_mci_caps wm8505_caps = {
|
|||
.max_blk_size = 2048,
|
||||
};
|
||||
|
||||
static struct of_device_id wmt_mci_dt_ids[] = {
|
||||
static const struct of_device_id wmt_mci_dt_ids[] = {
|
||||
{ .compatible = "wm,wm8505-sdhc", .data = &wm8505_caps },
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
|
|
@ -79,7 +79,7 @@ struct mmc_command {
|
|||
#define mmc_cmd_type(cmd) ((cmd)->flags & MMC_CMD_MASK)
|
||||
|
||||
unsigned int retries; /* max number of retries */
|
||||
unsigned int error; /* command error */
|
||||
int error; /* command error */
|
||||
|
||||
/*
|
||||
* Standard errno values are used for errors, but some have specific
|
||||
|
@ -108,7 +108,7 @@ struct mmc_data {
|
|||
unsigned int timeout_clks; /* data timeout (in clocks) */
|
||||
unsigned int blksz; /* data block size */
|
||||
unsigned int blocks; /* number of blocks */
|
||||
unsigned int error; /* data error */
|
||||
int error; /* data error */
|
||||
unsigned int flags;
|
||||
|
||||
#define MMC_DATA_WRITE (1 << 8)
|
||||
|
|
|
@ -44,6 +44,7 @@ struct mmc_data;
|
|||
* struct dw_mci - MMC controller state shared between all slots
|
||||
* @lock: Spinlock protecting the queue and associated data.
|
||||
* @regs: Pointer to MMIO registers.
|
||||
* @fifo_reg: Pointer to MMIO registers for data FIFO
|
||||
* @sg: Scatterlist entry currently being processed by PIO code, if any.
|
||||
* @sg_miter: PIO mapping scatterlist iterator.
|
||||
* @cur_slot: The slot which is currently using the controller.
|
||||
|
@ -79,7 +80,6 @@ struct mmc_data;
|
|||
* @current_speed: Configured rate of the controller.
|
||||
* @num_slots: Number of slots available.
|
||||
* @verid: Denote Version ID.
|
||||
* @data_offset: Set the offset of DATA register according to VERID.
|
||||
* @dev: Device associated with the MMC controller.
|
||||
* @pdata: Platform data associated with the MMC controller.
|
||||
* @drv_data: Driver specific data for identified variant of the controller
|
||||
|
@ -132,6 +132,7 @@ struct dw_mci {
|
|||
spinlock_t lock;
|
||||
spinlock_t irq_lock;
|
||||
void __iomem *regs;
|
||||
void __iomem *fifo_reg;
|
||||
|
||||
struct scatterlist *sg;
|
||||
struct sg_mapping_iter sg_miter;
|
||||
|
@ -172,7 +173,6 @@ struct dw_mci {
|
|||
u32 num_slots;
|
||||
u32 fifoth_val;
|
||||
u16 verid;
|
||||
u16 data_offset;
|
||||
struct device *dev;
|
||||
struct dw_mci_board *pdata;
|
||||
const struct dw_mci_drv_data *drv_data;
|
||||
|
@ -202,6 +202,8 @@ struct dw_mci {
|
|||
int irq;
|
||||
|
||||
int sdio_id0;
|
||||
|
||||
struct timer_list cmd11_timer;
|
||||
};
|
||||
|
||||
/* DMA ops for Internal/External DMAC interface */
|
||||
|
|
|
@ -79,12 +79,6 @@ struct mmc_ios {
|
|||
};
|
||||
|
||||
struct mmc_host_ops {
|
||||
/*
|
||||
* 'enable' is called when the host is claimed and 'disable' is called
|
||||
* when the host is released. 'enable' and 'disable' are deprecated.
|
||||
*/
|
||||
int (*enable)(struct mmc_host *host);
|
||||
int (*disable)(struct mmc_host *host);
|
||||
/*
|
||||
* It is optional for the host to implement pre_req and post_req in
|
||||
* order to support double buffering of requests (prepare one
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* include/linux/mmc/sdhci-spear.h
|
||||
*
|
||||
* SDHCI declarations specific to ST SPEAr platform
|
||||
*
|
||||
* Copyright (C) 2010 ST Microelectronics
|
||||
* Viresh Kumar <viresh.linux@gmail.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#ifndef LINUX_MMC_SDHCI_SPEAR_H
|
||||
#define LINUX_MMC_SDHCI_SPEAR_H
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
/*
|
||||
* struct sdhci_plat_data: spear sdhci platform data structure
|
||||
*
|
||||
* card_int_gpio: gpio pin used for card detection
|
||||
*/
|
||||
struct sdhci_plat_data {
|
||||
int card_int_gpio;
|
||||
};
|
||||
|
||||
/* This function is used to set platform_data field of pdev->dev */
|
||||
static inline void
|
||||
sdhci_set_plat_data(struct platform_device *pdev, struct sdhci_plat_data *data)
|
||||
{
|
||||
pdev->dev.platform_data = data;
|
||||
}
|
||||
|
||||
#endif /* LINUX_MMC_SDHCI_SPEAR_H */
|
|
@ -1,218 +0,0 @@
|
|||
/*
|
||||
* linux/include/linux/mmc/sdhci.h - Secure Digital Host Controller Interface
|
||||
*
|
||||
* Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*/
|
||||
#ifndef LINUX_MMC_SDHCI_H
|
||||
#define LINUX_MMC_SDHCI_H
|
||||
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
struct sdhci_host_next {
|
||||
unsigned int sg_count;
|
||||
s32 cookie;
|
||||
};
|
||||
|
||||
struct sdhci_host {
|
||||
/* Data set by hardware interface driver */
|
||||
const char *hw_name; /* Hardware bus name */
|
||||
|
||||
unsigned int quirks; /* Deviations from spec. */
|
||||
|
||||
/* Controller doesn't honor resets unless we touch the clock register */
|
||||
#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
|
||||
/* Controller has bad caps bits, but really supports DMA */
|
||||
#define SDHCI_QUIRK_FORCE_DMA (1<<1)
|
||||
/* Controller doesn't like to be reset when there is no card inserted. */
|
||||
#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2)
|
||||
/* Controller doesn't like clearing the power reg before a change */
|
||||
#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3)
|
||||
/* Controller has flaky internal state so reset it on each ios change */
|
||||
#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4)
|
||||
/* Controller has an unusable DMA engine */
|
||||
#define SDHCI_QUIRK_BROKEN_DMA (1<<5)
|
||||
/* Controller has an unusable ADMA engine */
|
||||
#define SDHCI_QUIRK_BROKEN_ADMA (1<<6)
|
||||
/* Controller can only DMA from 32-bit aligned addresses */
|
||||
#define SDHCI_QUIRK_32BIT_DMA_ADDR (1<<7)
|
||||
/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
|
||||
#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<8)
|
||||
/* Controller can only ADMA chunks that are a multiple of 32 bits */
|
||||
#define SDHCI_QUIRK_32BIT_ADMA_SIZE (1<<9)
|
||||
/* Controller needs to be reset after each request to stay stable */
|
||||
#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<10)
|
||||
/* Controller needs voltage and power writes to happen separately */
|
||||
#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<11)
|
||||
/* Controller provides an incorrect timeout value for transfers */
|
||||
#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12)
|
||||
/* Controller has an issue with buffer bits for small transfers */
|
||||
#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13)
|
||||
/* Controller does not provide transfer-complete interrupt when not busy */
|
||||
#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14)
|
||||
/* Controller has unreliable card detection */
|
||||
#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
|
||||
/* Controller reports inverted write-protect state */
|
||||
#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
|
||||
/* Controller does not like fast PIO transfers */
|
||||
#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
|
||||
/* 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 */
|
||||
#define SDHCI_QUIRK_NO_MULTIBLOCK (1<<21)
|
||||
/* Controller can only handle 1-bit data transfers */
|
||||
#define SDHCI_QUIRK_FORCE_1_BIT_DATA (1<<22)
|
||||
/* Controller needs 10ms delay between applying power and clock */
|
||||
#define SDHCI_QUIRK_DELAY_AFTER_POWER (1<<23)
|
||||
/* Controller uses SDCLK instead of TMCLK for data timeouts */
|
||||
#define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24)
|
||||
/* Controller reports wrong base clock capability */
|
||||
#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25)
|
||||
/* Controller cannot support End Attribute in NOP ADMA descriptor */
|
||||
#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26)
|
||||
/* Controller is missing device caps. Use caps provided by host */
|
||||
#define SDHCI_QUIRK_MISSING_CAPS (1<<27)
|
||||
/* Controller uses Auto CMD12 command to stop the transfer */
|
||||
#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28)
|
||||
/* Controller doesn't have HISPD bit field in HI-SPEED SD card */
|
||||
#define SDHCI_QUIRK_NO_HISPD_BIT (1<<29)
|
||||
/* Controller treats ADMA descriptors with length 0000h incorrectly */
|
||||
#define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC (1<<30)
|
||||
/* The read-only detection via SDHCI_PRESENT_STATE register is unstable */
|
||||
#define SDHCI_QUIRK_UNSTABLE_RO_DETECT (1<<31)
|
||||
|
||||
unsigned int quirks2; /* More deviations from spec. */
|
||||
|
||||
#define SDHCI_QUIRK2_HOST_OFF_CARD_ON (1<<0)
|
||||
#define SDHCI_QUIRK2_HOST_NO_CMD23 (1<<1)
|
||||
/* The system physically doesn't support 1.8v, even if the host does */
|
||||
#define SDHCI_QUIRK2_NO_1_8_V (1<<2)
|
||||
#define SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1<<3)
|
||||
#define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1<<4)
|
||||
/* Controller has a non-standard host control register */
|
||||
#define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5)
|
||||
/* Controller does not support HS200 */
|
||||
#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6)
|
||||
/* Controller does not support DDR50 */
|
||||
#define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7)
|
||||
/* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */
|
||||
#define SDHCI_QUIRK2_STOP_WITH_TC (1<<8)
|
||||
/* Controller does not support 64-bit DMA */
|
||||
#define SDHCI_QUIRK2_BROKEN_64_BIT_DMA (1<<9)
|
||||
/* need clear transfer mode register before send cmd */
|
||||
#define SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1<<10)
|
||||
/* Capability register bit-63 indicates HS400 support */
|
||||
#define SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1<<11)
|
||||
/* forced tuned clock */
|
||||
#define SDHCI_QUIRK2_TUNING_WORK_AROUND (1<<12)
|
||||
/* disable the block count for single block transactions */
|
||||
#define SDHCI_QUIRK2_SUPPORT_SINGLE (1<<13)
|
||||
|
||||
int irq; /* Device IRQ */
|
||||
void __iomem *ioaddr; /* Mapped address */
|
||||
|
||||
const struct sdhci_ops *ops; /* Low level hw interface */
|
||||
|
||||
/* Internal data */
|
||||
struct mmc_host *mmc; /* MMC structure */
|
||||
u64 dma_mask; /* custom DMA mask */
|
||||
|
||||
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
|
||||
struct led_classdev led; /* LED control */
|
||||
char led_name[32];
|
||||
#endif
|
||||
|
||||
spinlock_t lock; /* Mutex */
|
||||
|
||||
int flags; /* Host attributes */
|
||||
#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */
|
||||
#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
|
||||
#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
|
||||
#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
|
||||
#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
|
||||
#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */
|
||||
#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
|
||||
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
|
||||
#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
|
||||
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
|
||||
#define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */
|
||||
#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
|
||||
#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
|
||||
#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
|
||||
|
||||
unsigned int version; /* SDHCI spec. version */
|
||||
|
||||
unsigned int max_clk; /* Max possible freq (MHz) */
|
||||
unsigned int timeout_clk; /* Timeout freq (KHz) */
|
||||
unsigned int clk_mul; /* Clock Muliplier value */
|
||||
|
||||
unsigned int clock; /* Current clock (MHz) */
|
||||
u8 pwr; /* Current voltage */
|
||||
|
||||
bool runtime_suspended; /* Host is runtime suspended */
|
||||
bool bus_on; /* Bus power prevents runtime suspend */
|
||||
bool preset_enabled; /* Preset is enabled */
|
||||
|
||||
struct mmc_request *mrq; /* Current request */
|
||||
struct mmc_command *cmd; /* Current command */
|
||||
struct mmc_data *data; /* Current data request */
|
||||
unsigned int data_early:1; /* Data finished before cmd */
|
||||
unsigned int busy_handle:1; /* Handling the order of Busy-end */
|
||||
|
||||
struct sg_mapping_iter sg_miter; /* SG state for PIO */
|
||||
unsigned int blocks; /* remaining PIO blocks */
|
||||
|
||||
int sg_count; /* Mapped sg entries */
|
||||
|
||||
void *adma_table; /* ADMA descriptor table */
|
||||
void *align_buffer; /* Bounce buffer */
|
||||
|
||||
size_t adma_table_sz; /* ADMA descriptor table size */
|
||||
size_t align_buffer_sz; /* Bounce buffer size */
|
||||
|
||||
dma_addr_t adma_addr; /* Mapped ADMA descr. table */
|
||||
dma_addr_t align_addr; /* Mapped bounce buffer */
|
||||
|
||||
unsigned int desc_sz; /* ADMA descriptor size */
|
||||
unsigned int align_sz; /* ADMA alignment */
|
||||
unsigned int align_mask; /* ADMA alignment mask */
|
||||
|
||||
struct tasklet_struct finish_tasklet; /* Tasklet structures */
|
||||
|
||||
struct timer_list timer; /* Timer for timeouts */
|
||||
|
||||
u32 caps; /* Alternative CAPABILITY_0 */
|
||||
u32 caps1; /* Alternative CAPABILITY_1 */
|
||||
|
||||
unsigned int ocr_avail_sdio; /* OCR bit masks */
|
||||
unsigned int ocr_avail_sd;
|
||||
unsigned int ocr_avail_mmc;
|
||||
u32 ocr_mask; /* available voltages */
|
||||
|
||||
unsigned timing; /* Current timing */
|
||||
|
||||
u32 thread_isr;
|
||||
|
||||
/* cached registers */
|
||||
u32 ier;
|
||||
|
||||
wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
|
||||
unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
|
||||
|
||||
unsigned int tuning_count; /* Timer count for re-tuning */
|
||||
unsigned int tuning_mode; /* Re-tuning mode supported by host */
|
||||
#define SDHCI_TUNING_MODE_1 0
|
||||
struct timer_list tuning_timer; /* Timer for tuning */
|
||||
|
||||
struct sdhci_host_next next_data;
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
#endif /* LINUX_MMC_SDHCI_H */
|
|
@ -55,9 +55,6 @@ struct omap_hsmmc_platform_data {
|
|||
u32 caps; /* Used for the MMC driver on 2430 and later */
|
||||
u32 pm_caps; /* PM capabilities of the mmc */
|
||||
|
||||
/* switch pin can be for card detect (default) or card cover */
|
||||
unsigned cover:1;
|
||||
|
||||
/* use the internal clock */
|
||||
unsigned internal_clock:1;
|
||||
|
||||
|
@ -73,7 +70,8 @@ struct omap_hsmmc_platform_data {
|
|||
#define HSMMC_HAS_HSPE_SUPPORT (1 << 2)
|
||||
unsigned features;
|
||||
|
||||
int switch_pin; /* gpio (card detect) */
|
||||
int gpio_cd; /* gpio (card detect) */
|
||||
int gpio_cod; /* gpio (cover detect) */
|
||||
int gpio_wp; /* gpio (write protect) */
|
||||
|
||||
int (*set_power)(struct device *dev, int power_on, int vdd);
|
||||
|
|
Loading…
Reference in New Issue